03_【正点原子】DNESP32S3开发板移植LVGL
开发板 【正点原子】DNESP32S3开发板在正点原子 DNESP32S3 开发板上移植 LVGL 图形库。idf_component.ymldependencies:idf:4.4lvgl/lvgl:9.2.0lvgl_port.c#includelvgl_port.h#includeesp_timer.h#includeesp_log.h#includefreertos/FreeRTOS.h#includefreertos/task.h#includesys/lock.h#includesys/param.h// 修复提供 MAX/MIN 宏#includeunistd.h// 修复提供 usleep#includedriver/spi_master.h#includelcd_driver.h#includeassert.hstaticconstchar*TAGlvgl_port;// LVGL 线程安全互斥锁static_lock_t lvgl_api_lock;// 显示设备句柄内部使用staticlv_display_t*s_displayNULL;// 刷新完成回调通知 LVGL 刷新结束staticboollvgl_port_flush_ready_cb(esp_lcd_panel_io_handle_tpanel_io,esp_lcd_panel_io_event_data_t*edata,void*user_ctx){lv_display_t*disp(lv_display_t*)user_ctx;lv_display_flush_ready(disp);returnfalse;}// LVGL 刷新回调将渲染好的帧缓冲推送到 LCD 面板staticvoidlvgl_port_flush_cb(lv_display_t*disp,constlv_area_t*area,uint8_t*px_map){esp_lcd_panel_handle_tpanel_handlelv_display_get_user_data(disp);intoffsetx1area-x1;intoffsetx2area-x2;intoffsety1area-y1;intoffsety2area-y2;// SPI LCD 为大端需交换 RGB565 字节序lv_draw_sw_rgb565_swap(px_map,(offsetx21-offsetx1)*(offsety21-offsety1));esp_lcd_panel_draw_bitmap(panel_handle,offsetx1,offsety1,offsetx21,offsety21,px_map);}// LVGL 节拍递增回调staticvoidlvgl_port_tick_inc_cb(void*arg){lv_tick_inc(LVGL_TICK_PERIOD_MS);}// LVGL 主任务处理定时器、事件、渲染staticvoidlvgl_port_task(void*arg){ESP_LOGI(TAG,Starting LVGL task);uint32_ttime_till_next_ms0;uint32_tmin_delay_ms1000/CONFIG_FREERTOS_HZ;while(1){_lock_acquire(lvgl_api_lock);time_till_next_mslv_timer_handler();_lock_release(lvgl_api_lock);// 防止任务看门狗超时time_till_next_msMAX(time_till_next_ms,min_delay_ms);// 防止显示未就绪时空转过快time_till_next_msMIN(time_till_next_ms,LVGL_TASK_MAX_DELAY_MS);usleep(1000*time_till_next_ms);}}lv_display_t*lvgl_port_init(esp_lcd_panel_handle_tpanel_handle,esp_lcd_panel_io_handle_tio_handle){// 1. 初始化 LVGL 核心lv_init();// 2. 创建显示设备lv_display_t*displaylv_display_create(LCD_H_RES,LCD_V_RES);s_displaydisplay;// 3. 分配 DMA 绘制缓冲双缓冲局部渲染模式size_tdraw_buffer_szLCD_H_RES*LVGL_DRAW_BUF_LINES*sizeof(lv_color16_t);void*buf1spi_bus_dma_memory_alloc(LCD_HOST,draw_buffer_sz,0);assert(buf1);void*buf2spi_bus_dma_memory_alloc(LCD_HOST,draw_buffer_sz,0);assert(buf2);lv_display_set_buffers(display,buf1,buf2,draw_buffer_sz,LV_DISPLAY_RENDER_MODE_PARTIAL);// 4. 绑定面板句柄与刷新回调lv_display_set_user_data(display,panel_handle);lv_display_set_color_format(display,LV_COLOR_FORMAT_RGB565);lv_display_set_flush_cb(display,lvgl_port_flush_cb);// 5. 注册 SPI 传输完成回调修复直接使用传入的 io_handleconstesp_lcd_panel_io_callbacks_tcbs{.on_color_trans_donelvgl_port_flush_ready_cb,};esp_lcd_panel_io_register_event_callbacks(io_handle,cbs,display);returndisplay;}voidlvgl_port_start(void){// 1. 安装 LVGL 节拍定时器ESP_LOGI(TAG,Install LVGL tick timer);constesp_timer_create_args_tlvgl_tick_timer_args{.callbacklvgl_port_tick_inc_cb,.namelvgl_tick};esp_timer_handle_tlvgl_tick_timerNULL;ESP_ERROR_CHECK(esp_timer_create(lvgl_tick_timer_args,lvgl_tick_timer));ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer,LVGL_TICK_PERIOD_MS*1000));// 2. 创建 LVGL 处理任务ESP_LOGI(TAG,Create LVGL task);xTaskCreate(lvgl_port_task,LVGL,LVGL_TASK_STACK_SIZE,NULL,LVGL_TASK_PRIORITY,NULL);}voidlvgl_port_lock(void){_lock_acquire(lvgl_api_lock);}voidlvgl_port_unlock(void){_lock_release(lvgl_api_lock);}main.c/* * SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: CC0-1.0 */#includestdio.h#includeesp_log.h#includelcd_driver.h#includelvgl_port.h#includelvgl_ui.hstaticconstchar*TAGmain;i2c_obj_ti2c0_master;voidapp_main(void){i2c0_masteriic_init(I2C_NUM_0);/* 初始化IIC0 */xl9555_init(i2c0_master);/* IO扩展芯片初始化 */// 1. 初始化 LCD 硬件驱动同时获取面板句柄和IO句柄esp_lcd_panel_handle_tpanel_handleNULL;esp_lcd_panel_io_handle_tio_handleNULL;ESP_ERROR_CHECK(lcd_driver_init(panel_handle,io_handle));ESP_LOGI(TAG,LCD driver initialized);// 2. 初始化 LVGL 移植层传入面板句柄和IO句柄lv_display_t*displaylvgl_port_init(panel_handle,io_handle);ESP_LOGI(TAG,LVGL port initialized);// 3. 启动 LVGL 任务与节拍lvgl_port_start();// 4. 初始化 UI 界面加锁保护线程安全ESP_LOGI(TAG,Initialize LVGL UI interface);lvgl_port_lock();lvgl_ui_init(display);lvgl_port_unlock();}总结本文详细展示了在 ESP32-S3 平台上将 LVGL 图形库移植到 SPI 接口 LCD 屏幕的完整过程。核心工作集中在lvgl_port.c文件中它作为 LVGL 与底层硬件的桥梁主要实现了以下几个关键功能显示驱动对接通过lvgl_port_flush_cb回调函数将 LVGL 渲染好的帧缓冲区数据RGB565格式交换字节序后通过esp_lcd_panel_draw_bitmap推送到 LCD 面板。任务与定时器管理创建了独立的lvgl_port_task任务来周期性地调用lv_timer_handler()处理 LVGL 的内部定时器、事件和渲染逻辑。同时通过 ESP 硬件定时器为 LVGL 提供精确的节拍 (lv_tick_inc)。线程安全与同步使用_lock_t互斥锁 (lvgl_api_lock) 保护 LVGL API 的调用确保在多任务环境下的数据安全。并通过 SPI 传输完成回调 (lvgl_port_flush_ready_cb) 通知 LVGL 一帧数据刷新完成。内存管理使用spi_bus_dma_memory_alloc为 LVGL 分配 DMA 可访问的双绘图缓冲区设置为局部渲染模式以优化性能。在main.c中应用程序的启动流程清晰明了初始化 I2C 和 IO 扩展芯片。初始化 LCD 硬件驱动获取面板和 IO 句柄。调用lvgl_port_init初始化 LVGL 并绑定显示设备。调用lvgl_port_start启动 LVGL 任务和节拍定时器。在锁保护下初始化用户界面。idf_component.yml则简洁地定义了项目对 ESP-IDF (4.4) 和 LVGL (9.2.0) 的依赖关系。