Some PCB variants may not populate the PCF8563. Guards RTC init/read in HomeApp_OnStart(), the WriteDateTime() save in SetClockApp, and the I2C/RTC diagnostic scans in Self_Test() behind #if USE_PCF8563. When disabled, the firmware runs entirely on the Timer1-driven software clock; date/time just won't survive a power cycle without the chip.
175 lines
7.2 KiB
C
175 lines
7.2 KiB
C
#include "app_manager.h"
|
|
#include "clock.h"
|
|
#include "xtell.h"
|
|
#include "ui.h"
|
|
#include "rgb.h"
|
|
#include "event.h"
|
|
#include "system.h"
|
|
#include "../Drivers/pcf8563.h"
|
|
|
|
// 外部引用的全局系统状态变量
|
|
extern SystemState current_state;
|
|
extern volatile u16 ms_tick;
|
|
|
|
// 本应用私有的时间编辑状态
|
|
static u8 time_edit_field = 1; // 当前聚焦调节的字段 (1:小时, 2:分钟, 3:日, 4:月, 5:年)
|
|
static u8 time_edit_blink_on = 1; // 时钟修改时光标闪烁周期状态位
|
|
|
|
// 声明本应用的生命周期回调函数
|
|
static void SetClockApp_OnStart(void);
|
|
static void SetClockApp_onRun(void);
|
|
static void SetClockApp_onClose(void);
|
|
static EventResult SetClockApp_onEvent(SystemEvent *evt);
|
|
|
|
// 实体化定义时间设置应用控制块 (从 menu_app 的"HORLOGE"选项进入)
|
|
WristbandApp set_clock_app = {
|
|
APP_ID_SET_CLOCK,
|
|
"SetClock",
|
|
SetClockApp_OnStart,
|
|
SetClockApp_onRun,
|
|
SetClockApp_onClose,
|
|
SetClockApp_onEvent
|
|
};
|
|
|
|
/**
|
|
* @brief SetClockApp 启动回调
|
|
*/
|
|
static void SetClockApp_OnStart(void)
|
|
{
|
|
time_edit_field = 1;
|
|
time_edit_blink_on = 1;
|
|
current_state = STATE_SET_TIME; // 系统切入时间设置状态
|
|
|
|
// FCOB 幻彩灯条双灯珠均显示蓝色常亮 (R=0, G=0, B=180),进行模式视觉区分
|
|
RGB_Send(0, 0, 180, 0, 0, 180);
|
|
|
|
// 振动马达单次短震 50 毫秒,提供触觉反馈
|
|
MOTOR = 1;
|
|
Delay_ms(50);
|
|
MOTOR = 0;
|
|
|
|
// 渲染显示时间编辑界面,默认高亮编辑小时字段,消隐位开启
|
|
UI_ShowSetTimePage(current_hour, current_min, current_day, current_month, current_year, current_weekday, time_edit_field, 1);
|
|
XTELL_LOG("[SetClockApp] Started\r\n");
|
|
}
|
|
|
|
/**
|
|
* @brief SetClockApp 轮询主回调
|
|
*/
|
|
static void SetClockApp_onRun(void)
|
|
{
|
|
// ====== 时间设置闪烁动画 (周期 500ms) ======
|
|
u8 blink = (ms_tick / 500) % 2;
|
|
if (blink != time_edit_blink_on)
|
|
{
|
|
time_edit_blink_on = blink;
|
|
// 每次闪烁状态发生变化,只局部刷新正在调节的那个字段,不整屏重绘
|
|
UI_UpdateSetTimeDigit(current_hour, current_min, current_day, current_month, current_year, current_weekday, time_edit_field, time_edit_blink_on);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief SetClockApp 退出清理回调
|
|
*/
|
|
static void SetClockApp_onClose(void)
|
|
{
|
|
RGB_Send(0, 0, 0, 0, 0, 0); // 关闭灯条
|
|
XTELL_LOG("[SetClockApp] Closed\r\n");
|
|
}
|
|
|
|
/**
|
|
* @brief SetClockApp 接收按键事件处理回调
|
|
*/
|
|
static EventResult SetClockApp_onEvent(SystemEvent *evt)
|
|
{
|
|
if (evt->key_event == KEY_EVENT_UP_CLICK)
|
|
{
|
|
// 短按 ▲ 键增加当前聚焦字段的数值 (各字段独立循环)
|
|
switch (time_edit_field) {
|
|
case 1: current_hour = (current_hour + 1) % 24; break;
|
|
case 2: current_min = (current_min + 1) % 60; break;
|
|
case 3:
|
|
current_day = (current_day >= Days_In_Month(current_year, current_month)) ? 1 : (current_day + 1);
|
|
Clock_RecalcWeekday();
|
|
break;
|
|
case 4:
|
|
current_month = (current_month >= 12) ? 1 : (current_month + 1);
|
|
// 月份变化后,若当前日超出新月份的最大天数,截断到最大天数
|
|
if (current_day > Days_In_Month(current_year, current_month)) {
|
|
current_day = Days_In_Month(current_year, current_month);
|
|
}
|
|
Clock_RecalcWeekday();
|
|
break;
|
|
default: // 5: 年
|
|
current_year = (current_year >= 99) ? 0 : (current_year + 1);
|
|
if (current_day > Days_In_Month(current_year, current_month)) {
|
|
current_day = Days_In_Month(current_year, current_month);
|
|
}
|
|
Clock_RecalcWeekday();
|
|
break;
|
|
}
|
|
time_edit_blink_on = 1; // 调值后强制显示 (不处于消隐周期)
|
|
UI_UpdateSetTimeDigit(current_hour, current_min, current_day, current_month, current_year, current_weekday, time_edit_field, 1);
|
|
return EVENT_HANDLED;
|
|
}
|
|
if (evt->key_event == KEY_EVENT_DOWN_CLICK)
|
|
{
|
|
// 短按 ▼ 键减小当前聚焦字段的数值 (各字段独立循环)
|
|
switch (time_edit_field) {
|
|
case 1: current_hour = (current_hour == 0) ? 23 : (current_hour - 1); break;
|
|
case 2: current_min = (current_min == 0) ? 59 : (current_min - 1); break;
|
|
case 3:
|
|
current_day = (current_day <= 1) ? Days_In_Month(current_year, current_month) : (current_day - 1);
|
|
Clock_RecalcWeekday();
|
|
break;
|
|
case 4:
|
|
current_month = (current_month <= 1) ? 12 : (current_month - 1);
|
|
if (current_day > Days_In_Month(current_year, current_month)) {
|
|
current_day = Days_In_Month(current_year, current_month);
|
|
}
|
|
Clock_RecalcWeekday();
|
|
break;
|
|
default: // 5: 年
|
|
current_year = (current_year == 0) ? 99 : (current_year - 1);
|
|
if (current_day > Days_In_Month(current_year, current_month)) {
|
|
current_day = Days_In_Month(current_year, current_month);
|
|
}
|
|
Clock_RecalcWeekday();
|
|
break;
|
|
}
|
|
time_edit_blink_on = 1; // 调值后强制显示 (不处于消隐周期)
|
|
UI_UpdateSetTimeDigit(current_hour, current_min, current_day, current_month, current_year, current_weekday, time_edit_field, 1);
|
|
return EVENT_HANDLED;
|
|
}
|
|
if (evt->key_event == KEY_EVENT_CONFIRM_LONG)
|
|
{
|
|
// 长按 ■ 确认键切换调节字段 (小时 -> 分钟 -> 日 -> 月 -> 年 -> 循环回小时)
|
|
time_edit_field = (time_edit_field >= 5) ? 1 : (time_edit_field + 1);
|
|
time_edit_blink_on = 1; // 切换焦点后所有字段都应处于可见状态
|
|
// 全部字段各局部刷新一次,避免切换前正处于消隐周期的那个字段停留在涂黑状态
|
|
UI_UpdateSetTimeDigit(current_hour, current_min, current_day, current_month, current_year, current_weekday, 1, 1);
|
|
UI_UpdateSetTimeDigit(current_hour, current_min, current_day, current_month, current_year, current_weekday, 2, 1);
|
|
UI_UpdateSetTimeDigit(current_hour, current_min, current_day, current_month, current_year, current_weekday, 3, 1);
|
|
UI_UpdateSetTimeDigit(current_hour, current_min, current_day, current_month, current_year, current_weekday, 4, 1);
|
|
UI_UpdateSetTimeDigit(current_hour, current_min, current_day, current_month, current_year, current_weekday, 5, 1);
|
|
return EVENT_HANDLED;
|
|
}
|
|
if (evt->key_event == KEY_EVENT_CONFIRM_CLICK)
|
|
{
|
|
// 短按 ■ 确认键保存最新时间与日期配置,退出到主菜单 (父 App)
|
|
current_state = STATE_PAIR_MENU;
|
|
|
|
#if USE_PCF8563
|
|
// 同步将最新时间与日期 (含算好的星期) 一次性写入 PCF8563 芯片寄存器
|
|
PCF8563_WriteDateTime(current_hour, current_min, 0, current_day, current_month, current_year, current_weekday);
|
|
#endif
|
|
|
|
// 退出短振反馈
|
|
MOTOR = 1; Delay_ms(50); MOTOR = 0;
|
|
|
|
AppManager_SwitchToForeground(&menu_app);
|
|
return EVENT_HANDLED;
|
|
}
|
|
return EVENT_HANDLED; // 在时间设置模式下屏蔽拦截其他一切无关按键事件
|
|
}
|