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

ESP32-S3 LVGL GUI ํ”„๋ ˆ์ž„ ๋“œ๋กญ ํ•ด๊ฒฐ: PSRAM ๋Œ€์—ญํญ ๋ณ‘๋ชฉ ๊ทน๋ณต๊ณผ Direct Mode ์ด์ค‘ ๋ฒ„ํผ๋ง DMA ์ตœ์ ํ™” ๊ฐ€์ด๋“œ

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

๐ŸŒ English Abstract

This article provides an in-depth technical analysis of resolving GUI frame drops and micro-stuttering in ESP32-S3 applications using the LVGL graphics library. It details how to overcome PSRAM bandwidth bottlenecks, implement LVGL Direct Mode double buffering, and optimize GDMA (General Direct Memory Access) transfers for high-resolution displays. Key real-world hardware integration practices, including core pinning, cache coherence, and memory bus contention tuning, are thoroughly explored.

ESP32-S3๋Š” ๊ณ ์„ฑ๋Šฅ ๋“€์–ผ์ฝ”์–ด MCU์™€ ๋”๋ถˆ์–ด ๋Œ€์šฉ๋Ÿ‰ ์™ธ๋ถ€ PSRAM(Pseudo Static Random Access Memory)์„ ์ง€์›ํ•˜์—ฌ, ์ž„๋ฒ ๋””๋“œ ๋””๋ฐ”์ด์Šค์—์„œ ์ˆ˜๋ คํ•œ ๊ทธ๋ž˜ํ”ฝ ์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค(GUI, Graphical User Interface)๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ฐ ๋งค์šฐ ๋„๋ฆฌ ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํŠนํžˆ ์˜คํ”ˆ์†Œ์Šค ๊ทธ๋ž˜ํ”ฝ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ LVGL(Light and Versatile Graphics Library)๊ณผ์˜ ์กฐํ•ฉ์€ ์Šค๋งˆํŠธ ๊ฐ€์ „, ์‚ฐ์—…์šฉ HMI(Human Machine Interface), ์›จ์–ด๋Ÿฌ๋ธ” ๊ธฐ๊ธฐ ๊ฐœ๋ฐœ์˜ ํ‘œ์ค€์œผ๋กœ ์ž๋ฆฌ๋ฅผ ์žก์•˜์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ 480x480 ๋˜๋Š” 800x480 ํ•ด์ƒ๋„ ์ด์ƒ์˜ LCD(RGB ๋˜๋Š” QSPI/3-Wire SPI ์ธํ„ฐํŽ˜์ด์Šค)๋ฅผ ๊ตฌ๋™ํ•  ๋•Œ, ํ™”๋ฉด์ด ๋Š๊ธฐ๊ฑฐ๋‚˜ ์ฐข์–ด์ง€๋Š” ํ‹ฐ์–ด๋ง ํ˜„์ƒ(Tearing Effect), ๊ทธ๋ฆฌ๊ณ  ํ”„๋ ˆ์ž„ ๋ ˆ์ดํŠธ(FPS)๊ฐ€ 10~20fps ์ˆ˜์ค€์œผ๋กœ ๊ธ‰๊ฒฉํžˆ ๋–จ์–ด์ง€๋Š” ํ”„๋ ˆ์ž„ ๋“œ๋กญ(Frame Drop) ๋ฌธ์ œ๋ฅผ ๋นˆ๋ฒˆํ•˜๊ฒŒ ๊ฒช๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ESP32-S3์˜ ๋™์ž‘ ํด๋Ÿญ์ด 240MHz์— ๋‹ฌํ•จ์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๋ฐœ์ƒํ•˜๋Š” ์ด ๋ฌธ์ œ์˜ ๊ทผ๋ณธ ์›์ธ์€ CPU ์—ฐ์‚ฐ ๋Šฅ๋ ฅ์ด ์•„๋‹Œ PSRAM ๋Œ€์—ญํญ ๋ณ‘๋ชฉ(PSRAM Bandwidth Bottleneck)๊ณผ ์ž˜๋ชป๋œ ๋ฉ”๋ชจ๋ฆฌ ๋ฒ„ํผ๋ง ์ „๋žต์— ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณธ ํฌ์ŠคํŒ…์—์„œ๋Š” ESP32-S3 ๋ฐ LVGL ํ™˜๊ฒฝ์—์„œ ๋ฐœ์ƒํ•˜๋Š” ํ™”๋ฉด ์ €ํ•˜ ํ˜„์ƒ์˜ ํ•˜๋“œ์›จ์–ด์  ์›์ธ์„ ๊ตฌ์กฐ์ ์œผ๋กœ ๋ถ„์„ํ•˜๊ณ , ์ด๋ฅผ ์™„๋ฒฝํžˆ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” Direct Mode ์ด์ค‘ ๋ฒ„ํผ๋ง(Double Buffering) ๊ธฐ์ˆ  ๋ฐ DMA ๋น„๋™๊ธฐ ์ „์†ก(Asynchronous Transfer) ์ตœ์ ํ™” ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๊ธฐ๋ฒ•์„ ์‹ค๋ฌด ๊ด€์ ์—์„œ ๋‹ค๋ฃน๋‹ˆ๋‹ค.



