본문 바로가기
DSP, MCU/STM32 (ARM Cortex-M)

STM32 ] SPI 통신 사용하기

by eteo 2022. 7. 24.

 

 

 

SPI : Serial Peripheral Interface

동기식, 전이중 통신이며 마스터와 슬레이브 모드로 동작한다.

최대 클럭이 제한되어 있지 않아 속도 제한이 없어 I2C보다 훨씬 빠르다. 

기본은 SCLK, MOSI, MISO, CS 4개의 선이 필요한데 많은 베리에이션이 있다.

 

 

 

1:1 통신시

 

 

SCLK (Serial Clock) : 마스터가 출력하는 동기용 Clock 라인

MOSI (Master Output Slave Input) : 마스터 출력, 슬레이브 입력. SDI 로도 표기 된다.

MISO (Master Input Slave Output) : 슬레이브 출력, 마스터 입력. SDO 로도 표기 된다.

CS (Chip Select) : 마스터가 어떤 슬레이브와 통신할 지 선택할 때 사용하는 선. SS (Slave Select) 라고도 표기 한다. 위에 바가 붙었으니 Active Low로 동작한다.

 

 

 

 

1:N 통신시

 

 

Slave 칩의 갯수 만큼 CS선이 필요하다는 단점이 있다. 평소엔 HIGH이다가 통신하는 슬레이브 CS에만 LOW 를 줘서 통신한다.

 

 

 

 

 

 

SPI 설정

설정에 따라 4가지 모드를 가질 수 있다. 이건 칩의 데이터시트를 보고 확인해야 한다.

 

1. 클럭의 극성 (Clock Polarity : CPOL)

IDLE 상태일 때(통신하지 않고 있는 평소 상태) SCLK라인이 HIGH 인지 LOW인지를 선택

 

2. 클럭의 위상 ( Clock Phase : CPHA)

첫번째 엣지 1Edge 일 때 데이터를 가져갈 지 두번째 엣지 2Edge일 때 데이터를 가져갈지 선택

 

위의 타이밍 다이어그램은 CPOL이 LOW이고 CPHA가 1Edge인 설정이다.

 

 

 

 

 

SPI 통신 방식

출처 : https://m.blog.naver.com/PostView.naver?isHttpsRedirect=true&blogId=hanbulkr&logNo=221571648661

 

위 그림처럼 Shift Register에 의해 클럭 신호에 맞춰 데이터가 1bit씩 이동하는데 MOSI로 데이터를 출력하는 동시에 MISO로는 데이터가 수신된다. 수신되는 데이터는 상황에 따라 의미가 있는 값일 수도 있고 쓰레기 값일 수도 있다. 쓰레기 값이면 그냥 버리면 되고 슬레이브의 데이터를 읽기 위해서 쓰레기 값으로 밀어내기도 한다.

 

 

 

 

 

 

UART, I2C, SPI 비교

 

사진 출처 : https://hydroponicglass.tistory.com/224

 

 

 

 

 

 

예제

SPI를 테스트하기에 적당한 모듈이 없어서 이전에 했던 실습에서 GPIO로 구현했던 것을 HAL SPI 드라이버로 바꿔 구현해 보았다.

 

GPIO 사용 버전 링크 : https://eteo.tistory.com/81

 

STM32 , 74HC595 시프트 레지스터로 FND 제어하기 , 카운터 / 시계 ( SysTick 타이머 사용)

시프트 레지스터 두개로 4개의 FND를 제어할 수 있는 아두이노 호환보드를 사용하여 카운터 및 시계를 만들어 보겠습니다. 0부터 9999 까지 세는 카운터 시계, 왼쪽 두자리가 분이고 오른쪽 두자리

eteo.tistory.com

 

 

 

설정변경

 

 

칩에 쓰기만 하는거라 Transmit Only Master 를 선택해 주었고 데이터시트를 보니 CS(=SS)핀은 필요 없다.

 

따라서 SCLK 와 MOSI 핀만 활성화 된다. 물리적으로 핀도 이전과는 바꿔서 꼽아햐한다.

 

 

 

Prescaler 설정에 따라 Baud Rate 가 바뀌는데 적당히 선택해 주었다. CPOL과 CPHA는 칩의 데이터 시트를 읽고 설정해준다. 

 

 

 

 

/* USER CODE BEGIN 4 */
void DisplayFND(uint8_t fnd, uint8_t location){

	HAL_GPIO_WritePin(GPIOF, GPIO_PIN_14, 0);	// latch clock pin off

	uint16_t data = 0;
	data = fnd << 8;
	data |= 1 << (location-1);
	uint8_t lo = 1 << (location-1);

//	for(int i=0 ; i<16; i++){
//
//		if(data >> (15-i) & 1){	// MSB first in
//			HAL_GPIO_WritePin(GPIOF, GPIO_PIN_12, 1);	// Data line
//		}else {
//			HAL_GPIO_WritePin(GPIOF, GPIO_PIN_12, 0);	// Data line
//		}
//
//		HAL_GPIO_WritePin(GPIOF, GPIO_PIN_13, 1);	// shift clock pin on
//		HAL_GPIO_WritePin(GPIOF, GPIO_PIN_13, 0);	// shift clock pin off
//
//	}

	HAL_SPI_Transmit(&hspi1, &fnd, 1, 10);
	HAL_SPI_Transmit(&hspi1, &lo, 1, 10);

	HAL_GPIO_WritePin(GPIOF, GPIO_PIN_14, 1);	// latch clock pin on
}

 

실제 칩에 데이터를 쓰는 부분인데 지금 주석처리한 부분을 HAL_SPI_Transmit() 함수를 사용하여 단 두 줄로 바꾸었다.

 

 

 

 

 

결과물