前言
本篇文章使用STM32CubeIDE进行开发,移植LVGL到STM32F103C8T6。F103C8T6刚好卡在LVGL能用的范围。
**开源地址:**https://gitee.com/wei-yuliu/stm32-f103-c8-t6-lvgl-stm32-cube-ide.git
一年前尝试过移植LVGL到STM32F103C8T6,各种问题,一直失败,放弃了。最近突然想起这件事,发现其实不难(兴许是当时能力还不够)。文章可能会有很多问题讲述不是很清楚,也可能有很多错误,欢迎指正!
挖个坑:
移植到Linux-x86:文章还没写
移植到Linux-ARM:文章还没写
学到什么
- 使用STM32CubeIDE/CubeMX配置单片机的时钟,GPIO,外设,中断等。
- 使用STM32Hal库代码编写,函数调用。
- STM32CubeIDE/CubeMX项目配置,会合理工具使用会让编程事半功倍。
- 屏幕厂商提供的示例,移植驱动到自己开发板。
- 懂得LVGL移植的那点东西:
- 裁剪&配置(lv_conf.h);
- 提供时钟心lv_tick_inc(1)跳给lvgl;
- 提供绘图函数LCD_DrawArea()给lvgl;
- 时不时问问lvgl是否要刷新了lv_task_handler();
- 写lvgl应用(main.c的例子);
- 为后续Linux驱动开发,应用开发提供思维。我认为linux驱动和应用本质是硬件驱动提供接口给应用调用。你能移植设备厂商的代码了,跟应用开发人员(也还是你- -)规定一下接口需要什么参数,剩下的就不需要驱动工程师考虑的问题了,到应用开发人员头疼了(也还是你- -)。linux一堆规定,规定驱动要怎么写(mmp),规定提供给应用开发人员的接口(读写文件),这里就不深究了,后面移植到linux的时候再细细品味。
LVGL
下载
https://github.com/lvgl/lvgl/releases

裁剪


STM32Cube IDE配置
项目配置






生成的目录结构

配置LVGL目录