1. ESP32-S3 PSRAM ๋Œ€์—ญํญ ๋ณ‘๋ชฉ์˜ ๊ทผ๋ณธ ์›์ธ ๋ถ„์„

ESP32-S3๋Š” ๋‚ด๋ถ€ SRAM(Internal SRAM, ์•ฝ 512KB)๊ณผ ์™ธ๋ถ€ PSRAM(External PSRAM, ์ตœ๋Œ€ 16MB)์˜ 2๊ณ„์ธต ๋ฉ”๋ชจ๋ฆฌ ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค. ๋””์Šคํ”Œ๋ ˆ์ด ๋ฒ„ํผ(Frame Buffer)์˜ ํฌ๊ธฐ๊ฐ€ 480x480 ํ•ด์ƒ๋„(16๋น„ํŠธ RGB565 ๊ธฐ์ค€ ์•ฝ 460KB) ์ด์ƒ์ด ๋˜๋ฉด ๋‚ด๋ถ€ SRAM๋งŒ์œผ๋กœ๋Š” 2๊ฐœ ์ด์ƒ์˜ ๋ฒ„ํผ๋ฅผ ํ• ๋‹นํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ, ํ•„์ˆ˜์ ์œผ๋กœ PSRAM์— ๋ฒ„ํผ๋ฅผ ๋ฐฐ์น˜ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ณผ์ •์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํ•˜๋“œ์›จ์–ด์  ๋ณ‘๋ชฉ ์š”์†Œ๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

  • Quad PSRAM vs Octal PSRAM์˜ ์•ก์„ธ์Šค ์†๋„ ์ฐจ์ด: Quad SPI(QSPI) PSRAM์€ 4๊ฐœ ๋น„ํŠธ ๋ผ์ธ์„ ์‚ฌ์šฉํ•˜๋ฉฐ 80MHz ๋™์ž‘ ์‹œ ์‹คํšจ ๋Œ€์—ญํญ์ด ์ œํ•œ์ ์ž…๋‹ˆ๋‹ค. ๋ฐ˜๋ฉด Octal SPI(OPI) PSRAM์€ 8๊ฐœ ๋ผ์ธ์„ ์‚ฌ์šฉํ•˜์—ฌ 2๋ฐฐ ์ด์ƒ์˜ ๋Œ€์—ญํญ์„ ์ œ๊ณตํ•˜์ง€๋งŒ, ๋ Œ๋”๋ง๊ณผ ๋””์Šคํ”Œ๋ ˆ์ด ๊ฐฑ์‹ ์ด ๋™์‹œ์— ์ผ์–ด๋‚  ๊ฒฝ์šฐ ์—ฌ์ „ํžˆ ๋ฒ„์Šค ๊ฒฝํ•ฉ(Bus Contention)์ด ์ผ์–ด๋‚ฉ๋‹ˆ๋‹ค.
  • ์บ์‹œ ๋ฏธ์Šค(Cache Miss) ๋ฐ CPU ์Šคํ†จ(CPU Stall): CPU๊ฐ€ PSRAM์— ์ง์ ‘ ์ ‘๊ทผํ•˜์—ฌ ๋””์Šคํ”Œ๋ ˆ์ด ํ”ฝ์…€์„ ๊ทธ๋ฆด ๋•Œ(Software Rendering), L1 ๋ฐ์ดํ„ฐ ์บ์‹œ miss๊ฐ€ ๋ฐœ์ƒํ•˜๋ฉด CPU ํŒŒ์ดํ”„๋ผ์ธ์ด ๋Œ€๊ธฐ ์ƒํƒœ(Stall)์— ๋น ์ง‘๋‹ˆ๋‹ค. ์ด๋Š” LVGL ๋ Œ๋”๋ง ๋ฃจํ”„์˜ ์ „์ฒด ์ฒ˜๋ฆฌ ์‹œ๊ฐ„์„ ์ง€์—ฐ์‹œํ‚ต๋‹ˆ๋‹ค.
  • ๋ฉ”๋ชจ๋ฆฌ ๋ฒ„์Šค ์ ์œ  ๊ฒฝํ•ฉ(Memory Bus Contention): GDMA ์ปจํ…์ŠคํŠธ๊ฐ€ PSRAM์—์„œ ํ”ฝ์…€ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์–ด ๋””์Šคํ”Œ๋ ˆ์ด๋กœ ์†ก์‹ ํ•˜๋Š” ๋™์•ˆ, CPU๋Š” LVGL ์œ„์ ฏ์˜ ๋‹ค์Œ ํ”„๋ ˆ์ž„์„ PSRAM์— ์“ฐ๋ ค๊ณ  ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. ๋™์ผํ•œ SPI/OPI ๋ฒ„์Šค๋ฅผ ๋‘ ์ฃผ์ฒด๊ฐ€ ๋™์‹œ์— ์‚ฌ์šฉํ•˜๋ ค ํ•˜๋ฉด์„œ ๋ณ‘๋ชฉ์ด ๊ทน๋Œ€ํ™”๋ฉ๋‹ˆ๋‹ค.

