简介
ESP32-32出色的性价比,较好的性能与内存空间,可以好利用来完成GUI显示库的加载
LVGL
LVGL是一款比较流行的致力于MCU与MPU创建漂亮UI的嵌入式图形库,免费且开源。
硬件
硬件采用的是正点原子的ESP32-S3
屏幕使用的是SPI通信方式,配合IO口控制(RST,A0),来实现LCD屏幕的驱动
移植步骤
LVGL移植总的步骤主要是如下几步
1.调用lv_init();
2.初始化驱动
3.注册显示与输入驱动,显存的配置,显示响应回调函数的响应
4.lv_tick_inc(x) 在中断中定时更新,x设定取决于lv_tick_inc的调用频率
5.lv_timer_handler,定时调用,完成LVGL的响应(更新LVGL的响应)
具体示例
拷贝一个可以正常驱动LCD的工程
拷贝LVGL(V8.3.0)代码至工程,ESP32需要在指定的路径components下
由于ESP32并不需要去修改lv_conf.h这个文件来配置LVGL,可以通过设置项来修改LVGL的配置,具体的配置机制原理未深入了解
配置LVGL
在默认的设置下勾选MUSIC DEMO,由于MUSCI DEMO中还用到了其他字体,还需要勾选 Montserrat 12与Montserrat 16这两种字体
此时编译工程,应该是可以编译通过
移植LVGL相关代码
SemaphoreHandle_t xGuiSemaphore;void lvgl_demo(void)
{lv_init(); //LVGL初始化前都需要调用lv_port_disp_init(); //显示驱动的移植,初始化及配对,输出 lv_port_indev_init(); //输入驱动的移植xGuiSemaphore = xSemaphoreCreateMutex();const esp_timer_create_args_t lvgl_tick_timer_args = {.callback = &increase_lvgl_tick,.name = "lvgl_tick"};esp_timer_handle_t lvgl_tick_timer = NULL;ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer));ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer, 1 * 1000)); //创建定时器,更新LVGL的内部时钟基准lv_demo_music();while(1){if( pdTRUE == xSemaphoreTake(xGuiSemaphore, portMAX_DELAY)){lv_timer_handler(); //定时调用,更新LVGL的实现xSemaphoreGive(xGuiSemaphore);}vTaskDelay(pdMS_TO_TICKS(5));}
}static void increase_lvgl_tick(void *arg)
{lv_tick_inc(1);
}
显示初始化与绑定代码
void lv_port_disp_init(void)
{void *buf1 = NULL;buf1 = heap_caps_malloc(lcd_self.width * 10 * sizeof(lv_color_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT); //申请显存static lv_disp_draw_buf_t disp_buf;lv_disp_draw_buf_init(&disp_buf, buf1, NULL, lcd_self.width * 10); //绑定显存,显存绑定有多种形式,这是一种开销最小的单显存设置static lv_disp_drv_t disp_drv;lv_disp_drv_init(&disp_drv);disp_drv.hor_res = lcd_self.width; //屏幕宽度disp_drv.ver_res = lcd_self.height; //屏幕高度disp_drv.flush_cb = lvgl_disp_flush_cb;disp_drv.draw_buf = &disp_buf;lv_disp_drv_register(&disp_drv); //初始化显示屏幕尺寸,并绑定屏幕更新函数
}static void lvgl_disp_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
{lcd_color_fill(area->x1, area->y1, area->x2, area->y2, (uint16_t*)color_map);//显示屏区域更新像素点颜色,像素颜色在数组中lv_disp_flush_ready(drv); //发送更新完毕,这一句代码必须要有
}
输出驱动里面如果是空的也可以完成显示的移植,只是没有输入功能
demo示例,参考的正点原子提供的代码,有没有都不影响MUSIC DEMO的显示
MUSCI DEMO对这几个按键也没有响应,加上了,也无法判断是否已正常工作
void lv_port_indev_init(void)
{static lv_indev_drv_t indev_drv;keypad_init();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);
}uint32_t g_last_key = 0;void keypad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{uint32_t act_key = keypad_get_key();if(act_key != 0){data->state = LV_INDEV_STATE_PR;switch(act_key){case KEY0_PRES:act_key = LV_KEY_RIGHT;break;case KEY1_PRES:act_key = LV_KEY_NEXT;//back_act_key = KEY1_PRES;break;case KEY2_PRES:act_key = LV_KEY_LEFT;break;case KEY3_PRES:act_key = LV_KEY_ENTER;break;default:break;}g_last_key = act_key;}else{data->state = LV_INDEV_STATE_REL;g_last_key = 0;}data->key = g_last_key;
}uint32_t keypad_get_key(void)
{return xl9555_key_scan(0);
}
显示效果
V9.1.0移植
大体步骤与V8.3.0一致,期间出了一些问题,在这里也记录下
屏幕驱动API有些变化,更新程序的形参考声明有所变化,直接转换成uint16_t*的16位RGB565颜色也可以正常显示,暂时没有深究具体原因
void lv_port_disp_init(void)
{void *buf1 = NULL;buf1 = heap_caps_malloc(lcd_self.width * 10 * 4, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);lv_display_t *disp = lv_display_create(lcd_self.width, lcd_self.height);lv_display_set_flush_cb(disp, lvgl_disp_flush_cb);lv_display_set_buffers(disp, buf1, NULL, lcd_self.width * 10 * 4, LV_DISPLAY_RENDER_MODE_PARTIAL);
}static void lvgl_disp_flush_cb(lv_disp_t *drv, const lv_area_t *area, uint8_t *px_map)
{lcd_color_fill(area->x1, area->y1, area->x2, area->y2, (uint16_t*)px_map);lv_disp_flush_ready(drv);
}
无法正常显示,提示栈溢出
原因在于任务栈太小了,导致任务运行出错,增大任务栈即可正常工作。LVGL的使用,相对来说使用的STACK较深,系统默认的配置就不太够用
总结
LVGL需要配置显存跟内存,同时也需要考虑任务栈的问题,使用的过程中一定也会遇到很多问题,资料相对较少要自己多加摸索。
经过上述步骤 LVGL可以在ESP32上面运行DEMO,不过这个离会用LVGL还有一定的距离。
还需要查看更多的LVGL资料,以及ESP32-IDF编程的资料,才能进一步熟悉并使用。