LVGL 移植整体流程

LVGL 移植整体流程

一、确定输入、输出设备

输入:触摸屏、鼠标、键盘以及编码器;输出:显示屏。

二、确定所需功能

屏幕数据传输方式、系统、SRAM、内存管理算法。

三、准备 LVGL 库、例程

准备 V8.2 版本的 LVGL 库文件,还有支持所需功能的例程源码。

四、添加 LVGL 库到工程

按需裁剪、修改 LVGL 库文件,并添加到 MDK 工程中。

五、配置输入、输出设备

适配自己的输入和输出设备,添加所需功能(例如系统)。

六、提供心跳、测试

为 LVGL 提供时基,写测试代码检测移植是否成功。移植步骤一到四,略。

移植步骤五、配置输出(LCD屏幕)

  1. 把 lv_port_disp_templ.c / h 的条件编译#if 0 修改成 #if 1

  2. 在 lv_port_disp_templ.c 文件里,包含 屏设备驱动的 头文件,比如 #include "lcd.h"

  3. 在 lv_port_disp_templ.c 文件里,在disp_init()函数中初始化屏幕设备,设置横屏或者竖屏

比如:

void disp_init(void)

{

lcd_init();

}

  1. 在 lv_port_disp_templ.c 里,配置图形数据缓存模式。

这里有3种缓冲配置:

* 1. 单缓冲区:

* LVGL 会将显示设备的内容绘制到这里,并将他写入显示设备。

*

* 2. 双缓冲区:

* LVGL 会将显示设备的内容绘制到其中一个缓冲区,并将他写入显示设备。

* 需要使用 DMA 将要显示在显示设备的内容写入缓冲区。

* 当数据从第一个缓冲区发送时,它将使 LVGL 能够将屏幕的下一部分绘制到另一个缓冲区。

* 这样使得渲染和刷新可以并行执行。

*

* 3. 全尺寸双缓冲区

* 设置两个屏幕大小的全尺寸缓冲区,并且设置 disp_drv.full_refresh = 1。

* 这样,LVGL将始终以 'flush_cb' 的形式提供整个渲染屏幕,您只需更改帧缓冲区的地址。

根据内存选择,我选择的是 单缓存

static lv_disp_draw_buf_t draw_buf_dsc_1;

static lv_color_t buf_1MY_DISP_HOR_RES \* MY_DISP_BUF_LINES; /* 设置缓冲区的大小 */

lv_disp_draw_buf_init(&draw_buf_dsc_1, buf_1, NULL, MY_DISP_HOR_RES * MY_DISP_BUF_LINES); /* 初始化显示缓冲区 */

说明:

MY_DISP_HOR_RES 屏幕的水平宽度 ,MY_DISP_BUF_LINES 显示10行,如果显示效果不是很好,并且内存够用的话,可以修改大些。

全尺寸缓存区的话,5寸 (800 * 480)内存需要 800*480*2*2(第一个2: 16 bit,所以是2byte。第二个2,2块缓存区) 对内存要求高,如果开在外部内存上,外部读写速度慢,刷新速率不见得快。 所以如果使用外部内存,没必要使用全尺寸,速度不一定快。

  1. 在 lv_port_disp_templ.c 文件里,设置屏幕尺寸。

竖屏

disp_drv.hor_res = 272;

disp_drv.ver_res = 480;

横屏

disp_drv.hor_res = 480;

disp_drv.ver_res = 272;

  1. 在 lv_port_disp_templ.c 文件里, disp_flush 函数里,配置打点输出(在指定区域内填充指定颜色块)。

lcd_color_fill(area->x1, area->y1, area->x2, area->y2, (uint16_t *)color_p);

area->x1, area->y1 起始点 ,area->x2, area->y2终点的坐标。

移植步骤五、配置输入(触摸)

  1. 把 lv_port_indev_templ.c / h 的条件编译#if 0 修改成 #if 1

  2. 在 lv_port_indev_templ.c里,按需裁剪输入设备,文件里有 触摸屏、鼠标、键盘,编码器,键盘,根据自己的输入设备的类别,留下需要的代码。裁剪后的代码如下:

cpp 复制代码
/**
 * @file lv_port_indev_templ.c
 *
 */

 /*Copy this file as "lv_port_indev.c" and set this value to "1" to enable content*/
#if 1

/*********************
 *      INCLUDES
 *********************/