๊ธฐ์กด ๋ฐฉ์‹(Partial Render Buffer)์˜ ํ•œ๊ณ„

LVGL์˜ ๊ธฐ๋ณธ ๋™์ž‘ ๋ชจ๋“œ์ธ ๋ถ€๋ถ„ ๋ Œ๋”๋ง(Partial Rendering) ๋ชจ๋“œ๋Š” ํ™”๋ฉด ํ•ด์ƒ๋„์˜ 1/10 ํฌ๊ธฐ ๋ฒ„ํผ 1~2๊ฐœ๋ฅผ SRAM์— ๋‘๊ณ  ๋ณ€๊ฒฝ๋œ ์˜์—ญ๋งŒ ๊ทธ๋ฆฌ์ง€๋งŒ, ์ „์ฒด ํ™”๋ฉด์ด ์ง€์†์ ์œผ๋กœ ๋ฐ”๋€Œ๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜์ด๋‚˜ ๊ณ ํ•ด์ƒ๋„ ์ „ํ™˜ ์‹œ ์žฆ์€ Flush Callback ํ˜ธ์ถœ๊ณผ ์˜์—ญ ๊ณ„์‚ฐ ์˜ค๋ฒ„ํ—ค๋“œ๋กœ ์ธํ•ด ์‹ฌ๊ฐํ•œ ๋ณ‘๋ชฉ์„ ์œ ๋ฐœํ•ฉ๋‹ˆ๋‹ค.

2. LVGL Direct Mode ์ด์ค‘ ๋ฒ„ํผ๋ง ๋ชจ๋“œ ์ „ํ™˜ ๋ฐ ๋ฉ”์ปค๋‹ˆ์ฆ˜

