LVGL_V8.2 时钟动画 (持续更新中)

一、圆形表盘时钟

1. PC模拟器效果图

在这里插入图片描述

2.代码

lv_clock.c

#include "lv_clock.h"


static time_t timep;//时间戳
static struct tm time_temp;//具体时间,小时为24小时制

//导入图片
LV_IMG_DECLARE(watch_bg);
LV_IMG_DECLARE(hour);
LV_IMG_DECLARE(minute);
LV_IMG_DECLARE(second);

lv_obj_t* bg;
lv_obj_t* lvHour;
lv_obj_t* lvMinute;
lv_obj_t* lvSecond;

static void time_refresh(lv_event_t* event) {
     //获取当前时间
    time(&timep);
    memcpy(&time_temp, localtime(&timep), sizeof(struct tm));

    lv_img_set_angle(lvSecond, time_temp.tm_sec * 60);
    lv_img_set_angle(lvMinute, time_temp.tm_min * 60);
    lv_img_set_angle(lvHour, time_temp.tm_hour * 300);
}

static void send_event(void){
    lv_event_send(bg,LV_EVENT_REFRESH,NULL);
}

lv_obj_t* lv_clock_creat( lv_obj_t* parent)
{
/***************************************创建各时间对象obj**********************/
    //构造背景
    bg = lv_img_create(parent);//创建img对象
    lv_img_set_src(bg, &watch_bg);//设置源图片
    lv_obj_set_size(bg, 200, 200);//设置控件大小(图片大小为200*200,所以这句话可以不写)
    lv_obj_align(bg,  LV_ALIGN_CENTER, 0, 0);//居中控件

    //构造时针
    lvHour = lv_img_create(bg);
    lv_img_set_src(lvHour, &hour);
    lv_obj_align(lvHour, LV_ALIGN_CENTER, 0, 0);
    //lv_img_set_angle(lvHour, Hour * 300 + Minute * 5);

    //构造分针
    lvMinute = lv_img_create(bg);
    lv_img_set_src(lvMinute, &minute);
    lv_obj_align(lvMinute, LV_ALIGN_CENTER, 0, 0);
    //lv_img_set_angle(lvMinute, Minute * 6 * 10);

    //构造秒针
    lvSecond = lv_img_create(bg);
    lv_img_set_src(lvSecond, &second);
    lv_obj_align(lvSecond,LV_ALIGN_CENTER, 0, 0);
    //lv_img_set_angle(lvSecond, Second * 60);

/*******************************根据当前时间初始化所有角度************************/
    time(&timep);
    memcpy(&time_temp, localtime(&timep), sizeof(struct tm));

    lv_img_set_angle(lvSecond, time_temp.tm_sec * 60);
    lv_img_set_angle(lvMinute, time_temp.tm_min * 60);
    lv_img_set_angle(lvHour, time_temp.tm_hour * 300);

     /**< 给bg设置LV_EVENT_REFRESH事件的回调 */
    lv_obj_add_event_cb(bg, time_refresh,LV_EVENT_REFRESH ,NULL);
    
    /**<每个1秒给bg发一次LV_EVENT_REFRESH事件*/
    lv_timer_create((void*)send_event, 1000, NULL);

}

lv_clock.h

#ifndef LV_CLOCK_H
#define LV_CLOCK_H

#include "lvgl/lvgl.h"
#include <time.h>
//#include "PageManager.h"

lv_obj_t* lv_clock_creat(lv_obj_t* parent);

#endif // LV_CLOCK_H

main.c

#include <stdlib.h>
#include <unistd.h>
#include "lvgl/lvgl.h"
#include "lv_drivers/win32drv/win32drv.h"
#include <windows.h>



int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int nCmdShow)
{

    lv_init();
    lv_win32_init(hInstance, SW_SHOWNORMAL, 400,400, NULL);
    LV_LOG_USER("LVGL initialization completed!");

    //在主窗口上创建clock控件
    lv_clock_creat(lv_scr_act());

///
    while(!lv_win32_quit_signal) {
        /* Periodically call the lv_task handler.
         * It could be done in a timer interrupt or an OS task too.*/
        lv_task_handler();
        usleep(10000);       /*Just to let the system breath*/
    }

    return 0;
}


二、数字表盘无动画组件

1. PC模拟器效果图

在这里插入图片描述

2.代码

lv_clock.c

#include "lv_clock.h"

static time_t timep;//时间戳
static struct tm time_temp;//具体时间,小时为24小时制
/*上面的两个东西都是lvgl内置的,时间戳通过memcpy就可以转皇城我们常见的日期时间格式,具体请看tm结构体的定义*/
/*如果是在单片机中,这里可以改成自己的时间结构体,然后采用定时器中断,更新这个结构体,就和这里的效果一样了*/

