티스토리 뷰

입문

SPI

Just4Fun 2016. 4. 16. 16:01

SPI는 Serial Peripheral Interface의 머리글자이다.  주변 장치와 직렬 통신을 위한 방법중 한가지를 가리키는 것이다.  이미 앞에서 설명한바 있는 UART와는 다음과 같은 차이점이 있다.

UART는 비동기 통신임에 반하여 SPI는 동기 통신 방식이다.  즉, 클럭 신호를 마스터에서 제공하는 것이다.

UART는 1:1 통신으로 구성되며 Peer-to-Peer 방식이다.  내 보낼 데이터가 있으면 상대방이 연결되어 있든 없든 무조건 데이터를 주어신 속도에 맞춰 내보낸다.  그러나, SPI는 1:N의 마스터-슬레이브 방식으로 동작된다.  슬레이브는 마스터가 요구하는 동작을 수행하도록 되어 있다.

위의 그림은 하나의 SPI 마스터에 세개의 SPI 슬레이브가 연결되어 예를 보여주고 있다.  SPI마스터와 슬레이브는 SC(L)K라는 클럭신호와 MOSI라는 마스터에서 슬레이브로 내보내는 데이터 신호, 그리고 MISO라는 슬레이브에서 마스터로 보내는 데이터 신호를 모두 공유한다.  즉, 버스형태로 연결되는 것이다.  마스터에서는 모든 슬레이브와 동시에 통신을 수행할 수는 없다.  특정 슬레이브를 지정해 주어야 한다.  이를 위하여 SS라는 슬레이브 선택 신호를 각각의 슬레이브와 연결하여야 한다.  마스터는 통신하고자 하는 슬레이브와 연결된 SS 신호를 low 상태로 만들면 그 슬레이브가 선택되어 마스터와 통신을 수행할 수 있게 된다.

개략적인 SPI 통신의 특징을 이해하였으면, AVR에 있는 SPI 블럭은 어떻게 생겼는지 살펴보자.

위의 그림을 보면 Databus에 연결되어 있는 세개의 빨간색 박스로 표시된 레지스터를 볼 수 있다.  SPI의 동작을 제어하는 control 레지스터와 상태를 나타내는 status 레지스터, 그리고 슬레이브로 내보내거나 슬레이브로부터 들어온 데이터가 저장되는 data 레지스터가 있다.

AVR은 주로 SPI 마스터로 동작되므로 여기에서는 마스터로 설정하여 슬레이브로 데이터를 쓰고, 읽어 오는 방법에 대해서 설명하도록 하겠다.  SPI는 통신하는 기능만 놓고 보면 크게 어렵지는 않다.  어려운 것은 슬레이브를 원하는 방향으로 동작시키기 위한 절차를 어떻게 하느냐이다.

단지 한가지 주의할 점은 슬레이브 디바이스 마다 데이터를 샘플링 하는 시점이 각각 다르다는 것이다.  이 부분은 슬레이브 디바이스의 문서를 참고하여 슬레이브에서 요구하는 규격대로 동작시켜 주어야 한다.

SPI의 동작을 실습하기 위하여 다음과 같은 회로를 구성하도록 하겠다.

이번 프로젝트는 AVR 두개를 사용하여 SPI 통신이 어떻게 이뤄지는지 확인해 볼 것이다.  오른쪽에 있는 AVR의 연결은 이전 글 AVR에 실행 파일 다운로드에서 나온적이 있는 연결 방법이다.  ISP 장비와의 연결 방법과 동일하다.  심화편에서는 위의 회로를 이용하여 실제로 ISP 기능을 설명할 예정이다.  따라서 이번 프로젝트에서는 ISP의 맛보기 버전이라고 생각하면 될 것이다.

ISP에 대한 설명은 ATMEL 문서중 "AVR910: In-System Programming"을 참고하면 된다.

SPI 마스터에서 SS를 low로 만들면 이 신호는 SPI 슬레이브의 Reset핀을 low로 만들게 된다.  그러면 슬레이브는 즉시 ISP 프로그래밍 모드로 전환된다.  마스터에서는 4바이트로 이루어진 명령어를 이용하여 슬레이브 AVR의 플래쉬 메모리에 데이터를 쓰고 읽을수 있다.  플래쉬메모리 뿐만 아니라, EEPROM, Fuse 비트까지 변경할 수 있다.

AVR datasheet문서에는 ISP에서 사용되는 명령어들이 어떤것들이 있는지 설명하는 부분이 있다.

이번 프로젝트에서는 이 중에서 'Programming Enable' 명령과 'Read Signature Byte' 명령 두가지를 SPI 통신을 이용하여 실행해 보도록 하겠다.

ISP 기능을 시작하려면 SS신호를 low로 만든 다음 최소 20msec 시간이 지난 후 'Programming Enable' 명령을 수행하고, 그 다음부터 필요한 명령을 수행하여야 한다.

'AVR910' 문서에는 'Programming Enable'과 'Read Signature Byte'를 예를 들어 어떤식으로 명령어를 슬레이브로 보내고, 슬레이브에서는 어떤 데이터를 응답하는지에 대한 설명이 나와 있다.

