본문 바로가기
놀기/기초 공부

SPI 통신 정리

by Hi~ 2021. 7. 12.

※ 필요에 의해 공부하며 정리한 것이라 틀린 부분이 있을 수 있습니다.

개발 환경마다 SPI 예제가 있고 구글에 많은 자료가 있어 SPI에 대해 몰라도 사용하는 데는 무리가 없다. 그만큼 좋은 세상이 되었다. 그래도 알아야 사용하는데 편하고 응용도 가능하니 공부하자.

 

다음의 게시물을 기반으로 공부하면서 정리를 해보자.

  • https://www.digikey.kr/ko/articles/why-how-to-use-serial-peripheral-interface-simplify-connections-between-multiple-devices
  • https://en.wikipedia.org/wiki/Serial_Peripheral_Interface
  • https://ko.wikipedia.org/wiki/직렬_주변기기_인터페이스_버스

 

SPI 란

SPI는 1985년경 Motorola(현재는 NXP Semiconductors의 일부)에서 개발했다. SPI는 단거리 장치 간 통신을 위한 동기식 직렬 인터페이스다. 그 후 특히 마이크로 프로세서 및 마이크로 컨트롤러 분야의 많은 반도체 제조업체에서 채용하는 사실상 표준이 되었다.

Master / Slave 형태로 구성되며 Slave는 다수일 수 있다. Master는 그중 하나의 Slave를 선택하여 통신하게 된다. 

출처 : https://ko.wikipedia.org/wiki/직렬_주변기기_인터페이스_버스

 

SPI의 첫 번째 장점은 하드웨어 주소 지정된 인터페이스가 단순하기 때문에 전송되는 비트 수에 대한 완전한 유연성이 제공된다는 점이다. 마스터가 하나인 마스터-슬레이브 모델을 사용하며 최대 50 MHz의 Clock (Chip들의 Datasheet를 보면 50 MHz 이상으로 나옴)에서 작동하는 전이중 통신을 사용하여 여러 슬레이브 장치를 처리할 수 있습니다. 표준 프로토콜을 사용하지 않으며 데이터 패킷만 전송하므로 긴 데이터 스트림의 전송에 적합하다. 표준 프로토콜을 사용하지 않는다는 것은 개발 관점에서 상당한 장점이다. 

 

Interface

SPI 버스는 다음과 같이 4개의 Line으로 구성된다.

  • SCLK : 직렬 클럭 (마스터로부터의 출력)
  • MOSI (Master Out Slave In); SIMO (Slave In Master Out) : 마스터 출력, 슬레이브 입력 (마스터로부터의 출력)
  • MISO (Master In Slave Out); SOMI (Slave Out Master In) : 마스터 입력, 슬레이브 출력 (슬레이브로부터의 출력)
  • SS : 슬레이브 셀렉트 (active low, 마스터로부터의 출력)

또한 아래와 같은 이름으로 사용되기도 한다. 

  • SCLK - SCK; CLK
  • MOSI - SDI; DI, DIN, SI
  • MISO - SDO; DO, DOUT, SO
  • SS - nCS, CS, CSB, CSN, nSS, STE

 

다중 슬레이브 연결의 경우, 아래와 같이 두 가지 방법이 있다.

 

1) 직접 연결 (Direct Connection)

출처 : https://en.wikipedia.org/wiki/Serial_Peripheral_Interface

 

2) 데이지 체인 연결 (Daisy Chained Connection)

출처 : https://en.wikipedia.org/wiki/Serial_Peripheral_Interface

 

 

직접 연결은 각 슬레이브 장치에 대해 칩 선택 회선을 사용한다. 대부분의 마이크로 컨트롤러에는 3~4개의 칩 선택 회선이 있고 이로 인해 최대 슬레이브 수가 칩 선택 회선의 수로 제한됩니다. 대개 이 경우 이 점은 문제가 되지 않지만, 설계에 따라 버스에 더 많은 장치가 필요한 경우 데이지 체인 방식을 사용하여 구성할 수 있다. 데이지 체인을 사용하면 여러 슬레이브 장치에 공통 칩 선택이 사용되며 데이터는 공통 데이터 회선에서 스트리밍 된다. SPI 슬레이브 장치의 모델을 시프트 레지스터로 사용하면 슬레이브의 데이터가 직렬 멀티플렉싱 된 스트림으로 전파된다.

 

SS는 active low일 때 선택되는데 falling edge여야 하는 장치도 있다고 하니 사용하는 장치가 어떤 방식인지 확인할 필요가 있어 보인다.

 

Clock Polarity(CPOL) / Clock Phase(CPHA)

