【LVGL8.3】GUI、移植等调试记录
1.关于移植
关于LVGL移植,网上有已经有很多标准的博客供大家参考。
2.注册屏幕画点函数或区域填充函数
在移植LVGL中,将你所使用的屏幕和LVGL关联起来的一步重要工作,就是将屏幕(例如LCD)的画点函数或区域填充函数注册到LVGL中。
即在lv_port_disp.c中的下面程序中添加,两种方式二选其一即可。
static void disp_flush(lv_disp_drv_t * disp_drv, const lv_area_t * area, lv_color_t * color_p)
{
if(disp_flush_enabled) {
/*The most simple case (but also the slowest) to put all pixels to the screen one-by-one*/
#if 0
// 注册LCD画点函数的方式
int32_t x;
int32_t y;
for(y = area->y1; y <= area->y2; y++) {
for(x = area->x1; x <= area->x2; x++) {
/*Put a pixel to the display. For example:*/
/*put_px(x, y, *color_p)*/
LCD_DrawPixel(x,y,color_p->full);
color_p++;
}
}
#else
// 注册LCD区域填充函数的方式
LCD_FullArea(area->x1,area->y1,area->x2,area->y2,(uint16_t*)color_p);
#endif
}
/*IMPORTANT!!!
*Inform the graphics library that you are ready with the flushing*/
lv_disp_flush_ready(disp_drv);
}
这里有一个疑问就是,LCD_DrawPixel和LCD_FullArea你要怎么去写呢?
LCD_DrawPixel,画点函数,即输入参数为坐标x,y,颜色,即可。这个比较容易理解。那为什么在lvgl在调用它的时候,要有这一句呢----color_p++?
可以看到disp_flush最后一个关于颜色的参数是一个指针,我没有看它的底层实现,我猜测的是lvgl已经将要显示的区域的颜色全部按照刷新顺序一点一点的排好了,然后color_p++,逐一赋值给LCD的画点函数即可。
我是用的ILI9488的LCD,串行spi控制,注册画点函数,肉眼可见屏幕一行一行的刷新。
因为考虑用区域填充函数实现。
我一开始使用的是LCD_FullArea(area->x1,area->y1,area->x2,area->y2,color_p->full);结果屏幕上一片白。根据前面color_p++的解释,可以想到这样实现,相当于把一种颜色全部填充到了这个区域中,所以这个参数也应该传入指针,就像画点函数那样操作一样,也使color_p++。
3.lvgl显示优化
我使用spi与LCD【320*480】进行通信,中间经历了几个优化阶段。
①硬件spi + lcd画点函数,cpu占用奇高。
②硬件spi + lcd画区域函数,cpu降低为2-3%。但是使用动画效果cpu占用高达70%。
③硬件spi + dma + lcd画区域函数,cpu降低为60+%。
④硬件spi + dma + lcd画区域函数 + lvgl非全尺寸双缓冲,使用动画效果时,cpu占用50%。
仍旧可以优化的点:
使用的hal库
串行改并行
换大内存,使用lvgl全尺寸双缓冲
我的屏幕是18bit-rgb,lvgl是16bit-rgb,导致中间还需要自己转换。
0.常用API总结
(1)基础对象
/* 大小 */
void test_size(void)
{
/* 基础对象 */
lv_obj_t *obj = lv_obj_create(lv_scr_act());
/* 设置、获取大小 */
lv_obj_set_width(obj, 200);
lv_obj_set_height(obj, 90);
lv_obj_set_size(obj, 300, 150);
lv_obj_update_layout(obj);
printf("w:%d,h:%d\n",lv_obj_get_width(obj),lv_obj_get_height(obj));
}
/* 位置 */
void test_pos(void)
{
lv_obj_t *obj = lv_obj_create(lv_scr_act());
/* 设置位置 */
lv_obj_set_x(obj, 50);
lv_obj_set_y(obj, 60);
lv_obj_set_pos(obj, 90, 50);
lv_obj_set_x(obj, lv_pct(10)); //x = 基于父元素内容区域宽度的10%
lv_obj_set_y(obj, lv_pct(80));
}
/* 对齐 */
void test_align(void)
{
lv_obj_t *obj = lv_obj_create(lv_scr_act());
/* 对齐操作 */
/**
* 依照父对象进行对其,对齐方式相当于把坐标原点放到了对齐的位置,然后第三、四个参数即为相应偏移
LV_ALIGN_TOP_LEFT、 LV_ALIGN_TOP_MID、 LV_ALIGN_TOP_RIGHT 【顶部】
LV_ALIGN_BOTTOM_LEFT、LV_ALIGN_BOTTOM_MID、LV_ALIGN_BOTTOM_RIGHT 【底部】
LV_ALIGN_LEFT_MID、 LV_ALIGN_RIGHT_MID、 LV_ALIGN_CENTER 【中部】
**/
lv_obj_align(obj, LV_ALIGN_CENTER, -200, 100); // 子对象会跟随父对象自动对齐
lv_obj_align(obj, LV_ALIGN_LEFT_MID, -10, 100); // 假如,左对齐,然后x在左偏移10,那么会有10像素不显示。
lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);
/**
* 对象与任意参考对象对齐(外部)
LV_ALIGN_OUT_TOP_LEFT、 LV_ALIGN_OUT_TOP_MID、 LV_ALIGN_OUT_TOP_RIGHT 【顶部】
LV_ALIGN_OUT_BOTTOM_LEFT、LV_ALIGN_OUT_BOTTOM_MID、LV_ALIGN_OUT_BOTTOM_RIGHT 【底部】
LV_ALIGN_OUT_LEFT_TOP、 LV_ALIGN_OUT_LEFT_MID、 LV_ALIGN_OUT_LEFT_BOTTOM 【左部】
LV_ALIGN_OUT_RIGHT_TOP、 LV_ALIGN_OUT_RIGHT_MID、 LV_ALIGN_OUT_RIGHT_BOTTOM 【右部】
**/
lv_obj_t *obj_ref = lv_obj_create(lv_scr_act());
lv_obj_align(obj_ref, LV_ALIGN_CENTER, 0, 0);
//可以理解为,将坐标系重新定位到了对齐的点。正数左下移动,负数右上移动
lv_obj_align_to(obj, obj_ref, LV_ALIGN_OUT_TOP_MID, 0, 10); //不能在对象的坐标或参考对象的坐标发生变化时重新对齐对象
}
/* */
void test1(void)
{
lv_obj_t *obj = lv_obj_create(lv_scr_act());
/* 设置对象的内容区域,如果原对象大小不足,则会扩大对象 */
lv_obj_set_content_width(obj, 500); // 实际宽度:左内边距 + 50 + 右内边距
lv_obj_set_content_height(obj, 300); // 实际高度:顶部内边距 + 30 + 底部内边距
lv_obj_t *obj_c = lv_obj_create(obj);
lv_obj_set_size(obj_c, 290, 140);
lv_obj_update_layout(obj);
printf("w:%d,h:%d\n",lv_obj_get_width(obj),lv_obj_get_height(obj));
printf("w_c:%d,h_c:%d\n",lv_obj_get_content_width(obj),lv_obj_get_content_height(obj));
}
/* 盒子模型 */
void test_box(void)
{
lv_obj_t * obj = lv_obj_create(lv_scr_act());
lv_obj_set_size(obj, 200, 200);
lv_obj_align(obj, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_style_border_width(obj, 5, 0); // 【边框】第三个参数是样式选择器,例如仅在按下状态下有效
lv_obj_set_style_outline_width(obj, 10, 0); // 【边界】
lv_obj_set_style_pad_all(obj, 1, 0); // 【填充】也可以修改不同方向的填充,或者使用style修改
lv_obj_t *obj_c = lv_obj_create(obj);
lv_obj_set_size(obj_c, 50, 50);
lv_obj_update_layout(obj);
printf("w:%d,h:%d\n",lv_obj_get_width(obj),lv_obj_get_height(obj));
printf("w_c:%d,h_c:%d\n",lv_obj_get_content_width(obj),lv_obj_get_content_height(obj));
}
void test_translation(void)
{
static lv_style_t style_normal;
lv_style_init(&style_normal);
lv_style_set_y(&style_normal, 100);
static lv_style_t style_pressed;
lv_style_init(&style_pressed);
lv_style_set_translate_y(&style_pressed, -100); // 坐标平移
lv_style_set_translate_x(&style_pressed, -100);
lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_style(btn, &style_normal, LV_STATE_DEFAULT);
lv_obj_add_style(btn, &style_pressed, LV_STATE_PRESSED);
}
void test_transformation(void)
{
static lv_style_t style_pressed;
lv_style_init(&style_pressed);
lv_style_set_transform_width(&style_pressed, 50); // 像素变化
lv_style_set_transform_height(&style_pressed, 10);
lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_style(btn, &style_pressed, LV_STATE_PRESSED);
}
void test_minmax_size(void)
{
static lv_style_t style_max_height;
lv_style_init(&style_max_height);
lv_style_set_max_height(&style_max_height, 80); // 设置最大高度
lv_obj_t * obj = lv_obj_create(lv_scr_act());
lv_obj_set_height(obj, lv_pct(100));
lv_obj_add_style(obj, &style_max_height, LV_STATE_DEFAULT); //Limit the height to 80 px
}
/** 状态
LV_STATE_DEFAULT (0x0000) 正常,释放状态
LV_STATE_CHECKED (0x0001) 切换或检查状态
LV_STATE_FOCUSED (0x0002) 通过键盘或编码器聚焦或通过触摸板/鼠标点击
LV_STATE_FOCUS_KEY (0x0004) 通过键盘或编码器聚焦,但不通过触摸板/鼠标聚焦
LV_STATE_EDITED (0x0008) 由编码器编辑
LV_STATE_HOVERED (0x0010) 鼠标悬停(现在不支持)
LV_STATE_PRESSED (0x0020) 被按下
LV_STATE_SCROLLED (0x0040) 正在滚动
LV_STATE_DISABLED (0x0080) 禁用状态
**/
/** 部分
LV_PART_MAIN 类似矩形的背景
LV_PART_SCROLLBAR 滚动条
LV_PART_INDICATOR 指标,例如用于滑块、条、开关或复选框的勾选框
LV_PART_KNOB 像手柄一样可以抓取调整值
LV_PART_SELECTED 表示当前选择的选项或部分
LV_PART_ITEMS 如果小部件具有多个相似元素(例如表格单元格)
LV_PART_TICKS 刻度上的刻度,例如对于图表或仪表
LV_PART_CURSOR 标记一个特定的地方,例如文本区域或图表的光标
LV_PART_CUSTOM_FIRST 可以从这里添加自定义部件。
**/
void test_style(void)
{
/* 设置样式 */
static lv_style_t style;
lv_style_init(&style);
lv_style_set_width(&style, 100);
lv_style_set_bg_color(&style, lv_color_hex(0x000000)); // 设置背景色
lv_style_set_bg_opa(&style, LV_OPA_20); // 设置背景透明度
/* 应用样式 */
lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
lv_obj_add_style(btn, &style, LV_PART_MAIN); // 将样式应用于对象
/* 设置样式,应用样式 */
static lv_style_t style_p;
lv_style_init(&style_p);
lv_style_set_bg_color(&style_p, lv_color_hex(0xFF0000)); // 设置背景色
lv_obj_add_style(btn, &style_p, LV_STATE_PRESSED); // 当按键被按下时候的样式
/* 获取样式 */
lv_color_t color = lv_obj_get_style_bg_color(btn, LV_PART_MAIN); // 获取样式的背景颜色
/* 删除样式 */
lv_obj_remove_style_all(btn); // 删除对象的所有样式,直接变为白色了
lv_obj_remove_style(btn, NULL, LV_STATE_PRESSED); // 删除对象的特定的样式(匹配style)或特定状态下的样式(style为NULL,只匹配第三个参数)
/* 通知lvgl,样式发生更改 */
/* 设置本地样式,相当于不定义style,直接使用函数去设置样式 */
lv_obj_set_style_bg_color(btn, lv_color_hex(0x000000), LV_STATE_PRESSED); // 设置背景色
lv_obj_set_style_bg_opa(btn, LV_OPA_50, LV_STATE_PRESSED); // 设置背景透明度
}
/****************** 样式案例 手册 *********************/
void test_style_obj(void)
{
static lv_style_t style;
lv_style_init(&style);
lv_style_set_radius(&style, 10); // 圆角边框
lv_style_set_width(&style, 150);
lv_style_set_height(&style, LV_SIZE_CONTENT); // 根据内容自动
lv_style_set_pad_ver(&style, 20); // 设置填充区域
lv_style_set_pad_left(&style, 20);
lv_style_set_x(&style, lv_pct(50)); // 设置坐标位置
lv_style_set_y(&style, 80);
lv_obj_t *obj = lv_obj_create(lv_scr_act());
lv_obj_add_style(obj, &style, 0);
lv_obj_t * label = lv_label_create(obj);
lv_label_set_text(label, "Hello");
}
void test_style_background (void)
{
static lv_style_t style;
lv_style_init(&style);
lv_style_set_radius(&style, 5);
lv_style_set_bg_color(&style, lv_palette_lighten(LV_PALETTE_GREY, 1)); // 1-4级,明亮度
lv_style_set_bg_grad_color(&style, lv_palette_main(LV_PALETTE_BLUE)); // lv_palette_main设置渐变到达的主颜色
lv_style_set_bg_grad_dir(&style, LV_GRAD_DIR_VER); // 垂直方向渐变
lv_style_set_bg_main_stop(&style, 0); // 主颜色的停止位置,也就是渐变色开始的位置
lv_style_set_bg_grad_stop(&style, 255); // 渐变主颜色的停止位置
lv_obj_t * obj = lv_obj_create(lv_scr_act());
lv_obj_add_style(obj, &style, 0);
lv_obj_center(obj);
lv_obj_set_style_transform_angle(obj, 450, LV_STATE_PRESSED); // 不起作用,该函数只对图像类,起作用
}
void test_translation_time(void)
{
static const lv_style_prop_t props[] = {LV_STYLE_BG_COLOR, LV_STYLE_BORDER_COLOR, LV_STYLE_BORDER_WIDTH, 0};
/* A default transition
* Make it fast (100ms) and start with some delay (200 ms)*/
static lv_style_transition_dsc_t trans_def;
lv_style_transition_dsc_init(&trans_def, props, lv_anim_path_linear, 100, 200, NULL); // 延迟200ms开始,100ms的变化过程
/* A special transition when going to pressed state
* Make it slow (500 ms) but start without delay*/
static lv_style_transition_dsc_t trans_pr;
lv_style_transition_dsc_init(&trans_pr, props, lv_anim_path_linear, 500, 0, NULL); // 延迟0ms开始,500ms的变化过程
static lv_style_t style_def;
lv_style_init(&style_def);
lv_style_set_transition(&style_def, &trans_def);
static lv_style_t style_pr;
lv_style_init(&style_pr);
lv_style_set_bg_color(&style_pr, lv_palette_main(LV_PALETTE_RED));
lv_style_set_border_width(&style_pr, 6);
lv_style_set_border_color(&style_pr, lv_palette_darken(LV_PALETTE_RED, 3));
lv_style_set_transition(&style_pr, &trans_pr);
/*Create an object with the new style_pr*/
lv_obj_t * obj = lv_obj_create(lv_scr_act());
lv_obj_add_style(obj, &style_def, 0); // 将样式注册到不同的状态
lv_obj_add_style(obj, &style_pr, LV_STATE_PRESSED);
lv_obj_center(obj);
}
/*******************结束 样式案例 手册********************/
void my_timer(lv_timer_t * timer)
{
lv_obj_t * obj = lv_obj_create(lv_scr_act());
lv_obj_set_pos(obj, timer->user_data++, timer->user_data++);
}
void test_timer(void)
{
/* 创建定时器 */
static uint32_t user_data = 10;
lv_timer_t *tim = lv_timer_create(my_timer, 500, &user_data); // 回调函数、周期时间、用户数据
lv_timer_ready(tim); // 使计时器在下一次调用 lv_timer_handler时运行。
lv_timer_reset(tim); // 重置计时器的周期
lv_timer_set_cb(tim, my_timer); // 重新设置回调
lv_timer_set_period(tim, 500); // 重新设置周期
lv_timer_set_repeat_count(tim, 5); // 设置重复次数,-1表示一直重复
lv_timer_pause(tim); // 暂停指定的定时器
lv_timer_resume(tim); // 恢复指定的定时器
}
(2)事件触发
static void my_event_cb_1(lv_event_t * event)
{
printf("Clicked-One\n");
}
static void my_event_cb_2(lv_event_t * event)
{
printf("Clicked-Two\n");
}
static void my_event_cb_all(lv_event_t * event)
{
lv_obj_t * obj = lv_event_get_target(event); // 获取触发事件的部件(对象)
lv_event_code_t code = lv_event_get_code(event); // 获取当前部件(对象)触发的事件代码
lv_obj_t * obj_user = lv_event_get_user_data(event); // 获取添加事件时传递的用户数据
switch(code){
case LV_EVENT_PRESSED:
lv_obj_set_style_bg_color(obj_user, lv_color_hex(0x4cbe37), 0);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xc43e1c), 0); // 通过本地样式(私有样式)设置背景色
printf("LV_EVENT_PRESSED\n");
break;
case LV_EVENT_LONG_PRESSED:
lv_obj_set_style_bg_color(obj_user, lv_color_hex(0xc43e1c), 0);
lv_obj_set_style_bg_color(obj, lv_color_hex(0x4cbe37), 0); // 通过本地样式(私有样式)设置背景色
printf("LV_EVENT_LONG_PRESSED\n");
break;
default:
//printf("NONE\n");
break;
}
}
void test_event()
{
lv_obj_t *obj_1 = lv_obj_create(lv_scr_act());
lv_obj_align(obj_1, LV_ALIGN_TOP_MID, 0, 0);
lv_obj_t *obj_2 = lv_obj_create(lv_scr_act());
lv_obj_align(obj_2, LV_ALIGN_CENTER, 0, 0);
/* 可单独添加事件 */
lv_obj_add_event_cb(obj_1, my_event_cb_1, LV_EVENT_CLICKED, NULL); // 同一个状态,注册两个事件
lv_obj_add_event_cb(obj_1, my_event_cb_2, LV_EVENT_CLICKED, NULL); // 按顺序执行
/* 添加到一个全局的事件触发 */
lv_obj_add_event_cb(obj_2, my_event_cb_all, LV_EVENT_ALL, obj_1);
}
static void my_event_cb_bubble_all(lv_event_t * event)
{
lv_obj_t * obj = lv_event_get_target(event); // 获取触发事件的部件(对象)
lv_obj_t * parent = lv_event_get_current_target(event); // 获取触发事件对象的父对象(事件冒泡才有)
lv_event_code_t code = lv_event_get_code(event); // 获取当前部件(对象)触发的事件代码
lv_obj_t * obj_user = lv_event_get_user_data(event); // 获取添加事件时传递的用户数据
switch(code){
case LV_EVENT_PRESSED:
lv_obj_set_style_bg_color(parent, lv_color_hex(0x4cbe37), 0);
lv_obj_set_style_bg_color(obj, lv_color_hex(0xc43e1c), 0); // 通过本地样式(私有样式)设置背景色
printf("LV_EVENT_PRESSED\n");
break;
case LV_EVENT_LONG_PRESSED:
lv_obj_set_style_bg_color(parent, lv_color_hex(0xc43e1c), 0);
lv_obj_set_style_bg_color(obj, lv_color_hex(0x4cbe37), 0); // 通过本地样式(私有样式)设置背景色
printf("LV_EVENT_LONG_PRESSED\n");
break;
default:
//printf("NONE\n");
break;
}
}
/******
事件冒泡
点击子类obj4,会触发obj1的事件回调,obj2\3\4都不会触发,因为它们没有绑定事件回调
点击子类obj4,是事件发生了冒泡,回调函数给谁绑定了,只能由谁触发,回调无法冒泡。
*****/
void test_event_bubble(void)
{
lv_obj_t *obj1 = lv_obj_create(lv_scr_act());
lv_obj_center(obj1);
lv_obj_set_size(obj1, 500, 300);
lv_obj_t *obj2 = lv_obj_create(obj1);
lv_obj_center(obj2);
lv_obj_set_size(obj2, 300, 150);
lv_obj_add_flag(obj2, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_t *obj3 = lv_obj_create(obj2);
lv_obj_center(obj3);
lv_obj_set_size(obj3, 150, 75);
lv_obj_add_flag(obj3, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_t *obj4 = lv_obj_create(obj3);
lv_obj_center(obj4);
lv_obj_set_size(obj4, 50, 35);
lv_obj_add_flag(obj4, LV_OBJ_FLAG_EVENT_BUBBLE);
lv_obj_add_event_cb(obj1, my_event_cb_bubble_all, LV_EVENT_ALL, NULL);
}
/****************事件例子******************/
void test_my_callback(lv_event_t * event)
{
static uint32_t cnt = 1;
lv_obj_t * btn = lv_event_get_target(event); // 获取触发事件的部件(对象)
lv_obj_t * label = lv_obj_get_child(btn, 0);
lv_label_set_text_fmt(label, "%"LV_PRIu32, cnt);
cnt++;
}
void test_event_ex(void)
{
lv_obj_t * btn = lv_btn_create(lv_scr_act());
lv_obj_align(btn, LV_ALIGN_CENTER, 0, 0);
lv_obj_set_size(btn, 100, 50);
lv_obj_t *label = lv_label_create(btn);
lv_label_set_text(label, "Click me!");
lv_obj_center(label);
lv_obj_add_event_cb(btn, test_my_callback, LV_EVENT_CLICKED, NULL);
}
(3)label控件
void test_label(void)
{
/* 创建标签 */
lv_obj_t *label = lv_label_create(lv_scr_act());
lv_obj_center(label);
/* 设置内容 */
lv_label_set_text(label, "Hello"); // lvgl会开辟空间,把该字符串复制到内存中。
lv_label_set_text_fmt(label, "%s: %d", "Value", 15); // 格式化的方法,lvgl也会开辟空间
lv_label_set_text_static(label, "Hello World! Embracing new beginnings and endless possibilities, one step at a time. "); // 设置要显示的文字,直接使用给定的缓冲区(不常用)
/* 设置大小 */
lv_obj_set_size(label, 100, 200);
lv_obj_set_width(label, 100);
lv_obj_set_height(label, LV_SIZE_CONTENT); // LV_SIZE_CONTENT 自动适应文本长度
/* 滚动模式 */
lv_label_set_long_mode(label, LV_LABEL_LONG_WRAP); // 如果高度为LV_SIZE_CONTENT,那么高度会根据文本换行被自动扩展
//lv_label_set_long_mode(label, LV_LABEL_LONG_DOT); // 如果文本太长,就保持大小并在末尾写3个点【不能为static的写入方式,否则总是闪退】
lv_label_set_long_mode(label, LV_LABEL_LONG_CLIP); // 剪掉超出标签范围外的文本部分
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL); // 水平来回滚动
/* 如果文本比标签宽,则水平滚动它。如果它更高,就垂直滚动。只滚动一个方向,水平滚动的优先级更高。 */
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); // 持续滚动。
/* 文本颜色 */
lv_obj_set_style_text_color(label, lv_color_hex(0x173300), 0);
lv_obj_set_style_bg_color(label, lv_color_hex(0x00b300), 0);
lv_obj_set_style_bg_opa(label, 100, 0); // 默认的背景色,透明度为0,显示不出来
lv_label_set_recolor(label, true); // 这两个必须一块使用
lv_label_set_text(label, "#0000ff Re-color# #ff00ff words# #ff0000 of a# label"); // #颜色 文本#
/* 自带显示图标的使用 */
//lv_label_set_text(label, LV_SYMBOL_OK); // 直接显示图标
lv_label_set_text(label, LV_SYMBOL_OK " Apply"); // 图标与字符串一起使用
//lv_label_set_text(label, LV_SYMBOL_OK LV_SYMBOL_WIFI LV_SYMBOL_PLAY); // 多个图标一起使用
/* 中文显示CJK是自带的,只有1000多个字体 */
lv_obj_set_style_text_font(label, &lv_font_simsun_16_cjk, 0);
lv_label_set_text(label, "景景景景景景景景景景景景景景景景景景景景景景景景景");
/* 中文自己的字体 */
LV_FONT_DECLARE(lv_font_source_song_20);
lv_obj_set_style_text_font(label, &lv_font_source_song_20, 0);
lv_label_set_text(label, "嵌入式辛集电子");
}
如何使用自己的中文字库?
lvgl官方字体转换工具
编码字库对应区间
生成字体的c文件后,复制到lvgl的src的font文件夹,并添加到你的工程中。
在应用了该字体的c文件中,将字体声明为LV_FONT_DECLARE(my_font_name);或者 extern lv_font_t my_font_name;
后面的名称,在你生成的c文件中,拉到最下面就能看见。
label例子
void test_label_shadow(void)
{
/* 创建阴影样式 */
static lv_style_t style_shadow;
lv_style_init(&style_shadow);
lv_style_set_opa(&style_shadow,LV_OPA_30);
lv_style_set_text_color(&style_shadow, lv_color_black());
/* 创建阴影label */
lv_obj_t *label_shadow = lv_label_create(lv_scr_act());
lv_obj_add_style(label_shadow, &style_shadow, 0);
/* 创建主label */
lv_obj_t *label_main = lv_label_create(lv_scr_act());
lv_obj_center(label_main);
/* 给两个label设置好内容 */
lv_label_set_text(label_main, "Hello World! Embracing new beginnings\n"
"and endless possibilities,\n"
"one step at a time.");
lv_label_set_text(label_shadow, lv_label_get_text(label_main));
/* 相对位置设置 */
lv_obj_align_to(label_shadow, label_main, LV_ALIGN_BOTTOM_MID, 3, 3);
}
(4)btn控件
static void my_event_cb_btn(lv_event_t * event)
{
lv_obj_t * btn = lv_event_get_target(event); // 获取触发事件的部件(对象)
lv_event_code_t code = lv_event_get_code(event); // 获取当前部件(对象)触发的事件代码
lv_obj_t * obj_user = lv_event_get_user_data(event); // 获取添加事件时传递的用户数据
switch(code){
case LV_EVENT_VALUE_CHANGED:
if(lv_obj_has_state(btn, LV_STATE_CHECKED))
{
printf("Button is checked.\n");
}
else
{
printf("Button is not checked.\n");
}
break;
default:
//printf("NONE\n");
break;
}
}
void test_btn(void)
{
/* 创建一个按钮 */
lv_obj_t *btn = lv_btn_create(lv_scr_act());
lv_obj_set_style_bg_color(btn, lv_color_hex(0x5Efe1e), LV_PART_MAIN|LV_STATE_PRESSED);
/* LV_OBJ_FLAG_CHECKABLE使得按键为可以锁住的状态 */
lv_obj_add_flag(btn, LV_OBJ_FLAG_CHECKABLE); // 切换(Toggle)状态
lv_obj_add_event_cb(btn, my_event_cb_btn, LV_EVENT_VALUE_CHANGED, 0);
/* 使用物理按键或者编码器控制btn */
/* 创建一个组 */
lv_group_t * g = lv_group_create();
// lv_group_set_default(g); // 设置默认组,则之后创建的控件都会被自动添加到组中,无需手动添加
// lv_indev_set_group(lv_win32_keypad_device_object, g); // 键盘
lv_indev_set_group(lv_win32_encoder_device_object, g); // 鼠标上的滚轮(编码器)
lv_obj_t * btn1 = lv_btn_create(lv_scr_act()); // 创建一个btn部件(对象),他的父对象是活动屏幕对象
lv_obj_set_size(btn1, 100, 50);
lv_obj_align(btn1, LV_ALIGN_CENTER, 0, -100);
lv_obj_t * btn2 = lv_btn_create(lv_scr_act()); // 创建一个btn部件(对象),他的父对象是活动屏幕对象
lv_obj_set_size(btn2, 100, 50);
lv_obj_align_to(btn2, btn1, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
lv_obj_t * btn3 = lv_btn_create(lv_scr_act()); // 创建一个btn部件(对象),他的父对象是活动屏幕对象
lv_obj_set_size(btn3, 100, 50);
lv_obj_align_to(btn3, btn2, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
lv_obj_t * btn4 = lv_btn_create(lv_scr_act()); // 创建一个btn部件(对象),他的父对象是活动屏幕对象
lv_obj_set_size(btn4, 100, 50);
lv_obj_align_to(btn4, btn3, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
lv_obj_t * slider = lv_slider_create(lv_scr_act());
lv_obj_align_to(slider, btn4, LV_ALIGN_OUT_BOTTOM_MID, 0, 20);
/* 将组件添加到组 */
lv_group_add_obj(g, btn1);
lv_group_add_obj(g, btn2);
lv_group_add_obj(g, btn3);
lv_group_add_obj(g, btn4);
lv_group_add_obj(g, slider);
}
(5)bar控件
void my_bar_timer(lv_timer_t * timer)
{
static uint8_t count = 0;
lv_bar_set_value(timer->user_data,count * 10, LV_ANIM_ON);
count++;
if(count == 10)
{
count = 0;
}
}
void test_bar(void)
{
/* 进度条 */
lv_obj_t *bar = lv_bar_create(lv_scr_act());
lv_obj_center(bar);
lv_obj_set_size(bar, 200, 20); // 设置进度条的长宽,如果高度大于宽度,则为竖向进度条
lv_bar_set_range(bar, 0, 100); // 进度条区间,如果不设置默认为0-100
lv_bar_set_value(bar, 0, LV_ANIM_ON); // 设置当前值,LV_ANIM_ON表示动画的效果,达到设定值
lv_obj_set_style_anim_time(bar, 300, LV_PART_MAIN); // 设置动画的时间
lv_timer_t *tim = lv_timer_create(my_bar_timer, 1000, bar);
lv_timer_set_repeat_count(tim, -1);
}
/*********进度条 例子*********/
static void set_temp(void * bar, int32_t temp)
{
lv_bar_set_value(bar, temp, LV_ANIM_ON);
}
void test_bar_ex(void)
{
/* 设置样式 */
static lv_style_t style;
lv_style_init(&style);
// 设置背景颜色
lv_style_set_bg_color(&style, lv_palette_main(LV_PALETTE_RED));
lv_style_set_bg_opa(&style, LV_OPA_COVER);
// 设置渐变色
lv_style_set_bg_grad_color(&style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_bg_grad_dir(&style, LV_GRAD_DIR_VER);
/* 设置进度条 */
lv_obj_t *bar = lv_bar_create(lv_scr_act());
lv_obj_set_size(bar, 20, 200);
lv_bar_set_range(bar, 0, 100);
lv_obj_center(bar);
lv_obj_add_style(bar, &style, LV_PART_INDICATOR);
/* 设置动画 */
lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_exec_cb(&a, set_temp); // 设置动画执行的回调函数
lv_anim_set_time(&a, 3000); // 设置动画的正向播放时长
lv_anim_set_playback_time(&a, 3000); // 设置动画的反向播放时长
lv_anim_set_var(&a, bar); // 给动画绑定控件
lv_anim_set_values(&a, 0, 100); // 设置动画传入值的空间
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE); // 设置动画重复次数
lv_anim_start(&a);
}
(6)roller控件
void my_roller_timer(lv_timer_t * timer)
{
static uint8_t count = 0;
lv_obj_t * roller = (lv_obj_t *) timer->user_data;
lv_roller_set_selected(roller, count, LV_ANIM_ON);
count++;
if(count >= lv_roller_get_option_cnt(roller))
{
count = 0;
}
}
void my_roller_sta_timer(lv_timer_t * timer)
{
static uint8_t count = 0;
lv_obj_t * roller = (lv_obj_t *) timer->user_data;
lv_roller_set_selected(roller, count, LV_ANIM_ON);
count++;
if(count >= lv_roller_get_option_cnt(roller))
{
count = 0;
}
}
void test_roller(void)
{
/* 创建滚轮 */
lv_obj_t * roller = lv_roller_create(lv_scr_act());
lv_roller_set_options(roller,
"January\n"
"February\n"
"March\n"
"April\n"
"May\n"
"June\n"
"July\n"
"August\n"
"September\n"
"October\n"
"November\n"
"December",
LV_ROLLER_MODE_NORMAL ); // 第三个参数,改为LV_ROLLER_MODE_INFINITE为无限模式
lv_roller_set_visible_row_count(roller, 5); // 设置可以显示的行数
lv_obj_center(roller);
lv_timer_t *tim = lv_timer_create(my_roller_timer, 500, roller);
lv_obj_t * roller_sta = lv_roller_create(lv_scr_act());
lv_roller_set_options(roller_sta,
"Wait\n"
"Wait\n"
"Wait\n",
LV_ROLLER_MODE_NORMAL );
lv_roller_set_visible_row_count(roller_sta, 5); // 设置可以显示的行数
lv_obj_align_to(roller_sta, roller, LV_ALIGN_OUT_RIGHT_MID, 0, 0);
lv_timer_t *tim_sta = lv_timer_create(my_roller_sta_timer, 500, roller_sta);
}
(7)line控件
void test_line(void)
{
/*
设置一组点,用来提供给line组件画线
这个数组应该是静态、全局或动态分配的,不能是函数中的局部变量
因为lv_line_set_points保存的只是该数组的指针
*/
static lv_point_t line_points[] = { {5, 5}, {70, 70}, {120, 10}, {180, 60}, {240, 10},{240, 400} };
/*创建一个共享样式*/
static lv_style_t style_line;
lv_style_init(&style_line);
// 下面3个样式是 line 的专有样式接口,类似于arc
lv_style_set_line_width(&style_line, 8);
lv_style_set_line_color(&style_line, lv_palette_main(LV_PALETTE_BLUE));
// 使用这个样式能让画出来的线条看起来更平滑
lv_style_set_line_rounded(&style_line, true);
// 设置line的背景颜色
lv_style_set_bg_opa(&style_line, LV_OPA_10);
lv_style_set_bg_color(&style_line, lv_color_hex(0xed1c24));
/* 创建一个 line 组件(对象),他的父对象是活动屏幕对象 */
lv_obj_t * line = lv_line_create(lv_scr_act());
/*
如果设置了大小,那么有些超出设置大小的部分将会看不到
默认不需要设置大小,line会根据给出的points自动调整大小以此展示所有的points。这和label组件类似
*/
//lv_obj_set_size(line, 150, 60);
lv_line_set_y_invert(line, true); // 反转y轴,LCD坐标系->平面直角坐标系第一象限
lv_obj_center(line);
//lv_obj_set_align(line, LV_ALIGN_BOTTOM_LEFT); // 放在屏幕的右下角,看起来更直观
lv_line_set_points(line, line_points, 6); // 设置点数组。line将连接这些点,按顺序画出直线
lv_obj_add_style(line, &style_line, 0);
}
/**********************************sin曲线打印示例**************************************/
#define DISP_VER_RES 1024 /* 以屏幕宽度为x轴,每一个像素为一个整数点 */
#define SIN_POINT_MAX 600 /* 以屏幕高度为y轴,每一个像素为一个整数点 */
#define PI 3.1415926
/* 以屏幕宽度为x轴,每一个像素为一个整数点 */
static lv_point_t line_points[DISP_VER_RES] = { 0 };
// 参考链接:https://juejin.cn/post/6966760481528905764
static void sin_timer(lv_timer_t * timer)
{
/*Use the user_data*/
lv_obj_t * line = timer->user_data;
static uint16_t i = 0;
uint16_t j = 0;
float hd = 0.0; //弧度
float fz = 0.0; //峰值
j = SIN_POINT_MAX / 2;
hd = PI / j;
// 超出屏幕宽度?
if (i >= DISP_VER_RES) i = 0;
fz = j * sin( hd * i ) + j;
line_points[i].y = (uint16_t)fz;
line_points[i].x = i;
printf("[%d, %d]\n", line_points[i].x, line_points[i].y);
lv_line_set_points(line, line_points, i);
i++;
}
void test_line_sin(void)
{
/*初始化line组件的样式*/
static lv_style_t style_line;
lv_style_init(&style_line);
lv_style_set_line_width(&style_line, 8);
lv_style_set_line_color(&style_line, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_line_rounded(&style_line, true);
//设置背景颜色
//lv_style_set_bg_opa(&style_line, LV_OPA_100);
//lv_style_set_bg_color(&style_line, lv_color_hex(0x78246a));
/* 设置活动屏幕的背景颜色 */
// lv_obj_set_style_bg_opa(lv_scr_act(), LV_OPA_100, 0);
// lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x1e1e1e), 0);
/*创建line并应用上面初始化的样式*/
lv_obj_t * line;
line = lv_line_create(lv_scr_act());
// 不用设置大小,line会自动根据points调整大小
//lv_obj_set_size(line, 1024, 600);
lv_obj_add_style(line, &style_line, 0);
//lv_obj_align(line, LV_ALIGN_LEFT_MID, 0, 0); // 从屏幕左边中间往屏幕右边中间刷新
//lv_obj_align(line, LV_ALIGN_BOTTOM_LEFT, 0, 0); // 从屏幕左边往屏幕右边刷新
lv_obj_align(line, LV_ALIGN_BOTTOM_RIGHT, 0, 0); // 从屏幕右边往屏幕左边刷新
// 反转y轴
lv_line_set_y_invert(line, true);
/*
创建一个定时器每隔5ms刷新一次
这里要注意的是,虽然你设置的每隔5ms一次,
但是实际最小的周期时间,受限于 lv_task_handler 的睡眠时间;
所以,我们这里设置的是 5ms,但是实际上是 ≥10ms,因为我们的 lv_task_handler 睡眠时间是 10ms。
也就说我们创建的定时器的运行周期不可能小于 lv_task_handler 的运行周期
*/
lv_timer_t * timer = lv_timer_create(sin_timer, 5, line);
}