这里写目录标题
一、圆形表盘时钟
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秒)时开启这个定时器,那么就会出现表盘的更新时间与实际时间错开。
我的这个方法可以在单片机的定时器中断中调用表盘更新,只要保证单片机内部的时间数组与实际时间是对齐的,那么表盘的更新就与实际时间一致。