功能

  1. pcf8563模块测试。
  2. esp32s3联网授时,自动校准pcf8563时钟模块,并使用LVGL显示。

代码

参考了Bob_1993博主的文章LVGL8制作简易时钟

总体来说比较顺利,除了遇到几个小bug之外🤣🤣🤣。

变量定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "I2C_BM8563.h"
#include <WiFi.h>

// RTC BM8563 I2C port
// I2C pin definition for M5Stick & M5Stick Plus & M5Stack Core2
#define BM8563_I2C_SDA 47
#define BM8563_I2C_SCL 48
typedef struct _lv_clock
{
lv_obj_t *time_label; // 时间标签
lv_obj_t *date_label; // 日期标签
lv_obj_t *weekday_label; // 星期标签
} lv_clock_t;

I2C_BM8563 rtc(I2C_BM8563_DEFAULT_ADDRESS, Wire1);

const char *ntpServer = "ntp.aliyun.com";

UI设计

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
void demo10()
{
WiFi.begin("vivoS12", "7bsgbsg7"); // Or, Connect to the specified access point

Serial.print("Connecting to Wi-Fi ");
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
Serial.println(" CONNECTED");

I2C_BM8563_DateTypeDef dateStruct;
I2C_BM8563_TimeTypeDef timeStruct;

Wire1.begin(BM8563_I2C_SDA, BM8563_I2C_SCL);

// Init RTC
rtc.begin();

configTime(8 * 3600, 0, ntpServer);

// Init I2C

// Get local time
struct tm timeInfo;
if (getLocalTime(&timeInfo))
{
// Set RTC time
I2C_BM8563_TimeTypeDef timeStruct;
timeStruct.hours = timeInfo.tm_hour;
timeStruct.minutes = timeInfo.tm_min;
timeStruct.seconds = timeInfo.tm_sec;
rtc.setTime(&timeStruct);

// Set RTC Date
I2C_BM8563_DateTypeDef dateStruct;
dateStruct.weekDay = timeInfo.tm_wday;
dateStruct.month = timeInfo.tm_mon + 1;
dateStruct.date = timeInfo.tm_mday;
dateStruct.year = timeInfo.tm_year + 1900;
rtc.setDate(&dateStruct);
}

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, 5); // 设置样式圆角
lv_style_set_bg_opa(&date_time_clock_style, LV_OPA_20); // 设置样式背景透明度
lv_style_set_border_width(&date_time_clock_style, 0); // 设置样式边框宽度
lv_style_set_bg_color(&date_time_clock_style, lv_color_white()); // 设置样式背景颜色,白色
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, 100); // 设置对象大小
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(demo10_my_timer, 200, (void *)&lv_clock);
if (task_timer == NULL)
{
printf("[%s:%d] lv_timer_create failed\n", __FUNCTION__, __LINE__);
}

}

定时更新

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
void demo10_my_timer(lv_timer_t *timer)
{
I2C_BM8563_TimeTypeDef timeStruct;
I2C_BM8563_DateTypeDef dateStruct;
rtc.getTime(&timeStruct);
rtc.getDate(&dateStruct);

static const char *week_day[7] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

int year = dateStruct.year;
int month = dateStruct.month;
int day = dateStruct.date;
int weekday = dateStruct.weekDay;
int hour = timeStruct.hours;
int minutes = timeStruct.minutes;
int second = timeStruct.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);
}
}
}

修bug

1.字体

LV_FONT_MONTSERRAT_32 等字体无法使用,需要在 lv_conf.h 中打开开关

image-20230530173528736

2.回调函数的传递参数

用 int 等类型定义的结构体在定时器中无法取出成员,改为使用 lv_obj_t 结构体传递所有需要修改的标签

1
2
3
4
5
6
typedef struct _lv_clock
{
lv_obj_t *time_label; // 时间标签
lv_obj_t *date_label; // 日期标签
lv_obj_t *weekday_label; // 星期标签
} lv_clock_t;

最终效果

image-20230530130041919