新闻详情

SWM32SRET6——LVGL移植

540
发表时间:2022-10-21 08:53作者:全球芯 | glochip.com网址:http://glochip.com/news/

硬件资源介绍

SWM320是一款基于ARM Cortex-M4的32位微控制器,片上包含精度为1%以内的 20MHz/40MHz时钟,可通过PLL倍频到120MHz 时钟,提供多种内置FLASH/SRAM大小可供选择,支持ISP(在系统编程)操作及IAP(在应用编程)。

外设串行总线包括:1个CAN接口、内部叠封8MB SDRAM、多个UART接口、SPI 通信接口(支持主/从选择)及I2C接口(支持主/从选择)。

此外还包括1个32位看门狗定时器,6组32位通用定时器,1组32位专用脉冲宽度测量定时器,12通道16位的PWM发生器,2个8通道12位、1MSPS的逐次逼近型ADC模块,1个SDIO接口模块,TFT-LCD液晶驱动模块以及RTC实时时钟、SRAMC、NORFLC接口控制模块,同时提供欠压检测及低电压复位功能。

LittlevGL移植

LittlevGL概述

LittlevGL是一个免费的开放源代码图形库,它提供创建嵌入式GUI所需的一切,它具有易于使用的图形元素,精美的视觉效果和低内存占用。强大的构建块按钮,图表,列表,滑块,图像等,带有动画,抗锯齿,不透明度,平滑滚动的高级图形,各种输入设备的触摸板,鼠标,键盘,编码器等,多显示器支持,即同时使用更多的TFT和单色显示器,支持UTF-8编码的多语言,完全可定制的图形元素。

独立于任何微控制器或显示器使用的硬件,可扩展以使用较少的内存(80kB闪存,12 kB RAM),支持操作系统,外部存储器和GPU,但不是必需的,即使使用单帧缓冲区操作,也具有高级图形效果。

用C语言编写,以实现最大的兼容性(与C++兼容),模拟器可在没有嵌入式硬件的PC 上启动嵌入式GUI设计,快速GUI设计的教程,示例,主题,在线和离线文档,在 MIT 许可下免费和开源。

LittlevGL 官网:https://lvgl.io

GitHub 地址:https://github.com/lvgl/lvgl

LittlevGL硬件要求

● 16、32或64位微控制器或处理器

● 建议时钟频率大于16MHz

● 闪存/ ROM:对于非常重要的组件,其大小大于64 kB(建议大于180 kB)

● 内存:

● 静态RAM使用量:大约8至16 kB,具体取决于所使用的功能和对象类型

● 堆栈:大于2kB(建议大于4kB)

● 动态数据(堆):大于4 KB(如果使用多个对象,则建议大于16 kB)。LV_MEM_SIZE 在lv_conf.h中设置

● 显示缓冲区:大于“水平分辨率”像素(建议大于10× “水平分辨率”)

● C99或更高版本的编译器

● 基本的C(或C ++)知识:指针,结构,回调

LittlevGL移植

01、屏幕介绍

开发板板载的是一个RGB接口的屏幕JLT4301A,我们的板子使用的是RGB565接口。屏幕分辩率480*272,显示方向为横向。

触摸采用的是 GT911的电容触摸屏。

使用RGB屏,SDRAM是必须的,因为RGB屏需要使用显存,例如480*272的RGB565 屏幕,一个像素占用2字节的显存,总共需要480*272*2=261120 折合255KB的显存,内部RAM很显然是不够用的。那么RGB屏的驱动只需要使能背光、配置LCDC的外设以及显存的地址就可以了。然后往屏幕填充内容就是往对应的显存发送数据就可以了。不同 RGB 屏的配置参数可能不一样,显示方向也不一样。

开发板使用的是电容触摸屏,使用I2C进行通信,利用I2C初始化触摸IC GT911后,我们就可以通过I2C读出触摸的绝对位置,跟屏幕是一一对应的。

02、移植流程

LittlevGL 的移植过程也非常简单,总结了以下几个步骤:

1. 添加库文件到工程

