STM32F1,LVGL简易DEMO移植
简介
尝试过在ESP32上移植LVGL之后,再在STM32上面LVGL,确认下是不是可以用
虽然STM32F103ZE的ROM及RAM都没有ESP32丰富,便对应于LVGL的最低配置要求,应该也可以正常运行的。不过也只能移植简单的
按键显示,像复杂一些DEMO,在STM32F1不用了,资源不够,导致编译不通过。
LVGL
LVGL是一款比较流行的致力于MCU与MPU创建漂亮UI的嵌入式图形库,免费且开源。
最低要求
ROM > 64KB
RAM > 2K
STACK > 2K
HEAP > 2K
移植步骤
LVGL移植总的步骤主要是如下几步
1.调用lv_init();
2.初始化驱动
3.注册显示与输入驱动,显存的配置,显示响应回调函数的响应
4.lv_tick_inc(x) 在中断中定时更新,x设定取决于lv_tick_inc的调用频率
5.lv_timer_handler,定时调用,完成LVGL的响应(更新LVGL的响应)
具体示例
使用的是正点原子STM32F103ZE开发板
拷贝一个LCD可以正常驱动的工程
拷贝LVGL源码至工程
这里使用的是V8.3.0
拷贝完之后需要将源码添加至工程,并添加相应的include路径,STM32的添加LVGL的文件相对ESP32来说,就麻烦很多
需要每个文件都添加进工程
主要包括lvgl/src, lvgl/exmaples/porting(这个路径也可以不添加,在自己定义的移植文件路径添加即可)
如果一次没成功,也没事,可以根据提示的编译错误慢慢添加
重命令lv_conf.h,并使能#if 0 改为#if 1
LVGL需要编译器支持C99模式,不然无法编译通过
因为将lv_conf.h独立于LVGL的源码,需要添加预定义LV_CONF_INCLUDE_SIMPLE,没加的话,编译会提示这个错误,加上就可以正常编译
在INCLUDE的路径下添加LVGL源码路径
lvgl/example/porting目录下的移植文件拷贝到工程目录下,lv_port_disp_template与lv_port_indev_template并重命名为lv_port_disp.h, lv_port_disp.c, lv_port_indev.h, lv_port_indev.c
因为改变了路径,文件不放在LVGL源码的路径下, 直接include "lvgl.h"即可,并使能两个文件#if 0 改为 #if 1
屏幕驱动
分配好显存,移植LCD区域点阵更新操作
void lv_port_disp_init(void)
{
/*-------------------------
* Initialize your display
* -----------------------*/
disp_init();
/* Example for 1) */
lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
/*-----------------------------------
* Register the display in LVGL
*----------------------------------*/
static lv_disp_drv_t disp_drv; /*Descriptor of a display driver*/
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
///*Set up the functions to access to your display*/
///*Set the resolution of the display*/
disp_drv.hor_res = MY_DISP_HOR_RES;
disp_drv.ver_res = MY_DISP_VER_RES;
///*Used to copy the buffer's content to the display*/
disp_drv.flush_cb = disp_flush;
///*Set a display buffer*/
disp_drv.draw_buf = &draw_buf_dsc_1;
///*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
LCD_Color_Fill(area->x1, area->y1, area->x2, area->y2, (uint16_t*)color_p);
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
输入驱动
这里以电阻触摸屏为例,给到LVGL的触点信息必须是和屏幕位置相对应的
校准具体模拟值与感应像素点的位置的操作,应该在BSP层里面做好,最后传给LVGL是像素逻辑位置
void lv_port_indev_init(void)
{
static lv_indev_drv_t indev_drv;
touchpad_init();
/*Register a touchpad input device*/
lv_indev_drv_init(&indev_drv);
indev_drv.type = LV_INDEV_TYPE_POINTER;
indev_drv.read_cb = touchpad_read;
indev_touchpad = lv_indev_drv_register(&indev_drv);
}
/*Initialize your touchpad*/
static void touchpad_init(void)
{
/*Your code comes here*/
}
/*Will be called by the library to read the touchpad*/
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
{
static lv_coord_t last_x = 0;
static lv_coord_t last_y = 0;
/*Save the pressed coordinates and the state*/
if(touchpad_is_pressed()) {
touchpad_get_xy(&last_x, &last_y);
data->state = LV_INDEV_STATE_PR;
}
else {
data->state = LV_INDEV_STATE_REL;
}
/*Set the last pressed coordinates*/
data->point.x = last_x;
data->point.y = last_y;
}
/*Return true is the touchpad is pressed*/
static bool touchpad_is_pressed(void)
{
/*Your code comes here*/
tp_dev.scan(0);
if(tp_dev.sta&TP_PRES_DOWN)
return true;
else
return false;
}
/*Get the x and y coordinates if the touchpad is pressed*/
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y)
{
/*Your code comes here*/
(*x) = tp_dev.x[0];
(*y) = tp_dev.y[0];
}
初始化及调用
在SYSTICK中断函数里,将LVGL计时增加
void SysTick_Handler(void)
{
HAL_IncTick();
lv_tick_inc(1);
}
初始化并调用
lv_init();
lv_port_disp_init();
lv_port_indev_init();
lv_example_btn_1();
while(1)
{
lv_timer_handler();
delay_ms(3);
}
存在问题
编译通过后,烧录到硬件,发现只显示部分就卡死了
单步调试时,发生HardFault错误
原因是默认的STACK深度不够,导致跑LVGL的栈溢出了,整个程序就出错了
需要修改默认的栈深度,针对按钮测试DEMO,暂改为0x800的大小
Stack_Size EQU 0x00000800
AREA STACK, NOINIT, READWRITE, ALIGN=3
Stack_Mem SPACE Stack_Size
显示图片
其他
使用片上SRAM可以使用LVGL,使用外部的SRAM也可以正常使用LVGL
总结
这里只是验证了,LVGL在STM32F1上面是可以跑起来的,具体的使用及细节还是需要在实际用到的项目去了解及深入