반응형

Atmega128의 SPI에 대해 간단히 설명하고  포스트 끝에 마스터와 슬레이브 예제를 제공합니다.  



2016. 6. 29 최초작성

2021. 7. 18 최종작성



SPI (Serial Peripheral Interface)를 사용하면 AVR과 주변 장치 간  또는 여러 AVR 장치간에 고속 동기 데이터 전송이 가능합니다.

 

두 개의 장치가 SPI 통신을 하는 경우 한대는 마스터 장치가 되며 다른 한대는 슬레이브 장치가 됩니다. 

하나의 마스터 장치가 여러 대의 슬레이브 장치와 통신이 가능하지만 동시에는 일대일 통신만 가능합니다.

두 대 이상의 마스터 장치가 슬레이브 장치와 연결될 수 도 있지만 이때 주의점은 동시에 두 대 이상의 마스터 장치가 SPI 통신을 시작하지 않도록해야 합니다. 

 

센서 같은 주변 장치의 경우 SPI가 슬레이브 모드만 가능하지만 AVR의 SPI는 마스터 모드와 슬레이브 모드 둘 다 됩니다.  

AVR이 마스터 모드 또는 슬레이브 모드 중 어느 모드로 실행되지는 SPI 제어 레지스터 (SPCR)의 마스터 비트 (MSTR) 설정에 의해 결정됩니다.



마스터 장치는 SPI 통신이  활성화되면 클럭 신호를  슬레이브 장치에 제공해야 합니다. 마스터 장치가 클럭 신호를 생성시키면 슬레이브 장치는 마스터 장치에 데이터를 보내고 받을 수 있습니다. 슬레이브 장치는 클럭 신호를 생성 할 수 없으므로 자체적으로 SPI 통신을 활성화할 수 없습니다.

 

마스터 장치는 데이터를 보내는 동안에만 클럭 신호를 생성하기 때문에 마스터 장치는 슬레이브 장치로부터 데이터를 읽기 위해 슬레이브 장치로 데이터를 보내야합니다.



SPI를 ISP(In System Programming)를 위해서도 사용합니다. ISP는 임베디드 보드의  EEPROM, 플래시 메모리 등의 비휘발성 메모리의 내용물(보통 컴파일된 프로그램)을 바꾸는 용도로 사용하는 것입니다



마스터와 슬레이브 간의 데이터 전송

 

마스터와 슬레이브 AVR 간의 상호 작용은 다음과 같습니다.

 

 

마스터와 슬레이브 유닛의 기본 구조는 동일합니다.  마스터 유닛쪽에는 추가로 SPI 클럭 발생기가 있습니다.  그림에서 왼쪽이 마스터 유닛이고 오른쪽이 슬레이브 유닛입니다. 

 

두 유닛의 MISO, MOSI, SCK 핀은 서로 연결되어 있습니다. 장치가 마스터 모드 또는 슬레이브 모드로 실행되는지 여부에 따라 유닛의 MISO, MOSI, SCK 핀이 입력핀이  될지 출력핀이 될지 결정됩니다.  

 

한 클록 사이클에서 비트가 마스터에서 슬레이브로, 슬레이브에서 마스터로 동시에 시프트되기 때문에 두 8 비트 시프트 레지스터는 하나의 16 비트 순환 시프트 레지스터로 간주 될 수 있습니다.

 

이것은 8 개의 SCK 클럭 펄스 후에 마스터와 슬레이브 사이의 데이터가 교환된다는 것을 의미합니다. 하나의 클럭 펄스마다 한비트씩 교환되어  8개의 클럭 펄스 후 8비트 즉 1바이트가 이동합니다.  SPI는 전송 방향으로 단일 버퍼링되고 수신 방향으로 이중 버퍼링됩니다.



  • 전송 될 새 바이트는 전체 시프트 사이클이 완료되기 전에 데이터 레지스터 (SPDR) / 시프트 레지스터에 쓸 수 없습니다. 바이트에 대한 전송이 완료되어야 새로운 바이트를 전송할 수 있습니다. 

 

  • 수신된 바이트는 전송이 완료된 직후 수신 버퍼에 기록됩니다.

 

  • 다음 바이트 전송이 완료되기 전에 수신 버퍼를 읽어야합니다. 그렇지 않으면 데이터가 손실됩니다. 덮어씌워 진다는 의미입니다. 

 

  • 데이터 레지스터 (SPDR)를 읽으면 수신 버퍼의 데이터가 반환됩니다.

 