2. 配置屏幕大小以及颜色深度等跟显示相关的参数

3. 分配一个显示缓冲区并实现屏幕填充的接口

4. 实现输入设备接口,读取触摸屏坐标

5. 实现文件系统接口,实现文件的读取写入

6. 提供一个滴答时钟的接口 lv_tick_inc();

7. 完成库的初始化以及接口的初始化 lv_init();lv_port_disp_init();lv_port_indev_init(); lv_port_fs_init;

8. 定期调用任务处理函数,可设置为 5-10ms lv_task_handler();

03、源码下载

LittlevGL 的源码可以在GitHub 进行下载,https://github.com/littlevgl/lvgl,可以 clone 到本地也可以直接下载压缩包。除了下载源码以外,还可以下载example和drivers。example里面包含了各种应用展示和控件使用示例,drivers 里面包含了一些液晶屏驱动接口示例。


共下载3个文件夹


打开源码文件,里面包含了接口示例文件和配置示例文件,其中src文件夹下面又进行了分类,具体请查看源码。


04、添加库文件到工程

第一步,复制库文件

复制lvgl文件夹到工程文件夹APP下面。

将lvgl\lv_conf_template.h文件复制一份并改名为lv_conf.h。这个文件是lvgl的配置文件,后面再介绍如何修改。

将lv_examples文件夹复制到工程文件夹下面,这里面包含了各种应用和基础示例,我们后面需要使用。

第二步,添加库文件到工程

打开MDK工程,添加源码,在lvgl\src文件夹下面有如下文件,我们在MDK里面添加全部文件即可。


然后在MDK里面再新建一个lvgl_porting文件夹和一个lvgl_demo文件夹,前者用于添加接口文件,后者用于我们后面添加示例文件,将 lvgl\porting\lv_port_disp_template.c和lvgl\porting\lv_port_indev_template.c,lvgl\porting\lv_port_fs_template.c各复制一份,分别改名为lv_port_disp,c和lv_port_indev.c,lv_port_fs.c添加到MDK的lv_porting文件夹下面,后面再针对开发板的硬件进行对应的修改。



05、移植文件的适配

lvgl的源码中包含头文件有完整路径和简单路径两种方式,在MDK里面我们直接使用简单的头文件包含形式。

/*********************  *      INCLUDES  *********************/ #ifdef LV_CONF_INCLUDE_SIMPLE #include "lv_conf.h" #else #include "../../../lv_conf.h" #endif

我们使用简单的头文件包含,在MDK的宏定义里面添加LV_CONF_INCLUDE_SIMPLE 定义,如果后面还有lvgl的头文件路径是MDK不能识别的,读者根据实际情况修改为编译器能识别的格式,后面不再对此修改做介绍。


修改lv_conf文件,在我们之前复制过来的配置示例文件默认是被注释掉了。我们需要打开他。只需要将开头的#if 0修改为#if 1即可,如果后续的文件中再出现这种开关,读者自行打开即可。

/** /*  * COPY THIS FILE AS `lv_conf.h` NEXT TO the `lvgl` FOLDER  */ #if 1 /*Set it to "1" to enable content*/ #ifndef LV_CONF_H #define LV_CONF_H /* clang-format off */ #include

配置屏幕的大小,我们的液晶屏是480*272

/* Maximal horizontal and vertical resolution to support by the library.*/ #define LV_HOR_RES_MAX          (480) #define LV_VER_RES_MAX          (272)

配置颜色位数,我们使用RGB565模式,16位色

/* Color depth:  * - 1:   1 byte per pixel  * - 8:   RGB233  * - 16: RGB565  * - 32: ARGB8888  */ #define LV_COLOR_DEPTH     16 /* Swap the 2 bytes of RGB565 color.  * Useful if the display has a 8 bit interface (e.g. SPI)*/ #define LV_COLOR_16_SWAP   0

LVGL内存配置,这里区别与绘图缓冲区,这里配置的是lvgl的控件等使用的内存,需大于2KB,这里可以使用默认的32K配置,也可以将内存定义100K,从而分配更多的内存。