ํ”„๋ ˆ์ž„ ๋“œ๋กญ๊ณผ ํ™”๋ฉด ์ฐข์–ด์ง์„ ์™„๋ฒฝํžˆ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ๊ฐ€์žฅ ํšจ๊ณผ์ ์ธ ๊ตฌ๋™ ๋ฐฉ์‹์€ Direct Mode(์ง์ ‘ ๋ชจ๋“œ) ๊ธฐ๋ฐ˜์˜ ์ „์ฒด ํ”„๋ ˆ์ž„ ์ด์ค‘ ๋ฒ„ํผ๋ง(Full Frame Double Buffering)์ž…๋‹ˆ๋‹ค.

๐Ÿ’ก LVGL Direct Mode ํ•ต์‹ฌ ๊ฐœ๋…

Direct Mode์—์„œ๋Š” 2๊ฐœ์˜ ์ „์ฒด ํ•ด์ƒ๋„ ํ”„๋ ˆ์ž„ ๋ฒ„ํผ(FB1, FB2)๋ฅผ PSRAM์— ํ• ๋‹นํ•ฉ๋‹ˆ๋‹ค. ๋””์Šคํ”Œ๋ ˆ์ด ์ปจํŠธ๋กค๋Ÿฌ(LCD Driver)๊ฐ€ FB1์„ DMA๋กœ ๋‹น๊ฒจ์„œ ํ™”๋ฉด์— ์ถœ๋ ฅํ•˜๋Š” ๋™์•ˆ, CPU ๋ฐ LVGL์€ ์™„์ „ํžˆ ๋…๋ฆฝ๋œ FB2์— ๋‹ค์Œ ํ”„๋ ˆ์ž„์˜ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ง์ ‘ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค. ๋ Œ๋”๋ง์ด ์™„๋ฃŒ๋˜๋ฉด ํฌ์ธํ„ฐ๋งŒ ๊ต์ฒด(Pointer Swapping)ํ•ฉ๋‹ˆ๋‹ค.

LVGL v8 / v9 ์„ค์ • ์ฝ”๋“œ ๊ตฌํ˜„

๋‹ค์Œ์€ ESP-IDF ๋ฐ LVGL ํ™˜๊ฒฝ์—์„œ Direct Mode ์ด์ค‘ ๋ฒ„ํผ๋ฅผ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๊ตฌ์„ฑํ•˜๋Š” C์–ธ์–ด ์„ค์ • ์˜ˆ์‹œ์ž…๋‹ˆ๋‹ค.

// LVGL Display Driver ๋ฐ Buffer ์„ค์ • ์˜ˆ์‹œ (LVGL v8 ๊ธฐ์ค€)
static lv_color_t *buf1 = NULL;
static lv_color_t *buf2 = NULL;