전송이 완료된 후 SPI 상태 레지스터 (SPSR)의  SPIF (SPI 인터럽트 플래그)가 설정됩니다.

 

이 인터럽트와 글로벌 인터럽트가 활성화되면 해당 인터럽트가 실행됩니다.

 

SPI 제어 레지스터 (SPCR)의 SPI 인터럽트 활성화 비트(SPIE)를 설정하면 SPI의 인터럽트가 활성화되고 SREG의  I 비트를 설정하면 글로벌 인터럽트가 활성화됩니다.

 

SPI 핀 구성

SPI는 다음 4 개의 핀을 사용합니다.  

 

  • SCK (Serial Clock 또는 shift clock) .  CLK라고도 부릅니다.

마스터 디바이스에서는  클록 발생기의 출력핀이고 슬레이브 디바이스에서는 클록 시그널을 받는 입력핀입니다.

 

  • MOSI (Master Out Slave In)

마스터 디바이스에서는 출력핀으로 슬레이브 디바이스에서는 입력핀으로 설정됩니다. 마스터 디바이스로부터 슬레이브 디바이스로 데이터 전송이 이루어지는 채널입니다. 

 

  • MISO (Master In Slave Out)

마스터 디바이스에서는  입력핀으로  슬레이브 디바이스에서는  출력핀으로 설정합니다. 슬레이브 디바이스로부터 마스터 디바이스로 데이터 전송이 이루어지는 채널입니다. 

 

  • SS (Slave Select) .  CS라고도 부릅니다.

마스터 디바이스와 다수의 슬레이브 디바이스를 동시에  연결할 수 없고  마스터 장치와 슬레이브 장치간 일대일 통신만 가능하기 때문에,  통신하기 원하는 슬레이브를 선택하기 위한 방법이 필요합니다.  그래서 SS 핀이 사용되며 마스터 디바이스는 슬레이브 디바이스 수만큼 SS핀을 가지고 있어야 합니다.모든 슬레이브 디바이스의 SS핀이 HIGH 상태라면 모든 슬레이브 SPI핀들은 입력이 되고  수신되는 SPI 데이터를 받지 않습니다.하나의 슬레이브 디바이스의 SS'핀이 LOW 상태라면 해당 슬레이브 디바이스의 SPI 인터페이스가 활성화되어 수신되는 SPI 데이터를 받습니다.



마스터 디바이스와 슬레이브 디바이스의 같은 이름의 핀끼리 연결됩니다.  SPI가 마스터 모드 혹은 슬레이브 모드로 활성화냐에 따라 MOSI, MISO, SCK, SS 핀의 데이터 방향이 결정됩니다.

 

SPI가 활성화되면 입력 핀이(Input 부분) 다음 표처럼 자동으로 구성됩니다. 하지만 출력 핀은(User Defined 항목) 프로그램에서 수동으로 초기화해야 합니다.  사용자가 해당 핀의 현재 상태를 확인하고 처리하도록 하기 위해서 입니다.  

 



SPI 연결 예

 

일대일 연결인 경우

 

 

일대다 연결인 경우

슬레이브 디바이스 개수 만큼 마스터 디바이스에 SS 핀 역활을 하는 핀이 추가되어야 합니다. 




Atmega128의 SPI 관련 핀

Atmega128의 SPI 관련 핀은 다음과 같습니다.








SPI 통신

 

아래 그림은  SPI를 사용하여 마스터 CPU와 슬레이브 CPU 간의 상호 연결을 보여주고 있습니다.  SPI 시스템은 마스터와 슬레이브에 각각 있는 8비트 시프트 레지스터(Shift Registers)와 마스터에 만 있는  클록 발생기(Master clock generator, SPI CLOCK GENERATOR)로 구성됩니다.

 



마스터 디바이스에서 통신을 원하는 슬레이브 디바이스와 연결된 SS 핀을 LOW로 낮출 때 두 디바이스간 SPI 통신이 가능한 상태가 됩니다. 

 

