์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

ESP32-S3 ๊ธฐ๋ฐ˜ LVGL RGB ๋””์Šคํ”Œ๋ ˆ์ด ํ™”๋ฉด ์ฐข์–ด์ง ํ•ด๊ฒฐ ๋ฐ ๋ฐ”์šด์Šค ๋ฒ„ํผ ์ตœ์ ํ™” ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

๊ฒŒ์ž„๊ต์ˆ˜ 2026. 9. 1. 19:02
๋ฐ˜์‘ํ˜•

๐ŸŒ English Abstract

This technical article provides a deep dive into resolving screen tearing issues and optimizing DMA transmission bandwidth in ESP32-S3 based RGB display applications using LVGL. It explains the architectural bottlenecks of PSRAM latency during continuous pixel streaming and demonstrates how to effectively implement and tune Bounce Buffers in ESP-IDF. Developers will learn practical configuration strategies, memory allocation techniques, and clock timing adjustments to achieve smooth 60 FPS graphical user interfaces.

ESP32-S3๋Š” ๊ณ ์„ฑ๋Šฅ ๋“€์–ผ ์ฝ”์–ด MCU์™€ ํ•จ๊ป˜ ํ’๋ถ€ํ•œ GPIO, ๊ทธ๋ฆฌ๊ณ  Octal SPI PSRAM(Pseudo Static RAM)์„ ์ง€์›ํ•˜์—ฌ ์ž„๋ฒ ๋””๋“œ GUI ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ ๋งค์šฐ ๋งค๋ ฅ์ ์ธ ์†”๋ฃจ์…˜์œผ๋กœ ์ž๋ฆฌ ์žก์•˜์Šต๋‹ˆ๋‹ค. ํŠนํžˆ LVGL(Light and Versatile Graphics Library)๊ณผ 16๋น„ํŠธ RGB ์ธํ„ฐํŽ˜์ด์Šค(RGB565)๋ฅผ ์กฐํ•ฉํ•˜๋ฉด ๊ณ ํ•ด์ƒ๋„(์˜ˆ: 800x480, 480x480)์˜ ๋””์Šคํ”Œ๋ ˆ์ด์—์„œ๋„ ํ™”๋ คํ•œ ๊ทธ๋ž˜ํ”ฝ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ ์‹ค์ œ๋กœ ๋””๋ฐ”์ด์Šค๋ฅผ ๊ตฌ๋™ํ•ด ๋ณด๋ฉด, ํ™”๋ฉด์ด ๋น ๋ฅด๊ฒŒ ์ „ํ™˜๋˜๊ฑฐ๋‚˜ ๋ Œ๋”๋ง ๋ถ€ํ•˜๊ฐ€ ๋†’์•„์งˆ ๋•Œ ํ™”๋ฉด ์ฐข์–ด์ง(Tearing Effect) ํ˜„์ƒ์ด๋‚˜ ์ž”์ƒ, ์‹ฌ์ง€์–ด ํ”ฝ์…€ ๋ฐ์ดํ„ฐ๊ฐ€ ๋ฐ€๋ฆฌ๋Š” ํ˜„์ƒ์„ ์ž์ฃผ ๊ฒช๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ESP32-S3์˜ ๋‚ด๋ถ€ ๊ตฌ์กฐ์™€ PSRAM ์•ก์„ธ์Šค ๋Œ€์—ญํญ ์ œํ•œ์—์„œ ๋น„๋กฏ๋˜๋Š” ์ „ํ˜•์ ์ธ DMA ์–ธ๋”๋Ÿฐ(DMA Underrun) ๋ฌธ์ œ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

๋ณธ ๊ธ€์—์„œ๋Š” ESP32-S3 ๋ฐ LVGL ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ๊ฐœ๋ฐœ ์‹œ ๋ฐœ์ƒํ•˜๋Š” RGB ๋””์Šคํ”Œ๋ ˆ์ด ๋Œ€์—ญํญ ๋ถ€์กฑ ๋ฌธ์ œ์˜ ๊ทผ๋ณธ์ ์ธ ์›์ธ์„ ํ•˜๋“œ์›จ์–ด/ํ”Œ๋ž˜์‹œ ๋ฉ”๋ชจ๋ฆฌ ์•„ํ‚คํ…์ฒ˜ ๊ด€์ ์—์„œ ๋ถ„์„ํ•˜๊ณ , ์ด๋ฅผ ์™„๋ฒฝํ•˜๊ฒŒ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•œ ๋ฐ”์šด์Šค ๋ฒ„ํผ(Bounce Buffer) ์ตœ์ ํ™” ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๊ธฐ์ˆ ์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.



