【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显示优化

如何一步一步地优化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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辛集电子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值