知识点
《单片机—HLK-W801并口驱动ST7789》
《单片机—HLK-W801驱动触摸屏》
《单片机—HLK-W801图形框架LVGL移植》
简单介绍
根据前面的知识点内容,一步一步走了过来,实现了lvgl在HLK801上的运行,还是挺让人欣慰的,没有遇到让人半途而废的坑,这就和减肥一个道理,坚持下来,以后还能多吃点。
在前面的基础上,这次来学习一下绘制一个简单的界面,做一个简单的时钟显示。其实市面上很多类似的智能wifi时钟,都是在这个基础上实现的,能够通过网络同步时间,并且能够获得天气信息并显示出来。
今天就做个简单的RTC时钟,学习一下界面的绘制。
界面绘制
代码参考自LVGL8制作简易时钟
修改了全屏的黑色背景,其他部分没有修改。因为背景太白,有点伤眼。
界面部分
void lvgl_clock_start()
{
static lv_style_t date_time_clock_style; // 最外层对象的样式
lv_style_reset(&date_time_clock_style); // 重置样式
lv_style_init(&date_time_clock_style); // 初始化样式
lv_style_set_radius(&date_time_clock_style, 0); // 设置样式圆角,去掉圆角
lv_style_set_bg_opa(&date_time_clock_style, LV_OPA_100); // 设置样式背景透明度,完全不透
lv_style_set_border_width(&date_time_clock_style, 0); // 设置样式边框宽度
lv_style_set_bg_color(&date_time_clock_style, lv_color_black()); // 设置样式背景颜色,黑色
lv_style_set_pad_left(&date_time_clock_style, 1); // 设置样式左边padding填充宽度
lv_style_set_pad_right(&date_time_clock_style, 1); // 设置样式右边padding填充宽度
lv_style_set_pad_top(&date_time_clock_style, 0); // 设置样式顶部padding填充宽度
lv_style_set_pad_bottom(&date_time_clock_style, 0); // 设置样式底部padding填充宽度
static lv_style_t time_style; // 时间对象样式
lv_style_reset(&time_style);
lv_style_init(&time_style);
lv_style_set_bg_opa(&time_style, LV_OPA_COVER);
lv_style_set_border_width(&time_style, 0);
lv_style_set_radius(&time_style, 5);
lv_style_set_bg_color(&time_style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_pad_left(&time_style, 0);
lv_style_set_pad_right(&time_style, 0);
lv_style_set_pad_top(&time_style, 0);
lv_style_set_pad_bottom(&time_style, 0);
static lv_style_t date_style; // 日期对象样式
lv_style_reset(&date_style);
lv_style_init(&date_style);
lv_style_set_bg_opa(&date_style, LV_OPA_COVER);
lv_style_set_border_width(&date_style, 0);
lv_style_set_radius(&date_style, 5);
lv_style_set_bg_color(&date_style, lv_palette_main(LV_PALETTE_BLUE));
lv_style_set_pad_left(&date_style, 0);
lv_style_set_pad_right(&date_style, 0);
/* Time font */
static lv_style_t time_label_style; // 时间标签样式
lv_style_reset(&time_label_style); // 重置样式
lv_style_init(&time_label_style); // 初始化样式
lv_style_set_text_color(&time_label_style , lv_color_white()); // 设置标签样式文本颜色
lv_style_set_text_font(&time_label_style, &lv_font_montserrat_32); // 设置字体风格
lv_style_set_text_opa(&time_label_style, LV_OPA_COVER); // 设置字体透明度
lv_style_set_bg_opa(&time_label_style, LV_OPA_0); // 设置样式背景透明度
/* Date font */
static lv_style_t date_label_style; // 日期标签样式
lv_style_reset(&date_label_style);
lv_style_init(&date_label_style);
lv_style_set_text_opa(&date_label_style, LV_OPA_COVER);
lv_style_set_bg_opa(&date_label_style, LV_OPA_0);
lv_style_set_text_color(&date_label_style , lv_color_white());
lv_style_set_text_font(&date_label_style, &lv_font_montserrat_16);
/* Week font */
static lv_style_t week_lable_style; // 日期标签样式
lv_style_reset(&week_lable_style);
lv_style_init(&week_lable_style);
lv_style_set_text_opa(&week_lable_style, LV_OPA_COVER);
lv_style_set_bg_opa(&week_lable_style, LV_OPA_0);
lv_style_set_text_color(&week_lable_style, lv_color_white());
lv_style_set_text_font(&week_lable_style, &lv_font_montserrat_16);
/* Time & Date */
lv_obj_t *time_date_obj = lv_obj_create(lv_scr_act()); // 基于屏幕创建时间日期对象
if (time_date_obj == NULL)
{
printf("[%s:%d] time_date_obj create failed\n", __FUNCTION__, __LINE__);
return;
}
lv_obj_set_size(time_date_obj, 320, 240); // 设置对象大小
lv_obj_center(time_date_obj); // 对象居屏幕中间显示
lv_obj_add_style(time_date_obj, &date_time_clock_style, LV_STATE_DEFAULT); //给time_date_obj对象添加样式
/*Time display*/
lv_obj_t *time_obj = lv_obj_create(time_date_obj); // 基于time_date_obj对象创建时间对象
if (time_obj == NULL)
{
printf("[%s:%d] time_obj create failed\n", __FUNCTION__, __LINE__);
return;
}
lv_obj_set_size(time_obj, 158, 100); // 设置对象大小
lv_obj_align_to(time_obj, time_date_obj, LV_ALIGN_LEFT_MID, 0, 0); // 设置time_obj对象基于time_date_obj对象左边中间对齐
lv_obj_add_style(time_obj, &time_style, LV_STATE_DEFAULT); // 给time_obj对象添加样式
static lv_clock_t lv_clock = { 0 };
lv_clock.time_label = lv_label_create(time_obj); // 基于time_obj对象创建时间显示标签对象 lv_clock.time_label
if (lv_clock.time_label == NULL)
{
printf("[%s:%d] time_label create failed\n", __FUNCTION__, __LINE__);
return ;
}
lv_obj_add_style(lv_clock.time_label, &time_label_style, LV_STATE_DEFAULT); // 给对象 lv_clock.time_label添加样式
/*Date display*/
lv_obj_t *date_obj = lv_obj_create(time_date_obj); // 基于time_date_obj对象创建date_obj对象
if (date_obj == NULL)
{
printf("[%s:%d] date_obj create failed\n", __FUNCTION__, __LINE__);
return ;
}
lv_obj_set_size(date_obj, 158, 100); // 设置对象大小
lv_obj_align_to(date_obj, time_date_obj, LV_ALIGN_RIGHT_MID, 0, 0); //设置date_obj对象基于time_date_obj对象右边中部对齐
lv_obj_add_style(date_obj, &date_style, LV_STATE_DEFAULT); // 给date_obj对象添加样式
lv_clock.date_label = lv_label_create(date_obj); // 基于date_obj对象创建lv_clock.date_label日期显示对象
if (lv_clock.date_label == NULL)
{
printf("[%s:%d] date_label create failed\n", __FUNCTION__, __LINE__);
return ;
}
lv_obj_add_style(lv_clock.date_label, &date_label_style, LV_STATE_DEFAULT); // 给lv_clock.date_label对象添加样式
/*Week display*/
lv_clock.weekday_label = lv_label_create(date_obj); // 基于date_obj对象创建星期显示lv_clock.weekday_label对象
if (lv_clock.weekday_label == NULL)
{
printf("[%s:%d] weekday_label create failed\n", __FUNCTION__, __LINE__);
return;
}
lv_obj_add_style(lv_clock.weekday_label, &week_lable_style, LV_STATE_DEFAULT); // 给对象lv_clock.weekday_label添加样式
// 设置时间标签lv_clock.time_label对象基于父对象居中对齐
lv_obj_align_to(lv_clock.time_label, lv_obj_get_parent(lv_clock.time_label), LV_ALIGN_CENTER, 0, 0);
// 设置时间标签lv_clock.date_label对象基于父对象顶部中间对齐
lv_obj_align_to(lv_clock.date_label, lv_obj_get_parent(lv_clock.date_label), LV_ALIGN_TOP_MID, 2, 0);
// 设置时间标签lv_clock.weekday_label对象基于父对象底部中间对齐
lv_obj_align_to(lv_clock.weekday_label, lv_obj_get_parent(lv_clock.weekday_label), LV_ALIGN_BOTTOM_MID, -2, 0);
lv_timer_t* task_timer = lv_timer_create(clock_date_task_callback, 200, (void *)&lv_clock); // 创建定时任务,200ms刷新一次
if (task_timer == NULL)
{
printf("[%s:%d] lv_timer_create failed\n", __FUNCTION__, __LINE__);
}
}
RTC部分
前面的代码创建了定时任务,来定时获取时间并显示,这里我们只是简单的使用了一下HLK-W801的RTC模块,用来实时获取时间并显示。
简单也要学习一下,知识源自于demo。
RTC初始化,这里定义了时钟的初始化,内部初始化了RTC的时间,然后启动了RTC
static void RTC_Init(void)
{
hpmu.Instance = PMU;
hpmu.ClkSource = PMU_CLKSOURCE_32RC;
HAL_PMU_Init(&hpmu);
}
static void CLOCK_Init(void)
{
RTC_TimeTypeDef time;
RTC_Init();
time.Year = 122;
time.Month = 3;
time.Date = 10;
time.Hours = 15;
time.Minutes = 37;
time.Seconds = 10;
HAL_PMU_RTC_Start(&hpmu, &time);
}
然后我们在定时任务的回调函数中使用了时间的获取。
static void clock_date_task_callback(lv_timer_t *timer)
{
static const char *week_day[7] = { "Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday" };
static struct tm *time_info;
RTC_TimeTypeDef time;
HAL_PMU_RTC_GetTime(&hpmu, &time);
int year = (time.Year + 1900);
int month = time.Month;
int day = time.Date;
int weekday = CaculateWeekDay(year,month,day);
int hour =time.Hours;
int minutes = time.Minutes;
int second = time.Seconds;
if (timer != NULL && timer->user_data != NULL)
{
lv_clock_t * clock = (lv_clock_t *)(timer->user_data);
if (clock->time_label != NULL)
{
lv_label_set_text_fmt(clock->time_label, "%02d:%02d:%02d", hour, minutes, second);
lv_obj_align_to(clock->time_label, lv_obj_get_parent(clock->time_label), LV_ALIGN_CENTER, 0, 0);
}
if (clock->date_label != NULL)
{
lv_label_set_text_fmt(clock->date_label, "%d-%02d-%02d", year, month, day);
lv_obj_align_to(clock->date_label, lv_obj_get_parent(clock->date_label), LV_ALIGN_TOP_MID, 2, 0);
}
if (clock->weekday_label != NULL)
{
lv_label_set_text_fmt(clock->weekday_label, "%s", week_day[weekday]);
lv_obj_align_to(clock->weekday_label, lv_obj_get_parent(clock->weekday_label), LV_ALIGN_BOTTOM_MID, -2, 0);
}
}
}
就基本完成了时间的显示。
星期???
这里就有一个神奇的知识了,如何知道某年某月某日,是星期几?
有人说很简单,百度一下就行了。
现在世界各国通用一星期七天的制度。这个制度最早由君士坦丁大帝(Constantine the Great)制定。他在公元321年3月7日正式宣布7天为1周,这个制度一直沿用至今。一周7天的英文名称是Sunday(星期天)、Monday(星期一)、Tuesday(星期二)、Wednesday(星期三)、Thursday(星期四)、Friday(星期五)、Saturday(星期六)。
可是算了一下,那天是周一……。
试想一下:
大帝:今天开始实行星期制,一周七天,那么今天是第一天实行,就是星期一吧。
大臣:……星期一不是第二天吗?为啥今天不是星期天。
大帝:就你话多,来人呐~
大臣:
我们现在用的计算星期的算法,是根据一个公式《使用基姆拉尔森计算公式》得出来的,这里有推导过程《C语言根据日期(年,月,日)判断星期几(使用基姆拉尔森计算公式)》
这个公式也是有前提的,就是0年1月1日是星期1。感觉这个定义就比较能接受,比君士坦大帝那个常规 一点。
那么具体算法的话,
static int CaculateWeekDay(int y, int m, int d) //年月日换算成星期几
{
int week = 0;
if (m==1 || m==2)
{
m=(m==1?13:14);
y=y-1;
//此处表示把1,2月计算到上一年的13,14月
}
week=(d+2*m+3*(m+1)/5+y+y/4-y/100+y/400+1)%7;
return week+1;
}
至于为什么1月和2月单独处理,还是去看推导过程吧。一句两句也说不清楚。
加个背景图
黑乎乎的背景虽然省电,但是也不够美观呢,所以来试着加个图片。
首先是图片要转化为代码中的c数组,用的这个工具。
下载地址
图片要事先转化好大小。
然后包含到图像当中。为了不被遮挡,再设置一下时间和日期控件的透明度,就可以达到下面的效果了
可以猜一下是谁啊
结束语
好久没有烧烤了,孩子们玩的还挺开心,毕竟城里长大的孩子,哪有这种玩火的经历