1. ํ™”๋ฉด ์ฐข์–ด์ง(Tearing Effect)์˜ ๊ทผ๋ณธ์ ์ธ ์›์ธ ๋ถ„์„

ESP32-S3์˜ RGB ํŒจ๋„ ์ปจํŠธ๋กค๋Ÿฌ๋Š” LCD์— ์ง€์†์ ์œผ๋กœ ํ”ฝ์…€ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•˜๊ธฐ ์œ„ํ•ด continuous PCLK(Pixel Clock) ์ „์†ก ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. 800x480 ํ•ด์ƒ๋„๋ฅผ 60Hz๋กœ ๊ตฌ๋™ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์•ฝ 24MHz~30MHz์— ๋‹ฌํ•˜๋Š” PCLK๊ฐ€ ํ•„์š”ํ•œ๋ฐ, ์ด ๋ง์€ ๋‹จ ํ•œ ์น˜์˜ ์˜ค์ฐจ๋„ ์—†์ด ์ดˆ๋‹น 25MB ์ด์ƒ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ LCD ์ œ์–ด ํ•€์œผ๋กœ ๋ฟœ์–ด์ ธ ๋‚˜๊ฐ€์•ผ ํ•จ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.

๊ฐ€. PSRAM ์•ก์„ธ์Šค ๋ณ‘๋ชฉ๊ณผ ๋Œ€๊ธฐ ์‹œ๊ฐ„(Latency)

800x480 (16bpp) ํ•ด์ƒ๋„์˜ ํ”„๋ ˆ์ž„ ๋ฒ„ํผ(Framebuffer) ํ•˜๋‚˜๋Š” ์•ฝ 768KB์ž…๋‹ˆ๋‹ค. ESP32-S3 ๋‚ด๋ถ€ SRAM(Internal SRAM)์€ 512KB ์ˆ˜์ค€์ด๋ฏ€๋กœ ์ „์ฒด ํ”„๋ ˆ์ž„ ๋ฒ„ํผ๋ฅผ ๋‚ด๋ถ€ SRAM์— ์˜ฌ๋ฆฌ๋Š” ๊ฒƒ์€ ๋ถˆ๊ฐ€๋Šฅํ•˜๋ฉฐ, ๋ฐ˜๋“œ์‹œ ์™ธ๋ถ€ PSRAM์— ํ• ๋‹นํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • Octal SPI/Quad SPI ์ธํ„ฐํŽ˜์ด์Šค ํ•œ๊ณ„: ์™ธ๋ถ€ PSRAM์€ SPI/OPI ๋ฒ„์Šค๋ฅผ ํ†ตํ•ด ์•ก์„ธ์Šค๋˜๋ฏ€๋กœ ๋‚ด๋ถ€ SRAM์— ๋น„ํ•ด ๋Œ€๊ธฐ ์‹œ๊ฐ„(Access Latency)์ด ๊ธธ๊ณ  ๋Œ€์—ญํญ(Bandwidth)์— ์ œํ•œ์ด ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฒ„์Šค ๊ฒฝํ•ฉ(Bus Contention): GDMA(General DMA)๊ฐ€ LCD ํŒจ๋„๋กœ ํ”ฝ์…€์„ ์ „์†กํ•˜๊ธฐ ์œ„ํ•ด PSRAM์„ ์ฝ๊ณ  ์žˆ๋Š” ์ˆœ๊ฐ„, CPU(LVGL ๋ Œ๋”๋ง ์Šค๋ ˆ๋“œ)๋‚˜ Wi-Fi/Bluetooth ์Šคํƒ, Flash ์ฝ๊ธฐ ์ž‘์—…์ด ๋™์‹œ์— PSRAM/Flash ๋ฒ„์Šค๋ฅผ ์ ์œ ํ•˜๋ ค๊ณ  ํ•˜๋ฉด DMA ์ฝ๊ธฐ ์ง€์—ฐ์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • DMA ์–ธ๋”๋Ÿฐ(DMA Underrun): PCLK๋Š” ๊ณ„์† ๋›ฐ๊ณ  ์žˆ๋Š”๋ฐ DMA๊ฐ€ PSRAM์—์„œ ํ”ฝ์…€ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๋•Œ ๊ฐ€์ ธ์˜ค์ง€ ๋ชปํ•˜๋ฉด, LCD ์ปจํŠธ๋กค๋Ÿฌ๋Š” ์ด์ „ ๋ฐ์ดํ„ฐ๋ฅผ ํ˜๋ ค๋ณด๋‚ด๊ฑฐ๋‚˜ ์ž˜๋ชป๋œ ํ”ฝ์…€์„ ์ถœ๋ ฅํ•˜์—ฌ ํ™”๋ฉด์ด ์ฐข์–ด์ง€๊ฑฐ๋‚˜ ๋œ์ปฅ๊ฑฐ๋ฆฌ๋Š” ํ˜„์ƒ์ด ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค.

