Files

132 lines
4.0 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
****************************************************************************************************
* @file lvgl_port.c
* @author HiFi Player Project
* @version V1.0
* @date 2026-01-01
* @brief LVGL 8.x 移植层
*
* 移植要点:
* 1. 绘图缓冲区必须放在内部 RAM,并带 DMA 能力(SPI DMA 不能直接读 PSRAM)。
* 2. flush_cb 中把 LVGL 的小端 RGB565 数据做字节交换,再交给 spilcd_flush()。
* 3. spilcd_flush() 是阻塞的(等 SPI 传输完成回调),因此这里调用后可以直接
* lv_disp_flush_ready(),实现简单可靠。
****************************************************************************************************
*/
#include "lvgl_port.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "lvgl.h"
#include "spilcd.h"
#define TAG "lvgl_port"
#define DISP_HOR_RES SPI_LCD_H_RES /* 320 */
#define DISP_VER_RES SPI_LCD_V_RES /* 240 */
#define DRAW_BUF_LINES 40 /* 单缓冲区高度(40 行约 25KB) */
#define LVGL_TASK_STACK (8 * 1024)
#define LVGL_TASK_PRIO 4
static lv_disp_draw_buf_t s_draw_buf;
static lv_disp_drv_t s_disp_drv;
static lv_color_t *s_draw_buf_mem = NULL;
static esp_timer_handle_t s_tick_timer = NULL;
/**
* @brief LVGL 刷新回调:把绘制好的区域送到 SPI LCD
*/
static void disp_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_p)
{
int32_t width = lv_area_get_width(area);
int32_t height = lv_area_get_height(area);
uint32_t pixels = (uint32_t)width * (uint32_t)height;
uint16_t *buf = (uint16_t *)color_p;
/* LVGL 内部是 RGB565 小端,ST7789 要求高字节在前,这里原地交换 */
for (uint32_t i = 0; i < pixels; i++)
{
buf[i] = (uint16_t)((buf[i] << 8) | (buf[i] >> 8));
}
/* x2 / y2 使用“不包含”的坐标 */
spilcd_flush((uint16_t)area->x1, (uint16_t)area->y1,
(uint16_t)(area->x2 + 1), (uint16_t)(area->y2 + 1), buf);
lv_disp_flush_ready(drv);
}
/**
* @brief LVGL 1ms 时基
*/
static void lv_tick_cb(void *arg)
{
(void)arg;
lv_tick_inc(1);
}
esp_err_t lvgl_port_init(void)
{
lv_init();
/* ---------------- 绘图缓冲区(内部 DMA RAM) ---------------- */
size_t pixel_count = (size_t)DISP_HOR_RES * DRAW_BUF_LINES;
s_draw_buf_mem = (lv_color_t *)heap_caps_malloc(pixel_count * sizeof(lv_color_t),
MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL);
if (s_draw_buf_mem == NULL)
{
ESP_LOGE(TAG, "no memory for LVGL draw buffer");
return ESP_ERR_NO_MEM;
}
lv_disp_draw_buf_init(&s_draw_buf, s_draw_buf_mem, NULL, pixel_count);
/* ---------------- 注册显示设备 ---------------- */
lv_disp_drv_init(&s_disp_drv);
s_disp_drv.hor_res = DISP_HOR_RES;
s_disp_drv.ver_res = DISP_VER_RES;
s_disp_drv.draw_buf = &s_draw_buf;
s_disp_drv.flush_cb = disp_flush_cb;
lv_disp_drv_register(&s_disp_drv);
/* ---------------- 1ms 时基 ---------------- */
const esp_timer_create_args_t timer_args = {
.callback = lv_tick_cb,
.name = "lv_tick",
};
ESP_ERROR_CHECK(esp_timer_create(&timer_args, &s_tick_timer));
ESP_ERROR_CHECK(esp_timer_start_periodic(s_tick_timer, 1000)); /* 1000us */
ESP_LOGI(TAG, "LVGL %d.%d.%d ready, draw buffer %u px",
LVGL_VERSION_MAJOR, LVGL_VERSION_MINOR, LVGL_VERSION_PATCH,
(unsigned)pixel_count);
return ESP_OK;
}
static void lvgl_task(void *arg)
{
(void)arg;
while (1)
{
lv_timer_handler();
vTaskDelay(pdMS_TO_TICKS(5));
}
}
void lvgl_port_start(void)
{
if (xTaskCreate(lvgl_task, "lvgl", LVGL_TASK_STACK, NULL, LVGL_TASK_PRIO, NULL) != pdPASS)
{
ESP_LOGE(TAG, "create lvgl task failed");
}
}