마스터와 슬레이브는 각자의 시프트 레지스터에 전송할 데이터를 준비하고 마스터는 데이터를 교환하기 위해 SCK 라인에 필요한 클록 펄스를 발생시킵니다. 

 

데이터는 MOSI(Master Out – Slave In)에서 마스터에서 슬레이브로 이동하고 MISO(Master In – Slave Out)에서 슬레이브에서 마스터로 이동합니다.

 

전송과 수신이 동시에 일어나서 결과적으로 마스터와 슬레이브는 한 바이트씩 데이터를 교환하게 됩니다. 데이터 패킷 교환을 모두 끝낸 후(한 바이트 교환 완료할때마다), 마스터는 SS 라인을 HIGH로 만들어 슬레이브와 동기화해야 합니다. 

 

성공적으로 SPI 통신을 하기 위해서는 마스터와 슬레이브 디바이스는 반드시 클록 시그널 세팅(통신 속도)이 같아야 합니다.  

마스터가 1000kHz를 사용하다면 슬레이브도 1000kHz를 사용해야 합니다.

 

마스터 디바이스

 

디바이스가 마스터로 설정된 경우 SPI 인터페이스는 SS 라인을 자동으로 제어하지 않습니다. SPI 통신을 시작하기 전에 소프트웨어 적으로 SS 라인을 제어 해야 합니다.

 

SS 라인을 제어를 한 후,  SPI 데이터 레지스터에 1 바이트를 쓰면 SPI 클럭 발생기가 작동하고 하드웨어는 데이터 8 비트를 슬레이브 디바이스로 이동시킵니다. 1 바이트 이동 후 SPI 클록 발생기가 중지되며, 전송이 종료되었다는 것을 알리기 위해  전송 종료 플래그(SPIF)를 1로  설정합니다.

 

SPCR 레지스터의 SPI 인터럽트 활성화 비트 (SPIE)가 1로 설정되면 인터럽트가 요청됩니다.

 

마스터는 SPI 데이터 레지스터(SPDR)에 다음 바이트를 기록하여 계속 한 바이트를 슬레이브로 이동시키거나 Slave Select(=SS) 라인을 HIGH로 만들어서 슬레이브 디바이스에 패킷의 끝을 알릴 수 있습니다.

 

마지막으로 들어오는 바이트는 나중에 사용할 수 있도록 버퍼 레지스터에 보관됩니다.

 

슬레이브 디바이스

디바이스가 슬레이브로 구성되면 SS 핀이 HIGH를 유지하는 동안  슬레이브의 SPI 인터페이스는  대기하게 됩니다.

 

이 상태에서 소프트웨어는 SPI 데이터 레지스터(SPDR)의 내용을 업데이트 할 수 있지만 SS 핀이 LOW로 변경될 때까지 SCK 핀에서 들어오는 클록 펄스에 의해 데이터가 이동되지 않습니다.

 

SS핀이 LOW가 되면 SCK핀으로 입력되는 클록펄스를 이용하여 한바이트를 마스터로 이동시킵니다.  한 바이트가 이동하고 나면, 전송이 종료되었다는 것을 알리기위해  SPIF(전송 종료 플래그)를 1로  설정합니다.

 

SPCR 레지스터의 SPI 인터럽트 활성화 비트 (SPIE)가 1로 설정되면 인터럽트가 요청됩니다.

 

슬레이브는 수신한 데이터를 읽기 전에 SPI 데이터 레지스터(SPDR)로 보낼 새 데이터를 계속 배치 할 수 있습니다. 슬레이브는 계속해서 새로운 데이터를 SPI 데이터 레지스터(SPDR)에 넣어두고 새로운 데이터 입력을 대기합니다. 

 

마지막으로 들어오는 바이트는 나중에 사용할 수 있도록 버퍼 레지스터에 보관됩니다.



레지스터

 

SPI Control Register - SPCR

 

Bit 7 – SPIE: SPI Interrupt Enable

SPSR 레지스터의 SPIF 비트가 설정되고 SREG의 글로벌 인터럽트 활성화 비트가 설정된 경우, 이 비트가 설정되면  SPI 인터럽트가 실행되도록 합니다.



Bit 6 – SPE: SPI Enable