๋‚˜. LVGL ๋ Œ๋”๋ง ํƒ€์ด๋ฐ๊ณผ V-Sync ๋ถˆ์ผ์น˜

LVGL์ด ํ”„๋ ˆ์ž„ ๋ฒ„ํผ๋ฅผ ๊ฐฑ์‹ ํ•˜๋Š” ์‹œ์ ๊ณผ RGB ํŒจ๋„ ๋””์Šคํ”Œ๋ ˆ์ด๊ฐ€ ํ™”๋ฉด์„ ์Šค์บ”ํ•˜๋Š” ์‹œ์ ์ด ๋™๊ธฐํ™”(Vertical Synchronization, V-Sync)๋˜์ง€ ์•Š์œผ๋ฉด, ์Šค์บ” ์ค‘๊ฐ„์— ๋ฒ„ํผ์˜ ํ”ฝ์…€ ๊ฐ’์ด ๋ฐ”๋€Œ์–ด ํ”„๋ ˆ์ž„ ์ƒ๋‹จ๊ณผ ํ•˜๋‹จ์˜ ๊ทธ๋ฆผ์ด ์„œ๋กœ ๋‹ค๋ฅธ ํ”„๋ ˆ์ž„์˜ ์ด๋ฏธ์ง€๋ฅผ ๋‚˜ํƒ€๋‚ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

2. ๊ตฌ์›ํˆฌ์ˆ˜: ๋ฐ”์šด์Šค ๋ฒ„ํผ(Bounce Buffer)์˜ ๊ตฌ์กฐ์™€ ๋™์ž‘ ์›๋ฆฌ

์ด ๋Œ€์—ญํญ ๋ณ‘๋ชฉ ํ˜„์ƒ์„ ์™„ํ™”ํ•˜๊ธฐ ์œ„ํ•ด ESP-IDF์˜ LCD RGB ๋“œ๋ผ์ด๋ฒ„๋Š” ๋ฐ”์šด์Šค ๋ฒ„ํผ(Bounce Buffer)๋ผ๋Š” ์ค‘๊ณ„ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

[PSRAM Framebuffer (Large & High Latency)]
                | (Task/EDMA Copy)
                ↓
[Bounce Buffer in Internal SRAM (Fast & DMA Capable)]
                | (Hardware GDMA Stream)
                ↓
[RGB Display Panel]

