STM32G474(Nucleo-G474RE) + ST7789 (LCD IC) 포팅

STM32 NUCLEO-474RE 개발보드에서 2.4인치 LCD 모듈을 검증하여 간단하게 정리해 둔다.

2.4인치 LCD 모듈은 ST7789 IC를 사용하며, LCD 패널은 GST2D4495을 사용한다. 데이터시트는 아래 자료를 참고하도록 한다.

(TFT LCD)24QVGA LCD(EMB)231220 1.pdf
1.91MB
ST7789V2_SPEC_V11_240118_105823 1.pdf
3.08MB

LCD 모듈의 터치스크린 기능이 없기 때문에 ST7789 IC는 CS / SCK / MOSI으로 구성된 SPI 3-wrie 방식으로 제공하며, LCD 패널은 RGB565 방식의 16비트 컬러를 지원한다.

 

SPI 3-Wire 방식은 SPI TX 데이터 전송시 모든 바이트에 대해 Command / Data 인지를 판단할 수 있도록 아래 이미지와 같이 MSB에 1비트를 추가해야 한다. 따라서 이 방식을 사용할 경우 SPI 전송 데이터 크기는 8bit가 아닌 9bit로 설정해야 하며, 모든 데이터에 1비트를 추가하는 작업이 필요하기 때문에 DMA를 사용하는 것이 효율적인가 싶다.

SPI 4-Wire 방식은 DCX 입력으로 SPI TX 데이터가 Command / Data를 판단하기 때문에 SPI 전송 데이터의 크기는 8bit이며, 이 방식인 경우 MSB에 1비트를 추가하는 작업이 생략되므로 DMA 방식을 사용하는 것이 효율적이다.

2.4인치 LCD 모듈이 4-Wire 방식은 없는지 협력사에 확인해 본 결과 미니 사이즈의 LCD 모듈은 대부분 3-Wire 방식이며, 4-Wire 방식이 존재하긴 하지만 자재 수급이 좋지 않기 때문에  제공된 LCD 모듈을 사용하라고 한다. 일단 동작 안되는 것도 아니고 LCD에 최소 표현만 사용할 예정이므로 LCD 출력에 큰 문제가 되지 않을 것으로 판단되어 그냥 사용하기로 하였다.

 

깃허브에 STM32-ST7789 포팅한 소스가 있어, 이것을 그대로 사용하여 위 영상의 예제를 만들어 보았다. 폰트까지 포팅한 예제라 앞으로 진행할 프로젝트에서 이것을 사용할까 하다가 폰트 작업도 노가다이고, 그래픽 라이브러리 없이 생으로 하려니 막막하여 LVGL 라이브러리를 사용하는 쪽으로 생각하고 있다. 이것은 작업할 때 다시 정리하도록 하겠다.

 

GitHub - Floyd-Fish/ST7789-STM32: using STM32's Hardware SPI to drive a ST7789 based IPS displayer

using STM32's Hardware SPI to drive a ST7789 based IPS displayer - Floyd-Fish/ST7789-STM32

github.com

원본 소스는 4-Wire 방식을 사용하기 때문에 SPI 전송하는 부분을 3-Wire 방식으로 수정해야 하며, 가능한 원본 소스를 그대로 사용하기 위해 main.h 파일에서 Nucleo 보드와 ST7789 IC 핀맵을 맞추기 위해 아래와 같이 수정하였다.

#define ST7789_RST_GPIO_Port GPIOB
#define ST7789_RST_Pin       GPIO_PIN_11
#define ST7789_DC_GPIO_Port  GPIOC
#define ST7789_DC_Pin        GPIO_PIN_5
#define ST7789_CS_GPIO_Port  GPIOB
#define ST7789_CS_Pin        GPIO_PIN_12

그리고 SPI 방식은 DMA 방식을 사용하지 않고 일반 방식으로 사용하였지만, 모든 데이터 전송 바이트의 MSB 1비트를 추가하도록 수정하였다.

static void ST7789_WriteCommand(uint8_t cmd)
{
	uint16_t send_cmd = (0x00 << 8) | cmd;

	ST7789_Select();
	ST7789_DC_Clr();
	HAL_SPI_Transmit(&ST7789_SPI_PORT, (uint8_t *)(&send_cmd), 1, HAL_MAX_DELAY);
	ST7789_UnSelect();
}

// #define LENGTH 65535
#define LENGTH 4096
static uint16_t send_data[LENGTH];
static void ST7789_WriteData(uint8_t *buff, size_t buff_size)
{
	ST7789_Select();
	ST7789_DC_Set();

	// split data in small chunks because HAL can't send more than 64K at once

	while (buff_size > 0) {
		uint16_t chunk_size = buff_size > LENGTH ? LENGTH : buff_size;
		for (int i = 0 ; i < chunk_size ; i++) {
			send_data[i] = (0x01 << 8) | buff[i];
		}
		#ifdef USE_DMA
			if (DMA_MIN_SIZE <= buff_size)
			{
				HAL_SPI_Transmit_DMA(&ST7789_SPI_PORT, buff, chunk_size);
				while (ST7789_SPI_PORT.hdmatx->State != HAL_DMA_STATE_READY)
				{}
			}
			else
				HAL_SPI_Transmit(&ST7789_SPI_PORT, buff, chunk_size, HAL_MAX_DELAY);
		#else
			HAL_SPI_Transmit(&ST7789_SPI_PORT, (uint8_t *)send_data, chunk_size, HAL_MAX_DELAY);
		#endif
		buff += chunk_size;
		buff_size -= chunk_size;
	}

	ST7789_UnSelect();
}

static void ST7789_WriteSmallData(uint8_t data)
{
	uint16_t send_cmd = (0x01 << 8) | data;

	ST7789_Select();
	ST7789_DC_Set();
	HAL_SPI_Transmit(&ST7789_SPI_PORT, (uint8_t *)(&send_cmd), 1, HAL_MAX_DELAY);
	ST7789_UnSelect();
}

전체코드는 아래 깃허브를 참고하도록 한다.

 

STM32/G474/ST7789 at master · highgon2/STM32

STM32 TestCode. Contribute to highgon2/STM32 development by creating an account on GitHub.

github.com