/*******************************************<创建标签组,方便管理>*********************************/
static lv_obj_t* date_label_Grp[4];

static lv_obj_t* week_label_Grp[1];

static lv_obj_t* hour_label_Grp[2];

static lv_obj_t* min_label_Grp[2];

static lv_obj_t* sec_label_Grp[2];

static lv_obj_t* am_label_Grp[1];



/** \brief sec_label_Grp[0]的LV_EVENT_REFRESH事件回调函数(回调的组件是谁不重要,反正标签组是全局的)
 *  更新整个clock组件里的所有需要更新的label
 * \param
 * \param
 * \return
 *
 */
static void time_refresh(lv_event_t* event) {
    //获取当前时间
    time(&timep);
    memcpy(&time_temp, localtime(&timep), sizeof(struct tm));

    //更新秒的label
    lv_label_set_text_fmt(sec_label_Grp[0], "%d", time_temp.tm_sec / 10 % 10);
    lv_label_set_text_fmt(sec_label_Grp[1], "%d", time_temp.tm_sec  % 10);

    //秒为0时,更新分钟
    if(time_temp.tm_sec == 0){
        lv_label_set_text_fmt(min_label_Grp[0], "%d", time_temp.tm_min/ 10 % 10);
        lv_label_set_text_fmt(min_label_Grp[1], "%d", time_temp.tm_min % 10);
    }

    //分钟为0时,更新小时和上下午
    if(time_temp.tm_min == 0){
        if (time_temp.tm_hour <= 12){
            lv_label_set_text(am_label_Grp[0],"AM");
            lv_label_set_text_fmt(hour_label_Grp[0], "%d", time_temp.tm_hour  / 10 % 10);
            lv_label_set_text_fmt(hour_label_Grp[1], "%d", time_temp .tm_hour  % 10);
        }
        else{
            lv_label_set_text(am_label_Grp[0],"PM");
            lv_label_set_text_fmt(hour_label_Grp[0], "%d", (time_temp.tm_hour-12) / 10 % 10);
            lv_label_set_text_fmt(hour_label_Grp[1], "%d", (time_temp .tm_hour-12) % 10);
        }

    }

    //小时为0时,更新日期和星期
    if(time_temp.tm_hour == 0){
        lv_label_set_text_fmt(date_label_Grp[0], "%d", time_temp.tm_mon / 10 % 10);
        lv_label_set_text_fmt(date_label_Grp[1], "%d", time_temp.tm_mon % 10);
        lv_label_set_text_fmt(date_label_Grp[2], "%d", time_temp.tm_mday / 10 % 10);
        lv_label_set_text_fmt(date_label_Grp[3], "%d", time_temp.tm_mday % 10);

        lv_label_set_text_fmt(week_label_Grp[0], "%d", time_temp.tm_wday );
    }
}

/** \brief LV_EVENT_REFRESH事件发送函数
 *这里封装成一个函数,是因为我把这个函数设置成1秒执行一次,是个LVGL内部定时器的回调
 *其实只要调用里面的lv_event_send,就会执行上面的time_refresh函数,这样在单片机里就可以在定时器中断中使用lv_event_send,更新label了
 * \param
 * \param
 * \return
 *
 */
static void send_event(void){
    lv_event_send(sec_label_Grp[0],LV_EVENT_REFRESH,NULL);
}

//这个函数的实现在最下面,因为创建组件的程序要调用,提前声明
static void label_creat(lv_obj_t* parent, lv_coord_t* x_mod, lv_style_t* style, lv_obj_t* label_grp1[], uint8_t num);


/** \brief 创建整个组件的函数
 *
 * \param
 * \param
 * \return
 *
 */
