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完成。

相关推荐
DIY机器人工房7 小时前
关于解决 libwebsockets 库编译时遇到的问题的方法:
服务器·stm32·单片机·嵌入式硬件·tcp
GilgameshJSS8 小时前
STM32H743-ARM例程3-SYSTICK定时闪烁LED
arm开发·stm32·单片机·嵌入式硬件·学习
云山工作室9 小时前
基于单片机的按摩椅系统的设计(论文+源码)
stm32·单片机·嵌入式硬件·毕业设计·毕设
一枝小雨11 小时前
FreeRTOS内存分配与STM32内存布局详解
stm32·单片机·嵌入式·freertos·嵌入式系统·cortex-m3/m4
充哥单片机设计12 小时前
【STM32项目开源】基于STM32的智慧农业大棚系统
stm32·单片机·嵌入式硬件
Mr成文15 小时前
【usb】windows usb驱动框架简介
windows·stm32·单片机
Wave84516 小时前
STM32---了解
stm32·单片机·嵌入式硬件
一枝小雨17 小时前
STM32中的Flash、ROM与RAM全解析
stm32·单片机·嵌入式·arm·内存分布
balance_rui17 小时前
stm32进阶笔记
stm32·c#