๋ฐ”์šด์Šค ๋ฒ„ํผ๋Š” PSRAM์— ์กด์žฌํ•˜๋Š” ๊ฑฐ๋Œ€ํ•œ ํ”„๋ ˆ์ž„ ๋ฒ„ํผ์—์„œ ๋ช‡ ๋ผ์ธ(Line) ๋ถ„๋Ÿ‰์˜ ์ž‘์€ ํ”ฝ์…€ ๋ฐ์ดํ„ฐ ์กฐ๊ฐ์„ ์ดˆ๊ณ ์† ๋‚ด๋ถ€ SRAM(Internal SRAM)์— ๋จผ์ € ๋ณต์‚ฌํ•ด ๋‘” ๋’ค, GDMA๊ฐ€ ๋‚ด๋ถ€ SRAM์œผ๋กœ๋ถ€ํ„ฐ ์‹ค์‹œ๊ฐ„์œผ๋กœ LCD ํŒจ๋„์— ๋ฐ์ดํ„ฐ๋ฅผ ์˜์•„์ฃผ๋„๋ก ํ•˜๋Š” ๊ธฐ๋ฒ•์ž…๋‹ˆ๋‹ค.

  • ๋‚ด๋ถ€ SRAM์€ ๋Œ€๊ธฐ ์‹œ๊ฐ„์ด 0์— ๊ฐ€๊น๊ณ  ๋ฒ„์Šค ๋Œ€์—ญํญ์ด ๋งค์šฐ ๋„“์–ด PCLK ์ „์†ก ์‹œ DMA ์–ธ๋”๋Ÿฐ์ด ๊ฑฐ์˜ ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
  • ์ฃผ๋กœ 2๊ฐœ์˜ ๋ฐ”์šด์Šค ๋ฒ„ํผ๋ฅผ ํ•‘ํ(Ping-Pong) ๊ตฌ์กฐ๋กœ ์šด์šฉํ•˜์—ฌ, ํ•˜๋‚˜๊ฐ€ LCD๋กœ ์ „์†ก๋˜๋Š” ๋™์•ˆ ๋‹ค๋ฅธ ํ•˜๋‚˜๋Š” PSRAM์œผ๋กœ๋ถ€ํ„ฐ ๋‹ค์Œ ๋ผ์ธ ๋ฐ์ดํ„ฐ๋ฅผ ์ฑ„์›Œ ๋„ฃ๋Š” ๋ฐฉ์‹์„ ์ทจํ•ฉ๋‹ˆ๋‹ค.


3. ์‹ค๋ฌด ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๋ฐ ๋ฐ”์šด์Šค ๋ฒ„ํผ ์ตœ์ ํ™” ์„ค์ •

ESP-IDF ํ™˜๊ฒฝ์—์„œ `esp_lcd` ๋””์Šคํ”Œ๋ ˆ์ด ๋“œ๋ผ์ด๋ฒ„ ๊ตฌ์„ฑ ์‹œ ํ™”๋ฉด ์ฐข์–ด์ง์„ ์žก๊ธฐ ์œ„ํ•œ ๋‹จ๊ณ„๋ณ„ ํŠœ๋‹ ์ ˆ์ฐจ๋ฅผ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๊ฐ€. `esp_lcd_rgb_panel_config_t` ํ•ต์‹ฌ ํŒŒ๋ผ๋ฏธํ„ฐ ์ตœ์ ํ™”

RGB ํŒจ๋„์„ ์ดˆ๊ธฐํ™”ํ•  ๋•Œ ๊ตฌ์กฐ์ฒด ์„ค์ •์—์„œ ๋ฐ”์šด์Šค ๋ฒ„ํผ์™€ DMA ํ”„๋ ˆ์ž„๋ฒ„ํผ ์˜ต์…˜์„ ์„ธ๋ฐ€ํ•˜๊ฒŒ ์กฐ์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