lv_obj_t* lv_clock_creat(lv_obj_t* parent){

    /***************************************在父对象上创建主obj*********************/
    lv_obj_t* contTime = lv_obj_create(parent);
    lv_obj_remove_style_all(contTime);//清楚所有风格

    lv_obj_set_size(contTime, 135, 160);//设置大小
    lv_obj_align(contTime, LV_ALIGN_CENTER, 0, 0);//居中
    lv_obj_clear_flag(contTime, LV_OBJ_FLAG_SCROLLABLE);//禁止滚动

    /***************************************创建各时间对象obj**********************/
    /**<创建日期obj*/
    lv_obj_t* date = lv_obj_create(contTime);
    lv_obj_remove_style_all(date);
    lv_obj_set_size(date, 70, 30);
    lv_obj_align(date, LV_ALIGN_TOP_MID, -15, 0);
    lv_obj_clear_flag(date, LV_OBJ_FLAG_SCROLLABLE);

    /**<创建星期obj*/
    lv_obj_t* week = lv_obj_create(contTime);
    lv_obj_remove_style_all(week);
    lv_obj_set_size(week, 30, 30);
    lv_obj_align(week, LV_ALIGN_TOP_MID, 35, 0);
    lv_obj_clear_flag(week, LV_OBJ_FLAG_SCROLLABLE);

     /**<创建小时obj*/
    lv_obj_t* hour = lv_obj_create(contTime);
    lv_obj_remove_style_all(hour);
    lv_obj_set_size(hour, 100, 50);
    lv_obj_align(hour, LV_ALIGN_TOP_MID, 0, 30);
    lv_obj_clear_flag(hour, LV_OBJ_FLAG_SCROLLABLE);

     /**<创建分钟obj*/
    lv_obj_t* min = lv_obj_create(contTime);
    lv_obj_remove_style_all(min);
    lv_obj_set_size(min, 100, 50);
    lv_obj_align(min, LV_ALIGN_TOP_MID, 0, 80);
    lv_obj_clear_flag(min, LV_OBJ_FLAG_SCROLLABLE);

     /**<创建秒obj*/
    lv_obj_t* sec = lv_obj_create(contTime);
    lv_obj_remove_style_all(sec);
    lv_obj_set_size(sec, 50, 30);
    lv_obj_align(sec, LV_ALIGN_TOP_MID, -25, 130);
    lv_obj_clear_flag(sec, LV_OBJ_FLAG_SCROLLABLE);

     /**<创建上下午obj*/
    lv_obj_t* am = lv_obj_create(contTime);
    lv_obj_remove_style_all(am);
    lv_obj_set_size(am, 50, 30);
    lv_obj_align(am, LV_ALIGN_TOP_MID, 25, 130);
    lv_obj_clear_flag(am, LV_OBJ_FLAG_SCROLLABLE);

    /************************************<创建stytle>****************************/
    /**<导入字体*/
    LV_FONT_DECLARE(Robot_20)
    LV_FONT_DECLARE(Robot_30)
    LV_FONT_DECLARE(xx_130)

    /**<创建日期stytle*/
    static lv_style_t date_lable_style;
    lv_style_init(&date_lable_style);
    lv_style_set_text_color(&date_lable_style, lv_color_white());
    lv_style_set_text_font(&date_lable_style, &Robot_20);

    /**<创建小时stytle*/
    static lv_style_t hour_lable_style;
    lv_style_init(&hour_lable_style);
    lv_style_set_text_color(&hour_lable_style, lv_color_white());
    lv_style_set_text_font(&hour_lable_style, &xx_130);

    /**<创建秒stytle*/
    static lv_style_t sec_lable_style;
    lv_style_init(&sec_lable_style);
    lv_style_set_text_color(&sec_lable_style, lv_color_make(0xFF, 0, 0));
    lv_style_set_text_font(&sec_lable_style, &Robot_30);

    /**<创建上下午stytle*/
    static lv_style_t am_lable_style;
    lv_style_init(&am_lable_style);
    lv_style_set_text_color(&am_lable_style, lv_color_white());
    lv_style_set_text_font(&am_lable_style, &Robot_30);

    /***************************************放lable**************************/
    /**<放日期里红色的反斜杠"/"*/
    lv_obj_t* label = lv_label_create(date);
    lv_label_set_recolor(label,true);
    lv_label_set_text(label, "#ff0000 /#");
    lv_obj_align(label, LV_ALIGN_CENTER, 0, 0);
    lv_obj_add_style(label, &date_lable_style, 0);

    /**<创建4个日期的label*/
    lv_coord_t date_x[4] = { -28, -14, 14, 28};
    label_creat(date,date_x,&date_lable_style,date_label_Grp,4);

    /**<创建1个星期的label*/
    lv_coord_t week_x[1] = {0};
    label_creat(week,week_x,&date_lable_style,week_label_Grp,1);

    /**<创建2个小时的label*/
    lv_coord_t hour_x[2] = {-25,25};
    label_creat(hour,hour_x,&hour_lable_style,hour_label_Grp,2);

    /**<创建2个分钟的label*/
    label_creat(min,hour_x,&hour_lable_style,min_label_Grp,2);

    /**<创建2个秒的label*/
    lv_coord_t sec_x[2] = {-15,15};
    label_creat(sec,sec_x,&sec_lable_style,sec_label_Grp,2);

    /**<创建1个上下午的label*/
    lv_coord_t am_x[1] = {0};
    label_creat(am,am_x,&am_lable_style,am_label_Grp,1);

    /*******************************根据当前时间初始化所有label************************/
    /**<获取当前时间*/
    time(&timep);
    memcpy(&time_temp, localtime(&timep), sizeof(struct tm));

    /**<初始化label*/
    lv_label_set_text_fmt(date_label_Grp[0], "%d", time_temp.tm_mon / 10 % 10);
    lv_label_set_text_fmt(date_label_Grp[1], "%d", time_temp.tm_mon % 10);
    lv_label_set_text_fmt(date_label_Grp[2], "%d", time_temp.tm_mday / 10 % 10);
    lv_label_set_text_fmt(date_label_Grp[3], "%d", time_temp.tm_mday % 10);

    lv_label_set_text_fmt(week_label_Grp[0], "%d", time_temp.tm_wday);

    if (time_temp.tm_hour <= 12){
            lv_label_set_text(am_label_Grp[0],"AM");
            lv_label_set_text_fmt(hour_label_Grp[0], "%d",time_temp.tm_hour  / 10 % 10);
            lv_label_set_text_fmt(hour_label_Grp[1], "%d", time_temp.tm_hour  % 10);
    }
    else{
        lv_label_set_text(am_label_Grp[0],"PM");
        lv_label_set_text_fmt(hour_label_Grp[0], "%d", (time_temp.tm_hour-12) / 10 % 10);
        lv_label_set_text_fmt(hour_label_Grp[1], "%d", (time_temp.tm_hour-12) % 10);
    }

    lv_label_set_text_fmt(min_label_Grp[0], "%d", time_temp.tm_min / 10 % 10);
    lv_label_set_text_fmt(min_label_Grp[1], "%d", time_temp.tm_min % 10);

    lv_label_set_text_fmt(sec_label_Grp[0], "%d", time_temp.tm_sec / 10 % 10);
    lv_label_set_text_fmt(sec_label_Grp[1], "%d", time_temp.tm_sec % 10);


    /**< 给sec_label_Grp[0]设置LV_EVENT_REFRESH事件的回调 */
    lv_obj_add_event_cb(sec_label_Grp[0], time_refresh,LV_EVENT_REFRESH ,NULL);

    /**<每个1秒给sec_label_Grp[0]发一次LV_EVENT_REFRESH事件*/
    lv_timer_create((void*)send_event, 1000, NULL);

}