void init_lvgl_direct_mode(void) {
    // 1. PSRAM ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น (4byte alignment ์ง€์ •)
    size_t buffer_size = DISP_HOR_RES * DISP_VER_RES * sizeof(lv_color_t);
    
    buf1 = (lv_color_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
    buf2 = (lv_color_t *)heap_caps_malloc(buffer_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);

    assert(buf1 != NULL && buf2 != NULL);

    // 2. LVGL ๋ฒ„ํผ ๊ตฌ์กฐ์ฒด ์ดˆ๊ธฐํ™”
    static lv_disp_draw_buf_t draw_buf;
    lv_disp_draw_buf_init(&draw_buf, buf1, buf2, DISP_HOR_RES * DISP_VER_RES);

    // 3. ๋””์Šคํ”Œ๋ ˆ์ด ๋“œ๋ผ์ด๋ฒ„ ์„ค์ •
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.hor_res = DISP_HOR_RES;
    disp_drv.ver_res = DISP_VER_RES;
    disp_drv.flush_cb = my_lcd_flush_cb;
    disp_drv.draw_buf = &draw_buf;
    
    // โ˜… Direct Mode ํ™œ์„ฑํ™” โ˜…
    disp_drv.direct_mode = 1;
    disp_drv.full_refresh = 0; // Direct Mode ์‚ฌ์šฉ ์‹œ full_refresh๋Š” 0์œผ๋กœ ์„ค์ •

    lv_disp_drv_register(&disp_drv);
}

Direct Mode๋ฅผ ํ™œ์„ฑํ™”ํ•˜๋ฉด LVGL์€ ํ”ฝ์…€ ๋ฐ์ดํ„ฐ๋ฅผ ์ž„์‹œ ๋ฒ„ํผ์—์„œ ํ”„๋ ˆ์ž„ ๋ฒ„ํผ๋กœ ๋ณต์‚ฌํ•˜๋Š” memcpy ๋‹จ๊ณ„๋ฅผ ์™„์ „ํžˆ ์ƒ๋žตํ•˜๊ฒŒ ๋˜์–ด ๋Œ€ํญ์ ์ธ CPU ์ ์œ ์œจ ๊ฐ์†Œ ํšจ๊ณผ๋ฅผ ์–ป๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.



3. GDMA ๋น„๋™๊ธฐ ์ „์†ก ์ตœ์ ํ™” ๋ฐ ์บ์‹œ ์ผ๊ด€์„ฑ(Cache Coherence) ํ•ด๊ฒฐ

Direct Mode ์ ์šฉ ํ›„์—๋„ ํ™”๋ฉด ์ฐŒ๊ทธ๋Ÿฌ์ง์ด๋‚˜ ๋ฌด์ž‘์œ„ ๋…ธ์ด์ฆˆ ํ”ฝ์…€์ด ๋ฐœ์ƒํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Š” ESP32-S3์˜ GDMA(General Direct Memory Access) ์—”์ง„๊ณผ CPU ์บ์‹œ ์‚ฌ์ด์˜ ์บ์‹œ ์ผ๊ด€์„ฑ(Cache Coherence) ๋ถˆ์ผ์น˜ ๋ฐ ์ „์†ก ๋™๊ธฐํ™” ์‹คํŒจ๋กœ ์ธํ•ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

1) esp_lcd ๋“œ๋ผ์ด๋ฒ„์™€ DMA ๋น„๋™๊ธฐ Flush ๊ตฌํ˜„

LVGL์˜ flush_cb ๋‚ด๋ถ€์—์„œ blocking ๋ฐฉ์‹์œผ๋กœ ์ „์†ก ์™„๋ฃŒ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋ฉด ํ”„๋ ˆ์ž„์œจ์ด ๊ทน๋„๋กœ ํ•˜๋ฝํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜๋“œ์‹œ ๋น„๋™๊ธฐ ์ฝœ๋ฐฑ(Asynchronous Interrupt Callback) ๋ฐฉ์‹์œผ๋กœ ์ „์†ก ์™„๋ฃŒ ์‹œ์ ์„ ์ œ์–ดํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

// LCD ์ „์†ก ์™„๋ฃŒ ISR ์ฝœ๋ฐฑ ํ•จ์ˆ˜
static bool notify_lvgl_flush_ready(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx) {
    lv_disp_drv_t *disp_driver = (lv_disp_drv_t *)user_ctx;
    // LVGL์— DMA ์ „์†ก์ด ์™„๋ฃŒ๋˜์—ˆ์Œ์„ ๋น„๋™๊ธฐ๋กœ ์•Œ๋ฆผ
    lv_disp_flush_ready(disp_driver);
    return false;
}

// Flush ์ฝœ๋ฐฑ ๊ตฌํ˜„
static void my_lcd_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p) {
    esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t) drv->user_data;

    // Direct Mode์—์„œ๋Š” color_p๊ฐ€ ์ „์ฒด ํ”„๋ ˆ์ž„ ๋ฒ„ํผ์˜ ์‹œ์ž‘ ์ฃผ์†Œ๋ฅผ ๊ฐ€๋ฆฌํ‚ต๋‹ˆ๋‹ค.
    // ๋น„๋™๊ธฐ๋กœ DMA ์ „์†ก ์‹œ์ž‘
    esp_lcd_panel_draw_bitmap(panel_handle, area->x1, area->y1, area->x2 + 1, area->y2 + 1, color_p);
}