esp_lcd_rgb_panel_config_t panel_config = {
    .clk_src = LCD_CLK_SRC_DEFAULT,
    .timings = {
        .pclk_hz = 16 * 1000 * 1000, // PCLK ๋Œ€์—ญํญ ์กฐ์ ˆ (ํ•„์š” ์‹œ ๋‹ค์šดํด๋Ÿญ)
        .h_res = 800,
        .v_res = 480,
        .hsync_pulse_width = 4,
        .hsync_back_porch = 8,
        .hsync_front_porch = 8,
        .vsync_pulse_width = 4,
        .vsync_back_porch = 8,
        .vsync_front_porch = 8,
        .flags.pclk_active_neg = true,
    },
    .data_width = 16, // RGB565
    .bits_per_pixel = 16,
    .bounce_buffer_size_px = 800 * 10, // ๋ผ์ธ ๋‹จ์œ„ ๋ฐ”์šด์Šค ๋ฒ„ํผ ํฌ๊ธฐ (10 Line)
    .sram_trans_align = 64,
    .psram_trans_align = 64,
    .flags = {
        .fb_in_psram = 1, // ํ”„๋ ˆ์ž„ ๋ฒ„ํผ๋ฅผ PSRAM์— ๋ฐฐ์น˜
        .double_fb = 1,    // ๋”๋ธ” ๋ฒ„ํผ๋ง ์ ์šฉ (LVGL Direct mode ์—ฐ๋™ ์‹œ ํ•„์ˆ˜)
        .no_fb = 0,
        .bb_invalidate_cache = 1, // Cache coherent ์œ ์ง€
    }
};

๋‚˜. ๋ฐ”์šด์Šค ๋ฒ„ํผ ํฌ๊ธฐ ๊ณ„์‚ฐ ๋ฐ ์ตœ์ ์  ์ฐพ๊ธฐ

`bounce_buffer_size_px` ํŒŒ๋ผ๋ฏธํ„ฐ๋Š” ํ™”๋ฉด ์ฐข์–ด์ง๊ณผ ๋ฉ”๋ชจ๋ฆฌ ์†Œ๋ชจ ์‚ฌ์ด์˜ ํŠธ๋ ˆ์ด๋“œ์˜คํ”„(Trade-off)๋ฅผ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค.

  • ๋„ˆ๋ฌด ์ž‘์€ ๊ฐ’ (์˜ˆ: 1~2 ๋ผ์ธ): ๋‚ด๋ถ€ SRAM ์•„๋‚Œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•˜๋‚˜, PSRAM ์ฝ๊ธฐ ์ง€์—ฐ์„ ์ถฉ๋ถ„ํžˆ ๋ฐฉ์–ดํ•˜์ง€ ๋ชปํ•ด ์ฐข์–ด์ง ์ง€์† ๋ฐœ์ƒ.
  • ์ ์ • ์ถ”์ฒœ ๊ฐ’ (10 ~ 20 ๋ผ์ธ): 800px ๋„ˆ๋น„ ๊ธฐ์ค€ 10 ๋ผ์ธ์€ `800 * 10 * 2byte = 16KB` ์†Œ๋ชจ. ํ•‘ํ ๋ฒ„ํผ ๊ตฌ์กฐ๋กœ 2๊ฐœ๊ฐ€ ํ• ๋‹น๋˜๋ฏ€๋กœ ์ด 32KB์˜ ๋‚ด๋ถ€ SRAM ์‚ฌ์šฉ. PSRAM์˜ ๋กฑ ํŠธ๋žœ์žญ์…˜ ์ง€์—ฐ์„ ์ถฉ๋ถ„ํžˆ ํก์ˆ˜ํ•จ.
  • ๋„ˆ๋ฌด ํฐ ๊ฐ’ (์˜ˆ: 50 ๋ผ์ธ ์ด์ƒ): ๋‚ด๋ถ€ DRAM(SRAM) ๋ถ€์กฑ์œผ๋กœ ์ธํ•œ `ESP_ERR_NO_MEM` ์—๋Ÿฌ ๋ฐœ์ƒ ์œ„ํ—˜.

๋‹ค. PCLK(Pixel Clock) ์ฃผํŒŒ์ˆ˜ ๋‹ค์šดํด๋Ÿญ ํŠœ๋‹

