5709|2

155

帖子

1

TA的资源

一粒金砂(高级)

楼主
 

【平头哥Sipeed LicheeRV 86 Panel测评】十、lvgl定时器 [复制链接]

 

既然已经实现了86板对lvgl的移植,那么许多案例都可以基于lvgl框架建立。上篇还说感觉lvgl的文档条理不好,结果只是自认为“高手”,跳过了“Get started”环节而已。

本篇记录学习并使用lvgl自带定时器的过程。

1bug记录

基于习惯,本人调整了案例的代码结构,将lvgl初始化、对象初始化等代码封装成独立的函数,结果发现对于evdev输入初始化,如果封装起来在main()中调用,就会出现触摸功能不生效的情况。

有关这一点,本人还没有做进一步探究,后续如果有机会可以尝试在其它Linux开发板上是否也会出现同类现象,以确定是lvgl本身的问题,亦或是开发板的原因。

 

 

10-1 lvgl输入设备初始化bug

 

另外,输入设备描述符变量indev_drv”(代码中定义的变量名),如果被声明为static,编译时也会莫名报“函数(indev相关的几个函数)未定义”的错误。不过后续经过make clean后,重新make all,这个错误消失了。

2lvgl Timer介绍

lvgl官网手册中有定时器的介绍,链接为:https://docs.lvgl.io/8/overview/timer.html#create-a-timer

定时器的类型是结构体lv_timer_t,定义源码位于“../lvgl/src/misc/lv_timer.h”,具体如下。

 

/**
 * Timers execute this type of functions.
 */
typedef void (*lv_timer_cb_t)(struct _lv_timer_t *);

/**
 * Descriptor of a lv_timer
 */
typedef struct _lv_timer_t {
    uint32_t period; /**< How often the timer should run*/
    uint32_t last_run; /**< Last time the timer ran*/
    lv_timer_cb_t timer_cb; /**< Timer function*/
    void * user_data; /**< Custom user data*/
    int32_t repeat_count; /**< 1: One time;  -1 : infinity;  n>0: residual times*/
    uint32_t paused : 1;
} lv_timer_t;

 

经过测试发现,定时器默认repeat_count(重复次数)是1次,而且create后会马上开始运行。

另外,lvgl定时器是依靠lv_timer_handler()调用的,并且是非抢占式,所以如果有多个定时器同时溢出,会按照顺序依次执行回调——这里猜测应该是定义顺序即串接定时器描述符链表的顺序。

同时,lvgl工程的main()中会循环调用lv_task_handler()来实现lvgl的内部调度,而实际上lv_task_handler()只是又调用了lv_timer_handler()。这里本人理解lvgl内核即是一个定时器。

 

 

10-2 lvgl调度源码实现

 

 

10-3 lv_init()中初始化内核定时器

 

虽然是在Linux上使用lvgl,没有选择Linux系统定时器的原因是因为lvgl官网上的一句“lvgl不是线程安全的”,把本人给整不敢了。

 

 

10-4 手册中的线程说明

 

3lvgl定时器案例

选择定时器的原因,其实是为了后续完成一个像样的测试案例,比如做一个定时采集的物联网应用之类的,学习中本人先实现了一个点击按钮启动倒计时的Demo,直接上代码。

 

/* Includes ------------------------------------------------------- */
#include "lvgl/lvgl.h"
#include "lv_drivers/display/fbdev.h"
#include "lv_drivers/indev/evdev.h"
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <sys/time.h>


/* Private macro -------------------------------------------------- */
#define AITA_DISP_BUF_SIZE     (128 * 1024)
#define AITA_SCREEN_WIDTH      480
#define AITA_SCREEN_HEIGHT     480
#define AITA_TITLE_STRING      "AITA DEMO for LicheeRV with LVGL"


/* Global variables ----------------------------------------------- */
lv_indev_t *aita_indev;   //pointer of indev
lv_obj_t *sys_scr;        //pointer of system screen instance
lv_obj_t *head_label;     //pointer of title label instance
lv_obj_t *test_btn;       //pointer of test button instance
lv_obj_t *test_btn_label; //pointer of label instance for test button
int aita_counter = 0;     //counter varible for btn-clicked times
lv_timer_t *aita_timer;   //pointer of timer instance
int count_dn = 60;        //count-down varible
lv_obj_t *countdn_label;  //pointer of label instance for showing countdown numbers

/* Private function prototypes ------------------------------------ */
void aita_InitLVGL(void);
void aita_CreateMainUI(void);
void test_btn_click_cb(lv_event_t *e);
void aita_InitTimer(void);
void test_timer_cb(lv_timer_t *timer);


/* Private functions ---------------------------------------------- */
int main(void) {
//by author. initialize lvgl including displaybuffer, device for disp & input
    aita_InitLVGL();
    
//by author. initialize and register event device
//these code must be in main(), otherwise the touch will fail
    static lv_indev_drv_t indev_drv;
    lv_indev_drv_init(&indev_drv);
    indev_drv.type = LV_INDEV_TYPE_POINTER; //by author. input device type, choice touchpad here
    indev_drv.read_cb = evdev_read;         //by author. callback to read input device data
    aita_indev = lv_indev_drv_register(&indev_drv);

//by author. create the main view when the demo starts up    
    aita_CreateMainUI();

//by author. create a timer
    aita_InitTimer();
    
    /*Handle LitlevGL tasks (tickless mode)*/
    while(1) {
        lv_task_handler();
        usleep(5000);
    }

    return 0;
}