SPI의 4개의 모드를 지원한다. Master는 Slave의 모두에 맞게 설정하여 Slave로와 통신을 해야 한다. 어느 시점에 데이터를 읽느냐에 대한 것인데 개발자라면 이해하는데 무리는 없을 것이다.

출처 : https://en.wikipedia.org/wiki/Serial_Peripheral_Interface

 

CPOL, CPHA의 동작에 따라 아래의 4가지 모드가 된다.

 

장단점 정리

좋다고 해서 장점만 있는 것은 아니다. 

 

1) 장점

  • 완전한 전이중 통신
  • 전송되는 비트에 대한 완전한 프로토콜 유연성
  • 전송기가 필요하지 않음
  • 매우 단순한 하드웨어 인터페이스 처리
  • IC 패키지에 4개의 핀만 사용하며 이는 병렬 인터페이스에 비해 수가 적은 것이다.

2) 단점

  • 하드웨어 슬레이브 인식이 없음
  • 슬레이브에 의한 하드웨어 흐름 제어가 없음
  • 오류 검사 프로토콜이 정의되어 있지 않음
  • 일반적으로 노이즈 스파이크에 영향을 받는 경향이 있음 (통신 문제를 일으킬 수 있음)
  • RS-232, RS-485, CAN 버스보다 비교적 더 짧은 거리에서 동작
  • 하나의 마스터 장치만 지원

 

예제

https://www.arduino.cc/en/Tutorial/LibraryExamples/BarometricPressureSensor 아두이노 샘플을 예제 삼아 보자. 압력 센서를 SPI로 연결하여 제어하는 예시다. 소스 코드만 봐서는 알 수 없지만 SCP1000의 Datasheet를 보면 소스코드가 눈에 들어온다.

 

Absolute pressure sensor SCP1000의 Datasheet를 보면 아래와 같이 나와있다.

 

SCK가 low인 상태에서 SS가 low로 내려가고 rising edge에서 data를 읽으므로 CPOL=0, CPHA=0 이다. SPI.setDataMode() 호출을 하지 않은 것으로 보아 mode 0가 기본 값인 것 같다.

 

void setup() {

  Serial.begin(9600);

  // start the SPI library:
  SPI.begin();

  // initalize the  data ready and chip select pins:
  pinMode(dataReadyPin, INPUT);
  pinMode(chipSelectPin, OUTPUT);

  //Configure SCP1000 for low noise configuration:
  writeRegister(0x02, 0x2D);
  writeRegister(0x01, 0x03);
  writeRegister(0x03, 0x02);

  // give the sensor time to set up:
  delay(100);
}

 

GPIO pin 설정을 하고 아래와 SCP1000의 register 설정으로 setup() 함수 구성은 완료된다.

 

loop()에서는 SCP1000의 register를 읽어 온도 및 압력 값을 출력한다. 위의 Table을 보면 width가 8인 것도 있고 16인 것도 있다. 어느 데이터를 읽을 것이냐에 따라 1byte를 읽고 2byte를 읽고 이런 식으로 코드를 작성하면 된다.

void loop() {

  //Select High Resolution Mode
  writeRegister(0x03, 0x0A);

  // don't do anything until the data ready pin is high:
  if (digitalRead(dataReadyPin) == HIGH) {

    //Read the temperature data
    int tempData = readRegister(0x21, 2);

    // convert the temperature to celsius and display it:
    float realTemp = (float)tempData / 20.0;

    Serial.print("Temp[C]=");
    Serial.print(realTemp);

    //Read the pressure data highest 3 bits:
    byte  pressure_data_high = readRegister(0x1F, 1);
    pressure_data_high &= 0b00000111; //you only needs bits 2 to 0

    //Read the pressure data lower 16 bits:
    unsigned int pressure_data_low = readRegister(0x20, 2);

    //combine the two parts into one 19-bit number:
    long pressure = ((pressure_data_high << 16) | pressure_data_low) / 4;

    // display the temperature:
    Serial.println("\tPressure [Pa]=" + String(pressure));
  }
}

 

readRegister(), writeRegister() 함수는 소스코드와 Datasheet를 보면 큰 무리 없이 이해될 것으로 보인다. 보드가 있어 직접 해보면 좋겠지만, 없으니 Pass ~

 

참조 : 아두이노 SPI library 페이지

https://www.arduino.cc/en/Reference/SPI

 

Arduino - SPI

This library allows you to communicate with SPI devices, with the Arduino as the master device. To use this library #include A Brief Introduction to the Serial Peripheral Interface (SPI) Serial Peripheral Interface (SPI) is a synchronous serial data protoc

www.arduino.cc

댓글