【RTT】移植:LVGL 8.0.2

  开发板:潘多拉开发板
  系统版本:v4.0.3
  LVGL版本:8.0.2

  注:这篇文章就简单记录下编译成功,跑了一个 demo 的过程,至于是否完全移植成功,不确定哈,毕竟 LVGL 还没自学呢。。

  

一、创建目录

  在 bsp/stm32/stm32l475-atk-pandora/board/ports 目录下创建一个 lvgl 目录,然后将 lvgl 的源码放进该目录。
  目录结构如下所示。
  ├─lvgl
  │ ├─lvgl-8.0.2

  

二、添加配置文件

  将 lvgl-8.0.2/lv_conf_template.h 文件复制到 lvgl 同级目录下,并将其重命名为 lv_conf.h 。打开文件并将开头的 #if 0 更改为 #if 1 以使能其内容。

  将 lv_drivers-6.1.1/lv_drv_conf_template.h 文件复制到 lv_drivers 同级目录下,并将其重命名为 lv_drv_conf.h 。打开文件并将开头的 #if 0 更改为 #if 1 以使能其内容。

  这里需要注意一下 lv_conf.h 文件里面的宏,对于 8.0.2 版本来说,只需要修改下面这个宏就可以了,具体的数值根据你的 LCD 参数决定。

/* Color depth: 1 (1 byte per pixel), 8 (RGB332), 16 (RGB565), 32 (ARGB8888) */
#define LV_COLOR_DEPTH     16

  

三、将主要的源码添加到工程中

  在实际操作之前,我们需要知道有哪些代码是需要添加到工程里面的。
  ├─lvgl
  │ ├─lvgl-8.0.2
  │ │ ├─src (必须)
  当然,这里只是添加了部分的源码,可以基本运行,如果要完整的源码,可以自行添加。

  

1、

  在 bsp/stm32/stm32l475-atk-pandora/board/ports/lvgl、bsp/stm32/stm32l475-atk-pandora/board/ports/lvgl/lvgl-8.0.2/src 目录下创建一个内容和下面相同的 SConscript 文件,用于自动生成工程。

import os
from building import *

# get current dir path
cwd = GetCurrentDir()

# init src and inc vars
src = Glob('*.c')

inc = [cwd]

objs = DefineGroup('lvgl', src, depend = [], CPPPATH = inc)

list = os.listdir(cwd)

for item in list:
    if os.path.isfile(os.path.join(cwd, item, 'SConscript')):
        objs = objs + SConscript(os.path.join(item, 'SConscript'))

Return('objs')

  

2、

  在 bsp/stm32/stm32l475-atk-pandora/board/ports/lvgl/lvgl-8.0.2 创建一个内容和下面相同的 SConscript 文件,用于自动生成工程。

import os
from building import *

# get current dir path
cwd = GetCurrentDir()

objs = []

list = os.listdir(cwd)

for item in list:
    if os.path.isfile(os.path.join(cwd, item, 'SConscript')):
        objs = objs + SConscript(os.path.join(item, 'SConscript'))

Return('objs')

  

3、

  在 bsp/stm32/stm32l475-atk-pandora/board/ports/lvgl/lvgl-8.0.2/src 目录下的子目录 core、draw、font、hal、misc、widgets 中创建一个内容和下面相同的 SConscript 文件,用于自动生成工程。

from building import *

cwd = GetCurrentDir()
src = Glob('*.c') + Glob('*.cpp')+ Glob('*.a')

CPPPATH = [cwd, str(Dir('#'))]

# 因为是使用 keil 编译,所以要加上这句话
LOCAL_CCFLAGS = ' --c99 --gnu -g -W'
    
group = DefineGroup('lvgl', src, depend = [], CPPPATH = CPPPATH, LOCAL_CCFLAGS = LOCAL_CCFLAGS)

Return('group')

  

4、

  对于 bsp/stm32/stm32l475-atk-pandora/board/ports/lvgl/lvgl-8.0.2/src 目录下的子目录 extra 需要单独处理,因为它里面还有多个子目录。(所以下面的代码可维护性有点差)

from building import *

cwd = GetCurrentDir()
src = Glob('*.c')

src += Glob('./layouts/grid/*.c')
src += Glob('./layouts/flex/*.c')

src += Glob('./themes/basic/*.c')
src += Glob('./themes/default/*.c')
src += Glob('./themes/mono/*.c')

src += Glob('./widgets/animimg/*.c')
src += Glob('./widgets/calendar/*.c')
src += Glob('./widgets/chart/*.c')
src += Glob('./widgets/colorwheel/*.c')
src += Glob('./widgets/imgbtn/*.c')
src += Glob('./widgets/keyboard/*.c')
src += Glob('./widgets/led/*.c')
src += Glob('./widgets/list/*.c')
src += Glob('./widgets/meter/*.c')
src += Glob('./widgets/msgbox/*.c')
src += Glob('./widgets/span/*.c')
src += Glob('./widgets/spinbox/*.c')
src += Glob('./widgets/spinner/*.c')
src += Glob('./widgets/tabview/*.c')
src += Glob('./widgets/tileview/*.c')
src += Glob('./widgets/win/*.c')

