LittlevGL 是一个开源免费的GUI,支持触摸屏操作,移植简单方便,开发者一直在不断完善更新。LittlevGL 自带了丰富的控件:窗口、按键、标签、list、图表等,还可以自定义控件;支持很多特效:透明、阴影、自动显示隐藏滚动条、界面切换动画、图标打开关闭动画、平滑的拖拽控件、分层显示、反锯齿等等。
三星T200是一颗带WIFI的MCU,主频可到320M,对LittlevGL来说速度是足够了。主要难点是t200的系统用的是三星自己的TizenOS RT,是一个nuttX核的RTOS。这里简单记录一下我的移植步骤。
1.首先当然是下载源码包,得到lv_drivers.zip,lv_examples.zip,lvgl_v5_3.zip这三个源码包。
2.lv_drivers.zip可以暂时先不用,把lv_examples.zip和lvgl_v5_3.zip解压到APP Demo目录,.复制lvgl文件夹下的lv_conf_templ.h文件到文件夹lvgldemo下面,并重命名为lv_conf.h。修改lv_conf.h里的几个参数。最基本的3个:LCD的长和宽,像素位深。其他参数暂时不管。还需要将文件开头的#if 0 改为 #if 1;复制lv_examples文件夹下的lv_ex_conf_templ.h文件到文件夹lvgldemo下面,并重命名为lv_ex_conf.h。将文件开头的#if 0 改为 #if 1.
3.修改Makefile文件,以及LittleVGL的MK文件,让编译通过。生成相应的目标文件,其中可能需要修改一些宏来让其顺利编译。
4.在lvgldemo下面添加以下lv_main.c文件,来运行lvgl demo.在源码的移植中有三个重要文件lv_hal_disp.c,lv_hal_indev.c,lv_hal_tick.c在\lvgl\lv_hal目录,包括显示接口的移植,输入接口的移植以及tick移植。现在没有触摸,所以输入先不管。
5.因为我没有加载专业的driver做显示接口,所以我的显示接口在函数static void ex_disp_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p)里来实现。
6.根据系统修改tick_get
uint32_t lv_tick_get(void){
struct timeval tv;
if (gettimeofday(&tv, NULL) != 0)
return 0;
return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}
以下是lv_main.c文件源码。
#include "lvgl/lvgl.h"
#include "kmrtm28028.h"
#include "lv_examples/lv_apps/demo/demo.h"
/**********************
* STATIC PROTOTYPES
**********************/
static void ex_disp_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p);
static void ex_disp_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p);
static void ex_disp_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t color);
#if USE_LV_GPU
static void ex_mem_blend(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa);
static void ex_mem_fill(lv_color_t * dest, uint32_t length, lv_color_t color);
#endif
static bool ex_tp_read(lv_indev_data_t * data);
/**********************
* STATIC VARIABLES
**********************/
/* 添加 fb 支持 */
//#include "lv_drivers/display/fbdev.h"
EPD epd;
int lv_main(void)
{
/*LittlevGL init*/
lv_init();
/*Linux frame buffer device init*/
//fbdev_init();
if (kmrtm_Init(&epd) != 0) {
printf("e-Paper init failed\n");
return 0;
}
//Lcd_Init(); //tft初始化
kmrtm_Clear(&epd,BLACK); //清屏
/*Add a display the LittlevGL sing the frame buffer driver*/
lv_disp_drv_t disp_drv;
lv_disp_drv_init(&disp_drv); /*Basic initialization*/
/*Set up the functions to access to your display*/
disp_drv.disp_flush = ex_disp_flush; /*Used in buffered mode (LV_VDB_SIZE != 0 in lv_conf.h)*/
disp_drv.disp_fill = ex_disp_fill; /*Used in unbuffered mode (LV_VDB_SIZE == 0 in lv_conf.h)*/
disp_drv.disp_map = ex_disp_map; /*Used in unbuffered mode (LV_VDB_SIZE == 0 in lv_conf.h)*/
printf("use_lv_gpu in\r\n");
#if USE_LV_GPU
/*Optionally add functions to access the GPU. (Only in buffered mode, LV_VDB_SIZE != 0)*/
disp_drv.mem_blend = ex_mem_blend; /*Blend two color array using opacity*/
disp_drv.mem_fill = ex_mem_fill; /*Fill a memory array with a color*/
printf("use_lv_gpu in+++\r\n");
#endif
printf("use_lv_gpu out\r\n");
/*Finally register the driver*/
lv_disp_drv_register(&disp_drv);
/*************************
* Input device interface
*************************/
/*Add a touchpad in the example*/
/*touchpad_init();*/ /*Initialize your touchpad*/
lv_indev_drv_t indev_drv; /*Descriptor of an input device driver*/
lv_indev_drv_init(&indev_drv); /*Basic initialization*/
indev_drv.type = LV_INDEV_TYPE_POINTER; /*The touchpad is pointer type device*/
indev_drv.read = ex_tp_read; /*Library ready your touchpad via this function*/
lv_indev_drv_register(&indev_drv); /*Finally register the driver*/
printf("register touchpad driver\r\n");
/*************************************
* Run the task handler of LittlevGL
*************************************/
/* 选择示例启动 */
//benchmark_create();
//demo_create();
sysmon_create();
printf("benchmark_create out\r\n");
/*Handle LitlevGL tasks (tickless mode)*/
while(1) {
lv_tick_inc(5);
lv_task_handler();
usleep(5000);
}
return 0;
}
/**********************
* STATIC FUNCTIONS
**********************/
/* 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_flush_ready()' has to be called when finished
* This function is required only when LV_VDB_SIZE != 0 in lv_conf.h*/
static void ex_disp_flush(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p)
{
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
int32_t x;
int32_t y;
int i=0;
printf("ex_disp_flush,x1=%d,y1=%d,x2=%d,y2=%d.\r\n",x1,y1,x2,y2);
for(y = y1; y <= y2; y++) {
for(x = x1; x <= x2; x++) {
/* Put a pixel to the display. For example: */
/* put_px(x, y, *color_p)*/
kmrtm_lvgl_mem(i,(u16)color_p->full);
//kmrtm_lcd_mem(&epd,(u16)color_p->full);
//kmrtm_Paint(&epd,x,y,1,1);
i++;
color_p++;
}
}
if(x1==0)
kmrtm_lvgl_Paint(&epd,x1,y1,x2-x1+1,y2-y1+1);
else
kmrtm_lvgl_Paint(&epd,x1,y1,x2-x1,y2-y1);
/* IMPORTANT!!!
* Inform the graphics library that you are ready with the flushing*/
lv_flush_ready();
}
/* Write a pixel array (called 'map') to the a specific area on the display
* This function is required only when LV_VDB_SIZE == 0 in lv_conf.h*/
static void ex_disp_map(int32_t x1, int32_t y1, int32_t x2, int32_t y2, const lv_color_t * color_p)
{
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
int32_t x;
int32_t y;
printf("ex_disp_map\r\n");
for(y = y1; y <= y2; y++) {
for(x = x1; x <= x2; x++) {
/* Put a pixel to the display. For example: */
/* put_px(x, y, *color_p)*/
kmrtm_lvgl_mem(&epd,(u16)color_p->full);
kmrtm_lvgl_Paint(&epd,x,y,1,1);
color_p++;
}
}
}
/* Write a pixel array (called 'map') to the a specific area on the display
* This function is required only when LV_VDB_SIZE == 0 in lv_conf.h*/
static void ex_disp_fill(int32_t x1, int32_t y1, int32_t x2, int32_t y2, lv_color_t color)
{
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
int32_t x;
int32_t y;
printf("ex_disp_fill\r\n");
for(y = y1; y <= y2; y++) {
for(x = x1; x <= x2; x++) {
/* Put a pixel to the display. For example: */
/* put_px(x, y, *color)*/
kmrtm_lvgl_mem(&epd,(u16)color.full);
kmrtm_lvgl_Paint(&epd,x,y,1,1);
}
}
(void)color; /*Just to avid warnings*/
}
#if USE_LV_GPU
/* If your MCU has hardware accelerator (GPU) then you can use it to blend to memories using opacity
* It can be used only in buffered mode (LV_VDB_SIZE != 0 in lv_conf.h)*/
static void ex_mem_blend(lv_color_t * dest, const lv_color_t * src, uint32_t length, lv_opa_t opa)
{
/*It's an example code which should be done by your GPU*/
uint32_t i;
for(i = 0; i < length; i++) {
dest[i] = lv_color_mix(dest[i], src[i], opa);
}
}
/* If your MCU has hardware accelerator (GPU) then you can use it to fill a memory with a color
* It can be used only in buffered mode (LV_VDB_SIZE != 0 in lv_conf.h)*/
static void ex_mem_fill(lv_color_t * dest, uint32_t length, lv_color_t color)
{
/*It's an example code which should be done by your GPU*/
uint32_t i;
for(i = 0; i < length; i++) {
dest[i] = color;
}
}
#endif
/* Read the touchpad and store it in 'data'
* Return false if no more data read; true for ready again */
static bool ex_tp_read(lv_indev_data_t * data)
{
/* Read your touchpad */
/* data->state = LV_INDEV_STATE_REL or LV_INDEV_STATE_PR */
/* data->point.x = tp_x; */
/* data->point.y = tp_y; */
/*In LV_INDEV_STATE_REL state you should use the last pressed coordinates*/
(void)data; /*Just to avid warnings*/
return false; /*false: no more data to read because we are no buffering*/
LittlevGL里面有个简单的时间片调度系统,刷新显示和触摸处理等任务会周期的被调用处理。LCD刷新采用了局部缓存刷新,这样能避免闪烁出现。