#include "lv_port_indev_template.h"
#include "../../lvgl.h"
/* 导入屏幕触摸驱动头文件 */
#include "./BSP/TOUCH/touch.h"

/*********************
 *      DEFINES
 *********************/

/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  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 VARIABLES
 **********************/
lv_indev_t * indev_touchpad;    // 触摸屏

/**********************
 *      MACROS
 **********************/

/**********************
 *   GLOBAL FUNCTIONS
 **********************/

/**
 * @brief       初始化并注册输入设备
 * @param       无
 * @retval      无
 */
void lv_port_indev_init(void)
{
    static lv_indev_drv_t indev_drv;

    /*------------------
     * 触摸屏
     * -----------------*/

    /* 初始化触摸屏(如果有) */
    touchpad_init();

    /* 注册触摸屏输入设备 */
    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);
}

/**********************
 *   STATIC FUNCTIONS
 **********************/

/*------------------
 * 触摸屏
 * -----------------*/

/**
 * @brief       初始化触摸屏
 * @param       无
 * @retval      无
 */
static void touchpad_init(void)
{
    /*Your code comes here*/
    tp_dev.init();
    /* 电阻屏如果发现显示屏XY镜像现象,需要坐标矫正 */
    if (0 == (tp_dev.touchtype & 0x80)) {
        tp_adjust();
        tp_save_adjust_data();
    }
}

/**
 * @brief       图形库的触摸屏读取回调函数
 * @param       indev_drv   : 触摸屏设备
 *   @arg       data        : 输入设备数据结构体
 * @retval      无
 */
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;

    /* 保存按下的坐标和状态 */
    if(touchpad_is_pressed())
    {
        touchpad_get_xy(&last_x, &last_y);
        data->state = LV_INDEV_STATE_PR;
    } 
    else
    {
        data->state = LV_INDEV_STATE_REL;
    }

    /* 设置最后按下的坐标 */
    data->point.x = last_x;
    data->point.y = last_y;
}

/**
 * @brief       获取触摸屏设备的状态
 * @param       无
 * @retval      返回触摸屏设备是否被按下
 */
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;
}

/**
 * @brief       在触摸屏被按下的时候读取 x、y 坐标
 * @param       x   : x坐标的指针
 *   @arg       y   : y坐标的指针
 * @retval      无
 */
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];
}

#else /*Enable this file at the top*/

/*This dummy typedef exists purely to silence -Wpedantic.*/
typedef int keep_pedantic_happy;
#endif
  1. 在 lv_port_indev_templ.c里,包含输入设备驱动头文件。

/* 导入屏幕触摸驱动头文件 */

#include "./BSP/TOUCH/touch.h"

  1. 在touchpad_init()函数中初始化触摸屏

  2. 配置触摸检测函数,touchpad_is_pressed(),扫描屏幕,扫描按下,返回真,没有被按下,则返回假。

  3. 配置坐标获取函数,touchpad_get_xy(),在触摸屏被按下的时候读取 x、y 坐标。

移植步骤六: LVGL 提供时基

  1. 添加定时器驱动 timer.c

  2. 在定时器驱动.c 文件中包含 : #include "lvgl.h"

  3. 在定时器中断函数(回调)中调用: lv_tick_inc(x)

重点:

初始化定时器时,需保证:进入中断的时间间隔=x毫秒

移植步骤六: 测试

LVGL移植 main函数部分 要点整理

4个步骤

1.引入头文件在main.c包含:定时器、lvgl.h、显示驱动模板、输入设备驱动模板。

#include "./BSP/TIMER/btim.h"

#include "lvgl.h"

#include "lv_port_disp_template.h"

#include "lv_port_indev_template.h"

2.初始化

btim_timx_int_init(10−1,9000−1); 初始化基础定时器(用来提供LVGL毫秒时基,中断内调用lv_tick_inc(1))

lv_init():初始化LVGL内核

lv_port_disp_init():初始化显示设备驱动

lv_port_indev_init():初始化触摸等输入设备驱动

3.主循环调用任务处理

在while(1)循环里每隔5ms调用一次 lv_timer_handler()(LVGL在这里刷新画面、处理控件事件、运行定时器动画)

注意:lv_timer_handler()是LVGL任务调度入口,占用CPU,必须在主大循环执行。

4.编写UI测试代码

创建控件,设置大小、位置,验证移植是否成功。

示例创建开关控件:

lv_obj_t* switch_obj = lv_switch_create(lvscract());