CPPPATH = [cwd]

CPPPATH += [cwd + '/layouts']
CPPPATH += [cwd + '/layouts/grid']
CPPPATH += [cwd + '/layouts/flex']

CPPPATH += [cwd + '/themes']
CPPPATH += [cwd + '/themes/default']
CPPPATH += [cwd + '/themes/basic']
CPPPATH += [cwd + '/themes/mono']

CPPPATH += [cwd + '/widgets']
CPPPATH += [cwd + '/widgets/animimg']
CPPPATH += [cwd + '/widgets/calendar']
CPPPATH += [cwd + '/widgets/chart']
CPPPATH += [cwd + '/widgets/colorwheel']
CPPPATH += [cwd + '/widgets/imgbtn']
CPPPATH += [cwd + '/widgets/keyboard']
CPPPATH += [cwd + '/widgets/led']
CPPPATH += [cwd + '/widgets/list']
CPPPATH += [cwd + '/widgets/meter']
CPPPATH += [cwd + '/widgets/msgbox']
CPPPATH += [cwd + '/widgets/span']
CPPPATH += [cwd + '/widgets/spinbox']
CPPPATH += [cwd + '/widgets/spinner']
CPPPATH += [cwd + '/widgets/tabview']
CPPPATH += [cwd + '/widgets/tileview']
CPPPATH += [cwd + '/widgets/win']

# 因为是使用 keil 编译,所以要加上这句话
LOCAL_CCFLAGS = ' --c99 --gnu -g -W'
    
group = DefineGroup('lvgl', src, depend = [], CPPPATH = CPPPATH, LOCAL_CCFLAGS = LOCAL_CCFLAGS)

Return('group')

   到这里,可以使用 scons --target=mdk5 来生成 keil 工程,然后编译一次,如果编译通过,那就移植完成了,后面就是初始化了。

  

四、lvgl 初始化

   对于 LVGL 的初始化,在 RT-Thread 中创建一个线程就可以了。
   对于 LVGL 心跳来说,在 RT-Thread 中创建一个软定时器就可以了。(注意内核要开启软定时器线程)
   其他的嘛,暂时没管。
   初始化完成后,就可以使用 demo 命令来运行 demo 了。

#include <rtthread.h>
#include <rtdevice.h>
#include <board.h>

#include <fal.h>
#include <dfs_fs.h>
#include <dfs_posix.h>

#include <drv_lcd.h>
#include <lvgl.h>

/* defined the LED0 pin: PE7 */
#define LED0_PIN    GET_PIN(E, 7)

static rt_thread_t led_tid = RT_NULL;

static rt_thread_t lvgl_tid = RT_NULL;
static rt_timer_t lvgl_timer = RT_NULL;

#define MY_DISP_HOR_RES 240

static void led_thread_entry(void *parameter)
{
    rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT);

    while (1)
    {
        rt_pin_write(LED0_PIN, PIN_HIGH);
        rt_thread_mdelay(500);
        rt_pin_write(LED0_PIN, PIN_LOW);
        rt_thread_mdelay(500);
    }
} 

int thread_sample(void)
{
    led_tid = rt_thread_create("led",
                               led_thread_entry, 
                               RT_NULL,
                               256,     /* stack size */
                               25,      /* priority */
                               5);      /* time slice */
    if (led_tid != RT_NULL)
        rt_thread_startup(led_tid);

    return 0;
}

/* 这个函数定义在 lcd_drv.c 文件中,因为它使用了一些 lcd 驱动提供的函数 */
extern void lcd_lvgl_draw(rt_uint16_t x1, rt_uint16_t y1, rt_uint16_t x2, rt_uint16_t y2, rt_uint16_t* color);

/**
 * void lcd_lvgl_draw(rt_uint16_t x1, rt_uint16_t y1, rt_uint16_t x2, rt_uint16_t y2,  rt_uint16_t* color)
 * {
 *   rt_uint16_t i,j;            
 *   rt_uint16_t width = x2 - x1 + 1;        /* 得到填充的宽度 */
 *   rt_uint16_t height = y2 - y1 + 1;       /* 高度 */
 *   
 *   lcd_address_set(x1,y1,x2,y2);
 *   rt_pin_write(LCD_DC_PIN, PIN_HIGH);
 *
 *   for(i = 0; i < height; i++)
 *   {
 *       for(j = 0; j < width; j++)
 *       {
 *           lcd_write_half_word(color[i*width+j]);
 *       }   
 *   }
 *
 * }
 **/

void my_flush_cb(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
    lv_coord_t hres = disp_drv->rotated == 0 ? disp_drv->hor_res : disp_drv->ver_res;
    lv_coord_t vres = disp_drv->rotated == 0 ? disp_drv->ver_res : disp_drv->hor_res;
    
    if(area->x2 < 0 || area->y2 < 0 || area->x1 > hres - 1 || area->y1 > vres - 1) 
    {
        lv_disp_flush_ready(disp_drv);
        return;
    }
    
    lcd_lvgl_draw(area->x1, area->y1, area->x2, area->y2, (rt_uint16_t*)color_p);

    lv_disp_flush_ready(disp_drv);
}

