feat(ui): show real weekday/day/month on HOME screen instead of placeholder

UI_ShowClockPage()/UI_ShowSetTimePage() now take weekday/day/month(/year)
and build the date bar dynamically via Build_DateString() instead of
the fixed placeholder string. app_home.c reads the full date from RTC
via PCF8563_ReadDateTime() and recalculates weekday since the RTC's
stored date may differ from the software clock's. config.h exposes
current_day/month/year/weekday alongside the existing time fields so
main.c's debug commands don't need a separate include.
This commit is contained in:
edisondeng
2026-07-31 19:43:43 +08:00
parent 81ebf7e302
commit 6baf93c550
5 changed files with 131 additions and 36 deletions

View File

@@ -44,16 +44,17 @@ static void HomeApp_OnStart(void)
current_state = STATE_NORMAL; // 系统切入正常待机状态
RF_SetMode(1); // 开启天线并置为接收模式,监听无线探测器
// 尝试从 PCF8563 RTC 芯片中读取硬件时间 (具备非阻塞防卡死保护)
// 尝试从 PCF8563 RTC 芯片中读取硬件时间与日期 (具备非阻塞防卡死保护)
if (PCF8563_Init()) {
PCF8563_ReadTime(&current_hour, &current_min, &current_sec);
PCF8563_ReadDateTime(&current_hour, &current_min, &current_sec, &current_day, &current_month, &current_year);
Clock_RecalcWeekday(); // 读回的日期可能与软时钟不同,重新算一次星期
XTELL_LOG("[HomeApp] RTC Hardware Synced OK!\r\n");
} else {
XTELL_LOG("[HomeApp] RTC Hardware Offline, using system clock.\r\n");
}
// 绘制并渲染待机时钟主页面画面
UI_ShowClockPage(current_state, current_hour, current_min);
UI_ShowClockPage(current_state, current_hour, current_min, current_weekday, current_day, current_month);
// 关断/熄灭 FCOB 双灯条
RGB_Send(0, 0, 0, 0, 0, 0);
@@ -69,7 +70,7 @@ static void HomeApp_onRun(void)
// 软时钟发生分钟改变,需要通知屏幕重画时钟主页
if (clock_updated) {
clock_updated = 0;
UI_ShowClockPage(current_state, current_hour, current_min);
UI_ShowClockPage(current_state, current_hour, current_min, current_weekday, current_day, current_month);
}
}
@@ -117,7 +118,7 @@ static EventResult HomeApp_onEvent(SystemEvent *evt)
}
// 布撤防状态切换成功后进行 100ms 短震确认,并重新刷写时钟待机页
MOTOR = 1; Delay_ms(100); MOTOR = 0;
UI_ShowClockPage(current_state, current_hour, current_min);
UI_ShowClockPage(current_state, current_hour, current_min, current_weekday, current_day, current_month);
return EVENT_HANDLED;
}

View File

@@ -141,6 +141,10 @@ extern SystemState current_state; // 系统当前整体运行状态
extern u8 current_hour; // 当前系统时间:小时
extern u8 current_min; // 当前系统时间:分钟
extern u8 current_sec; // 当前系统时间:秒
extern u8 current_day; // 当前系统日期:日
extern u8 current_month; // 当前系统日期:月
extern u8 current_year; // 当前系统日期:年 (0~99代表 2000~2099)
extern u8 current_weekday; // 当前系统日期:星期 (0=周日, ..., 6=周六)
extern volatile u8 clock_updated; // 时钟分钟更新标志 (1表示需要刷新待机页时间)
extern volatile bit second_tick_flag; // 整秒滴答标志位,每秒置 1由主循环转换为 KEY_EVENT_SECOND_TICK 事件

View File

