STM32 NUCLEO-474RE 개발보드에서 2.4인치 LCD 모듈을 검증하여 간단하게 정리해 둔다.
2.4인치 LCD 모듈은 ST7789 IC를 사용하며, LCD 패널은 GST2D4495을 사용한다. 데이터시트는 아래 자료를 참고하도록 한다.
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 라이브러리를 사용하는 쪽으로 생각하고 있다. 이것은 작업할 때 다시 정리하도록 하겠다.
원본 소스는 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();
}
전체코드는 아래 깃허브를 참고하도록 한다.