lv_disp_draw_buf_t disp_buf;
lv_disp_drv_t disp_drv;
lv_disp_t *disp;
lv_color_t g_buf[MY_DISP_HOR_RES * 10];

static void lvgl_thread_entry(void *parameter)
{
    /* lvgl 库初始化 */
    lv_init();
    
    /* 显示缓冲区 */
    lv_disp_draw_buf_init(&disp_buf, g_buf, NULL, MY_DISP_HOR_RES * 10);
    
    /* 在 LVGL 中注册显示设备驱动程序 */
    lv_disp_drv_init(&disp_drv);
    disp_drv.draw_buf = &disp_buf;
    disp_drv.flush_cb = my_flush_cb;
    disp_drv.hor_res = 240;
    disp_drv.ver_res = 240;     
    disp = lv_disp_drv_register(&disp_drv);
    
    while (1)
    {
        lv_task_handler();
        rt_thread_mdelay(5);	/* 官方手册是提议至少 5 ms */
    }
}

static void lvgl_timeout(void *parameter)
{
    lv_tick_inc(10);    /* 因为一个时钟是 10ms */
}

int lvgl_sample(void)
{
    lvgl_tid = rt_thread_create("lvgl",
                               lvgl_thread_entry, 
                               RT_NULL,
                               4096,     /* stack size */
                               12,      /* priority */
                               5);      /* time slice */
    if (lvgl_tid != RT_NULL)
        rt_thread_startup(lvgl_tid );
        
    lvgl_timer = rt_timer_create("lvgl_timer",
    			                 lvgl_timeout,
                                 RT_NULL,
                                 1,	/* 超时时间,单位是时钟节拍 */
                                 RT_TIMER_FLAG_PERIODIC);
    if (lvgl_timer != RT_NULL) 
    	rt_timer_start(lvgl_timer );
     
    return 0;
}

void fs_init(void)
{
    fal_init();

    fal_blk_device_create("filesystem");
    if (dfs_mount("filesystem", "/", "elm", 0, 0) != 0)
    {
        if(dfs_mkfs("elm", "filesystem") == 0)
        {
    	    if (dfs_mount("filesystem", "/", "elm", 0, 0) != 0)
            {
    		    rt_kprintf("file system initialization failed!\n");
    	    }
        }
    }

    if (opendir("/mnt") == RT_NULL)
    {
        if (mkdir("mnt", 0x777) == -1)
            return;
    }    
        
    if(rt_device_find("sd0") == RT_NULL)
    {
        rt_kprintf("failed to find sd card device.\n");
        return;
    }

    if (dfs_mount("sd0", "/mnt", "elm", 0, 0) != RT_EOK)
    {
        rt_kprintf("sd card mount to '/mnt' failed!\n");
    }
}

/* 这个是官方例程,代码路径为:https://github.com/lvgl/lvgl/blob/master/examples/widgets/label/lv_example_label_1.c */
void demo(void)
{
    lv_obj_t * label1 = lv_label_create(lv_scr_act());
    lv_label_set_long_mode(label1, LV_LABEL_LONG_WRAP);     /*Break the long lines*/
    lv_label_set_recolor(label1, true);                      /*Enable re-coloring by commands in the text*/
    lv_label_set_text(label1, "#0000ff Re-color# #ff00ff words# #ff0000 of a# label, align the lines to the center "
                              "and wrap long text automatically.");
    lv_obj_set_width(label1, 150);  /*Set smaller width to make the lines wrap*/
    lv_obj_set_style_text_align(label1, LV_TEXT_ALIGN_CENTER, 0);
    lv_obj_align(label1, LV_ALIGN_CENTER, 0, -40);


    lv_obj_t * label2 = lv_label_create(lv_scr_act());
    lv_label_set_long_mode(label2, LV_LABEL_LONG_SCROLL_CIRCULAR);     /*Circular scroll*/
    lv_obj_set_width(label2, 150);
    lv_label_set_text(label2, "It is a circularly scrolling text. ");
    lv_obj_align(label2, LV_ALIGN_CENTER, 0, 40);
}
MSH_CMD_EXPORT(demo, demo);

int main(void)
{
    fs_init();  
	thread_sample();    
    lvgl_sample();
    return RT_EOK;
}

  

五、实际运行效果

在这里插入图片描述

版权声明:本文为CSDN博主「Zeepunt」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/h451884098/article/details/119086173

生成海报
点赞 0

Zeepunt

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

RT-Thread移植TouchGFX

RT-Thread移植TouchGFX 我的硬件条件:反客STM32H743IIT67寸RGB屏幕800*480 为什么基于RT-Thread? 支持国产,RT-Thread 已经拥有一个国内最大的嵌

freertos与rtthread内核实现的不同处

一直在使用rtos作为主要开发内容,却没有详细了解过rtos的内核实现机制。最近一个月,抽了点时间将freertos和rtthread的内核代码看了下,了解了实时系统的实现机制和设计思想。这里学习fre