SPE 비트가 1에 기록되면 SPI가 활성화됩니다. 



• Bit 5 – DORD: Data Order

DORD 비트에 1 이 기록되면 데이터 워드의 LSB가 먼저 전송됩니다.

DORD 비트에 0 이 기록되면 데이터 워드의 MSB가 먼저 전송됩니다.



• Bit 4 – MSTR : Master/Slave Select  - 마스터 또는 슬레이브 모드 선택

이 비트가 1로 설정되면 마스터 SPI 모드가 선택되고 이 비트에 0을 설정하면 슬레이브 SPI 모드가 선택합니다.

 

SS 핀이 입력으로 설정되고 MSTR이 1로 설정된 상태에서 SS 핀이 Low 상태로 구동되면  MSTR이 0으로 클리어되고 SPI 상태 레지스터(SPSR)의 SPIF가 1로 설정됩니다. 사용자는 SPI 마스터 모드를 다시 활성화하도록 MSTR을 1로 설정해야 합니다.(??)



• Bit 3 – CPOL: Clock Polarity

이 비트에 1이 기록되면 유휴 상태 일 때 SCK가 HIGH 상태가 됩니다. 

이 비트에 0이 기록되면 유휴 상태 일 때 SCK가 LOW 상태가 됩니다. 

 

 

CPOL 기능은 다음과 같습니다.

 

 

• Bit 2 – CPHA: Clock Phase

클럭 위상 비트 (CPHA : clock phase bit )의 설정은 데이터가 SCK의 선행, 후행 에지에서 샘플링되는지 여부를 결정합니다.

 

CPHA 기능은 아래에 요약되어 있습니다. 

 

 

• Bits 1, 0 – SPR1, SPR0: SPI Clock Rate Select 1 and 0

이 두 비트는 마스터로 구성된 장치의 SCK 속도를 제어합니다. SPR1 및 SPR0은 슬레이브에 영향을주지 않습니다.

SCK와 오실레이터 클록 주파수 f_osc 간의 관계는 다음 표에 나와 있습니다.

 



SPI Status Register - SPSR



• Bit 7 – SPIF: SPI Interrupt Flag

직렬 전송이 완료되면 SPIF 플래그가 설정됩니다. SPCR의 SPIE가 설정되고 글로벌 인터럽트가 활성화되면 인터럽트가 발생합니다.

 

SS가 입력이고 SPI가 마스터 모드에 있을 때 LOW로 구동되면 SPIF 플래그도 설정됩니다. SPIF는 해당 인터럽트 처리 벡터를 실행할 때 하드웨어에 의해 클리어됩니다. 

 

또는 SPIF가 설정된 SPI 상태 레지스터를 먼저 읽은 다음 SPI 데이터 레지스터 (SPDR)에 액세스하여 SPIF 비트를 지 웁니다.

 

• Bit 6 – WCOL: Write COLlision flag

WCOL 비트는 데이터 전송 중에 SPI 데이터 레지스터(SPDR)이 기록되면 설정됩니다.

 

WCOL 비트 (및 SPIF 비트)는 먼저 WCOL이 설정된 SPI 상태 레지스터를 읽은 다음 SPI 데이터 레지스터에 액세스하면 지워집니다.

 

• Bit 5..1 – Res: Reserved Bits

이 비트는 ATmega128에서 예약 된 비트이며 항상 0으로 읽습니다.



• Bit 0 – SPI2X: Double SPI Speed Bit

이 비트가 로직 1로 기록되면 SPI가 마스터 모드에있을 때 SPI 속도 (SCK 주파수)가 두 배가됩니다. 이는 최소 SCK 기간이 2 CPU 클럭 기간임을 의미합니다. SPI가 Slave로 구성되면 SPI는 f_osc / 4 이하에서만 작동하도록 보장됩니다.

 

ATmega128의 SPI 인터페이스는 프로그램 메모리 및 EEPROM 다운로드 또는 업로드에도 사용됩니다. SPI 직렬 프로그래밍 및 검증은 300 페이지를 참조하십시오.




SPI Data Register - SPDR

 

 

SPI 데이터 레지스터는 레지스터 파일과 SPI 시프트 레지스터 간의 데이터 전송에 사용되는 읽기 / 쓰기 레지스터입니다.