2) PSRAM Cache Write-back ๋ฐ Invalidate ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ…

ESP32-S3 CPU๊ฐ€ PSRAM์— ๋ฐ์ดํ„ฐ๋ฅผ ๊ธฐ๋กํ•  ๋•Œ, ๋ฐ์ดํ„ฐ๋Š” ์ฆ‰๊ฐ PSRAM ์‹ค์ฒด์— ์จ์ง€์ง€ ์•Š๊ณ  L1 Cache์— ๋จธ๋ฌผ๋Ÿฌ ์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(Write-back Cache). ์ด ์ƒํƒœ์—์„œ GDMA๊ฐ€ PSRAM ๋ฉ”์ธ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ๋ฐ”๋กœ ์ฝ์–ด ๋””์Šคํ”Œ๋ ˆ์ด๋กœ ์ „์†กํ•˜๋ฉด ์ด์ „ ํ”„๋ ˆ์ž„์˜ ์ž”์ƒ์ด๋‚˜ ๊ทธ๋ž˜ํ”ฝ ๊นจ์ง์ด ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด esp_lcd ๋“œ๋ผ์ด๋ฒ„ ์„ค์ • ์‹œ DMA ๋‚ด๋ถ€ ํ”Œ๋Ÿฌ์‹œ ์˜ต์…˜์„ ํ™œ์„ฑํ™”ํ•˜๊ฑฐ๋‚˜, ํ”„๋ ˆ์ž„ ์ „์†ก ์ง์ „์— ์บ์‹œ ๋ผ์ธ ํ”Œ๋Ÿฌ์‹œ APIs(esp_cache_msync)๋ฅผ ํ˜ธ์ถœํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  • ESP-IDF ๊ตฌ์„ฑ ์„ค์ •(sdkconfig): CONFIG_SPIRAM_TRY_ALLOCATE_SET_MEM_ON_FAIL=y ๋ฐ CONFIG_CACHE_L1_CACHE_SIZE ์ตœ์ ํ™” ์„ค์ •.
  • SRAM ๋ฐ”์šด๋”ฉ line buffer ์‚ฌ์šฉ: RGB ์ธํ„ฐํŽ˜์ด์Šค(RGB LCD)๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, Framebuffer๋Š” PSRAM์— ๋‘๋˜ bounce_buffer_size๋ฅผ SRAM ๋‚ด๋ถ€ ๋ฉ”๋ชจ๋ฆฌ์— ์ตœ์†Œ 10~20 ๋ผ์ธ ๋ถ„๋Ÿ‰์„ ํ• ๋‹นํ•˜์—ฌ DMA ๋ฒ„์Šค ์ ์œ ์œจ ํƒ€์ž„์•„์›ƒ์„ ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

4. ์‹ค๋ฌด ์—ฃ์ง€ ์ผ€์ด์Šค ๋ฐ ์„ฑ๋Šฅ ๊ทน๋Œ€ํ™” ์ฒดํฌ๋ฆฌ์ŠคํŠธ

ํ•˜๋“œ์›จ์–ด ๋ฐ ๋ฉ€ํ‹ฐํƒœ์Šคํ‚น ํ™˜๊ฒฝ์—์„œ ํ”„๋ ˆ์ž„ ๋“œ๋กญ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ๋ฐ˜๋“œ์‹œ ์ ๊ฒ€ํ•ด์•ผ ํ•  3๊ฐ€์ง€ ์—ฃ์ง€ ์ผ€์ด์Šค ์ตœ์ ํ™” ๊ธฐ๋ฒ•์ž…๋‹ˆ๋‹ค.

1) FreeRTOS ๋ฉ€ํ‹ฐ์ฝ”์–ด ํƒœ์Šคํฌ ๋ถ„๋ฆฌ (Core Pinning)

