Embedded System/AVR

[AVR] SPI 통신

임베지수 2017. 4. 2. 14:44

  SPI(Serial Peripheral Interface)는 직렬 주변기기 인터페이스다. CPU와 다수의 CPU들 또는 CPU와 다수의 주변장치들 사이에 고속으로 데이터를 동기 전송하는데 사용되는 직렬 통신 방법 중에 하나이다. 장치들은 마스터 슬레이브 모드로 통신하며 여러 슬레이브 장치들은 개별 슬레이브 셀렉트(SS or Chip Select(CS)) 라인과 함께 동작할 수 있다.

 

[그림 1] 싱글 마스터와 싱글 슬레이브

 

SPI 통신은 4가지 논리신호를 지정한다.

 

  ◎ SCLK(Serial Clock) : 직렬클럭

  ◎ MOSI(Mater Output Slave Input) : 마스터 출력, 슬레이브 입력

  ◎ MISO(Mater Input Slave Output) : 마스터 입력, 슬레이브 출력

  ◎ SS(Slave Select) : 슬레이브 선택

SPI 통신에 사용되는 핀의 이름은 제조사에 따라 다르게 명명되기도 한다.

  ◎ SCLK → CLK

  ◎ MOSI → SDI, DI, DIN, SI

  ◎ MISO → SDO, DO, DOUT, SO

  ◎ SS → CS, CSB, CSN, STE

  SPI 통신 모듈에는 시프트 레지스터가 있다. 시프트 레지스터는 레지스터 내의 데이터를 1비트씩 이동하면서 1비트씩 출력하는 장치이다. SPI 모듈 내의 시프트 레지스터 데이터 크기는 장치에 따라 다르다. ATMEGA128의 SPI 통신 모듈은 8비트 시프트 레지스터로 구성되어 있다.

 

[그림 2] SPI 통신의 마스터/슬레이브 연결

 

 

이제 통신을 하기위해 레지스터를 건드릴 차례다. 먼저 SPI Control Resister를 보자.

 

 

7번 비트 SPIE는 SPI인터럽트 허용이다.

6번 비트 SPE는 SPI 허용이다. SPI를 사용하려면 set 시켜준다.

5번 비트 SORD는 데이터 순서이다. 1이면 최하위비트(LSB)가 먼저 송신되고 0이면 최상위비트(MSB)가 먼저 송신된다.

4번 비트 MSTR는 마스터/슬레이브 선택이다. 1이면 마스터 모드, 0이면 슬레이브모드로 동작한다.

3번 비트 CPOL은 클럭 극성이다. 1이면 Idle상태에서 SCK가 HIGH로 유지된다.

2번 비트 CPHA는 클럭 위상이다. 1이면 SCK의 뒷 모서리에서 샘플된다.

1~0번 비트 SPR1:0은 클럭속도 선택이다. 마스터로 설정된 장치의 SCK속도를 제어한다. 아래 표를 참조하여 설정한다.

(※어디서 봤는지 기억은 안나는데 속도는 클럭의 1/4가 최대라고 본것 같다. 즉 16MHz면 최대 속도는 4MHz라는 듯?)

 

 

다음으로 SPI Status Register다. 즉 SPI의 상태를 나타내는 레지스터이다.

 

 

7번 비트 SPIF는 SPI 인터럽트 플래그이다. 전송이 완료되었을때 set된다.

6번 비트 WCOL은 충돌플래그이다. 데이터 전송중에 데이터레지스터를 쓰게되면 set된다.

0번 비트 SPI2X는 2배속 전송이다. SPI가 마스터모드로 동작할때 이 비트를 set하면 SPI의 속도가 배가 된다.

 

 

마지막으로 SPI Data Resister다. SPI 시프트 레지스터와 파일 사이의 데이터 전송에 사용된다.

 

 

코드의 작성은 마스터와 슬레이브를 나누어 각각의 디바이스에 넣어줘야한다.

먼저 마스터 소스이다.

 

