当前位置:   article > 正文

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

lvgl表盘

一、圆形表盘时钟

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);

}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

二、数字表盘无动画组件

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;
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263

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
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

3.单片机实际运行

请添加图片描述

4. 写在后面

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

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


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

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/471975
推荐阅读
相关标签
  

闽ICP备14008679号