레지스터에 쓰면 데이터 전송이 시작됩니다. 레지스터를 읽으면 시프트 레지스터 수신 버퍼를 읽습니다.




실행결과

테스트는 다음처럼 진행했습니다. 

 

마스터 역할을 하는 AVR과 슬레이브 역할을 하는 AVR에 각각 USB to Serial 장치를 연결하여 PC와 시리얼 통신 연결 및 전원 공급도 받습니다. 장비 특성인지 AVR 하나를 연결하면 양쪽 AVR에 전원이 공급되었습니다. 

 

그 영향인지 마스터 역할을 하는 AVR을 먼저 PC에 연결하면 SPI 통신이 먹통이 되고 슬레이브 역할을 하는 AVR을 먼저 PC에 연결하면 SPI 통신이 정상 동작을 했습니다. 




마스터 장치와 연결한 터미널 결과 

 

  • 슬레이브에서 전송한 S 문자를 일정한 간격으로 출력합니다.

 



슬레이브 장치에 연결한 터미널 결과 

 

  • 마스터에서 전송한 M 문자를 일정한 간격으로 출력합니다. 

 





코드

마스터와 슬레이브가 번갈아가며 서로 한바이트씩 주고 받는 코드입니다.  기존에 문자열을 수신받았던 코드는 기존 슬레이브 코드 항목에 남겨두었습니다. 마스터와 슬레이브가 문자열을 주고 받으려면 기존 슬레이브 코드를 참고하여 수정해야 할 듯 보입니다. 언제 작업해볼지는 미정입니다.



마스터 모드에서의 동작은 다음과 같습니다. 

 

1. SS, MOSI,SCK 핀을 출력으로, MISO 핀은 입력으로 설정합니다. 

 

2. SPCR 레지스터의 SPR0 비트를 설정하여 SCK 주파수를 선택합니다. (마스터에서만 필요한 부분입니다)

 

3. 마스터 모드로 변경하기 위하여 SPCR 레지스터의 MSTR 비트를 1로 설정합니다. 

 

4. SPCR 레지스터의 SPE 비트를 1로 설정하여 SPI를 활성화합니다. 

 

5. 전송할 데이터를 SPDR 레지스터로 복사합니다. 

    SPSR 레지스터의 SPIF 비트가 1로 설정될때까지 대기합니다. 

 

6. SPSR 레지스터의 SPIF 비트가 1로 설정될때까지 대기합니다. 

    수신된 데이터를 SPDR 레지스터로부터 가져옵니다. 

 

7. 시리얼로 데이터를 전송합니다. 




슬라이브 모드에서의 동작은 다음과 같습니다. 

 

1. MISO 핀을 출력으로하고 나머지 핀들은 입력으로 설정합니다. 

 

2. 슬레이브 모드로 변경하기 위하여 SPCR 레지스터의 MSTR 비트를 0으로 설정합니다. 

 

3. SPCR 레지스터의 SPE 비트를 1로 설정하여 SPI를 활성화합니다. 

 

4. SPSR 레지스터의 SPIF비트가 1로 설정될때까지 대기합니다. 

    수신된 데이터를 SPDR 레지스터로부터 가져옵니다. 

 

5. 시리얼로 데이터를 전송합니다. 

 

6. 전송할 데이터를 SPDR 레지스터로 복사합니다. 

    SPSR 레지스터의 SPIF 비트가 1로 설정될때까지 대기합니다. 



참고로 SPI_slave_write 함수와 SPI_master_write 함수,  SPI_slave_read 함수와 SPI_master_read 함수는 이름만 다를뿐 기능이 동일합니다. 



마스터 코드

#define F_CPU 16000000UL

//1. baud rate를 선택
#define USART_BAUDRATE 9600

//2.시스템 클록과 원하는 baud rate를 이용하여 UBRR 값을 계산한다.
#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

#define SPI_DDR DDRB
#define SS PINB0
#define MOSI PINB2
#define MISO PINB3
#define SCK PINB1
 
#include <avr/io.h>
#include "util/delay.h"