@@ -186,21 +186,21 @@ void Debug_ProcessCommand(char cmd)
// 模拟切换至 正常撤防 (NORMAL) 状态并刷新时钟
if (AppManager_GetActiveAppID() == APP_ID_HOME) {
current_state = STATE_NORMAL;
UI_ShowClockPage(current_state, current_hour, current_min);
UI_ShowClockPage(current_state, current_hour, current_min, current_weekday, current_day, current_month);
}
Uart_SendString("[UART] State: NORMAL\r\n");
} else if (cmd == '2') {
// 模拟切换至 已布防 (ARMED) 状态,普通门磁可触发警报
if (AppManager_GetActiveAppID() == APP_ID_HOME) {
current_state = STATE_ARMED;
UI_ShowClockPage(current_state, current_hour, current_min);
UI_ShowClockPage(current_state, current_hour, current_min, current_weekday, current_day, current_month);
}
Uart_SendString("[UART] State: ARMED\r\n");
} else if (cmd == '3') {
// 模拟切换至 已撤防 (DISARMED) 状态,忽略普通报警
if (AppManager_GetActiveAppID() == APP_ID_HOME) {
current_state = STATE_DISARMED;
UI_ShowClockPage(current_state, current_hour, current_min);
UI_ShowClockPage(current_state, current_hour, current_min, current_weekday, current_day, current_month);
}
Uart_SendString("[UART] State: DISARMED\r\n");
} else if (cmd == 'z' || cmd == 'Z') {

125
App/ui.c
View File

@@ -7,19 +7,77 @@
// 引入外部主菜单对码项名称的字符串 ID 表 (例如门磁/PIR/烟感等防区类型)
extern const StringID device_menu_ids[5];
// 底部日期栏固定用 8x16 小字体、LCD_ShowStringCentered 居中绘制在 y=195
// 编辑日期时对 日/月/年 做局部刷新需要精确知道每个字段的像素 X 坐标,这里按
// LCD_ShowStringCentered 的居中公式 (LCD_WIDTH - 字符数*8) / 2 手工算好,
// 前提是字符串格式固定为 "WWW DD-MM-YY" (12 个字符),不能随意改动格式。
#define DATE_BAR_Y 195
#define DATE_BAR_START_X 19 // (135 - 12*8) / 2 = 19
#define DATE_BAR_DAY_X (DATE_BAR_START_X + 4 * 8) // 第 4 个字符起 = 51
#define DATE_BAR_MONTH_X (DATE_BAR_START_X + 7 * 8) // 第 7 个字符起 = 75
#define DATE_BAR_YEAR_X (DATE_BAR_START_X + 10 * 8) // 第 10 个字符起 = 99
#define DATE_BAR_FIELD_W 16 // 2 位数字8px/字符
/**
* @brief 拼出"星期缩写 日-月"格式的日期字符串 (如 "VEN 10-07"),供 HOME 页时钟显示
* @param out 输出缓冲区,调用方需保证至少 10 字节 (3字母+空格+2位+'-'+2位+'\0')
*/
static void Build_DateString(char *out, u8 weekday, u8 day, u8 month)
{
char *wd = Lang_Get((StringID)(STR_WEEKDAY_SUN + weekday));
u8 i = 0;
while (*wd) {
out[i++] = *wd++;
}
out[i++] = ' ';
out[i++] = '0' + (day / 10);
out[i++] = '0' + (day % 10);
out[i++] = '-';
out[i++] = '0' + (month / 10);
out[i++] = '0' + (month % 10);
out[i] = '\0';
}
/**
* @brief 拼出"星期缩写 日-月-年"格式的日期字符串 (如 "VEN 10-07-25"),供时间设置页编辑日期时显示
* @param out 输出缓冲区,调用方需保证至少 13 字节 (3字母+空格+2位+'-'+2位+'-'+2位+'\0')
*/
static void Build_DateStringWithYear(char *out, u8 weekday, u8 day, u8 month, u8 year)
{
char *wd = Lang_Get((StringID)(STR_WEEKDAY_SUN + weekday));
u8 i = 0;
while (*wd) {
out[i++] = *wd++;
}
out[i++] = ' ';
out[i++] = '0' + (day / 10);
out[i++] = '0' + (day % 10);
out[i++] = '-';
out[i++] = '0' + (month / 10);
out[i++] = '0' + (month % 10);
out[i++] = '-';
out[i++] = '0' + (year / 10);
out[i++] = '0' + (year % 10);
out[i] = '\0';
}
/**
* @brief 渲染并显示系统待机时钟主页面
* @param state 当前的系统布防状态 (STATE_ARMED: 已布防, 否则为正常或撤防)
* @param hour 当前待显示的小时数 (0~23)
* @param min 当前待显示的分钟数 (0~59)
* @param weekday 当前星期 (0=周日, ..., 6=周六)
* @param day 当前日 (1~31)
* @param month 当前月 (1~12)
* @details 该页面被划分为三大功能视觉区:
* 1. 顶部状态栏:左侧绘制布撤防指示锁,右侧显示真实剩余电量百分比以及电池单色位图。
* 2. 中部核心栏:垂直居中以 16x32 大号粗字体绘制格式化时间 "HH:MM",在其下方标注 AM/PM 标识。
* 3. 底部信息栏:使用灰色小字模绘制法语星期日期 "VEN 10-07" (星期五 7月10日)。
* 3. 底部信息栏:使用灰色小字模绘制真实星期日期 "VEN 10-07" (星期五 7月10日)。
*/
void UI_ShowClockPage(SystemState state, u8 hour, u8 min)
void UI_ShowClockPage(SystemState state, u8 hour, u8 min, u8 weekday, u8 day, u8 month)
{
char time_str[6];
char date_str[10];
char batt_str[7];
u8 batt;
@@ -74,8 +132,9 @@ void UI_ShowClockPage(SystemState state, u8 hour, u8 min)
// ====== 绘制 AM/PM 状态 ======
LCD_ShowStringCentered(150, Lang_Get(STR_CLOCK_AMPM), COLOR_WHITE, COLOR_BLACK);
// ====== 绘制日期星期栏 ======
LCD_ShowStringCentered(195, Lang_Get(STR_CLOCK_DATE), COLOR_GRAY, COLOR_BLACK);
// ====== 绘制日期星期栏 (真实日期,不再是写死的占位文案) ======
Build_DateString(date_str, weekday, day, month);
LCD_ShowStringCentered(DATE_BAR_Y, date_str, COLOR_GRAY, COLOR_BLACK);
}
/**
@@ -364,10 +423,14 @@ void UI_ShowMainMenu(u8 selected_index)
}
/**
* @brief 渲染修改时间闪烁调节页面 (调节小时或分钟)
* @brief 渲染修改时间/日期闪烁调节页面
* @param hour 修改状态中的小时
* @param min 修改状态中的分钟
* @param selection 当前正在编辑聚焦的属性位 (1: 修改小时, 2: 修改分钟)
* @param day 修改状态中的日
* @param month 修改状态中的月
* @param year 修改状态中的年 (0~99代表 2000~2099)
* @param weekday 根据当前日期算出的星期 (0=周日, ..., 6=周六)
* @param selection 当前正在编辑聚焦的属性位 (1:小时, 2:分钟, 3:日, 4:月, 5:年)
* @param blink_on 当前 500ms 控制隐现的闪烁周期标志位 (1-绘制正常字0-以空格消隐该数值字符)
*/
// 小时/分钟数字在屏幕上的固定坐标 (16x32 字体,两位数字宽度为 32px)
@@ -377,9 +440,10 @@ void UI_ShowMainMenu(u8 selected_index)
#define SET_TIME_HOUR_X 23
#define SET_TIME_MIN_X 75
void UI_ShowSetTimePage(u8 hour, u8 min, u8 selection, u8 blink_on)
void UI_ShowSetTimePage(u8 hour, u8 min, u8 day, u8 month, u8 year, u8 weekday, u8 selection, u8 blink_on)
{
char time_str[6];
char date_str[13];
LCD_Clear(COLOR_BLACK);
// 绘制绿锁头等背景静态信息
@@ -392,7 +456,7 @@ void UI_ShowSetTimePage(u8 hour, u8 min, u8 selection, u8 blink_on)
LCD_ShowString(77, 24, "85%", COLOR_GRAY, COLOR_BLACK);
LCD_DrawMonoBitmap(107, 27, 20, 10, bmp_battery_20x10, COLOR_GRAY, COLOR_BLACK);
// 整页绘制时,小时/分钟数字一律完整显示 (消隐闪烁效果交给 UI_UpdateSetTimeDigit 局部刷新处理)
// 整页绘制时,数字一律完整显示 (消隐闪烁效果交给 UI_UpdateSetTimeDigit 局部刷新处理)
time_str[0] = '0' + (hour / 10);
time_str[1] = '0' + (hour % 10);
time_str[2] = '\0';
@@ -408,30 +472,49 @@ void UI_ShowSetTimePage(u8 hour, u8 min, u8 selection, u8 blink_on)
// 补齐底部的固定辅助元素
LCD_ShowStringCentered(150, Lang_Get(STR_CLOCK_AMPM), COLOR_WHITE, COLOR_BLACK);
LCD_ShowStringCentered(195, Lang_Get(STR_CLOCK_DATE), COLOR_GRAY, COLOR_BLACK);
// 编辑日期时,日期栏额外带上年份 ("WWW DD-MM-YY"),方便看清正在改的年份数值
Build_DateStringWithYear(date_str, weekday, day, month, year);
LCD_ShowStringCentered(DATE_BAR_Y, date_str, COLOR_GRAY, COLOR_BLACK);
}
/**
* @brief 局部刷新时间设置界面里"正在调节的那一个"字 (小时分钟)
* @param selection 1:刷新小时区域, 2:刷新分钟区域
* @brief 局部刷新时间设置界面里"正在调节的那一个"字 (小时/分钟/日/月/年)
* @param selection 1:小时, 2:分钟, 3:日, 4:月, 5:年
* @param blink_on 1:显示数值, 0:消隐(涂黑),用于 500ms 周期闪烁效果
* @details 只重画 32x32px 的数字区域,不触碰锁头/电池/冒号/AM-PM/日期等其它内容
* @details 小时/分钟用 32x32px 大字体区域;日/月/年用日期栏里对应的 16x16px 小字体区域
* 都只重画自己那一小块,不触碰锁头/电池/冒号/AM-PM/星期文字等其它内容,
* 避免每次闪烁或调值都整屏 LCD_Clear() 造成全屏闪烁。
*/
void UI_UpdateSetTimeDigit(u8 hour, u8 min, u8 selection, u8 blink_on)
void UI_UpdateSetTimeDigit(u8 hour, u8 min, u8 day, u8 month, u8 year, u8 selection, u8 blink_on)
{
char time_str[3];
u16 x = (selection == 1) ? SET_TIME_HOUR_X : SET_TIME_MIN_X;
u8 value = (selection == 1) ? hour : min;
char buf[3];
u16 x;
u16 y;
u16 w;
u16 h;
u8 value;
switch (selection) {
case 1: x = SET_TIME_HOUR_X; y = SET_TIME_DIGIT_Y; w = SET_TIME_DIGIT_W; h = SET_TIME_DIGIT_H; value = hour; break;
case 2: x = SET_TIME_MIN_X; y = SET_TIME_DIGIT_Y; w = SET_TIME_DIGIT_W; h = SET_TIME_DIGIT_H; value = min; break;
case 3: x = DATE_BAR_DAY_X; y = DATE_BAR_Y; w = DATE_BAR_FIELD_W; h = 16; value = day; break;
case 4: x = DATE_BAR_MONTH_X; y = DATE_BAR_Y; w = DATE_BAR_FIELD_W; h = 16; value = month; break;
default: x = DATE_BAR_YEAR_X; y = DATE_BAR_Y; w = DATE_BAR_FIELD_W; h = 16; value = year; break;
}
if (!blink_on) {
// 灭周期:直接把这一小块涂黑,形成消隐效果
LCD_FillRect(x, SET_TIME_DIGIT_Y, SET_TIME_DIGIT_W, SET_TIME_DIGIT_H, COLOR_BLACK);
LCD_FillRect(x, y, w, h, COLOR_BLACK);
return;
}
time_str[0] = '0' + (value / 10);
time_str[1] = '0' + (value % 10);
time_str[2] = '\0';
LCD_ShowString16x32(x, SET_TIME_DIGIT_Y, time_str, COLOR_CYAN, COLOR_BLACK);
buf[0] = '0' + (value / 10);
buf[1] = '0' + (value % 10);
buf[2] = '\0';
if (selection <= 2) {
LCD_ShowString16x32(x, y, buf, COLOR_CYAN, COLOR_BLACK);
} else {
LCD_ShowString(x, y, buf, COLOR_GRAY, COLOR_BLACK);
}
}

View File

@@ -10,10 +10,13 @@
* @param state 当前的系统运行状态 (STATE_NORMAL/STATE_ARMED/STATE_DISARMED)
* @param hour 当前待显示的小时
* @param min 当前待显示的分钟
* @param weekday 当前星期 (0=周日, ..., 6=周六)
* @param day 当前日 (1~31)
* @param month 当前月 (1~12)
* @details 负责在 120x240 大小的 AMOLED 屏幕上居中绘制巨大的时钟字样,并在顶部
* 绘制对应的状态图标 (如一把打开的锁代表已撤防、闭合的锁代表已布防等)。
*/
void UI_ShowClockPage(SystemState state, u8 hour, u8 min);
void UI_ShowClockPage(SystemState state, u8 hour, u8 min, u8 weekday, u8 day, u8 month);
/**
* @brief 渲染显示传感器对码的二级设备选择菜单页面
@@ -74,19 +77,23 @@ void UI_ShowBindingPage(u32 addr);
void UI_ShowMainMenu(u8 selected_index);
/**
* @brief 渲染显示修改时间页面 (时钟设置编辑状态)
* @brief 渲染显示修改时间/日期页面 (时钟设置编辑状态)
* @param hour 待修改的小时
* @param min 待修改的分钟
* @param selection 当前选中的修改项 (1:修改小时, 2:修改分钟)
* @param day 待修改的日
* @param month 待修改的月
* @param year 待修改的年 (0~99代表 2000~2099)
* @param weekday 根据当前日期算出的星期 (0=周日, ..., 6=周六)
* @param selection 当前选中的修改项 (1:小时, 2:分钟, 3:日, 4:月, 5:年)
* @param blink_on 控制被选中数值以 500ms 周期进行隐现闪烁的周期标志位 (1-显示, 0-消隐)
*/
void UI_ShowSetTimePage(u8 hour, u8 min, u8 selection, u8 blink_on);
void UI_ShowSetTimePage(u8 hour, u8 min, u8 day, u8 month, u8 year, u8 weekday, u8 selection, u8 blink_on);
/**
* @brief 局部刷新时间设置界面里正在调节的那一个字 (小时分钟),用于闪烁/调值时避免整屏重绘
* @param selection 1:刷新小时区域, 2:刷新分钟区域
* @brief 局部刷新时间设置界面里正在调节的那一个字 (小时/分钟/日/月/年),用于闪烁/调值时避免整屏重绘
* @param selection 1:小时, 2:分钟, 3:日, 4:月, 5:年
* @param blink_on 1:显示数值, 0:消隐(涂黑)
*/
void UI_UpdateSetTimeDigit(u8 hour, u8 min, u8 selection, u8 blink_on);
void UI_UpdateSetTimeDigit(u8 hour, u8 min, u8 day, u8 month, u8 year, u8 selection, u8 blink_on);
#endif // __UI_H__