ํŒจ๋„์˜ ์ŠคํŽ™ํŠธ์— ๊ธฐ์žฌ๋œ ์ตœ๋Œ€ PCLK(์˜ˆ: 30MHz)๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•˜๋ฉด PSRAM ์š”๊ตฌ ๋Œ€์—ญํญ์ด ์นฉ์…‹์˜ ๋ฌผ๋ฆฌ์  ํ•œ๊ณ„๋ฅผ ์ดˆ๊ณผํ•ฉ๋‹ˆ๋‹ค.

  • PCLK๋ฅผ 16MHz ~ 20MHz ์ˆ˜์ค€์œผ๋กœ ๋‚ฎ์ถ”๋ฉด, ์ดˆ๋‹น ํ•„์š”ํ•œ DMA ๋ฐ์ดํ„ฐ ์ „์†ก๋Ÿ‰์ด ๊ฐ์†Œํ•˜์—ฌ PSRAM ๋Œ€์—ญํญ์— ์—ฌ์œ ๊ฐ€ ์ƒ๊น๋‹ˆ๋‹ค.
  • Refresh Rate๊ฐ€ ์•ฝ 45Hz~50Hz๋กœ ์•ฝ๊ฐ„ ๋‚ฎ์•„์ง€์ง€๋งŒ, ํ™”๋ฉด ์ฐข์–ด์ง๊ณผ ๋…ธ์ด์ฆˆ ํ”ฝ์…€์ด ์™„์ „ํžˆ ์‚ฌ๋ผ์ง€๋Š” ์•ˆ์ •์ ์ธ ๊ฒฐ๊ณผ๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ผ. LVGL ๋ฒ„ํผ ๋ชจ๋“œ ์„ค์ • (Direct Mode vs Full Refresh)

LVGL ๋“œ๋ผ์ด๋ฒ„ ์„ค์ • ์‹œ `LV_DISP_RENDER_MODE_DIRECT` ๋ชจ๋“œ๋ฅผ ํ™œ์šฉํ•˜๋Š” ๊ฒƒ์ด ๊ณ ํ•ด์ƒ๋„ PSRAM ํ™˜๊ฒฝ์—์„œ ๋งค์šฐ ํšจ๊ณผ์ ์ž…๋‹ˆ๋‹ค.

// LVGL Display Driver ์ดˆ๊ธฐํ™” ์˜ˆ์‹œ
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);

// PSRAM์— ์ƒ์„ฑ๋œ ๋‘ ๊ฐœ์˜ ํ”„๋ ˆ์ž„ ๋ฒ„ํผ ํฌ์ธํ„ฐ ์ง€์ •
disp_drv.draw_buf = &disp_buf;
disp_drv.buf1 = fb1_psram_ptr; 
disp_drv.buf2 = fb2_psram_ptr;

// Direct Mode๋ฅผ ํ†ตํ•ด ๋ณ€๊ฒฝ๋œ ์˜์–ต๋งŒ ํ”„๋ ˆ์ž„ ๋ฒ„ํผ ๊ฐ„ ๋™๊ธฐํ™” ๋ Œ๋”๋ง
disp_drv.render_mode = LV_DISP_RENDER_MODE_DIRECT;
disp_drv.flush_cb = rgb_lcd_lvgl_flush_cb;
lv_disp_drv_register(&disp_drv);

Direct Mode๋Š” ํ™”๋ฉด ์ „์ฒด๋ฅผ ๋งค๋ฒˆ ์ƒˆ๋กœ ๊ทธ๋ฆฌ๋Š” ๋Œ€์‹  ๋ณ€๊ฒฝ๋œ ์œ„์ ฏ ์˜์—ญ๋งŒ PSRAM ํ”„๋ ˆ์ž„ ๋ฒ„ํผ์— ์ง์ ‘ ๋ฎ์–ด์“ฐ๋ฏ€๋กœ PSRAM ์“ฐ๊ธฐ ํŠธ๋ž˜ํ”ฝ์„ ๋น„์•ฝ์ ์œผ๋กœ ์ค„์—ฌ์ค๋‹ˆ๋‹ค.

4. ์—ฃ์ง€ ์ผ€์ด์Šค(Edge Cases) ๋Œ€์‘ ๋ฐ ์„ฑ๋Šฅ ๊ณ ๋„ํ™” ์ „๋žต

๋ฐ”์šด์Šค ๋ฒ„ํผ๋ฅผ ์„ค์ •ํ–ˆ์Œ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ํŠน์ • ์ƒ์šฉํ™” ์กฐ๊ฑด์—์„œ ์ฐข์–ด์ง์ด๋‚˜ ํ”„๋ ˆ์ž„ ๋“œ๋ž์ด ์žฌ๋ฐœํ•˜๋Š” ์—ฃ์ง€ ์ผ€์ด์Šค๋“ค์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