/* 1: use custom malloc/free, 0: use the built-in `lv_mem_alloc` and `lv_mem_free` */ #define LV_MEM_CUSTOM      0 #if LV_MEM_CUSTOM == 0 /* Size of the memory used by `lv_mem_alloc` in bytes (>= 2kB)*/ #   define LV_MEM_SIZE    (100 * 1024U)

GPU配置,这里关闭,我们使用LCD中断进行buffer的填充

/* 1: Enable GPU interface*/ #define LV_USE_GPU              0

LOG接口配置,这里暂未使用,读者在使用是可以将其配置为串口等

/*1: Enable the log module*/ #define LV_USE_LOG      0 #if LV_USE_LOG /* How important log should be added:  * LV_LOG_LEVEL_TRACE       A lot of logs to give detailed information  * LV_LOG_LEVEL_INFO        Log important events  * LV_LOG_LEVEL_WARN        Log if something unwanted happened but didn't cause a problem  * LV_LOG_LEVEL_ERROR       Only critical issue, when the system may fail  * LV_LOG_LEVEL_NONE        Do not log anything  */ #   define LV_LOG_LEVEL    LV_LOG_LEVEL_WARN /* 1: Print the log with 'printf';  * 0: user need to register a callback with `lv_log_register_print_cb`*/ #   define LV_LOG_PRINTF   1 #endif   /*LV_USE_LOG*/

06、配置植接口

LittlevGL绘制的过程需要有一个缓冲区disp_buf,lvgl内部将位图绘制到这个缓冲区,缓冲区满了以后调用flush_cb接口函数进行屏幕的填充,我们将这部分缓冲区的内容通过LCD中断搬运到液晶屏,当填充完成后,调用lv_disp_flush_ready函数通知库绘制已经完成,可以开始其他绘制过程。

LittlevGL已经提供了一个示例文件,我们上面提到的复制文件也是使用他提供的文件,只需要我们修改几个接口函数即可。

定义lvgl绘制的缓冲区,这里需定义在外部SDRAM定义两个全屏的缓冲区。

static lv_disp_buf_t disp_buf; #ifdef SWM_USING_SRAM static lv_color_t lcdbuf_1[LV_HOR_RES_MAX * LV_VER_RES_MAX] __attribute__((at(SRAMM_BASE)))           = {0x00000000}; static lv_color_t lcdbuf_2[LV_HOR_RES_MAX * LV_VER_RES_MAX] __attribute__((at(SRAMM_BASE + 0x3FC00))) = {0x00000000}; #endif #ifdef SWM_USING_SDRAM static uint32_t lcdbuf_1[LV_HOR_RES_MAX * LV_VER_RES_MAX / 2] __attribute__((at(SDRAMM_BASE)))           = {0x00000000}; static uint32_t lcdbuf_2[LV_HOR_RES_MAX * LV_VER_RES_MAX / 2] __attribute__((at(SDRAMM_BASE + 0x3FC00))) = {0x00000000}; #endif     lv_disp_buf_init(&disp_buf, lcdbuf_1, lcdbuf_2, LV_HOR_RES_MAX * LV_VER_RES_MAX); /*Initialize the display buffer*/

定义一个lv_disp_drv_t的变量并初始化。

lv_disp_drv_t disp_drv;      /*Descriptor of a display driver*/     lv_disp_drv_init(&disp_drv); /*Basic initialization*/

设置缓冲区。

/*Set a display buffer*/     disp_drv.buffer = &disp_buf;

设置屏幕填充接口,这里的 disp_fluash 是一个函数,在示例中已经定义,我们直接对其修改就行了。

/*Used to copy the buffer's content to the display*/     disp_drv.flush_cb = disp_flush;

注册驱动程序。

/*Finally register the driver*/     lv_disp_drv_register(&disp_drv);

修改disp_flush函数以适应我们自己的硬件和屏幕。

static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p) {     LCD->SRCADDR = (uint32_t)disp_drv->buffer->buf_act;     LCD_Start(LCD);     /* IMPORTANT!!!      * Inform the graphics library that you are ready with the flushing*/     lv_disp_flush_ready(disp_drv); }

