1,LVGL(V8.3.10版本)裸机移植教程
最近在学习NXP的Guider软件,图形化设计LVGL并导入到自己的STM32_LVGL工程中。由于LVGL版本问题导致很多的API函数不同,报错很多。(STM32工程是正点原子的例程LVGL 版本为 V8.2、而Guider设计软件的LVGL版本为V8.3.10)。
为了适用Guider(LVGL版本V8.3.10)的设计,本教程将从头带领我们一起移植LVGL到STM32工程中。
一、移植前准备资料
移植前我们需要准备STM32工程(包含内存管理、LCD显示、触摸屏、定时器中断这四种功能),我这里是均采用正点原子的例程。以及LVGL源码版本为V8.3.10,可以在官网找到或者GitHub仓库找到。
https://github.com/lvgl/lvgl/releaseshttp://LVGL的GitHub仓库
1.1 STM32裸机工程
将内存管理、LCD显示、触摸屏、定时器中断这四种相关文件添加到裸机移植\Drivers\BSP位置。
并在keil5中添加相关的.c文件和头文件路径。
1.2 LVGL源码
官网下载的LVGL源码有许多是用不上的,我们需要进行裁剪再添加到STM32裸机工程中。下面是解压缩的LVGL源码:
1.2.1 文件夹裁剪
将.github、docs、env_support、scripts、tests文件夹删除,仅保留如下三个文件夹:
1.2.2 文件裁剪
再此页面仅保留lvgl.h、lv_conf_template.h,其余文件均删除,并且将lv_conf_template.h重命名为lv_conf.h。裁剪完毕后如下:
1.2.3 裁剪examples文件夹
examples文件夹仅保留porting文件夹,其余文件夹均删除掉。
1.2.4 复制文件夹、文件到工程目录
首先创建如下LVGL目录:
将demos文件夹复制到如下目录:工程名称\Middlewares\LVGL\GUI_APP
再将其他文件和文件夹复制到如下目录:工程名称\Middlewares\LVGL\GUI\lvgl
二、Keil V5添加LVGL文件
2.1 .c文件的添加
在Keil V5中添加工程目录,在目录下分别添加如下的.c源文件。
在Keil V5中添加工程目录:
2.2 头文件路径添加
根据如下图片选择头文件路径进行添加:
三、修改代码
3.1 LVGL时基
在btime.c文件中添加lvgl的1ms心跳节拍。
添加lvgl头文件:
#include "lvgl.h"
在定时器中断回调函数中添加lvgl心跳函数:
/*** @brief 回调函数,定时器中断服务函数调用* @param 无* @retval 无*/
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{if (htim->Instance == BTIM_TIMX_INT){lv_tick_inc(1); /*lvgl的1ms心跳*/}
}
在mian.c中初始化BTIM定时器为1ms周期:
btim_timx_int_init(10-1,8400-1); /* 初始化定时器1ms */
3.2 开启配置文件
打开lvgl_conf.h文件,将#if 0 改为 #if 1,开启配置文件:
#if 1 /*Set it to "1" to enable content*/
3.3 配置显示屏
配置显示屏主要与两个文件有关:lv_port_disp_template.c和lv_port_disp_template.h。两个文件目录位置在:工程名称/Middlewares/lvgl/examples/porting。
3.3.1 开启文件
将lv_port_disp_template.c和lv_port_disp_template.h中的#if 0 改为 #if 1:
3.3.2 在.c文件添加头文件
主要是将控制LCD的头文件添加到lv_port_disp_template.c。
/* 导入lcd驱动头文件 */
#include "./BSP/LCD/lcd.h"
3.3.3 设置屏幕分辨率
我这里使用的是正点原子4.3寸的屏幕。分辨率为800x480:
#define MY_DISP_HOR_RES (800) /* 屏幕宽度 */
#define MY_DISP_VER_RES (480) /* 屏幕高度 */
3.3.4 定义正点原子LCD绘制函数
此函数用于刷新显示函数disp_flush()中,原有LVGL自带的刷新绘制效率较低。
/*** @brief LCD加速绘制函数* @param (sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex - sx + 1) * (ey - sy + 1)* @param color:要填充的颜色* @retval 无*/
void lcd_draw_fast_rgb_color(int16_t sx, int16_t sy,int16_t ex, int16_t ey, uint16_t *color)
{uint16_t w = ex-sx+1;uint16_t h = ey-sy+1;lcd_set_window(sx, sy, w, h);uint32_t draw_size = w * h;lcd_write_ram_prepare();for(uint32_t i = 0; i < draw_size; i++){lcd_wr_data(color[i]);}
}
3.3.5 修改显示屏初始化函数
将lcd的初始化函数传入lvgl初始化显示中。
/*Initialize your display and the required peripherals.*/
static void disp_init(void)
{/*You code here*/lcd_init(); /* 初始化LCD */lcd_display_dir(1); /* 设置横屏 */
}
3.3.6 修改刷新显示函数
添加3.3.4 定义正点原子LCD绘制函数。
/*Flush the content of the internal buffer the specific area on the display*You can use DMA or any hardware acceleration to do this operation in the background but*'lv_disp_flush_ready()' has to be called when finished.*/
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
// if(disp_flush_enabled) {
// /*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/// int32_t x;
// int32_t y;
// for(y = area->y1; y <= area->y2; y++) {
// for(x = area->x1; x <= area->x2; x++) {
// /*Put a pixel to the display. For example:*/
// /*put_px(x, y, *color_p)*/
// color_p++;
// }
// }
// }lcd_draw_fast_rgb_color(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);
}
3.4 配置触摸输入
配置触摸输入主要与两个文件有关:lv_port_indev_template.c和lv_port_indev_template.h。两个文件目录位置在:工程名称/Middlewares/lvgl/examples/porting。
3.4.1 开启文件
将lv_port_disp_template.c和lv_port_disp_template.h中的#if 0 改为 #if 1:
3.4.2 在.c文件添加头文件
主要是将控制触摸的头文件添加到lv_port_indev_template.c。
/* 导入驱动头文件 */
#include "./BSP/TOUCH/touch.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"
3.4.3 屏蔽其他静态原型
除触摸屏外,其他输入均注释掉。
/*********************** STATIC PROTOTYPES**********************/static void touchpad_init(void);
static void touchpad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
static bool touchpad_is_pressed(void);
static void touchpad_get_xy(lv_coord_t * x, lv_coord_t * y);//static void mouse_init(void);
//static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
//static bool mouse_is_pressed(void);
//static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y);//static void keypad_init(void);
//static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
//static uint32_t keypad_get_key(void);//static void encoder_init(void);
//static void encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
//static void encoder_handler(void);//static void button_init(void);
//static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data);
//static int8_t button_get_pressed_id(void);
//static bool button_is_pressed(uint8_t id);
3.4.4 屏蔽其他静态变量
除触摸屏外,其他输入均注释掉。
/*********************** STATIC VARIABLES**********************/
lv_indev_t * indev_touchpad;
//lv_indev_t * indev_mouse;
//lv_indev_t * indev_keypad;
//lv_indev_t * indev_encoder;
//lv_indev_t * indev_button;//static int32_t encoder_diff;
//static lv_indev_state_t encoder_state;
3.4.5 屏蔽其他初始化函数
除触摸屏外,其他输入均注释掉。
/*********************** GLOBAL FUNCTIONS**********************/void lv_port_indev_init(void)
{/*** Here you will find example implementation of input devices supported by LittelvGL:* - Touchpad* - Mouse (with cursor support)* - Keypad (supports GUI usage only with key)* - Encoder (supports GUI usage only with: left, right, push)* - Button (external buttons to press points on the screen)** The `..._read()` function are only examples.* You should shape them according to your hardware*/static lv_indev_drv_t indev_drv;/*------------------* Touchpad* -----------------*//*Initialize your touchpad if you have*/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);/*------------------* Mouse* -----------------*//*Initialize your mouse if you have*/
// mouse_init();// /*Register a mouse input device*/
// lv_indev_drv_init(&indev_drv);
// indev_drv.type = LV_INDEV_TYPE_POINTER;
// indev_drv.read_cb = mouse_read;
// indev_mouse = lv_indev_drv_register(&indev_drv);// /*Set cursor. For simplicity set a HOME symbol now.*/
// lv_obj_t * mouse_cursor = lv_img_create(lv_scr_act());
// lv_img_set_src(mouse_cursor, LV_SYMBOL_HOME);
// lv_indev_set_cursor(indev_mouse, mouse_cursor);// /*------------------
// * Keypad
// * -----------------*/// /*Initialize your keypad or keyboard if you have*/
// keypad_init();// /*Register a keypad input device*/
// lv_indev_drv_init(&indev_drv);
// indev_drv.type = LV_INDEV_TYPE_KEYPAD;
// indev_drv.read_cb = keypad_read;
// indev_keypad = lv_indev_drv_register(&indev_drv);// /*Later you should create group(s) with `lv_group_t * group = lv_group_create()`,
// *add objects to the group with `lv_group_add_obj(group, obj)`
// *and assign this input device to group to navigate in it:
// *`lv_indev_set_group(indev_keypad, group);`*/// /*------------------
// * Encoder
// * -----------------*/// /*Initialize your encoder if you have*/
// encoder_init();// /*Register a encoder input device*/
// lv_indev_drv_init(&indev_drv);
// indev_drv.type = LV_INDEV_TYPE_ENCODER;
// indev_drv.read_cb = encoder_read;
// indev_encoder = lv_indev_drv_register(&indev_drv);// /*Later you should create group(s) with `lv_group_t * group = lv_group_create()`,
// *add objects to the group with `lv_group_add_obj(group, obj)`
// *and assign this input device to group to navigate in it:
// *`lv_indev_set_group(indev_encoder, group);`*/// /*------------------
// * Button
// * -----------------*/// /*Initialize your button if you have*/
// button_init();// /*Register a button input device*/
// lv_indev_drv_init(&indev_drv);
// indev_drv.type = LV_INDEV_TYPE_BUTTON;
// indev_drv.read_cb = button_read;
// indev_button = lv_indev_drv_register(&indev_drv);// /*Assign buttons to points on the screen*/
// static const lv_point_t btn_points[2] = {
// {10, 10}, /*Button 0 -> x:10; y:10*/
// {40, 100}, /*Button 1 -> x:40; y:100*/
// };
// lv_indev_set_button_points(indev_button, btn_points);
}
3.4.6 修改触摸初始化函数
/*Initialize your touchpad*/
static void touchpad_init(void)
{/*Your code comes here*/tp_dev.init();/* 电阻屏坐标矫正 */if (key_scan(0) == KEY0_PRES) /* KEY0按下,则执行校准程序 */{lcd_clear(WHITE); /* 清屏 */tp_adjust(); /* 屏幕校准 */tp_save_adjust_data();}
}
3.4.7 修改触摸检测相关函数
触摸屏按下检测函数:
/*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;}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];
}
触摸屏读取函数:
/*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;
}
3.4.8 屏蔽其他设备检测函数
其他设备检测函数均注释掉。
/*------------------* Mouse* -----------------*////*Initialize your mouse*/
//static void mouse_init(void)
//{
// /*Your code comes here*/
//}///*Will be called by the library to read the mouse*/
//static void mouse_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
//{
// /*Get the current x and y coordinates*/
// mouse_get_xy(&data->point.x, &data->point.y);// /*Get whether the mouse button is pressed or released*/
// if(mouse_is_pressed()) {
// data->state = LV_INDEV_STATE_PR;
// }
// else {
// data->state = LV_INDEV_STATE_REL;
// }
//}///*Return true is the mouse button is pressed*/
//static bool mouse_is_pressed(void)
//{
// /*Your code comes here*/// return false;
//}///*Get the x and y coordinates if the mouse is pressed*/
//static void mouse_get_xy(lv_coord_t * x, lv_coord_t * y)
//{
// /*Your code comes here*/// (*x) = 0;
// (*y) = 0;
//}///*------------------
// * Keypad
// * -----------------*////*Initialize your keypad*/
//static void keypad_init(void)
//{
// /*Your code comes here*/
//}///*Will be called by the library to read the mouse*/
//static void keypad_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
//{
// static uint32_t last_key = 0;// /*Get the current x and y coordinates*/
// mouse_get_xy(&data->point.x, &data->point.y);// /*Get whether the a key is pressed and save the pressed key*/
// uint32_t act_key = keypad_get_key();
// if(act_key != 0) {
// data->state = LV_INDEV_STATE_PR;// /*Translate the keys to LVGL control characters according to your key definitions*/
// switch(act_key) {
// case 1:
// act_key = LV_KEY_NEXT;
// break;
// case 2:
// act_key = LV_KEY_PREV;
// break;
// case 3:
// act_key = LV_KEY_LEFT;
// break;
// case 4:
// act_key = LV_KEY_RIGHT;
// break;
// case 5:
// act_key = LV_KEY_ENTER;
// break;
// }// last_key = act_key;
// }
// else {
// data->state = LV_INDEV_STATE_REL;
// }// data->key = last_key;
//}///*Get the currently being pressed key. 0 if no key is pressed*/
//static uint32_t keypad_get_key(void)
//{
// /*Your code comes here*/// return 0;
//}///*------------------
// * Encoder
// * -----------------*////*Initialize your keypad*/
//static void encoder_init(void)
//{
// /*Your code comes here*/
//}///*Will be called by the library to read the encoder*/
//static void encoder_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
//{// data->enc_diff = encoder_diff;
// data->state = encoder_state;
//}///*Call this function in an interrupt to process encoder events (turn, press)*/
//static void encoder_handler(void)
//{
// /*Your code comes here*/// encoder_diff += 0;
// encoder_state = LV_INDEV_STATE_REL;
//}///*------------------
// * Button
// * -----------------*////*Initialize your buttons*/
//static void button_init(void)
//{
// /*Your code comes here*/
//}///*Will be called by the library to read the button*/
//static void button_read(lv_indev_drv_t * indev_drv, lv_indev_data_t * data)
//{// static uint8_t last_btn = 0;// /*Get the pressed button's ID*/
// int8_t btn_act = button_get_pressed_id();// if(btn_act >= 0) {
// data->state = LV_INDEV_STATE_PR;
// last_btn = btn_act;
// }
// else {
// data->state = LV_INDEV_STATE_REL;
// }// /*Save the last pressed button's ID*/
// data->btn_id = last_btn;
//}///*Get ID (0, 1, 2 ..) of the pressed button*/
//static int8_t button_get_pressed_id(void)
//{
// uint8_t i;// /*Check to buttons see which is being pressed (assume there are 2 buttons)*/
// for(i = 0; i < 2; i++) {
// /*Return the pressed button's ID*/
// if(button_is_pressed(i)) {
// return i;
// }
// }// /*No button pressed*/
// return -1;
//}///*Test if `id` button is pressed or not*/
//static bool button_is_pressed(uint8_t id)
//{// /*Your code comes here*/// return false;
//}
3.5 修改main.c
这里主要包括添加LVGL头文件、添加LVGL初始化、添加LVGL官方压力测试DEMO。
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/KEY/key.h"
#include "./BSP/SRAM/sram.h"
#include "./BSP/TIMER/btim.h"/* LVGL */
#include "lvgl.h"
#include "lv_port_indev_template.h"
#include "lv_port_disp_template.h"
#include "lv_demo_stress.h"int main(void)
{HAL_Init(); /* 初始化HAL库 */sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */delay_init(168); /* 延时初始化 */usart_init(115200); /* 串口初始化为115200 */led_init(); /* 初始化LED */key_init(); /* 初始化按键 */sram_init(); /* SRAM初始化 */btim_timx_int_init(10-1,8400-1); /* 初始化定时器 *//* lvgl初始化 */lv_init();lv_port_disp_init();lv_port_indev_init();/* lvgl官方demo */lv_demo_stress(); /* 官方例程测试 */while (1){lv_task_handler();delay_ms(5);}
}
注:为了运行官方压力测试DEMO还需要在lv_conf.h中开启压力测试DEMO的宏定义,如下所示。
/*Stress test for LVGL*/
#define LV_USE_DEMO_STRESS 1
四、烧录与显示
编译无误进行烧录,成功后可以看到显示屏运行压力测试程序,裸机移植LVGL完成。