๊ฐ€. Wi-Fi ๋ฐ Bluetooth ๋™์‹œ ๋™์ž‘ ์‹œ ๋ฉ”๋ชจ๋ฆฌ ๋ฒ„์Šค ๊ฐ„์„ญ

ESP32-S3์—์„œ Wi-Fi ์Šคํƒ์ด ํ™œ์„ฑํ™”๋˜๋ฉด ์•”ํ˜ธํ™” ์—”์ง„ ๋ฐ ํŒจํ‚ท ๋ฒ„ํผ ์ž‘์—…์œผ๋กœ ์ธํ•ด ๋‚ด๋ถ€ Interconnect ๋ฒ„์Šค์˜ ์ ์œ ์œจ์ด ํญ์ฆํ•ฉ๋‹ˆ๋‹ค. ์ด๋กœ ์ธํ•ด DMA ํ”„๋ ˆ์น˜(Fetch) ๋Œ€๊ธฐ ์‹œ๊ฐ„์ด ๊ธธ์–ด์ง‘๋‹ˆ๋‹ค.

  • ํ•ด๊ฒฐ์ฑ…: Menuconfig์—์„œ `CONFIG_SPIRAM_TRY_ALLOCATE_WIFI_LWIP` ์˜ต์…˜์„ ๋„๊ณ , Wi-Fi ๋ฒ„ํผ๋Š” ๊ฐ€๋Šฅํ•œ ๋‚ด๋ถ€ SRAM์—๋งŒ ๋ฐฐ์น˜ํ•˜์—ฌ PSRAM ๋ฒ„์Šค๋ฅผ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋„๋ก ๋…๋ฆฝ์‹œํ‚ต๋‹ˆ๋‹ค.
  • LVGL ๋ Œ๋”๋ง ํƒœ์Šคํฌ์™€ RGB DMA ๊ด€๋ จ ํƒœ์Šคํฌ์˜ CPU Core Affinity(์ฝ”์–ด ๊ณ ์ •)๋ฅผ ๋ถ„๋ฆฌํ•ฉ๋‹ˆ๋‹ค. (์˜ˆ: Core 0 = Wi-Fi/System, Core 1 = LVGL GUI Task).

๋‚˜. PSRAM ์†๋„ ๋ชจ๋“œ ํ™•์ธ (Quad vs Octal 80MHz)

ESP32-S3 ๋ชจ๋“ˆ ์นฉ์…‹์— ๋”ฐ๋ผ PSRAM ์—ฐ๊ฒฐ ๋ฐฉ์‹์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค. N8R8, N16R8 ๋“ฑ ๋’ค์— 'R8'์ด ๋ถ™์€ ๋ชจ๋“ˆ์€ 8-line Octal SPI PSRAM์„ ํƒ‘์žฌํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

  • `sdkconfig` ์„ค์ •์—์„œ PSRAM Clock์ด 80MHz Flash / 80MHz PSRAM (Octal DDR)๋กœ ์ •์ƒ ์„ค์ •๋˜์–ด ์žˆ๋Š”์ง€ ํ•„ํžˆ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • 40MHz๋กœ ํ•˜ํ–ฅ ์„ค์ •๋˜์–ด ์žˆ๊ฑฐ๋‚˜ Quad mode๋กœ ๋™์ž‘ ์ค‘์ด๋ผ๋ฉด RGB565 800x480 ํ™”๋ฉด ๊ตฌ๋™ ์‹œ ๋Œ€์—ญํญ ๋ฏธ๋‹ฌ๋กœ ๋ฌด์กฐ๊ฑด ์ฐข์–ด์ง์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

๋‹ค. V-Sync ์ธํ„ฐ๋ŸฝํŠธ๋ฅผ ์ด์šฉํ•œ ๋ ˆ์ง€์Šคํ„ฐ ํ”„๋ ˆ์ž„ ์Šค์™€ํ•‘