void init_serial()
{
//3. UBRR0은 16비트 레지스터이기 때문에 8비트씩 나누어서 넣어야 한다.
UBRR0H = (uint8_t)(UBRR_VALUE>>8);
UBRR0L = (uint8_t) UBRR_VALUE;

//4. USART 설정
UCSR0C |= (1<<UCSZ00)|(1<<UCSZ01); //Charecter size : 8비트
UCSR0C &= ~(1<<USBS0); //stop  bit : 1비트
UCSR0C &= ~((1<<UPM01)|(1<<UPM00)); // no parity mode

//5. 송수신을 가능하게 한다.
UCSR0B=(1<<RXEN0)|(1<<TXEN0);
}

void serial_write_byte(uint8_t data) {

//이전 전송이 끝나기를 기다림
while(!(UCSR0A&(1<<UDRE0))) ;

UDR0 = data;                                         
}

uint8_t serial_read_byte(void) {

// 수신 되기를 기다림
while(!(UCSR0A&(1<<RXC0))) ;

return UDR0;                               
}


void init_spi_master()
{
SPI_DDR &= ~(1<<MISO); // MISO --> inputs
SPI_DDR |= (1 << SS) | (1 << MOSI) | (1 << SCK); // MOSI, SCK, SS   --> output


//16000KHz / 16
SPCR |= (1 << SPR0);
 
// Enable SPI, Master mode
SPCR = (1 << SPE) | (1 << MSTR);
}


void SPI_master_write(uint8_t data)
{
SPDR = data;

while (!(SPSR & (1 << SPIF))) ;
}


uint8_t SPI_master_read()
{
SPDR = 0x00;

while (!(SPSR & (1 << SPIF))) ;

return SPDR;
}


int main(void)
{

  init_spi_master();
  init_serial();

  while (1)
  {

    SPI_master_write('M');
    _delay_ms(1000);

    uint8_t ch = SPI_master_read();
    serial_write_byte(ch);
    serial_write_byte('\r');
    serial_write_byte('\n');
    _delay_ms(150);
  }
}




슬레이브 코드

 

#define F_CPU 16000000UL

//1. baud rate를 선택
#define USART_BAUDRATE 9600

//2.시스템 클록과 원하는 baud rate를 이용하여 UBRR 값을 계산한다.
#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

#define SPI_DDR DDRB
#define SS PINB0
#define MOSI PINB2
#define MISO PINB3
#define SCK PINB1


#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

void init_serial()
{
//3. UBRR0은 16비트 레지스터이기 때문에 8비트씩 나누어서 넣어야 한다.
UBRR0H = (uint8_t)(UBRR_VALUE>>8);
UBRR0L = (uint8_t) UBRR_VALUE;

//4. USART 설정
UCSR0C |= (1<<UCSZ00)|(1<<UCSZ01); //Charecter size : 8비트
UCSR0C &= ~(1<<USBS0); //stop  bit : 1비트
UCSR0C &= ~((1<<UPM01)|(1<<UPM00)); // no parity mode

//5. 송수신을 가능하게 한다.
UCSR0B=(1<<RXEN0)|(1<<TXEN0);
}

void serial_write_byte(uint8_t data) {

//이전 전송이 끝나기를 기다림
while(!(UCSR0A&(1<<UDRE0))) ;

UDR0 = data;                                         
}

uint8_t serial_read_byte(void) {

// 수신 되기를 기다림
while(!(UCSR0A&(1<<RXC0))) ;

return UDR0;                               
}


void  init_spi_slave( ) {

SPI_DDR &= ~((1<<SCK)|(1<<MOSI)|(1<<SS)); // SCK, MOSI, SS --> inputs
SPI_DDR |= (1<<MISO);                  // MISO --> output

//slave mode
SPCR &= ~(1<<MSTR);

//enable SPI
SPCR |= ((1<<SPE));
}

void SPI_slave_write(uint8_t data)
{
SPDR = data;

while (!(SPSR & (1 << SPIF))) ;
}


uint8_t SPI_slave_read()
{
SPDR = 0x00;

while (!(SPSR & (1 << SPIF))) ;

return SPDR;
}


int main(void)
{
init_spi_slave();
init_serial();


while(1)
{
uint8_t ch = SPI_slave_read();
serial_write_byte(ch);
serial_write_byte('\r');
serial_write_byte('\n');
_delay_ms(50);

SPI_slave_write('S');
_delay_ms(50);


}
}




