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头文件:

cpp 复制代码
#include "lvgl.h"

在定时器中断回调函数中添加lvgl心跳函数:

cpp 复制代码
/**
 * @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周期:

cpp 复制代码
    btim_timx_int_init(10-1,8400-1);    /* 初始化定时器1ms */

3.2 开启配置文件

打开lvgl_conf.h文件,将#if 0 改为 #if 1,开启配置文件:

cpp 复制代码
#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。

cpp 复制代码
/* 导入lcd驱动头文件 */
#include "./BSP/LCD/lcd.h"

3.3.3 设置屏幕分辨率

我这里使用的是正点原子4.3寸的屏幕。分辨率为800x480:

cpp 复制代码
#define MY_DISP_HOR_RES (800)   /* 屏幕宽度 */
#define MY_DISP_VER_RES (480)   /* 屏幕高度 */

3.3.4 定义正点原子LCD绘制函数

此函数用于刷新显示函数disp_flush()中,原有LVGL自带的刷新绘制效率较低。

cpp 复制代码
/**
 * @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初始化显示中。

cpp 复制代码
/*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绘制函数。

cpp 复制代码
/*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。

cpp 复制代码
/* 导入驱动头文件 */
#include "./BSP/TOUCH/touch.h"
#include "./BSP/LCD/lcd.h"
#include "./BSP/KEY/key.h"

3.4.3 屏蔽其他静态原型

除触摸屏外,其他输入均注释掉。

cpp 复制代码
/**********************
 *  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 屏蔽其他静态变量

除触摸屏外,其他输入均注释掉。

cpp 复制代码
/**********************
 *  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 屏蔽其他初始化函数

除触摸屏外,其他输入均注释掉。

cpp 复制代码
/**********************
 *   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 修改触摸初始化函数

cpp 复制代码
/*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 修改触摸检测相关函数

触摸屏按下检测函数:

cpp 复制代码
/*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;
}

触摸屏获取坐标函数:

cpp 复制代码
/*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];
}

触摸屏读取函数:

cpp 复制代码
/*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 屏蔽其他设备检测函数

其他设备检测函数均注释掉。

cpp 复制代码
/*------------------
 * 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。

cpp 复制代码
#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的宏定义,如下所示。

cpp 复制代码
/*Stress test for LVGL*/
#define LV_USE_DEMO_STRESS 1

四、烧录与显示

编译无误进行烧录,成功后可以看到显示屏运行压力测试程序,裸机移植LVGL完成。

相关推荐
十月的皮皮8 小时前
stm20260720-从新手 C 到量产 STM32 工程:程序设计推导指南
c语言·开发语言·stm32·stm32cubemx·hal库
开发笔记-阿牛8 小时前
解析一款有意思的产品||CK6159A语音芯片设计的儿童专注力训练机-舒尔特玩具
stm32·单片机·嵌入式硬件·音频
搞全栈小苏10 小时前
SIMetrix_simplis8.4安装教程——附安装包
stm32·单片机·嵌入式硬件·电力电子
qq_5739678211 小时前
STM32控制器
stm32·单片机·嵌入式硬件
某林21212 小时前
履带小车底盘stm32F103RCT6开发
人工智能·stm32·单片机·嵌入式硬件·架构·人机交互·ros2
做萤石二次开发的哈哈14 小时前
萤石 ERTC Windows SDK 包含哪些类型定义?
stm32·单片机·嵌入式硬件
亿道电子Emdoor14 小时前
【Aitium】PCB导出DXF文件出现eDuplicateRecordName报错
stm32·单片机·嵌入式硬件·altium
巴巴媛6661 天前
STM32学习笔记【38.DHT11实验】
笔记·stm32·学习
深圳市泰凌微电子1 天前
HK航顺 HK32F407RGT7替STM32F405RGT6 微控制器32位MCU
stm32·单片机·嵌入式硬件
小心亦新1 天前
STM32学习16--定时器输出比较2直流电机
stm32·嵌入式硬件·学习