ESP-IDF์˜ `on_vsync` ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ๋“ฑ๋กํ•˜์—ฌ LVGL์ด ๋ Œ๋”๋ง์„ ๋งˆ์นœ ๋ฒ„ํผ๋ฅผ V-Sync ๋ธ”๋žญํฌ ๊ตฌ๊ฐ„(Vertical Blanking Interval)์—์„œ๋งŒ ์ˆ˜ํ‰ ์ „ํ™˜(Swap)ํ•˜๋„๋ก ์ œ์–ดํ•˜์‹ญ์‹œ์˜ค.

static bool on_vsync_event(esp_lcd_panel_handle_t panel, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx)
{
    BaseType_t high_task_awoken = pdFALSE;
    // V-Sync ์‹ ํ˜ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ๋งŒ LVGL Flush ์™„๋ฃŒ ์„ธ๋งˆํฌ์–ด๋ฅผ ํ•ด์ œ
    xSemaphoreGiveFromISR(vsync_sem, &high_task_awoken);
    return high_task_awoken == pdTRUE;
}


5. ๊ฒฐ๋ก  ๋ฐ ์š”์•ฝ ์ฒดํฌ๋ฆฌ์ŠคํŠธ

ESP32-S3 ๊ธฐ๋ฐ˜์—์„œ LVGL๊ณผ RGB ๋””์Šคํ”Œ๋ ˆ์ด๋ฅผ ํ™œ์šฉํ•ด ํ™”๋ฉด ์ฐข์–ด์ง ์—†๋Š” ๋ถ€๋“œ๋Ÿฌ์šด 60 FPS UI๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ํ•ต์‹ฌ ์š”์†Œ๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋ฒ„์Šค ๋Œ€์—ญํญ ๊ด€๋ฆฌ์™€ ๋ฐ”์šด์Šค ๋ฒ„ํผ์˜ ์˜ฌ๋ฐ”๋ฅธ ํ™œ์šฉ์ž…๋‹ˆ๋‹ค.

์ ๊ฒ€ ํ•ญ๋ชฉ ๊ถŒ์žฅ ์ตœ์ ํ™” ์„ค์ •
PSRAM ๋™์ž‘ ๋ชจ๋“œ Octal SPI @ 80MHz (DDR) ํ•„์ˆ˜ ์ ์šฉ
Bounce Buffer ํฌ๊ธฐ ํ•ด์ƒ๋„ ๋„ˆ๋น„ x 10~20 ๋ผ์ธ (๋‚ด๋ถ€ SRAM DMA ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น)
PCLK (Pixel Clock) 16MHz ~ 20MHz ์‚ฌ์ด ํƒ€ํ˜‘์  ๊ฒ€์ƒ‰ (๋Œ€์—ญํญ ๋ถ€์กฑ ์™„ํ™”)
LVGL ๋ Œ๋” ๋ชจ๋“œ Direct Mode (`LV_DISP_RENDER_MODE_DIRECT`) ์‚ฌ์šฉ
๋™๊ธฐํ™” ๊ธฐ์ˆ  RGB Panel V-Sync ์ธํ„ฐ๋ŸฝํŠธ ๊ธฐ๋ฐ˜ ํ”„๋ ˆ์ž„๋ฒ„ํผ ์Šค์™€ํ•‘ ๊ตฌํ˜„

์ž„๋ฒ ๋””๋“œ ๊ทธ๋ž˜ํ”ฝ ๊ฐœ๋ฐœ ์‹œ ์œ„ ๊ฐ€์ด๋“œ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๋””๋ฒ„๊น…๊ณผ ๋ฉ”๋ชจ๋ฆฌ ํŠœ๋‹์„ ์ง„ํ–‰ํ•œ๋‹ค๋ฉด, ESP32-S3์˜ ํ•˜๋“œ์›จ์–ด ํ•œ๊ณ„๋ฅผ ๋„˜์–ด ์Šค๋งˆํŠธ์›Œ์น˜๋‚˜ ๊ณ ๊ธ‰ ๊ฐ€์ „์ œํ’ˆ ์ˆ˜์ค€์˜ ๋งค๋„๋Ÿฝ๊ณ  ์•ˆ์ •์ ์ธ GUI๋ฅผ ์™„์„ฑํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•