ESP32-S3๋Š” 2๊ฐœ์˜ ์ฝ”์–ด(Core 0, Core 1)๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. LVGL GUI ํƒ€์ด๋จธ ์ฒ˜๋ฆฌ ๋ฃจํ‹ด๊ณผ Wi-Fi/Bluetooth, ์‚ฌ์šฉ์ž ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ๋กœ์ง์ด ๋™์ผํ•œ ์ฝ”์–ด์—์„œ ์‹คํ–‰๋˜๋ฉด ๋ Œ๋”๋ง ์ฃผ๊ธฐ๊ฐ€ ๋ถˆ๊ทœ์น™ํ•ด์ง‘๋‹ˆ๋‹ค.

  • Core 0: Wi-Fi / Bluetooth ์Šคํƒ, ์‹œ์Šคํ…œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์ž‘์—…, Protocol Task
  • Core 1: LVGL ๋ Œ๋”๋ง ์ „์šฉ ํƒœ์Šคํฌ (lv_timer_handler() ์‹คํ–‰), Touch Driver ์ธํ„ฐ๋ŸฝํŠธ ์ฒ˜๋ฆฌ
// Core 1์— LVGL ์ „์šฉ ํƒœ์Šคํฌ ํ• ๋‹น
xTaskCreatePinnedToCore(
    gui_task,          /* ํƒœ์Šคํฌ ํ•จ์ˆ˜ */
    "LVGL Task",       /* ํƒœ์Šคํฌ ์ด๋ฆ„ */
    1024 * 8,          /* ์Šคํƒ ํฌ๊ธฐ */
    NULL,              /* ๋งค๊ฐœ๋ณ€์ˆ˜ */
    5,                 /* ์šฐ์„ ์ˆœ์œ„ (๋†’๊ฒŒ ์„ค์ •) */
    NULL,              /* ํƒœ์Šคํฌ ํ•ธ๋“ค */
    1                  /* Core 1์— ๊ณ ์ • */
);

2) Octal PSRAM Clocks (80MHz vs 120MHz DDR) ์„ค์ •

ESP32-S3 N16R8 ๋˜๋Š” N8R8 ๋ชจ๋“ˆ๊ณผ ๊ฐ™์ด Octal SPI PSRAM์„ ํƒ‘์žฌํ•œ ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, sdkconfig์—์„œ PSRAM Clock์„ 80MHz OPI ๋ชจ๋“œ๋กœ ์„ค์ •ํ•ด์•ผ๋งŒ ์ดˆ๋‹น 80MB/s ์ด์ƒ์˜ ์ฝ๊ธฐ/์“ฐ๊ธฐ ์†๋„๊ฐ€ ํ™•๋ณด๋ฉ๋‹ˆ๋‹ค. Quad SPI PSRAM ๋ชจ๋“ˆ ์‚ฌ์šฉ ์‹œ์—๋Š” 480x480 ์ด์ƒ์˜ ํ•ด์ƒ๋„์—์„œ 30fps ๋‹ฌ์„ฑ์ด ๊ตฌ์กฐ์ ์œผ๋กœ ์–ด๋ ค์šฐ๋ฏ€๋กœ ์˜ค๋ฒ„ํด๋ก ๋ฐ ํŒŒํ‹ฐ์…œ ๋ Œ๋”๋ง ์กฐํ•ฉ์„ ์—„๊ฒฉํžˆ ๊ฒ€ํ† ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

3) LVGL ๋ Œ๋”๋ง ์˜ต์…˜ ์กฐ์ • (lv_conf.h)

