摘要
使用正点原子ESP32开发板学习LVGL时发现正点原子的LVGL开发指南里的内容好像不能直接用于新版本的LVGL(9.2版本),于是自己学了一下LVGL的新API,结合正点原子原来的例程成功运行LVGL,做了一个简单的小测试程序,基于正点原字的示例代码中的24_touch示例工程。
1. 功能说明
整体效果如下图所示,整个画面有三个组件组成,使用下方两个按键控制上方的进度条。程序中控制的其实是一个变量,在实际应用中改成需要被控制的变量即可,例如PWM的占空比控制LED亮度。
2. 硬件说明
2.1 开发板
正点原子DNESP32S3开发板。
2.2 屏幕
正点原子4.3寸RGBLCD电容触摸屏800*480。
3. 代码
3.1 移植LVGL
esp32idf移植LVGL非常简单,如下图中所示,只需要在组件管理里搜LVGL然后直接install就行
1. 点idf的欢迎界面中最后一个按钮进入组件管理
2. 搜索LVGL
3. 选择这个lvgl,点进去
4. 点安装。
点击install后LVGL就已经移植好了,idf会帮我们自动把cmakefile和其他的东西都弄好。
下面是与其他平台不同的注意点:
不要动lv_conf_template.h文件,这个是esp组件已经改好的东西了。
想要更改LVGL的设置也不需要改lv_conf_template.h文件,而是在SDK configuration中配置LVGL操作如下图所示。
1. 点vscode最下方蓝色一排的那个小齿轮
2. 在打开的SDK configuration中看左边目录,往下翻找到LVGL配置
这边就是设置LVGL参数的地方,在这个示例中用默认配置就行,这边就不改了。
3.2 主要代码
3.2.1 LCD接口函数
首先LVGL自带字库和很多图形,所以LCD屏只需要留一个初始化和一些基础的函数就行,其他画字和画字符串的函数可以删掉,不删也没事。
LVGL需要一个往屏幕上批量打点的函数接口,在24_touch中没有这样的函数,正点原子教程中直接调用了一个esp_lcd_panel的底层函数,但是这样需要引用头文件啥的,反正很麻烦,我直接在LCD屏中新做了一个给LVGL刷点的函数。如下图。
图中代码如下:
void ltdc_draw_lvgl(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t * color)
{
taskENTER_CRITICAL(&my_spinlock);
esp_lcd_panel_draw_bitmap(panel_handle, x1, y1, x2, y2, color);
taskEXIT_CRITICAL(&my_spinlock);
}
3.2.2 主程序代码
然后修改main.c,代码量较大就不放图了,直接给代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "led.h"
#include "ltdc.h"
#include "touch.h"
#include "lvgl.h"
#include "esp_timer.h"
i2c_obj_t i2c0_master;
uint16_t num;
lv_obj_t * bar1;
static void lvgl_disp_flush_cb(lv_display_t *display, const lv_area_t *area, lv_color16_t *px_map)
{
ltdc_draw_lvgl(area->x1, area->y1, area->x2 + 1, area->y2 + 1, px_map);
lv_disp_flush_ready(display);
}
static void increase_lvgl_tick(void *arg)
{
lv_tick_inc(10);
}
void lcd_init()
{
lv_init();
ltdc_init();
void *buf1 = NULL;
void *buf2 = NULL;
buf1 = heap_caps_malloc(ltdcdev.width * 60 * sizeof(lv_color_t), MALLOC_CAP_DMA);
buf2 = heap_caps_malloc(ltdcdev.width * 60 * sizeof(lv_color_t), MALLOC_CAP_DMA);
lv_display_t *display1 = lv_display_create(ltdcdev.width, ltdcdev.height);
lv_display_set_buffers(display1, buf1, buf2, ltdcdev.width * 60, LV_DISPLAY_RENDER_MODE_PARTIAL);
lv_display_set_flush_cb(display1, lvgl_disp_flush_cb);
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, 10 * 1000));
}
static bool touchpad_is_pressed(void)
{
tp_dev.scan(0); /* 触摸按键扫描 */
if (tp_dev.sta & TP_PRES_DOWN)
{
return true;
}
return false;
}
static void touchpad_get_xy(lv_coord_t *x, lv_coord_t *y)
{
(*x) = tp_dev.x[0];
(*y) = tp_dev.y[0];
}
void touchpad_read(lv_indev_t * indev, 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_PRESSED;
}
else
{
data->state = LV_INDEV_STATE_RELEASED;
}
data->point.x = last_x;
data->point.y = last_y;
}
void touch_init()
{
tp_dev.init();
lv_indev_t * indev = lv_indev_create();
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
lv_indev_set_read_cb(indev, touchpad_read);
}
void lv_example_bar_1(void)
{
bar1 = lv_bar_create(lv_screen_active());
lv_obj_set_size(bar1, 200, 20);
lv_obj_align(bar1, LV_ALIGN_CENTER, 0, -50);
lv_bar_set_value(bar1, 50, LV_ANIM_OFF);
}
static void event_handler(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_CLICKED) {
//printf("Clicked\r\n");
if(num<100)
num += 10;
lv_bar_set_value(bar1, num, LV_ANIM_OFF);
}
}
static void event_handler2(lv_event_t * e)
{
lv_event_code_t code = lv_event_get_code(e);
if(code == LV_EVENT_CLICKED) {
//printf("Clicked\r\n");
if(num>0)
num -= 10;
lv_bar_set_value(bar1, num, LV_ANIM_OFF);
}
}
void lv_example_button_1(void)
{
lv_obj_t * label;
lv_obj_t * btn1 = lv_button_create(lv_screen_active());
lv_obj_add_event_cb(btn1, event_handler, LV_EVENT_ALL, NULL);
lv_obj_align(btn1, LV_ALIGN_CENTER, 100, 100);
lv_obj_add_flag(btn1, LV_OBJ_FLAG_CLICKABLE);
label = lv_label_create(btn1);
lv_label_set_text(label, "btn1");
lv_obj_center(label);
lv_obj_t * btn2 = lv_button_create(lv_screen_active());
lv_obj_add_event_cb(btn2, event_handler2, LV_EVENT_ALL, NULL);
lv_obj_align(btn2, LV_ALIGN_CENTER, -100, 100);
lv_obj_add_flag(btn2, LV_OBJ_FLAG_CLICKABLE);
label = lv_label_create(btn2);
lv_label_set_text(label, "btn2");
lv_obj_center(label);
}
void app_main(void)
{
esp_err_t ret;
ret = nvs_flash_init(); /* 初始化NVS */
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
num = 50;
i2c0_master = iic_init(I2C_NUM_0); /* 初始化IIC0 */
xl9555_init(i2c0_master); /* 初始化XL9555 */
lcd_init();
touch_init();
lv_example_button_1();
lv_example_bar_1();
while (1)
{
lv_timer_handler();
vTaskDelay(10);
}
}
整段代码直接复制到main.c中就可以运行出开头的效果。
3.3 其他说明
这里为了简单,没有对num做锁,实际应用时,可以改成freertos的通知,队列或者信号量等,对变量控制更安全一些。
因为加载的慢,要白屏很久才能显示内容。