单片机---HLK-W801图形框架LVGL下开发(一)
知识点
《单片机—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数组,用的这个工具。
下载地址
图片要事先转化好大小。
然后包含到图像当中。为了不被遮挡,再设置一下时间和日期控件的透明度,就可以达到下面的效果了
可以猜一下是谁啊
结束语
好久没有烧烤了,孩子们玩的还挺开心,毕竟城里长大的孩子,哪有这种玩火的经历