告别裸机点屏:用LVGL给你的STM32F103 TFT-LCD做个酷炫GUI(附工程源码)
2026/5/7 5:38:42 网站建设 项目流程

从裸机到GUI:LVGL在STM32F103上的实战移植指南

当你在STM32F103上成功点亮TFT-LCD屏幕后,那种成就感确实令人兴奋。但很快你会发现,直接操作像素点绘制界面不仅效率低下,而且难以实现复杂的交互效果。这就是为什么我们需要LVGL这样的图形库——它能将你的嵌入式设备变成一个拥有现代GUI的小型计算机。

1. 为什么选择LVGL?

在嵌入式领域,GUI框架的选择往往需要在功能和资源消耗之间寻找平衡。LVGL(Light and Versatile Graphics Library)以其独特的优势脱颖而出:

  • 轻量级:核心库仅需约50KB Flash和10KB RAM
  • 硬件无关:支持任何微控制器和显示设备
  • 丰富的组件:按钮、图表、列表等30+UI元素
  • 多语言支持:包括中文在内的多种文字显示
  • 开源免费:MIT许可证,商业项目也可使用

与裸机编程相比,使用LVGL开发界面效率可提升5-10倍。我曾在一个智能家居项目中,仅用3天就完成了原本需要2周的界面开发工作。

2. 移植前的准备工作

2.1 硬件配置检查

确保你的STM32F103满足LVGL的最低要求:

项目最低要求推荐配置
Flash64KB128KB+
RAM16KB32KB+
时钟频率48MHz72MHz
显示缓存1/10屏幕1/4屏幕

提示:使用SystemCoreClock变量可以获取当前系统时钟频率

2.2 开发环境搭建

  1. 工具链配置

    # 安装ARM GCC工具链(以Ubuntu为例) sudo apt install gcc-arm-none-eabi
  2. 工程结构调整

    /Project ├── /Core ├── /Drivers ├── /GUI │ ├── /lvgl # LVGL核心库 │ ├── /lv_drivers # 显示/触摸驱动 │ └── /lv_examples # 示例代码 └── /User
  3. 关键编译器设置

    • 启用C99模式
    • 堆栈大小设置为0x800(2KB)
    • 添加编译选项--diag_suppress=111忽略特定警告

3. 驱动层适配:让LVGL认识你的硬件

3.1 显示驱动移植

lv_port_disp.c是显示适配的关键文件,需要修改三个核心部分:

  1. 初始化函数

    static void disp_init(void) { // 替换为你的LCD初始化代码 LCD_Init(); }
  2. 缓存配置(选择最适合的方案):

    // 方案1:简单缓存(适合RAM有限的场景) static lv_disp_buf_t disp_buf; static lv_color_t buf[LV_HOR_RES_MAX * 10]; lv_disp_buf_init(&disp_buf, buf, NULL, LV_HOR_RES_MAX * 10);
  3. 刷新函数优化

    static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) { HAL_SuspendTick(); // 暂停系统滴答定时器 LCD_SetWindow(area->x1, area->y1, area->x2 - area->x1 + 1, area->y2 - area->y1 + 1); for(int y = area->y1; y <= area->y2; y++) { for(int x = area->x1; x <= area->x2; x++) { LCD_Write_DATA(color_p->full); color_p++; } } lv_disp_flush_ready(disp_drv); HAL_ResumeTick(); // 恢复系统滴答定时器 }

3.2 触摸驱动适配

lv_port_indev.c负责输入设备对接,重点修改触摸读取函数:

static bool touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data) { static int16_t last_x = 0, last_y = 0; if(TP_Read()) { // 你的触摸检测函数 >void SysTick_Handler(void) { HAL_IncTick(); lv_tick_inc(1); // 假设SysTick配置为1ms中断 }

同时,在主循环中处理LVGL任务:

while(1) { lv_task_handler(); HAL_Delay(5); // 适当延时减少CPU占用 }

5. 从Hello World到专业UI

5.1 创建第一个界面

void create_first_ui(void) { lv_obj_t * label; lv_obj_t * btn = lv_btn_create(lv_scr_act(), NULL); lv_obj_set_pos(btn, 10, 10); lv_obj_set_size(btn, 100, 50); label = lv_label_create(btn, NULL); lv_label_set_text(label, "Click me!"); lv_obj_set_event_cb(btn, btn_event_cb); } static void btn_event_cb(lv_obj_t * btn, lv_event_t event) { if(event == LV_EVENT_CLICKED) { lv_obj_t * msg = lv_mbox_create(lv_scr_act(), NULL); lv_mbox_set_text(msg, "Hello LVGL!"); lv_obj_set_width(msg, 200); } }

5.2 性能优化实战

通过DMA加速显示刷新:

  1. 修改disp_flush函数:

    static void disp_flush(...) { LCD_SetWindow(...); HAL_LTDC_Write_DMA(&hltdc, (uint32_t)color_p, (area->x2 - area->x1 + 1) * (area->y2 - area->y1 + 1)); // 注意:需要在DMA完成回调中调用lv_disp_flush_ready }
  2. 添加DMA完成回调:

    void HAL_LTDC_Write_DMA_Complete_Callback(void) { lv_disp_flush_ready(&disp_drv); }

6. 常见问题与解决方案

问题1:界面刷新闪烁严重

  • 检查是否使用了双缓存
  • 确保lv_disp_flush_ready在数据传输完成后调用
  • 降低刷新区域的重叠度

问题2:触摸坐标不准确

// 在touchpad_read中添加校准代码>

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询