기존 슬레이브 코드

 

기존에 포스트에 있던 슬레이브  코드도 남겨둡니다. 인터럽트 방식으로 SPI를 통해 문자열을 수신받아 시리얼로 출력하는 코드입니다. 

 


 /*
* spi_slave.c
*
* Created: 2016-06-28
* Author : webnautes
*/

#define F_CPU 16000000UL

//1. baud rate를 선택
#define USART_BAUDRATE 9600

//2.시스템 클록과 원하는 baud rate를 이용하여 UBRR 값을 계산한다.
#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)

#include <avr/io.h>
#include <avr/interrupt.h>


void usartInit()
{
    //3. UBRR0은 16비트 레지스터이기 때문에 8비트씩 나누어서 넣어야 한다.
    UBRR0H = (uint8_t)(UBRR_VALUE>>8);
    UBRR0L = (uint8_t) UBRR_VALUE;
   
    //4. USART 설정
    UCSR0C |= (1<<UCSZ00)|(1<<UCSZ01); //Charecter size : 8비트
    UCSR0C &= ~(1<<USBS0); //stop  bit : 1비트
    UCSR0C &= ~((1<<UPM01)|(1<<UPM00)); // no parity mode
   
    //5. 송수신을 가능하게 한다.
    UCSR0B=(1<<RXEN0)|(1<<TXEN0);
}

void transmitByte(uint8_t data) {
   
    //이전 전송이 끝나기를 기다림
    while(!(UCSR0A&(1<<UDRE0))){};
   
    UDR0 = data;                                            /* send data */
}

uint8_t receiveByte(void) {
    // 수신 되기를 기다림
    while(!(UCSR0A&(1<<RXC0))){};
    return UDR0;                                /* return register value */
}


void printString(volatile unsigned char str[]) {
    uint8_t i = 0;
    while (str[i]!='\0') {
        transmitByte(str[i]);
        i++;
    }
}

void readString(char str[], uint8_t maxLength) {
    char response;
    uint8_t i;
    i = 0;
    while (i < (maxLength - 1)) {                   /* prevent over-runs */
        response = receiveByte();
        transmitByte(response);                                    /* echo */
        if (response == '\r') {                     /* enter marks the end */
            break;
        }
        else {
            str[i] = response;                       /* add in a letter */
            i++;
        }
    }
    str[i] = 0;                          /* terminal NULL character */
}

void  init_SPI( ) {
   
    DDRB &= ~((1<<PB1)|(1<<PB2)|(1<<PB0)); // SCK, MOSI, SS --> inputs
    DDRB |= (1<<PB3);                  // MISO --> output
   
    //slave mode
    SPCR &= ~(1<<MSTR);
   
    //16000KHz / 16
    SPCR  |= (1<<SPR0);
   
    //enable SPI
    SPCR |= ((1<<SPE)|(1<<SPIE));

}




unsigned char SPI_write(unsigned char cData) {
    SPDR = cData;
    while ( !(SPSR & (1<<SPIF))) ;
    return SPDR;
}

unsigned char SPI_read( void ) {
    SPDR = 0x0;
    while ( !(SPSR & (1<<SPIF))) ;
    return SPDR;
}



volatile unsigned char str[100];


ISR(SPI_STC_vect)
{
    static uint8_t idx=0;
    unsigned char ch = SPDR;

    if ( ch == '\0' )
    {
        str[idx]='\0';
        printString(str);
        idx=0;
    }
    else {
            str[idx]=ch;
            idx++;
    }   


}

int main (void)
{
    init_SPI();
    usartInit();
    sei();
   
    while(1)
    {


    }
}




참고

 

Atmega128 datasheet

 

https://ww1.microchip.com/downloads/en/AppNotes/Atmel-2585-Setup-and-Use-of-the-SPI_ApplicationNote_AVR151.pdf







반응형

문제 발생시 지나치지 마시고 댓글 남겨주시면 가능한 빨리 답장드립니다.

도움이 되셨다면 토스아이디로 후원해주세요.
https://toss.me/momo2024


제가 쓴 책도 한번 검토해보세요 ^^

+ Recent posts