반응형

SPI는 Atmega128과 주변 디바이스 또는 AVR 디바이스 간에 고속의 동기화 데이터 전송을 할 수 있다.


그림 76은 SPI를 이용한 마스터 CPU와 슬레이브 CPU간의 상호접속을 보여주고 있다. SPI의 핵심은 마스터 디바이스 및  슬레이브 디바이스 양쪽에 있는 8비트 시프트 레지스터와 마스터 디바이스가 발생시키는 클록 시그널이다.



마스터에서 원하는 SPI 슬레이브와 연결되어 있는 SS핀을 LOW로 낮추어야 SPI통신이 가능한 상태가 된다.

마스터와 슬레이브에서 각자의 시프트 레지스터에 전송한 데이터를 준비한다. 그리고 나서 마스터에서 데이터 교환을 위해 SCK 라인에 필요한 클록 펄스를 발생시킨다. 그러면 데이터는 마스터에서 MOSI(Master Out – Slave In)라인으로 시프트된다.  그리고 동시에 슬레이브로부터 MISO(Master In – Slave Out)라인으로 시프트된다. 전송과 수신이 동시에 일어나서 결과적으로 마스터와 슬레이브는 한 바이트씩 데이터를 교환하게 된다. 데이터 패킷 교환을 끝내려면  마스터는 SS라인을 HIGH로 만들어 주어야한다.


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


SPI 마스터 디바이스라면, 통신이 시작된 후에 SS라인을 소프트웨어적으로 제어해주어야 한다.  SPI 데이터 레지스터에 한 바이트를 기록하면 SPI 클록 발생기가 작동하고 8비트가 슬레이브로 시프트된다. 한 바이트가 시프트 된 후, SPI 클록 발생기를 멈추고, 전송이 종료되었다는 것을 알리기 위해 SPIF를 1로 설정한다. SPCR 레지스터의 SPI 인터럽트 인에이블 비트(SPIE)가 1로 설정되었다면, 인터럽트가 요청된다. 마스터는 SPI 데이터 레지스터(SPDR)으로 다음 바이트를 기록함으로써 다음 바이트를 슬레이브로 시프트한다. 마지막 바이트까지 전송이 완료되었으면 SS라인에 HIGH를 출력한다.


SPI 슬레이브 디바이스라면 SPI인터페이스는 SS핀이 HIGH인 동안 대기하게 된다. 이 상태에서 소프트웨어에서 SPI 데이터 레지스터(SPDR)의 내용을 업데이트한다. SS핀이 LOW가 되면 SCK핀으로 입력되는 클록펄스를 이용하여 데이터를 마스터로 시프트하게 된다. 한 바이트가 시프트되고 나면, 전송이 종료되고 SPIF가 1로 설정된다. SPCR 레지스터의 SPI 인터럽트 인에이블 비트(SPIE)가 1로 설정되었다면, 인터럽트가 요청된다. 슬레이브는 계속해서 새로운 데이터를 SPDR에 넣어두고 새로운 데이터 입력을 대기한다.



SPI에서는  4개의 핀이 사용되어 진다.  


MISO (Master In Slave Out)

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


MOSI (Master Out Slave In)

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


SCK (Serial Clock) - CLK라고도 불린다.

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


SS' (Slave Select) - CS라고도 불린다.

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


다음은 일반적인 SPI연결을 그림으로 보여주고 있다.



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

1. SS, MOSI,SCK 핀을 출력으로 설정한다.

2. 마스터 디바이스 모드로 변경하기 위하여 SPCR 레지스터의 MSTR 비트를 1로 설정한다.

3. SPCR레지스터의 SPR1, SPR0비트를 설정하여 SCK 주파수를 선택한다.

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

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

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



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

1.MOSI핀을 출력으로하고 나머지 핀들은 입력으로한다.

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

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

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


다음은 SPI master코드이다.

태스트 후 추가예정..


다음은 SPI slave 코드이다. 시리얼 터미널로 마스터 디바이스에서 보낸 문자열을 확인해볼 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
/*
 * 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)
    {
 
 
    }
}
 
 
cs


반응형

포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
질문을 남겨주면 가능한 빨리 답변드립니다.

여러분의 응원으로 좋은 컨텐츠가 만들어집니다.
지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 ~~

유튜브 구독하기


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

  1. 2018.11.12 08:28

    비밀댓글입니다

+ Recent posts