lvgl移植到stm32(stm32H743IIT6
1.简介
LVGL(轻巧而多功能的图形库)是一个免费的开放源代码图形库,它提供创建具有易于使用的图形元素,精美的视觉效果和低内存占用的嵌入式GUI所需的一切。
2.移植准备
硬件准备
我使用的是正点原子的stm32H743IIt6核心板+7寸 ALENTEK 7’’ RGB TFTLCDC触摸屏。(其他屏幕也可以,但是必须包含lcd与touch驱动函数)
lvgl源码
我使用的是lvgl 7.10.1
3.移植过程
1.准备一个lcd触摸屏的例程
2.在工程目录下新建一个GUI文件夹,GUI文件夹下包含两个子文件夹
3.移植lvgl
打开下载的lvgl-8.0.1
把刚才下载的lvgl-8.0.1里所有内容复制在lvgl文件夹下,lvgl_app文件夹暂时留空。
4.porting文件更名操作
我们把刚才复制后的GUI\lvgl\examples\porting下的文件进行一个改名
(1)原始
(2)更改名字之后
5.把GUI\lvgl\下的lv_conf_template.h文件复制到GUI文件夹下并更名为lv_conf.h:
5.keil
(1)新建Group
(1)lvgl_app暂时空着
(2)将显示函数与触摸函数导入lvgl_porting中
(3)将lvgl源码中所有的.c文件全部导入lvgl_src
并且导入这些.c的路径
6.修改堆栈大小
7.设计c99模式
8.使能l条件编译
要使能lv_conf.h,lv_port_indev.c。lv_port_indev.h,lv_port_disp.c,lv_port_disp.h的条件编译,如下
下面部分注释掉,只留一个即可,否则会报错
即可编译通过
4.连接底层
显示移植
与touch.h相关
这个连接的画点函数,比较慢
// An highlighted block
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
/*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)*/
GUI_DrawPoint(x,y,color_p->full);
color_p++;
}
}
/* IMPORTANT!!!
* Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
下面展示一些 内联代码片
。
直接调用颜色块会很快
// An highlighted block
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
/*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)*/
// LCD_Fast_DrawPoint(x,y,color_p->full);
// color_p++;
// }
// }
// /*IMPORTANT!!!
// *Inform the graphics library that you are ready with the flushing*/
//
LCD_Color_Fill(area->x1,area->y1,area->x2,area->y2,(u16*)color_p);
//最后必须得调用,通知lvgl库你已经flushing拷贝完成了
lv_disp_flush_ready(disp_drv);
}
两者选一个就可以
触摸移植
下面展示一些 内联代码片
。
// An highlighted block
/*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;
tp_dev.scan(0);
if (tp_dev.sta & TP_PRES_DOWN) //
{
printf("x坐标:%d,Y坐标:%d\r\n", tp_dev.x[0], tp_dev.y[0]);
last_x = tp_dev.x[0];
last_y = tp_dev.y[0];
data->point.x = last_x;
data->point.y = last_y;
data->state = LV_INDEV_STATE_PR;
}
else
{
data->point.x = last_x;
data->point.y = last_y;
data->state = LV_INDEV_STATE_REL;
}
}
有外部sdram时
可以这样定义
下面展示一些 内联代码片
。
// An highlighted block
*********************/
#define LTDC_LCD_FRAMEBUF_SIZE (1280*800*2) //ltdc.c中ltdc_lcd_framebuf缓冲区的大小
#define COLOR_BUF_SIZE (1024*10) //全屏的大小
static lv_color_t color_buf[COLOR_BUF_SIZE] __attribute__((at(LCD_FRAME_BUF_ADDR+LTDC_LCD_FRAMEBUF_SIZE+COLOR_BUF_SIZE*2))); //分配到外部SDRAM,需要跳过ltdc.c中分配的帧缓冲区
static lv_color_t color_buf2[COLOR_BUF_SIZE] __attribute__((at(LCD_FRAME_BUF_ADDR+LTDC_LCD_FRAMEBUF_SIZE+COLOR_BUF_SIZE*4)));//lvgl的第二个缓冲区,紧跟在第一个缓冲区的后面
5.屏幕配置
挺重要的,有时候搞完一直出不来,可能就是屏幕横屏竖屏没弄好
下面展示一些 内联代码片
。
这里面屏幕的配置和你的lcd里横竖屏应该一致
// An highlighted block
void lv_port_disp_init(void)
{
/*-------------------------
* Initialize your display
* -----------------------*/
disp_init();
/*-----------------------------
* Create a buffer for drawing
*----------------------------*/
/**
* LVGL requires a buffer where it internally draws the widgets.
* Later this buffer will passed to your display driver's `flush_cb` to copy its content to your display.
* The buffer has to be greater than 1 display row
*
* There are 3 buffering configurations:
* 1. Create ONE buffer:
* LVGL will draw the display's content here and writes it to your display
*
* 2. Create TWO buffer:
* LVGL will draw the display's content to a buffer and writes it your display.
* You should use DMA to write the buffer's content to the display.
* It will enable LVGL to draw the next part of the screen to the other buffer while
* the data is being sent form the first buffer. It makes rendering and flushing parallel.
*
* 3. Double buffering
* Set 2 screens sized buffers and set disp_drv.full_refresh = 1.
* This way LVGL will always provide the whole rendered screen in `flush_cb`
* and you only need to change the frame buffer's address.
*/
/* Example for 1) */
/*A buffer for 10 rows*/
lv_disp_draw_buf_init(&draw_buf_dsc_1, color_buf, color_buf2, 1024 * 10); /*Initialize the display buffer*/
// /* Example for 2) */
// static lv_disp_draw_buf_t draw_buf_dsc_2;
// static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*A buffer for 10 rows*/
// static lv_color_t buf_2_1[MY_DISP_HOR_RES * 10]; /*An other buffer for 10 rows*/
// lv_disp_draw_buf_init(&draw_buf_dsc_2, buf_2_1, buf_2_1, MY_DISP_HOR_RES * 10); /*Initialize the display buffer*/
// /* Example for 3) also set disp_drv.full_refresh = 1 below*/
// static lv_disp_draw_buf_t draw_buf_dsc_3;
// static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*A screen sized buffer*/
// static lv_color_t buf_3_1[MY_DISP_HOR_RES * MY_DISP_VER_RES]; /*An other screen sized buffer*/
// lv_disp_draw_buf_init(&draw_buf_dsc_3, buf_3_1, buf_3_2, MY_DISP_VER_RES * LV_VER_RES_MAX); /*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 = 1024;
disp_drv.ver_res = 600;
/*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;
/*Required for Example 3)*/
//disp_drv.full_refresh = 1
/* Fill a memory array with a color if you have GPU.
* Note that, in lv_conf.h you can enable GPUs that has built-in support in LVGL.
* But if you have a different GPU you can use with this callback.*/
//disp_drv.gpu_fill_cb = gpu_fill;
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
}
LCD_Display_Dir(1); lcd里这个函数配置横竖屏
在 lv_conf.h里进行一些配置
色彩深度: #define LV_COLOR_DEPTH 16
DPI: #define LV_DPI 100
提供给lvgl的空间: #define LV_MEM_SIZE (32U * 1024U)
6.给lvgl一个心跳
下面展示一些 内联代码片
。
TIM7_Init(10-1,20000-1); 1ms的心跳
// An highlighted block
void TIM7_Init(u16 arr,u16 psc)
{
TIM7_Handler.Instance=TIM7; //通用定时器3
TIM7_Handler.Init.Prescaler=psc; //分频
TIM7_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器
TIM7_Handler.Init.Period=arr; //自动装载值
TIM7_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子
HAL_TIM_Base_Init(&TIM7_Handler);
HAL_TIM_Base_Start_IT(&TIM7_Handler); //使能定时器3和定时器3更新中断:TIM_IT_UPDATE
}
//定时器底册驱动,开启时钟,设置中断优先级
//此函数会被HAL_TIM_Base_Init()函数调用
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM7)
{
__HAL_RCC_TIM7_CLK_ENABLE(); //使能TIM3时钟
HAL_NVIC_SetPriority(TIM7_IRQn,1,3); //设置中断优先级,抢占优先级1,子优先级3
HAL_NVIC_EnableIRQ(TIM7_IRQn); //开启ITM3中断
}
}
//定时器3中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim==(&TIM7_Handler))
{
lv_tick_inc(1);
}
}
在主函数while循环里写入 lv_task_handler();
7. 移植demos
1.复制lv_examples-7.10.1整个文件夹至GUI\lvgl_app文件夹下并更名为lv_examples:
2.把GUI\lvgl_app\lv_examples下的lv_demo_conf_template.h复制到GUI文件夹下并更名为lv_demo_conf.h
使用哪个就在哪个地方打开预编译
在lv_demo_conf.h中
下面展示一些 内联代码片
。
// An highlighted block
/*Show some widget*/
#define LV_USE_DEMO_WIDGETS 0
#if LV_USE_DEMO_WIDGETS
#define LV_DEMO_WIDGETS_SLIDESHOW 0
#endif
/*Printer demo, optimized for 800x480*/
#define LV_USE_DEMO_PRINTER 0
/*Demonstrate the usage of encoder and keyboard*/
#define LV_USE_DEMO_KEYPAD_AND_ENCODER 1
/*Benchmark your system*/
#define LV_USE_DEMO_BENCHMARK 0
/*Stress test for LVGL*/
#define LV_USE_DEMO_STRESS 0
我用的是//lv_demo_keypad_encoder();的demo
将lv_demo_keypad_encoder文件夹的c文件添加进lvgl_app
我的主函数
下面展示一些 内联代码片
。
// An highlighted block
int main(void)
{
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(160,5,2,4); //设置时钟,400Mhz
delay_init(400); //延时初始化
uart_init(115200); //串口初始化
usmart_dev.init(200); //初始化USMART
TIM3_Init(10-1,20000-1);
LED_Init(); //初始化LED
KEY_Init(); //初始化按键
SDRAM_Init(); //初始化SDRAM
LCD_Init(); //初始化LCD
// TP_Init();
lv_init();
lv_port_disp_init();
lv_port_indev_init();
tp_dev.init(); //触摸屏初始化
// POINT_COLOR=RED;
// LCD_ShowString(30,50,200,16,16,"Apollo STM32H7");
// LCD_ShowString(30,70,200,16,16,"TOUCH TEST");
// LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
// LCD_ShowString(30,110,200,16,16,"2017/8/14");
// if(tp_dev.touchtype!=0XFF)LCD_ShowString(30,130,200,16,16,"Press KEY0 to Adjust");//电阻屏才显示
// delay_ms(1500);
// Load_Drow_Dialog();
// if(tp_dev.touchtype&0X80)ctp_test();//电容屏测试
// else rtp_test(); //电阻屏测试
//
// lv_demo_widgets();
// lv_demo_stress();
// lv_demo_benchmark();
lv_demo_keypad_encoder();
// lv_demo_test();
while(1){
lv_task_handler();
}
}
移植成功
第一次写博客好开心哦__,嘻嘻