/** \brief 在组件上根据所给的x偏移坐标,水平创建对应个数的label,并放到标签组内
 *
 * \param
 * \param
 * \return
 *
 */
static void label_creat(lv_obj_t* parent, lv_coord_t* x_mod, lv_style_t* style, lv_obj_t* label_grp[],uint8_t num){
     for (uint8_t i = 0; i < num; i++){
        lv_obj_t* label = lv_label_create(parent);
        lv_obj_align(label, LV_ALIGN_CENTER, x_mod[i], 0);
        lv_obj_add_style(label, style,0);

        label_grp[i] = label;
    }
}

lv_clock.h

#ifndef LV_CLOCK_H
#define LV_CLOCK_H

#include "lvgl/lvgl.h"
#include <time.h>


lv_obj_t* lv_clock_creat(lv_obj_t *win);

#endif // LV_CLOCK_H

main.c

#include <stdlib.h>
#include <unistd.h>
#include "lvgl/lvgl.h"
#include "lv_drivers/win32drv/win32drv.h"
#include <windows.h>

#include "GUI/weights/lv_clock.h"


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int nCmdShow)
{
    lv_init();
    lv_win32_init(hInstance, SW_SHOWNORMAL,400, 400, NULL);
    LV_LOG_USER("LVGL initialization completed!");

    LV_IMG_DECLARE(ImgBg); //从图片源文件加载图片(已经在工程目录下的图片的源码)
    lv_obj_t * background = lv_img_create(lv_scr_act()); // 在主窗口上创建名字为background的img对象
    lv_img_set_src(background, &ImgBg); //将图片绑定到img对象上
    lv_obj_center(background);//居中

    lv_clock_creat(background);
///
    while(!lv_win32_quit_signal) {
        /* Periodically call the lv_task handler.
         * It could be done in a timer interrupt or an OS task too.*/
        lv_task_handler();
        usleep(10000);       /*Just to let the system breath*/
    }

    return 0;
}

3.单片机实际运行

请添加图片描述

4. 写在后面

此组件通过发送事件的形式更新事件,在任何地方调用发送事件函数即可更新表盘。

很多博主的代码都是通过设置一个1秒钟的LVGL内部定时器去更新表盘,这样是很不准确的,无法保证这个定时器开启时刚好是某1秒的开始。大家可以想象一下,在单片机里如果用这个方法,刚好在实际秒数的一半(假如是0.5秒)时开启这个定时器,那么就会出现表盘的更新时间与实际时间错开。


我的这个方法可以在单片机的定时器中断中调用表盘更新,只要保证单片机内部的时间数组与实际时间是对齐的,那么表盘的更新就与实际时间一致。

三、数字表盘带动画组件(主要是对第2个表盘的优化,实在是有点丑,哈哈,大家期待一下吧,如果哪里看不懂或者是需要图片字体资源,评论区邮箱,我会一个个发的)

评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

握不住的草

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

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

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

打赏作者

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

抵扣说明:

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

余额充值