/*Set in lv_conf.h as `LV_TICK_CUSTOM_SYS_TIME_EXPR`*/
uint32_t custom_tick_get(void)
{
    static uint64_t start_ms = 0;
    if(start_ms == 0) {
        struct timeval tv_start;
        gettimeofday(&tv_start, NULL);
        start_ms = (tv_start.tv_sec * 1000000 + tv_start.tv_usec) / 1000;
    }

    struct timeval tv_now;
    gettimeofday(&tv_now, NULL);
    uint64_t now_ms;
    now_ms = (tv_now.tv_sec * 1000000 + tv_now.tv_usec) / 1000;

    uint32_t time_ms = now_ms - start_ms;
    return time_ms;
}

void aita_InitLVGL(void) {
    /*LittlevGL init*/
    lv_init();

    /*Linux frame buffer device init*/
    fbdev_init(); //by author. initialize framebuffer device for display
    evdev_init(); //by author. initialize event device for touchpad

    /*A small buffer for LittlevGL to draw the screen's content*/
    static lv_color_t buf[AITA_DISP_BUF_SIZE];
    /*Initialize a descriptor for the buffer*/
    static lv_disp_draw_buf_t disp_buf;
    lv_disp_draw_buf_init(&disp_buf, buf, NULL, AITA_DISP_BUF_SIZE);
    /*Initialize and register a display driver*/
    static lv_disp_drv_t disp_drv;
    lv_disp_drv_init(&disp_drv);
    disp_drv.draw_buf   = &disp_buf;
    disp_drv.flush_cb   = fbdev_flush;
    disp_drv.hor_res    = 480;
    disp_drv.ver_res    = 480;
    lv_disp_drv_register(&disp_drv);
}

void aita_CreateMainUI(void) {
    //by author. create system screen which is basic graphic level
    sys_scr = lv_obj_create(lv_scr_act());
    lv_obj_set_size(sys_scr, AITA_SCREEN_WIDTH, AITA_SCREEN_HEIGHT);
    //by author. create the main title which is just a label
    head_label = lv_label_create(sys_scr);
    lv_label_set_text(head_label, AITA_TITLE_STRING);
    lv_obj_align(head_label, LV_ALIGN_TOP_MID, 0, 50);
    //by author. create the test button for demo test
    test_btn = lv_btn_create(sys_scr);
    lv_obj_set_size(test_btn, 200, 50);
    lv_obj_align(test_btn, LV_ALIGN_BOTTOM_MID, 0, -50);
    //by author. register clicked-event callback for the "test_btn" object
    lv_obj_add_event_cb(test_btn, test_btn_click_cb, LV_EVENT_CLICKED, NULL);
    test_btn_label = lv_label_create(test_btn);
    lv_label_set_text(test_btn_label, "Click");
    //by author. as you seen, the button's text actually is a label object attached to the button
    lv_obj_center(test_btn_label);

    countdn_label = lv_label_create(sys_scr);
    lv_label_set_text(countdn_label, "60");
    lv_obj_align(countdn_label, LV_ALIGN_CENTER, 0, 0);
    lv_obj_set_style_text_font(countdn_label, &lv_font_montserrat_48, 0);
}

//by author. the event callback -- make console output & change button's label
void test_btn_click_cb(lv_event_t *e) {
    char buf[64];
    sprintf(buf, "Clicked %d times", ++aita_counter);
    printf("%s\n", buf);
    lv_label_set_text(test_btn_label, buf);

    lv_timer_resume(aita_timer);
}

void test_timer_cb(lv_timer_t *timer) {
    char buf[10];
    if(count_dn > 0) {
        sprintf(buf, "%d", count_dn--);
        printf("%s\n", buf);
        lv_label_set_text(countdn_label, buf);
    } else {
        lv_timer_pause(aita_timer);
        count_dn = 60;
    }  
}

void aita_InitTimer(void) {
    aita_timer = lv_timer_create(test_timer_cb, 1000, NULL);
    lv_timer_set_repeat_count(aita_timer, -1);
    lv_timer_pause(aita_timer);
}

 

 

最新回复

时基驱动   详情 回复 发表于 2022-4-11 17:10
点赞 关注
 
 

回复
举报

6828

帖子

0

TA的资源

五彩晶圆(高级)

沙发
 

lvgl内核就是个定时器,lvgl定时器是依靠lv_timer_handler()调用理解这点很重要。

mark

 
 
 

回复

7671

帖子

2

TA的资源

五彩晶圆(高级)

板凳
 

时基驱动

个人签名

默认摸鱼,再摸鱼。2022、9、28

 
 
 

回复
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/6 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2025 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表