07、配置输入设备接口

lvgl支持键盘、鼠标、按键、触摸屏作为输入设备,我们这里仅仅使用触摸屏进行输入。

在lv_port_indev_template.c中,注册一个触摸屏设备需要以下步骤,我们只需要对触摸屏读取的回调函数进行修改即可。

/*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);

打开触摸屏读取的回调函数touchpad_read函数

* Will be called by the library to read the touchpad */ static bool 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;     /*Return `false` because we are not buffering and no more data to read*/     return false; }

从touchpad_read函数可以看出,用户只需要完成两个接口函数即可。这两个函数的实现如下:

/*Return true is the touchpad is pressed*/ static bool touchpad_is_pressed(void) {     /*Your code comes here*/     if(tp_dev.sta & 0x80)     {         return true;     }     return false; } /*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]; }

08、配置文件系统接口

文件系统使用FATFS,对接前需要完成文件系统的挂载。

文件系统注册。

void lv_port_fs_init(void) {     /*----------------------------------------------------      * Initialize your storage device and File System      * -------------------------------------------------*/     fs_init();     /*---------------------------------------------------      * Register the file system interface   in LittlevGL      *--------------------------------------------------*/     /* Add a simple drive to open images */     lv_fs_drv_t fs_drv;     lv_fs_drv_init(&fs_drv);     /*Set up fields...*/     fs_drv.file_size     = sizeof(file_t);     fs_drv.letter        = 'P';     fs_drv.open_cb       = fs_open;     fs_drv.close_cb      = fs_close;     fs_drv.read_cb       = fs_read;     fs_drv.write_cb      = fs_write;     fs_drv.seek_cb       = fs_seek;     fs_drv.tell_cb       = fs_tell;     fs_drv.free_space_cb = fs_free;     fs_drv.size_cb       = fs_size;     fs_drv.remove_cb     = fs_remove;     fs_drv.rename_cb     = fs_rename;     fs_drv.trunc_cb      = fs_trunc;     fs_drv.rddir_size   = sizeof(dir_t);     fs_drv.dir_close_cb = fs_dir_close;     fs_drv.dir_open_cb   = fs_dir_open;     fs_drv.dir_read_cb   = fs_dir_read;     lv_fs_drv_register(&fs_drv); }

09、配置LittlevGL的嘀嗒心跳时钟接口

LittlevGL的使用需要需要周期性的时钟支持,用户需要定期调用 lv_tick_inc(uint32_t tick_period)函数,我们这里利用滴答定时器的1KHz去调用这个函数。在滴答定时器的中断服务函数中添加lvgl时基函数。

uint8_t tick_indev = 0; void SysTick_Handler_cb(void) {     lv_tick_inc(1);     tick_indev++;     if (tick_indev > 100)     {         tick_indev = 0;         GT911_Scan();     } }

10、初始化

包含lvgl的初始化以及显示和触摸,文件系统接口的初始化。

lv_init(); lv_port_disp_init();     lv_port_indev_init();     lv_port_fs_init();

我们需要在主循环中周期性调用LittelvGL的任务处理函数。

while (1 == 1)     {         lv_task_handler();     }

然后就可以进行应用的开发了。


Home                                    Product                                        News                                   About                                        Contact
Tel:0755-84866816  13924645577
Tel:  0755-84828852   13924649321
Web:  www.glochip.com
Mail:kevin@glochip.com
Add: 深圳市龙岗区大运软件小镇1栋401
Mobile: 13924645577    13924649321 
       微信咨询
加密芯片  华芯微特   艾迪科泰    博雅科技    补丁科技    晶存科技   华大电子   妙存科技   三星半导体   海力士  镁光科技     南亚科技  铠侠半导体  金士顿   Skyhigh  Netsol

SRAM MRAM SDRAM DDR1 DDR2 DDR3 DDR4 DDR5 LPDDR3 LPDDR4 LPDDR4X LPDDR5 LPDDR5X NAND FLASH  NOR FLASH eMMC UFS eMCP uMCP