아트메가/이론

ATMEGA128 SPI통신 알아보기 1편

원원 2023. 2. 5. 13:16

안녕하세요. 오늘은 ATMEGA128로 SPI통신에대해 알아보겠습니다. 이 글에서는 SPI통신 WRITE만 알아보고 실제로  8*8 dot matrix(MAX7219사용)을 제어해보겠습니다

기본적인 SPI개념은 아래 글 참조바랍니다

https://wowon.tistory.com/231

 

SPI통신 아두이노와 오실로스코프로 확인해보기 1편

안녕하세요. 오늘은 SPI통신에 대해 알아보겠습니다. SPI란 Serial Peripheral Interface의 약자입니다. 특징으로는 데이터를 동시에 보내고 받을 수 있는 전이중 모드를 지원하는 동기식 직렬 통신 인터

wowon.tistory.com

https://wowon.tistory.com/276

 

SPI통신 아두이노와 오실로스코프로 확인해보기 2편

안녕하세요. 오늘은 SPI통신을 알아보는 2편입니다. 1편: https://wowon.tistory.com/231 SPI통신은 SS핀을 LOW로 설정한다음에 MOSI핀과 CLOCK핀을 흔들면 됩니다. CLOCK은 8번 흔들리고 MOSI핀도 8번 흔들립니다

wowon.tistory.com

 

*사용 핀

ATMEGA128에서 SS,SCK,MOSI,MISO 핀은 아래의 핀을 사용합니다

 

*SPI관련 레지스터

SPCR 레지스터입니다.  이 레지스터에서는 SPI통신을 하기위한 다양한 설정을 합니다.

※ SPCR 레지스터

7bit SPIE : SPI 인터럽트 Enable. Enable일때 인터럽트가 뜨려면 SPIF bit 와 SREG 비트가 set되어야함
6bit SPE : SPE Enable
5bit DORD : [1]은 LSB, [0]은 MSB
4bit MSTR  : [1]은 Master mode, [0]은 Slave mode
3bit CPOL : Clock parity
2bit CPHA : Clock phase
1bit SPR1 : Clock Rate Speed1
0bit SPR0 : Clock Rate Speed1

SPI통신에는 interrupt함수가 1개입니다.(SPI_STC_vect) Master mode일때 인터럽트를 사용하면, 인터럽트는 데이터가 송신완료되고나서 발생하고 Slave mode일때 인터럽트를 사용하면 데이터를 수신되고나서 인터럽트가 발생합니다. 아래에서 Master mode일때 인터럽트방식과 polling방식 모두 테스트해보겠습니다.

 

SPSR 레지스터

7bit SPIF  : SPI Interrupt Flag. 데이터 전송이 완료되면 set 됩니다.
6bit WCOL : Write COLlsion flag. 데이터 전송중에 SPDR가 기록되면 set됩니다. 
0bit SPI2X :  Double SPI Speed Bit. 

 

 SPDR 레지스터

Read/WRite할때 사용하는 레지스터입니다.

 

이제 실제로 코드를 작성해서 SPI통신을 해보겠습니다.
Master모드, polling방식, CPOL 0, CPHA 0, Clock Speed 4Mhz, MSB
SPSR레지스터 설정
7bit SPIE : 0
6bit SPE : 1
5bit DORD : 0
4bit MSTR  : 1
3bit CPOL : 0
2bit CPHA : 0
1bit SPR1 : 0
0bit SPR0 : 0

사실상 Master모드와 SPI를 사용한다는 설정말고는 기본값입니다

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <string.h>


 void SPI_WRITE(unsigned char data)
 {
     SPDR = data;
     while(!(SPSR&(1<<SPIF)));
 }
  
 void SPI0_init()
 {
     /* 
     PB0 : SS
     PB1 : SCK
     PB2 : MOSI
     PB3 : MISO
     */
     DDRB |= 0x07; // SS, SCK, MOSI OUTPUT
     DDRB &= ~0x08; // MISO INPUT
     SPCR = (1<<SPE)|(1<<MSTR);
 }
/* SPI */ 


int main(void)
{
	SPI0_init();
	
	while(1)
	{
		PORTB &= ~0x01;
		SPI_WRITE(0xAA);
		PORTB |= 0x01;
	}
}