위의 표에서 보는것처럼 'Programming Enable' 명령어는 0xAC, 0X53, xx, yy 로 이루어져 있다. xx, yy는 의미없는 값이다.  마스터에서 0xAC를 쓰면 슬레이브에서는 zz라는 의미 없는 값을 내 보낸다. 그다음 마스터에서 53을 보내면 슬레이브에서는 이전에 받았던 데이터인 AC를 되돌려 준다.

'Programming Enable' 명령을 주고 난 다음 'Read Signature Byte' 명령을  수행한다.  Signature Byte는 모두 세바이트로 이루어져 있다.  이 값은 datasheet에 나와 있다.

SPI 슬레이브로 동작되고 있는 AVR이 ATmega328P이라면 0x1E,0x95,0x0F가 signature byte가 된다.  이것을 다른말로 Device ID라고도 한다.

Signature byte가 세개로 되어 있기 때문에 'Read signature byte'명령이 세번 수행되어야 한다.  한번 수행될 때 마다 명령어의 세번째 바이트값을 0,1,2를 주면 각각의 device ID값을 읽어 올 수 있다.

이러한 배경지식을 기반으로 실제 슬레이브 AVR의 device ID를 읽어와서 문서와 맞는지 확인해 보기로 하겠다.

먼저 SPI를 동작하기 위한 구조체를 만들어 본다.

이미 설명한대로 SPI는 SPCR, SPSR, SPDR 이렇게 세개의 레지스터를 가지고 있다.

SPI를 동작시키기 위한 초기화 코드는 다음과 같다.

SPI블럭의 외부와 연결된 핀은 SCK, MOSI, MISO가 있고 슬레이브를 선택하기 위한 SS핀이 필요하다.  이 핀들은 모두 Port B에 있고, SPI 초기화와 별도로 Port I/O 설정을 해 주어야 한다.

SCK, MOSI, SS를 출력으로 설정하고 SS는 평소에 high 상태로 유지하고, MISO 신호는 입력으로 동작되지만 평소에 안정된 신호를 유지하기 위하여 내부 pull-up 저항을 활성화 시켜 놓았다.

6번 라인이 SPI 초기화 설정 부분이다. SPI를 enable하고 마스터 모드로 동작되도록 하였다.  SCK는 1/16로 분주되도록 하여 일단 낮은 클럭으로 동작되도록 하였다.

SPI 로 데이터를 쓰고, 읽기 위한 코드는 다음과 같다.

SPI로 데이터를 내보내기 위하여 SPDR 레지스터에 데이터를 쓰면 자동으로 송신이 된다.  송신이 완료되면 SPSR레지스터의 SPIF 비트가 set 된다.  SPIF 비트가 set 된 이후 SPDR값을 읽으면 슬레이브로 부터 수신된 데이터를 읽을 수 있다.

ISP 명령어를 주고 그 결과를 받아오는 함수도 만들어야겠다.

out_data와 cmd_data는 네개의 바이트 배열이다.  cmd_data에 명령어를 써놓고 isp_cmd()를 부르면 해당 명령어가 슬레이브로 내려가고, 슬레이브로 부터 받은 데이터는 out_data배열에 기록되도록 하였다.

'Programming Enable' 명령어 코드는 다음과 같다.

5~8번 라인이 'Programming Enable' 명령어를 cmd_data배열에 넣어주는 부분이고 isp_cmd()를 불러 SPI로 내보낸다.  그 결과는 out_data에 들어가 있다.  out_data를 출력하여 문서에서 설명한 대로 결과가 나오는지 확인하는 코드가 들어가 있다.

이번에는 실제로 Device ID를 읽어 오는 코드를 만들어 보면 다음과 같이 된다.

cmd_data[2]의 값을 0~2로 바꾸어 가며 슬레이브로 Read Signature Byte 명령을 수행하고 있다.  각각의 Signature byte는 out_data[3]에 들어 있으므로 이 데이터만 sig[] 배열에 복사해 놓는다.  모든 통신이 완료된 다음 sig[] 배열을 print하여 Device ID가 제대로 읽혀 왔는지 확인해 본다.

위와 같이 setup() 코드를 만들어 보드에서 어떤 결과가 나오는지 확인해 보자.

ATmega328P의 device ID가 제대로 읽어 왔음을 확인할 수 있었다.  그러나 Program Enable에서의 결과는 문서와 달랐다.  이 부분은 여러가지 방법을 달리해 가면서 이유를 분석해 보아야 할것 같다.



main.c


입문 과정 목차

'입문' 카테고리의 다른 글

시스템 클럭  (8) 2016.04.18
TWI(I2C)  (13) 2016.04.16
ADC(Analog to Digital Converter)  (0) 2016.04.14
타이머/카운터 - PWM(Phase Correct PWM Mode)  (0) 2016.04.12
타이머/카운터 - PWM(Fast PWM Mode)  (2) 2016.04.12
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
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
글 보관함