系列文章目录
知不足而奋进 望远山而前行
目录
系列文章目录
文章目录
前言
重要步骤
lv_tick_inc(x)
lv_timer_handler()
1. 声明一把锁
2. 初始化这把锁
3. 创建一个任务
4. 编写任务的内容
完整示例代码
总结
前言
在嵌入式系统开发中,使用LVGL(LittlevGL)库可以轻松实现现代化的图形用户界面(GUI)。移植LVGL到特定的硬件平台是一项关键任务,需要遵循一系列重要步骤以确保库能够正确运行并充分发挥其功能。本文将介绍在LVGL移植过程中的关键步骤,帮助开发者快速上手并成功将LVGL集成到自定义的嵌入式项目中。
重要步骤
在LVGL移植文档Set up a project — LVGL documentation中, 总共有5个重要步骤:
- 调用
lv_init()
:lvgl的初始化核心 - 初始化我们自己的屏幕驱动相关,I2C,SPI,ST7789,CST816T等
- 调用
lv_port_disp_init()
(负责显示部分) 和lv_port_indev_init()
(负责输入部分) - 调用
lv_tick_inc(x)
:这个是负责维持lvgl核心库的心跳 - 调用
lv_timer_handler()
: 负责lvgl中的相关任务
前面1,2,3个步骤都是与初始化相关,我们把它们写在启动任务中
static void start_task() {
taskENTER_CRITICAL();
lv_init();
lv_port_disp_init();
lv_port_indev_init();
vTaskDelete(start_handler);
taskEXIT_CRITICAL();
}
lv_tick_inc(x)
我们需要维持lvgl自身的心跳,以便让它能够处理它自身内部的任务,例如动画的渲染,长按单击事件的判断等等, 我们将这个函数放到FreeRTOS的调用函数中
例如,我们在main.c文件中定义如下名称函数:
void vApplicationTickHook( void ){
lv_tick_inc(1);
}
注意,定义了这个函数,我们还需要打开它对应的宏,在FreeRTOSConfig.h
文件中第57行
#define configUSE_TICK_HOOK 1
lv_timer_handler()
lvgl中的所有的任务处理都在这个函数里,lvgl是线程不安全的,这个函数同时只能有一个线程调用它,保险起见我们需要给它的调用加一把锁
1. 声明一把锁
SemaphoreHandle_t xMutex;
2. 初始化这把锁
xMutex = xSemaphoreCreateMutex();
3. 创建一个任务
xTaskCreate(lvgl_timer_task, "lvgl_timer_task", 1024, NULL, 4, NULL);
4. 编写任务的内容
void lvgl_timer_task(void * pvParameters)
{
xMutex = xSemaphoreCreateMutex();
lv_obj_t * btn2 = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn2,100,100);
lv_obj_align(btn2, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_flag(btn2, LV_OBJ_FLAG_CHECKABLE);
lv_obj_t * label2 = lv_label_create(btn2);
lv_label_set_text(label2, "Toggle");
lv_obj_center(label2);
while(1)
{
if(pdTRUE == xSemaphoreTake(xMutex,portMAX_DELAY))
{
lv_timer_handler();
xSemaphoreGive(xMutex);
}
vTaskDelay(pdMS_TO_TICKS(1));
}
}
创建刷新的任务
static void lvgl_refresh_task() {
lv_obj_t * label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "count:");
lv_obj_set_style_text_color(label, lv_palette_main(LV_PALETTE_BLUE), 0);
lv_obj_set_pos(label,10,10);
int count = 0;
while(1) {
lv_label_set_text_fmt(label,"count:%d",count);
count++;
vTaskDelay(pdMS_TO_TICKS(1000));
printf("task1 \r\n");
}
}
完整示例代码
#include "gd32f4xx.h"
#include "systick.h"
#include <stdio.h>
#include <string.h>
#include "main.h"
#include "bsp_usart.h"
#include "FreeRTOS.h"
#include "task.h"
#include "lvgl.h"
#include "lv_port_disp.h"
#include "lv_port_indev.h"
#include "i2c.h"
#include "st7789.h"
#include "semphr.h"
static TaskHandle_t start_handler;
static SemaphoreHandle_t xMutex;
void Usart0_recv(uint8_t *data, uint32_t len) {
printf("Usart0_recv:%s \r\n",data);
}
static void lvgl_refresh_task() {
lv_obj_t * label = lv_label_create(lv_scr_act());
lv_label_set_text(label, "count:");
lv_obj_set_style_text_color(label, lv_palette_main(LV_PALETTE_BLUE), 0);
lv_obj_set_pos(label,10,10);
int count = 0;
while(1) {
lv_label_set_text_fmt(label,"count:%d",count);
count++;
vTaskDelay(pdMS_TO_TICKS(1000));
printf("task1 \r\n");
}
}
void lvgl_timer_task(void * pvParameters)
{
xMutex = xSemaphoreCreateMutex();
lv_obj_t * btn2 = lv_btn_create(lv_scr_act());
lv_obj_set_size(btn2,100,100);
lv_obj_align(btn2, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_flag(btn2, LV_OBJ_FLAG_CHECKABLE);
lv_obj_t * label2 = lv_label_create(btn2);
lv_label_set_text(label2, "Toggle");
lv_obj_center(label2);
printf("lcd_refresh_tas\r\n");
while(1)
{
if(pdTRUE == xSemaphoreTake(xMutex,portMAX_DELAY))
{
lv_timer_handler();
xSemaphoreGive(xMutex);
}
vTaskDelay(pdMS_TO_TICKS(1));
}
}
static void start_task() {
taskENTER_CRITICAL();
bsp_usart_dma_init(115200);
printf("start123\r\n");
I2C_init();
lv_init();
lv_port_disp_init();
lv_port_indev_init();
xTaskCreate(lvgl_timer_task, "lvgl_timer_task", 1024, NULL, 4, NULL);
xTaskCreate(lvgl_refresh_task, "lvgl_refresh_task", 256, NULL, 4, NULL);
vTaskDelete(start_handler);
taskEXIT_CRITICAL();
}
int main(void)
{
nvic_priority_group_set(NVIC_PRIGROUP_PRE4_SUB0);
systick_config();
xTaskCreate(start_task, "start_task", 512, NULL, 1, &start_handler);
vTaskStartScheduler();
while(1) {
}
}
void vApplicationTickHook( void ){
lv_tick_inc(1);
}
总结
LVGL的移植过程涉及几个关键步骤,包括初始化LVGL核心、配置屏幕和输入设备驱动、以及确保LVGL正常运行所需的心跳维持和任务处理。通过调用lv_init()初始化核心、配置驱动、设置心跳维持以及处理定时器任务等步骤,开发者可以在不同的硬件平台上成功移植LVGL库,从而为项目提供强大的GUI功能支持。这些步骤不仅确保了LVGL在目标设备上的稳定运行,还为开发者提供了自定义GUI界面的基础。