lv_obj_set_size(switchobj,120,60);

lv_obj_align(switchobj,LVALIGNCENTER,0,0);

cpp 复制代码
完整main简易示例
int main(void)
{
    //硬件初始化
    btim_timx_int_init(10−1,9000−1);
    lv_init();
    lv_port_disp_init();
    lv_port_indev_init();
    //测试UI
    lv_obj_t* switch_obj = lv_switch_create(lvscract());
    lv_obj_set_size(switchobj,120,60);
    lv_obj_align(switchobj,LVALIGNCENTER,0,0);
    while(1)
    {
        lv_timer_handler();
        delay_ms(5);
    }
}

移植步骤: DMA2D 的使用

要求 mcu芯片 硬件有 DMA2D外设,屏幕必须是 RGB屏幕

  1. 修改lv_conf.h 中相关的宏定义,开启 DMA2D
cpp 复制代码
/*-------------
 * 2. GPU
 *-----------*/

/* 使用STM32的DMA2D(又名Chrom Art) GPU */
#define LV_USE_GPU_STM32_DMA2D              1
#if LV_USE_GPU_STM32_DMA2D
    /* 必须定义包括目标处理器的CMSIS头的路径
       如。"stm32f769xx.h"或"stm32f429xx.h"*/
    #define LV_GPU_DMA2D_CMSIS_INCLUDE stm32l4r7xx.h
#endif
  1. 在 MDK 配置中 添加 对应芯片的定义

Edit-> Configuration-> C/C++-> Preprocessor Symbols-> define-> 加上 STM32L4

  1. 配置 DMA2D 外设
cpp 复制代码
void lcd_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t color)
{
    uint32_t psx, psy, pex, pey;
    uint32_t offline, addr, timeout = 0;

    if (sx >= LCD_WIDTH || sy >= LCD_HEIGHT) return;
    if (ex >= LCD_WIDTH)  ex = LCD_WIDTH - 1;
    if (ey >= LCD_HEIGHT) ey = LCD_HEIGHT - 1;
    if (sx > ex || sy > ey) return;

    lcd_coord_convert(sx, sy, ex, ey, &psx, &psy, &pex, &pey);
    if (psx > pex || psy > pey) return;

    offline = LCD_PANEL_WIDTH - (pex - psx + 1);
    addr = LCD_FRAMEBUF + LCD_PIXSIZE * (LCD_PANEL_WIDTH * psy + psx);

    __HAL_RCC_DMA2D_CLK_ENABLE();
    DMA2D->CR &= ~(DMA2D_CR_START);
    DMA2D->CR = DMA2D_R2M;
    DMA2D->OPFCCR = LCD_PIXFORMAT;
    DMA2D->OOR = offline;
    DMA2D->OCOLR = color;
    DMA2D->OMAR = addr;
    DMA2D->NLR = (pey - psy + 1) | ((pex - psx + 1) << 16);
    DMA2D->CR |= DMA2D_CR_START;

    while ((DMA2D->ISR & DMA2D_FLAG_TC) == 0)
    {
        if (++timeout > 0x1FFFFF) break;
    }
    DMA2D->IFCR |= DMA2D_FLAG_TC;
}
相关推荐
Brilliantwxx3 小时前
【STM32】 RTC 实时时钟详解:从概念到预分频器
stm32·单片机·嵌入式硬件·mcu·bash
lhbweilai6 小时前
分析FreeRTOS内核对象API的源码实现(四)
c语言·stm32·单片机
inrichtt9 小时前
第八课时:SPI总线揭秘:四线全双工通信的奥
c语言·数据结构·stm32·单片机·嵌入式硬件
半杯咖啡半行码10 小时前
Linux字符设备驱动开发基础:从设备号到ioctl的完整实战
stm32·单片机·嵌入式硬件
碧海银沙音频科技研究院11 小时前
基于炬芯ATS3089X的usb U盘/usb audio声卡/usb cdc虚拟串口/录音dump到本地功能实现
stm32·单片机·嵌入式硬件
@松12 小时前
学习stm32cudemx +vs 创建hex文件,刷机
stm32
小范的技术工坊13 小时前
Linux应用开发(文件IO)
linux·stm32·单片机
桀人15 小时前
I2C概念引入
stm32·单片机·嵌入式硬件
我是人✓17 小时前
STM32上跑FreeRTOS:实时任务调度全解析
stm32·单片机·嵌入式硬件