STM32G474 + ST7789 + LVGL 포팅 - RGB Swap

지난 포스트에서 LVGL이 동작하는 것을 확인하였다. 하지만 색상이 이상하게 출력되는 문제가 있어 그것을 수정한 것을 정리한다.

 

STM32G474 + ST7789 + LVGL 포팅

신규 프로젝트가 LCD 패널이 추가되면서, UI 출력이 필요한 상황이라 예전에 STM32F429에 LVGL 라이브러리를 포팅한 경험이 있어 이것을 사용하려고 한다. LVGL 라이브러리는 MIT 라이센서로 상용으로

memories.tistory.com

LVGL RGB 순서가 ST7789 IC의 맵핑이 BRG되어 있어 아래 이미지와 같은 현상이 발생한다. 이것은 그래픽 라이브러리 포팅할 때 빈번하게 일어나는 문제이며, 셋톱박스 개발할 때도 이런 문제 때문에 스왑함수를 만들어 해결하기도 하였다.

그런데, 여기서 약간 헷갈린 상황이 과연 RGB 핀맵이 스왑된 것인지 LVGL 라이브러리가 BRG 순서로 구성된 것인지 확인이 필요하다. 그래서 ST7789 IC 검증코드에서 작업한 small.png 이미지 파일을 아래 깃허브의 PNG2RGB565 변환 파이썬 프로그램을 사용한 것과 LVGL 사이트에서 제공하는 변환툴을 사용했을 때의 차이를 알아보고자 한다.

 

GitHub - jimmywong2003/PNG-to-RGB565: Python Script how to convert the PNG to RGB565 format

Python Script how to convert the PNG to RGB565 format - jimmywong2003/PNG-to-RGB565

github.com

LVGL 변환툴은 uint8_t 배열로 변환하며, 위 파이썬 프로그램은 uint16_t 배열로 변환되기 때문에 이것을 다시 uint8_t 변환하도록 간단하 C 코드를 작성하였다. 파이썬을 수정하도록 하였으나, 잘 안되어... 우선 간단하게 C로 변환 코드를 작성하였다.

#include <stdio.h>
#include <stdlib.h>
#include "s.h"

int main(void) 
{
  FILE *f = fopen("small", "w");
  printf("img_size = %ld\n", sizeof(img));
  fprintf(f, "const uint8_t img[] = {\n  ");
  for(int i = 0 ; i < sizeof(img) / 2 ; i++) {
    fprintf(f, "0x%02x, ", (img[i] & 0x00FF));
    fprintf(f, "0x%02x, ", (img[i] & 0xFF00) >> 8);
    if ((i % 0x10) == 0x0F) {
      fprintf(f, "\n  ");
    }
  }
  fprintf(f, "};\n");
  fclose(f);
  return 0;
}

아래 이미지의 좌측은 PNG2RGB565 변환 툴을 사용한 것이고, 우측은 LVGL 변환 툴을 사용한 것이다. 

원본 이미지를 PNG2RGB565로 사용하여 SPI로 전송한 경우 문제없이 출력되지만, LVGL 변환 툴을 사용한 것은 RGB가 라이브러리 내부에서 순서가 틀리다는 것을 확인하였다. 따라서 LVGL 위젯 컴포넌트 및 변환 툴을 사용하여 LCD 모듈에 출력할 경우 LVGL 전체 출력 색상이 잘못되는 것이다. 따라서 LVGL 라이브러리 레벨에서 RGB 스왑시키는 시프트 연산이 필요하다.

 

다행스럽게도 LVGL 라이브러리에서 스왑함수를 제공하고 있어 아래와 같이 LCD 모듈에 Blit(flush)하기 전에 이미지 데이터를 스왑하면 간단하게 이 문제를 해결할 수 있다.

diff --git a/G474/LVGL/Core/Src/main.c b/G474/LVGL/Core/Src/main.c
index 67a39ef..37066fd 100644
--- a/G474/LVGL/Core/Src/main.c
+++ b/G474/LVGL/Core/Src/main.c
@@ -80,6 +80,8 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
   }
 }

+#include "small.h"
+#include "small_lvgl.h"
 #include "lvgl/src/drivers/display/st7789/lv_st7789.h"
 #include "lvgl/demos/lv_demos.h"

@@ -125,6 +127,8 @@ static void lcd_send_color(lv_display_t *disp, const uint8_t *cmd, size_t cmd_si
   {
     while(param_size > 0) {
       uint16_t length = param_size > SPI_MAX_BUF ? SPI_MAX_BUF : param_size;
+
+      lv_draw_sw_rgb565_swap(param, length/2);
       for (int i = 0 ; i < length ; i++) send_data[i] = (0x01 << 8) | param[i];
       HAL_SPI_Transmit(&hspi2, (uint8_t *)send_data, length, BUS_SPI1_POLL_TIMEOUT);
       param += length;
@@ -217,7 +221,10 @@ int main(void)
   }
   lv_display_set_buffers(lcd_disp, buf1, buf2, buf_size, LV_DISPLAY_RENDER_MODE_PARTIAL);
   // lv_demo_widgets();
-  rgb_color();
+  // rgb_color();
+  lv_obj_t* img = lv_image_create(lv_screen_active());
+  lv_image_set_src(img, &small_lvgl);
+  lv_obj_align(img, LV_ALIGN_CENTER, 0, 0);
   /* USER CODE END 2 */

여기서 주의할 점은 lv_draw_sw_rgb565_swap() 함수에서 length 파라미터는 uint16_t 사이즈로 계산해야 한다. 따라서 length / 2로 설정되어야 하며, 이것을 입력할 경우 잘못된 시프트연산으로 인해 시스템 행업이 발생한다.

 

아래 깃허브에서 전체 코드를 다운받을 수 있다.

 

 

STM32/G474/LVGL at master · highgon2/STM32

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

github.com