11강. SPI 통신
본 글은 Atmega의 SPI 통신에 대해 간략하게 알아본다.
자세한 내용은 데이터시트에서 SPI 항목을 보면 나와있어서, 여기서는 최소한의 지식만 알아볼 예정이다.
- SPI 통신?
SPI는 Serial Peripheral Interface의 줄임말이다. 풀어보면, 주변 장치와 직렬 통신으로 연결한다는 말 정도가 된다.
SPI는 마스터와 슬레이브로 이루어져있는데, 일반적으로 마스터는 MCU, 슬레이브는 주변 장치(Peripheral)이 되는게 일반적이다.
마스터가 하는 일은 다음과 같다.
1. 슬레이브 선택 (SS 핀을 high로 올린다.)
2. 통신주기 생성(SCLK 핀에서 생성된다.)
이때 주의할 점은, 마스터는 데이터를 보낼수도 있고, 받을수도 있다는 점이다. 마스터라고 해서 데이터를 보내기만 하는건 아니다.
SPI는 다음의 구조로 이루어져 있다.
SCLK : Serial Clock. 0/1이 반복되는 신호를 빠르게 내놓는다. 이 신호마다 데이터 1비트를 받거나 보내게 된다. 밑의 그림을 참고하면 이해가 쉽다.
MOSI : Master Output Slave Input. 마스터->슬레이브 방향으로 데이터를 보내는 선이다.
MISO : Master Input Slave Output. 슬레이브->마스터 방향으로 데이터를 보내는 선이다.
SS : Slave Select. 마스터 입장에서는 Slave Select, 슬레이브 입장에서는 Chip Select라고 한다. SPI 통신은 1:1통신도 되고 1:n 통신도 되는데, SS는 슬레이브를 선택하는 선이 된다.
일반적으로는, 마스터에서 SS를 High로 올려야 통신이 시작된다.
- SPI 특징
SPI의 중요한 특징으로는 다음과 같다.
> 양방향 통신(Full Duplex) : 전화하는 것처럼 말하면서 들을 수 있다. 즉, 마스터가 데이터를 보내면서 동시에 받을수도 있다.
> 매우 빠른 속도 : UART, 뒤에서 볼 I2C는 클럭마다 데이터를 보내지 않지만, SPI는 클럭마다 데이터를 보낸다. 따라서, UART, SPI, I2C 중에 통신 속도가 가장 빠르다고 알려져있다.
이 때, 통신 속도는 슬레이브의 SPI 통신 관련 회로에 의해서 결정 된다.
> 슬레이브를 직접 선택해야 한다 : SPI 통신은 슬레이브를 선택하는 알고리즘이 없다. 따라서, 회로에서 직접 SS 신호를 다른 곳으로 보내서 슬레이브를 선택해야 한다.
- SPI 핀 (Atmega128a 기준)
- SPI 예제 코드
사실 Atmega 데이터시트에 다 나와있다.
Atemga128a 데이터시트 주소 : http://ww1.microchip.com/downloads/en/devicedoc/atmel-8151-8-bit-avr-atmega128a_datasheet.pdf
// Atmega 데이터시트에 있는 예제 코드입니다.
// 이 코드는 SPI 통신이 Master가 데이터를 보내고, Slave가 데이터를 받는 통신 예제이다.
// 이 때, Master, slave는 전부 MCU이다.
#define F_CPU 16000000
#include <avr/io.h>
#include <util/delay.h>
/* For master MCU */
void SPI_MasterInit(void)
{
/* Set MOSI and SCK output, all others input */
/* For Master, set SS pin to high to start SPI communication!! */
DDRB = (1<<PINB2)|(1<<PINB1)|(1<<PINB0);
/* Enable SPI, Master, set clock rate fck/16 */
SPCR = (1<<SPE)|(1<<MSTR)|(1<<SPR0);
}
void SPI_MasterTransmit(char cData)
{
/* Start transmission */
SPDR = cData;
/* Wait for transmission complete */
while(!(SPSR & (1<<SPIF)));
}
int main(void)
{
SPI_MasterInit();
while (1)
{
SPI_MasterTransmit('M');
}
}
// -----------------------------------------------------
/* For Slave MCU */
void SPI_SlaveInit(void)
{
/* Set MISO output, all others input */
DDRB = (1<<PB3);
/* Enable SPI */
SPCR = (1<<SPE);
}
char SPI_SlaveReceive(void)
{
/* Wait for reception complete */
while(!(SPSR & (1<<SPIF)));
/* Return Data Register */
return SPDR;
}
int main(void)
{
char data;
SPI_SlaveInit();
while(1)
{
data = SPI_SlaveReceive();
// 여기에 UART으로 data 변수를 확인하거나,
// 혹은 받은 data에 따라 if문으로 LED를 켜는 등으로 확인할 수 있다.
}
}
- SPI 레지스터
데이터시트를 참고하면 된다. 사실.. 일반적으로 SPI 통신은 예제 코드 정도 수준으로만 사용하기 때문에, 레지스터 하나하나를 굳이 알아보고 깊게 익힐 필요까지는 없을수도 있다. 관심있으면 데이터시트를 참고하면 된다..