/*
 * master.c
 *
 *  Created on: 2013. 2. 19.
 *      Author: RRL
 */


#include "lcd.h"
#include "avr/io.h"
#include "stdio.h"

#define SPI_DDR		DDRB
#define SPI_PORT	PORTB
#define SS			PB0
#define SCK			PB1
#define MOSI		PB2
#define MISO		PB3

void SPI_init_master()
{
	SPI_PORT |= (1 << SS);
	SPI_DDR |= ((1 << SS) | (1 << SCK) | (1 << MOSI));
	SPCR = ((1 << SPE)|(1 << MSTR)|(1 << SPR0)); // 16M/16=1Mhz
}


unsigned short SPImasterwriteword(unsigned short data) // 2바이트 데이터쓰기
{
	unsigned short rtn;
	unsigned char *pdata, *prtn;

	pdata=(unsigned char *) &data;
	prtn=(unsigned char *) &rtn;

	SPI_PORT &= ~(1 << SS);
	SPDR = *pdata++;
	while(!(SPSR & (1 << SPIF)));
	*prtn++ = SPDR;

	SPDR = *pdata;
	while(!(SPSR & (1 << SPIF)));
	*prtn = SPDR;

	SPI_PORT |= (1 << SS);

	return rtn;
}


unsigned char pushsw()
{
	char push, out;

	while((push=~PIND&0x07)==0);
	_delay_ms(30);
	while((~PIND & 0x07));
	_delay_ms(30);
	if(push==1)
		out=0;
	else if(push==2)
		out=1;
	else if(push==4)
		out=2;

	return out;
}

void main()
{
	char text[32];
	unsigned char push;
	unsigned short period;
	unsigned short old_period;
	unsigned short slave_rtn;

	DDRC = 0xff; // lcd data
	DDRE = 0xff; // lcd control
	DDRA = 0xff; // led
	PORTA = 0xff; // led init

	LCD_initialize();
	SPI_init_master();
	period = 500;
	old_period = period;
	slave_rtn = SPImasterwriteword(period);

	sprintf(text, "period:%d", period);
	LCD_string(0x80, text);

	while(1)
	{
		push=pushsw();
		switch(push)
		{
		case 0:
			period+=50;
			break;
		case 1:
			period-=50;
			break;
		case 2:
			period=500;
			break;
		}

		if(period != old_period)
		{
			slave_rtn = SPImasterwriteword(period);
			sprintf(text, "period:%d", period);
			LCD_string(0x80, text);
			old_period=period;
		}
	}
}

 

슬래이브 소스이다.

/*
 * slave.c
 *
 *  Created on: 2013. 2. 19.
 *      Author: RRL
 */


#include "avr/io.h"
#include "avr/interrupt.h"

#define SPI_DDR		DDRB
#define SPI_PORT	PORTB
#define SS			PB0
#define SCK			PB1
#define MOSI		PB2
#define MISO		PB3

static volatile unsigned short period;
unsigned int cnt=0;

ISR(TIMER0_OVF_vect)
{
	cnt++;
	TCNT0=5;

	if(cnt == period)
	{
		PORTA = ~(PORTA);
		cnt=0;
	}

}

void SPI_init_slave()
{
	SPI_DDR |= (1 << MISO);
	SPCR = (1 << SPE);
}

void SPI_slaveRead(unsigned short* data)
{
	unsigned char *pdata;

	pdata = (unsigned char *)data;

	while(!(SPSR&(1 << SPIF)));
	*pdata++ = SPDR;

	while(!(SPSR&(1 << SPIF)));
	*pdata = SPDR;
}


void main()
{
	unsigned short l_period;
	DDRA = 0xff;
	PORTA = 0xff;

	SPI_init_slave();

	TCCR0 = 0x04; //prescale 64
	TCNT0 = 5; // 250 cnt
	TIMSK = 0x01;
	sei();


	SPI_slaveRead(&l_period);
	period=l_period;

	while(1)
	{
		SPI_slaveRead(&l_period);
		period = l_period;
	}
}

 

동작영상