코드에서 while(!(SPSR&(1<<SPIF)));부분이 있습니다. while(!(SPSR&(0x08));과 같은거고 SPSR의 7번째 bit가 0이면 대기하고있고 1이면 통과입니다. SPSR의 7번째 비트는 SPIF입니다. SPIF는 데이터전송이 완료하고나서 1이되므로 SRDR = data에서 데이터를 보내고 기다리고있다가 SPIF가 1이되면 다음코드로 진행입니다.

그럼 위의 코드에서 SRDR = data를 하고 SPIF비트가 set될때까지 기다리지 않는다면 나오는 파형을 보겠습니다.

 void SPI_WRITE(unsigned char data)
 {
     SPDR = data;
     //while(!(SPSR&(1<<SPIF)));
 }

SS라인이 LOW일때 클럭이 8번 흔들려야하는데, SPIF를 기다리지않고 계속 SRDR에 데이터를 보내니까 데이터를 인식하지 못합니다.

 

그럼 위의 코드에서 SRDR = data 뒤에 delay를 1ms 줘보겠습니다

void SPI_WRITE(unsigned char data)
 {
     SPDR = data;
     //while(!(SPSR&(1<<SPIF)));
     _delay_ms(1);
 }

데이터를 잘 인식합니다

 

위의 방식은 polling 방식이였고 이제 interrupt 방식을 해보겠습니다.
Master모드, interrupt방식, CPOL 0, CPHA 0, Clock Speed 4Mhz, MSB
SPSR레지스터 설정
7bit SPIE : 1
6bit SPE : 1
5bit DORD : 0
4bit MSTR  : 1
3bit CPOL : 0
2bit CPHA : 0
1bit SPR1 : 0
0bit SPR0 : 0

그리고 interrupt를 사용하므로 전역인터럽트르 set 해줘야합니다 (sei();)

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <string.h>

unsigned char spi_interrupt = 0;
/* SPI */
ISR(SPI_STC_vect)
{
	spi_interrupt = 1;
}

 void SPI_WRITE(unsigned char data)
 {
     SPDR = data;
     while(!spi_interrupt);
     spi_interrupt = 0;
 }
  
 void SPI0_init()
 {
     /* 
     PB0 : SS
     PB1 : SCK
     PB2 : MOSI
     PB3 : MISO
     */
     DDRB |= 0x07; // SS, SCK, MOSI OUTPUT
     DDRB &= ~0x08; // MISO INPUT
     SPCR = (1<<SPIE)|(1<<SPE)|(1<<MSTR);
 }
/* SPI */ 


int main(void)
{
	sei();
	SPI0_init();
	
	while(1)
	{
		PORTB &= ~0x01;
		SPI_WRITE(0xAA);
		PORTB |= 0x01;
	}
}

 

SPI통신을 사용해서 실제 모듈과 통신을 해보겠습니다. 사용할 모듈은 8*8 dot matrix(MAX7219) 입니다. 이 모듈은 SPI READ할 필요는 없고 SPI WRITE만 해서 dot matrix를 키면 됩니다. polling 방식을 사용할겁니다.
8*8 dot matrix(MAX7219)랑 통신할때 필요한 기본적인 설정방법은 https://wowon.tistory.com/276 사이트와 데이터시트 참조 바랍니다.SPI 통신에 관한 글이므로 MAX7219에 대해 자세히는 설명하지않겠습니다.
기본적으로 MAX7219랑 통신하려면 먼저 MAX7219 세팅을 해주고나서,dot matrix를 켜주면 됩니다.

#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <string.h>

/* SPI */
 void SPI_WRITE(unsigned char data)
 {
     SPDR = data;
     while(!(SPSR&(1<<SPIF)));
 }
 
 void SPI0_init()
 {
     /* 
     PB0 : SS
     PB1 : SCK
     PB2 : MOSI
     PB3 : MISO
     */
     DDRB |= 0x07; // SS, SCK, MOSI OUTPUT
     DDRB &= ~0x08; // MISO INPUT
     SPCR = (1<<SPE)|(1<<MSTR);
 }
/* SPI */ 

void MAX7219(unsigned char address, unsigned char data)
{
	PORTB &= ~0x01;
	SPI_WRITE(address);
	SPI_WRITE(data);
	PORTB |= 0x01;
}

int main(void)
{
	SPI0_init();
	DDRA= 0xff;
	int line = 0;
	int i;
	unsigned char display_dotmatrix[2][8] = {
		{0xc3, 0xc3, 0xc3, 0xff, 0xff, 0xc3, 0xc3, 0xc3},
		{0xff, 0xff, 0x18, 0x18, 0x18, 0x18, 0xff, 0xff},
	};
	
	MAX7219(0x09,0x00);
	MAX7219(0x0A,0x01);
	MAX7219(0x0b,0x07);
	MAX7219(0x0c,0x01);
	MAX7219(0x0f,0x00);
	
	while(1)
	{
		PORTB &= ~0x01;
		for (i = 0; i < 8; i++) {
			MAX7219(i+1, display_dotmatrix[line][i]);
		}	
		PORTB |= 0x01;
		_delay_ms(1000);
		line = !line;
	}
}

 

다음에는 SPI READ를 해보겠습니다