๋ถˆํ•„์š”ํ•œ GPU/SW ์—ฐ์‚ฐ ๋ถ€๋‹ด์„ ์ค„์ด๊ธฐ ์œ„ํ•œ ์ฃผ์š” lv_conf.h ๋งคํฌ๋กœ ์„ค์ • ๊ฐ’์ž…๋‹ˆ๋‹ค.

  • LV_COLOR_DEPTH 16: 16๋น„ํŠธ(RGB565) ์ปฌ๋Ÿฌ ๋ชจ๋“œ ์‚ฌ์šฉ (๋ฉ”๋ชจ๋ฆฌ ๋Œ€์—ญํญ 33% ์ ˆ๊ฐ)
  • LV_DISP_DEF_REFR_PERIOD 16: ๋ฆฌํ”„๋ ˆ์‹œ ์ฃผ๊ธฐ๋ฅผ 16ms(์•ฝ 60Hz)๋กœ ์„ค์ •ํ•˜์—ฌ ๋ถˆํ•„์š”ํ•œ ์—ฐ์‚ฐ ๋ฐฉ์ง€
  • LV_USE_SHADOW 0: ๋ Œ๋”๋ง ์ฝ”์ŠคํŠธ๊ฐ€ ๋งค์šฐ ๋†’์€ ์„€๋„์šฐ(๊ทธ๋ฆผ์ž) ํšจ๊ณผ ์ œํ•œ


๊ฒฐ๋ก : ์ตœ์ ํ™” ๊ตฌ์กฐ ์ข…ํ•ฉ ๋ฐ ์„ฑ๊ณผ ์ธก์ •

ESP32-S3 ๊ธฐ๋ฐ˜์˜ ๊ณ ํ•ด์ƒ๋„ ๋””์Šคํ”Œ๋ ˆ์ด ๊ฐœ๋ฐœ์—์„œ ํ”„๋ ˆ์ž„ ๋“œ๋กญ ๋ฌธ์ œ๋Š” ๋‹จ์ˆœํžˆ MCU ์„ฑ๋Šฅ์˜ ํ•œ๊ณ„๊ฐ€ ์•„๋‹Œ, ๋ฉ”๋ชจ๋ฆฌ ์•„ํ‚คํ…์ฒ˜ ๋ฐ ๋Œ€์—ญํญ ์ œ์–ด์˜ ๋ฌธ์ œ์ž…๋‹ˆ๋‹ค.

๊ตฌ๋ถ„ ๊ธฐ์กด ๋ฐฉ์‹ (SRAM Partial Buffer) ์ตœ์ ํ™” ๋ฐฉ์‹ (PSRAM Direct Mode + DMA)
ํ‰๊ท  ํ”„๋ ˆ์ž„(FPS) 12 ~ 18 FPS (์Šคํƒ€ํ„ฐ๋ง ๋ฐœ์ƒ) 45 ~ 60 FPS (๋งค์šฐ ๋ถ€๋“œ๋Ÿฌ์›€)
CPU ์‚ฌ์šฉ๋ฅ  85% ~ 98% (๋ฉ”๋ชจ๋ฆฌ ๋ณต์‚ฌ ๋ณ‘๋ชฉ) 25% ~ 40% (Zero-Copy ๋‹ฌ์„ฑ)
ํ‹ฐ์–ด๋ง(Tearing) ํ™”๋ฉด ์ฐข์–ด์ง ์ž์ฃผ ๋ฐœ์ƒ ์™„๋ฒฝํžˆ ์ œ๊ฑฐ๋จ

์ •๋ฆฌํ•˜์ž๋ฉด, 1) PSRAM 80MHz OPI ํ™œ์„ฑํ™”, 2) LVGL Direct Mode ๊ธฐ๋ฐ˜์˜ Double Buffering ๊ตฌํ˜„, 3) FreeRTOS Core 1 ์ „์šฉ ํƒœ์Šคํฌ ํ• ๋‹น ๋ฐ GDMA ๋น„๋™๊ธฐ ์ธํ„ฐ๋ŸฝํŠธ ํ†ต์ง€ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•˜๋ฉด ESP32-S3 ํ™˜๊ฒฝ์—์„œ๋„ ์ƒ์šฉ ์ œํ’ˆ ์ˆ˜์ค€์˜ ์œ ๋ คํ•˜๊ณ  ์™„๋ฒฝํ•œ GUI ์„ฑ๋Šฅ์„ ์ด๋Œ์–ด๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•