Перейти к содержимому

lcd_ltdc — дисплей RGB 800×480

Выводит на дисплей с параллельным интерфейсом RGB картинку с SD-карты и гоняет по ней квадрат. Контроллер LTDC сам читает кадр из SDRAM и выдаёт его на дисплей, рисует DMA2D — процессор в этом почти не участвует.

Дисплей 800×480 с интерфейсом RGB (24 бита данных, CLK, HSYNC, VSYNC, DE) — через переходник на разъёмы J1 и J2. Подсветка и питание дисплея — от переходника.

Сигнал Вывод Контакт Сигнал Вывод Контакт Сигнал Вывод Контакт
R0 PG13 J1-22 G0 PE5 J1-28 B0 PE4 J1-27
R1 PA2 J2-32 G1 PE6 J1-29 B1 PC10 J1-14
R2 PA1 J2-33 G2 PA6 J2-27 B2 PA3 J2-30
R3 PB0 J2-23 G3 PC9 J1-7 B3 PA8 J1-10
R4 PA5 J2-28 G4 PB10 J2-18 B4 PA10 J1-12
R5 PA9 J1-9 G5 PC1 J2-31 B5 PB5 J1-24
R6 PB1 J2-22 G6 PC7 J1-5 B6 PA15 J1-11
R7 PC4 J2-25 G7 PD3 J1-18 B7 PD2 J1-15

CLK — PG7 (J2-7), HSYNC — PC6 (J1-6), VSYNC — PA4 (J2-29), DE — PC5 (J2-24).

Эти 28 выводов заняты дисплеем целиком: ШИМ Lua-модуля pio на PA1–PA3, PA5, PA6, PA8, PB0, PB1, PC6–PC9 с ним не совместим. Свободными остаются, например, I2C1 (PB8, PB7), FDCAN2 (PB12, PB13) и USART1.

Окно терминала
cmake --preset Release -DH723_RECIPES="lcd_ltdc"

Картинка — /SD/lcd.bmp: BMP 24 бита без сжатия, лучше 800×480 (меньшая встанет по центру). Без файла на экране цветные полосы.

lcd_ltdc: started
lcd_ltdc: 800x480 RGB565, pixel clock 25000 kHz, refresh 43 Hz
lcd_ltdc: /SD/lcd.bmp 800x480 loaded in 175 ms
lcd_ltdc: 43 fps, draw 506 us/frame

43 кадра в секунду — по одному на каждый кадр развёртки; на рисование кадра уходит 0,5 мс. С работающим дисплеем SD-карта пишет 10,1 МБ/с и читает 10,7 МБ/с (sd_file), SSH, SFTP и Lua работают как без дисплея.

  • Такт пикселя. LTDC тактируется от выхода R PLL3: 250 МГц / 10 = 25 МГц (PeriphCommonClock_Config в main.c). Полный кадр с порогами — 1057 × 549 тактов, отсюда 43 Гц.
  • Тайминги развёртки — LCD_HSYNC, LCD_HBP, LCD_HFP и вертикальные в lcd_ltdc.h, из документации панели. В регистры LTDC пишутся накопленные суммы: импульс, + задний порог,
    • видимая часть, + передний порог, каждая минус 1.
  • Регистры, а не HAL. Модули HAL для LTDC и DMA2D в проекте не включены (их нет в .ioc), а регистров немного — рецепт не зависит от настроек CubeMX.
  • Кадровые буферы — в SDRAM, два по 768 КБ (RGB565, 2 байта на пиксель). На экране один, рисуется другой; lcdSwap меняет адрес слоя с перезагрузкой по кадровому гасящему импульсу (LTDC_SRCR_VBR) и ждёт его — поэтому картинка не рвётся.
  • RGB565 вместо ARGB8888 — вдвое меньше памяти и нагрузки на шину SDRAM. Линии R0–R2, G0–G1, B0–B2 LTDC при этом всё равно выдаёт, дополняя цвет до 24 бит.
  • DMA2D рисует сам: заливка прямоугольника (режим «регистр → память»), копия картинки («память → память») и перевод BMP из RGB888 в RGB565 («память → память с преобразованием»).
  • Перерисовывается только изменившееся. Шину SDRAM делят LTDC (~33 МБ/с на чтение кадра), DMA2D, процессор, Lua и RAM-диск. Копия всего фона в каждом кадре добавила бы ~65 МБ/с и 17 мс работы DMA2D. Поэтому фон пишется в оба буфера один раз, а в каждом кадре закрывается фоном только место, где квадрат был два кадра назад (буферы чередуются).
  • Своя периферия с DMA рядом с дисплеем. Пока LTDC читает кадр, доступ к SDRAM идёт с очередью. Буферы DMA для периферии, которой важно успевать (SD-карта, быстрый UART, SPI), лучше держать во внутренней памяти (pvPortMalloc, AXI SRAM) — так сделан и драйвер SD-карты.
  • Кэш. SDRAM кэшируется. Память под буферы инвалидируется после выделения: иначе «грязные» строки кэша от прежних владельцев могли бы вытесниться поверх того, что пишет DMA2D. Данные BMP, прочитанные процессором, перед DMA2D выгружаются из кэша (SCB_CleanDCache_by_Addr). Буферы выровнены по 32 байта — размер строки кэша.
  • BMP хранит строки снизу вверх и выравнивает их до 4 байт, поэтому DMA2D переводит картинку по строке. Файл читается через FatFs блоками по 32 КБ: драйвер SD передаёт их одной командой, мелкие чтения через stdio в разы медленнее.
  • Другая панель — размер и тайминги в lcd_ltdc.h; частоту пикселя меняет делитель PLL3R (в .ioc, от него же тактируется АЦП). Полярности сигналов — регистр LTDC->GCR.
  • Текст, кнопки, графики и сенсорный ввод — рецепт lvgl_demo: LVGL рисует прямо в кадровые буферы этого драйвера (lcdFrameBuffer, lcdShow).
  • Рисование процессором по пикселям — в backBuffer, а перед lcdSwap выгрузить изменённое из кэша (SCB_CleanDCache_by_Addr), иначе LTDC покажет старые данные.
  • Сенсорная панель дисплея обычно подключается по I2C — см. рецепт i2c_bme280.

Исходники рецепта: recipes/lcd_ltdc