移植屏幕厂商驱动测试
-
移植屏幕驱动最重要的一点是:给LVGL提供绘图函数。
-
由于每个人使用的屏幕不同,这里不写代码了,可以前往开原仓库中查看参考,地址:
画矩形函数:
void LCD_DrawArea(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t *colors);
- 在Core文件夹新增Hardware文件夹
- 新增lcd.h和lcd.c存放移植屏幕厂商代码
LVGL移植代码编写
裁剪lvgl功能
修改后的lv_conf.h文件可以前往:https://gitee.com/wei-yuliu/stm32-f103-c8-t6-lvgl-stm32-cube-ide.git,直接改一下LV_HOR_RES_MAX,LV_VER_RES_MAX就能用了。
- 详细说明
在lv_conf.h文件(原lv_conf_template.h)做如下修改:
-
#if 0改成#if 1:这个是开启lvgl,如果为0为不使用lvgl,为0整个.h文件失效
-
屏幕大小:LV_HOR_RES_MAX,LV_VER_RES_MAX设置成自己屏幕大小
-
颜色翻转:如果是SPI屏要设置LV_COLOR_16_SWAP为1
-
屏幕旋转(0,90°,180°,270°)缓冲区:LV_DISP_ROT_MAX_BUF调小一点,10u变成1u,测试没有用到屏幕旋转功能,LV_DISP_ROT_MAX_BUF很小都没事。
-
LV_MEM_SIZE:内存池,32 * 1024(32KB)改成2 * 1024(2KB),RAM占用2KB,C8T6只有20KB,所以我们这里调小一点。这里就不得不说LVGL内存的两种分配方式了:
- 手动分配LV_MEM_CUSTOM为0时,一个大小固定的数组(2KB)
- 自动分配LV_MEM_CUSTOM为1时,由malloc分配,单片机不怎么使用。移植LVGL到linux这种带操作系统才会用。
-
功能裁剪:
| 功能模块 | 修改后 | 说明 |
|---|---|---|
LV_USE_ANIMATION | 0 | 禁用动画 |
LV_USE_SHADOW | 0 | 禁用阴影绘制 |
LV_USE_PATTERN | 0 | 禁用矩形图案填充 |
LV_USE_VALUE_STR | 0 | 禁用矩形上的数值字符串绘制 |
LV_USE_BLEND_MODES | 0 | 禁用混合模式(仅正常混合) |
LV_USE_OPA_SCALE | 0 | 禁用整体透明度缩放 |
LV_USE_IMG_TRANSFORM | 0 | 禁用图像旋转和缩放 |
LV_USE_API_EXTENSION_V6 | 0 | 禁用 v6 API 兼容 |
LV_USE_API_EXTENSION_V7 | 0 | 禁用 v7 API 兼容 |
LV_USE_DEBUG | 0 | 完全禁用调试断言 |
-
对象裁剪:
-
保留对象:
LV_USE_BTN→ 1LV_USE_CONT→ 1LV_USE_LABEL→ 1
-
裁剪掉的对象:
对象 修改后 控件名称 LV_USE_ARC0 Arc(圆弧) LV_USE_BAR0 Bar(进度条) LV_USE_BTNMATRIX0 Button Matrix(按钮矩阵) LV_USE_CALENDAR0 Calendar(日历) LV_USE_CANVAS0 Canvas(画布) LV_USE_CHECKBOX0 Checkbox(复选框) LV_USE_CHART0 Chart(图表) LV_USE_CPICKER0 Color Picker(颜色选择器) LV_USE_DROPDOWN0 Dropdown List(下拉列表) LV_USE_GAUGE0 Gauge(仪表盘) LV_USE_IMG0 Image(图像) LV_USE_IMGBTN0 Image Button(图像按钮) LV_USE_KEYBOARD0 Keyboard(虚拟键盘) LV_USE_LED0 LED(指示灯) LV_USE_LINE0 Line(线条) LV_USE_LIST0 List(列表) LV_USE_LINEMETER0 Line Meter(线性仪表) LV_USE_OBJMASK0 Object Mask(对象遮罩) LV_USE_MSGBOX0 Message Box(消息框) LV_USE_PAGE0 Page(页面) LV_USE_SPINNER0 Spinner(加载动画) LV_USE_ROLLER0 Roller(滚轮选择器) LV_USE_SLIDER0 Slider(滑动条) LV_USE_SPINBOX0 Spinbox(数值调节框) LV_USE_SWITCH0 Switch(开关) LV_USE_TEXTAREA0 Textarea(文本区域) LV_USE_TABLE0 Table(表格) LV_USE_TABVIEW0 Tabview(选项卡视图) LV_USE_TILEVIEW0 Tileview(磁贴视图) LV_USE_WIN0 Window(窗口)
-
配置LVGL
- 在Core/Hardware文件夹下新增lvglc.h和lvglc.c
/*
* lvglc.h
*/
#ifndef HARDWARE_LVGLC_H_
#define HARDWARE_LVGLC_H_
#include "lcd.h" //引入lcd驱动
#include "lvgl.h" //引入lvgl
void lvgl_init(void);
void lvgl_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p);
#endif
/*
* lvglc.c
*/
#include "lvglc.h"
#define LV_BUF_SIZE (LCD_WIDTH * 5) // 缓冲区大小(行数)
static lv_disp_buf_t draw_buf; // LVGL绘制缓冲区
static lv_color_t buf1[LV_BUF_SIZE]; // 第一缓冲区
/**
* lvgl初始化函数
*
*/
void lvgl_init() {
lv_init();
lv_disp_buf_init(&draw_buf, buf1, NULL, LV_BUF_SIZE);
static lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv);
disp_drv.hor_res = LCD_WIDTH; //屏幕宽度
disp_drv.ver_res = LCD_HEIGHT; //屏幕高度
disp_drv.flush_cb = lvgl_disp_flush;//刷新回调
disp_drv.buffer = &draw_buf; //缓冲区
lv_disp_drv_register(&disp_drv);
}
/* 显示刷新回调函数 */
void lvgl_disp_flush(lv_disp_drv_t *disp, const lv_area_t *area, lv_color_t *color_p) {
//绘图矩形这里调用的就是lcd驱动,也就是说,lcd只需要给lvgl提供一个绘图函数就可以了。这也就是为什么lvgl能移植到各种地方的原因之一
LCD_DrawArea(area->x1, area->y1, area->x2, area->y2, (uint16_t*)color_p);
lv_disp_flush_ready(disp); // 通知LVGL刷新完成
}
给lvgl提供时钟
- stm32f103c8t6需要配置时钟和定时器为lvgl提供时钟
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //定时中断,在main.c中直接定义这个函数,按需修改
{
if(htim->Instance==TIM2)//如果是定时器2
lv_tick_inc(1); //给lvgl提供时钟
}
}
使用lvgl!
int main(void)
{
//其他代码
//....
//....
//启动定时器
HAL_TIM_Base_Start_IT(&htim2);
LCD_Init();//lcd初始化
lvgl_init();//lvgl初始化,lvglc.c中的函数
lv_obj_t *btn = lv_btn_create(lv_scr_act(), NULL); //创建按钮
lv_obj_set_size(btn, 120, 50); // 宽度120像素,高度50像素
lv_obj_align(btn, NULL, LV_ALIGN_CENTER, 0, 0); // 居中显示
//设置按钮背景颜色
lv_obj_set_style_local_bg_color(btn, LV_BTN_PART_MAIN, LV_STATE_DEFAULT, lv_color_hex(0x0000FF));
//在while中调用lv_task_handler()
while(1)
{
lv_task_handler();
HAL_Delay(5);
}
}
代码优化
- 不开启代码优化是绝对超出flash的!
properties -> c/c++build -> settings -> optimization level设置成**-Os**

编译烧录
