作为一名嵌入式开发工程师,最近我在STM32F407ZET6开发板上成功移植了LVGL 8.3图形库。这个项目让我深刻体会到,在资源受限的嵌入式平台上实现流畅的GUI界面并非易事。本文将详细记录整个移植过程,从硬件选型到软件配置,希望能为同样需要将LVGL移植到STM32平台的开发者提供参考。
LVGL(Light and Versatile Graphics Library)是一款开源的嵌入式图形库,具有轻量级、高性能的特点。在STM32平台上移植LVGL主要需要解决以下几个核心问题:
我使用的是STM32F407ZET6开发板,主要基于以下考虑:
提示:STM32F407ZET6和ZGT6的主要区别在于Flash大小,其他方面基本相同。如果你的开发板是ZGT6,本文的移植方法同样适用。
我选用的是2.8寸电阻触摸屏,主要参数如下:
| 参数 | 规格 |
|---|---|
| 分辨率 | 320×240 |
| 接口类型 | 16位并行总线 |
| 色彩深度 | RGB565 (16位色) |
| 触摸屏 | 电阻式,支持XPT2046控制器 |
这种屏幕的优势在于:
LCD屏幕与STM32开发板的连接主要通过FSMC接口实现。具体引脚连接如下:
显示接口连接:
| 开发板引脚 | LCD引脚 | 功能 |
|---|---|---|
| PD14 | DB0 | 数据线0 |
| PD15 | DB1 | 数据线1 |
| PD0 | DB2 | 数据线2 |
| PD1 | DB3 | 数据线3 |
| PE7 | DB4 | 数据线4 |
| PE8 | DB5 | 数据线5 |
| PE9 | DB6 | 数据线6 |
| PE10 | DB7 | 数据线7 |
| PE11 | DB8 | 数据线8 |
| PE12 | DB9 | 数据线9 |
| PE13 | DB10 | 数据线10 |
| PE14 | DB11 | 数据线11 |
| PE15 | DB12 | 数据线12 |
| PD8 | DB13 | 数据线13 |
| PD9 | DB14 | 数据线14 |
| PD10 | DB15 | 数据线15 |
| PD5 | WR | 写使能 |
| PD4 | RD | 读使能 |
| 复位引脚 | RST | 复位信号 |
| PF12 | RS | 寄存器选择 |
| PG12 | CS | 片选信号 |
| 3.3V | BL | 背光电源 |
触摸接口连接:
| 开发板引脚 | LCD引脚 | 功能 |
|---|---|---|
| PB1 | PEN | 触摸中断 |
| PB2 | MISO | SPI数据输入 |
| PF11 | MOSI | SPI数据输出 |
| PC13 | T_CS | 触摸片选 |
| PB0 | CLK | SPI时钟 |
注意:背光控制可以根据需要连接。如果开发板有背光控制电路,建议使用PWM控制背光亮度;如果不需要调光,可以直接接3.3V。
我使用的是Keil MDK作为开发环境,主要因为:
当然,你也可以选择其他开发环境如IAR、STM32CubeIDE等,移植过程大同小异。
LVGL目前有多个版本,我选择8.3版本主要基于以下考虑:
| 版本 | 特点 | 适用场景 |
|---|---|---|
| LVGL 7.x | 稳定但功能较少 | 资源极其有限的平台 |
| LVGL 8.3 | 功能丰富,资源占用适中 | STM32F4等中端平台 |
| LVGL 9.x | 最新功能,资源占用较大 | 高性能平台 |
选择LVGL 8.3的具体原因:
LVGL源码可以从GitHub获取:
提示:建议下载8.3.11这个稳定版本,避免使用开发中的版本可能带来的不稳定因素。
首先创建一个基本的STM32工程,包含:
将下载的LVGL源码解压后,按以下步骤添加到工程:
文件组织结构:
code复制Project/
├── LVGL/
│ ├── src/ # LVGL核心源码
│ ├── examples/ # 示例代码
│ ├── lv_conf.h # 配置文件
│ └── lvgl.h # 主头文件
├── Drivers/ # 硬件驱动
├── Inc/ # 项目头文件
└── Src/ # 项目源文件
需要修改以下工程设置:
LV_LVGL_H_INCLUDE_SIMPLE,简化头文件包含注意:堆栈大小不足会导致程序运行异常,如HardFault等错误。如果后续运行中出现异常,可以适当增大这些值。
LVGL的核心配置文件是lv_conf.h,需要根据实际需求进行调整:
#if 0改为#if 1LV_COLOR_DEPTH 16(匹配RGB565屏幕)LV_MEM_SIZE关键配置示例:
c复制#define LV_COLOR_DEPTH 16 /* 颜色深度设置为16位 */
#define LV_HOR_RES_MAX 320 /* 水平分辨率 */
#define LV_VER_RES_MAX 240 /* 垂直分辨率 */
#define LV_USE_DEMO_WIDGETS 1 /* 启用widget演示 */
#define LV_MEM_SIZE (32 * 1024) /* 为LVGL分配32KB内存 */
显示驱动移植主要实现以下功能:
实现步骤:
lv_port_disp_template.c为lv_port_disp.cdisp_init()函数,初始化LCDdisp_flush()函数,处理绘图缓冲区的刷新disp_flush示例实现:
c复制static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
/* 将颜色缓冲区内容复制到指定区域 */
LCD_Fill(area->x1, area->y1, area->x2, area->y2, (uint16_t *)color_p);
/* 通知LVGL刷新完成 */
lv_disp_flush_ready(disp_drv);
}
提示:
LCD_Fill是底层LCD驱动提供的函数,需要根据实际使用的LCD控制器实现。
对于触摸屏支持,需要移植输入设备驱动:
lv_port_indev_template.c为lv_port_indev.c关键函数实现:
c复制static void touchpad_init(void)
{
/* 初始化触摸屏控制器 */
TP_Init();
}
static bool touchpad_is_pressed(void)
{
/* 检测触摸屏是否被按下 */
return TP_Scan() == 1;
}
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
/* 获取触摸点坐标 */
static uint16_t x_val, y_val;
TP_Get_XY(&x_val, &y_val);
*x = x_val;
*y = y_val;
}
LVGL需要系统心跳来驱动动画和定时任务。通常使用定时器中断提供1ms的心跳:
c复制void TIM3_IRQHandler(void)
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) {
lv_tick_inc(1); /* 通知LVGL 1ms过去了 */
TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
}
}
定时器初始化:
c复制void TIM3_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_InitStruct;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
TIM_InitStruct.TIM_Prescaler = 84 - 1; /* 84MHz/84 = 1MHz */
TIM_InitStruct.TIM_Period = 1000 - 1; /* 1MHz/1000 = 1kHz (1ms) */
TIM_InitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_InitStruct);
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE);
TIM_Cmd(TIM3, ENABLE);
}
LVGL支持多种缓冲区配置方式,根据资源情况选择:
单缓冲:
双缓冲:
全帧缓冲:
双缓冲配置示例:
c复制static lv_disp_buf_t disp_buf;
static lv_color_t buf1[LV_HOR_RES_MAX * 10]; /* 行缓冲区1 */
static lv_color_t buf2[LV_HOR_RES_MAX * 10]; /* 行缓冲区2 */
lv_disp_buf_init(&disp_buf, buf1, buf2, LV_HOR_RES_MAX * 10);
STM32F407有192KB SRAM,可以这样分配:
如果使用外部RAM,可以考虑:
LVGL需要定期调用lv_task_handler()来处理任务:
c复制while(1) {
lv_task_handler();
delay_ms(5); /* 5ms间隔 */
}
更好的做法是使用RTOS任务或定时器中断来调用。
可能原因:
解决方案:
LV_COLOR_DEPTH与屏幕匹配disp_flush实现是否正确可能原因:
解决方案:
症状:
解决方案:
完成移植后,可以运行LVGL提供的demo程序测试效果:
c复制int main(void)
{
/* 硬件初始化 */
System_Init();
/* LVGL初始化 */
lv_init();
lv_port_disp_init();
lv_port_indev_init();
/* 运行demo */
lv_demo_widgets();
/* 主循环 */
while(1) {
lv_task_handler();
delay_ms(5);
}
}
成功运行后,屏幕上将显示LVGL的widget演示界面,包括按钮、滑块、图表等各种控件。
初始移植包含了所有功能,实际项目中可以根据需求裁剪:
通过以上步骤,我们成功将LVGL 8.3移植到了STM32F407平台。在实际项目中,还需要根据具体需求进行进一步优化和定制。希望本文能为你的LVGL移植工作提供有价值的参考。