diff --git a/App/clock.c b/App/clock.c index a8ca2a9..682f069 100644 --- a/App/clock.c +++ b/App/clock.c @@ -5,15 +5,62 @@ u8 current_hour = 10; // 系统软时钟:小时 (默认初始化为 10 点) u8 current_min = 35; // 系统软时钟:分钟 (默认初始化为 35 分) u8 current_sec = 0; // 系统软时钟:秒 +u8 current_day = 1; // 系统软时钟:日 (默认初始化为 1 号) +u8 current_month = 1; // 系统软时钟:月 (默认初始化为 1 月) +u8 current_year = 25; // 系统软时钟:年 (默认初始化为 2025 年) +u8 current_weekday = 0; // 系统软时钟:星期 (0=周日, 由 Clock_RecalcWeekday 现算) volatile u8 clock_updated = 0; // 分钟跳变标志位,置位后用于驱动 UI 刷新时间显示 volatile bit second_tick_flag = 0; // 整秒滴答标志位,每次秒进位置 1,由主循环转换为 KEY_EVENT_SECOND_TICK 事件 // 毫秒计数器 (Timer1 每 1ms 累加) static u16 ms_cnt = 0; +// 每月天数表 (2 月固定按 28 天,闰年在 Days_In_Month 里单独加 1 天处理) +static code u8 days_in_month_table[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +u8 Days_In_Month(u8 year, u8 month) +{ + if (month < 1 || month > 12) { + return 31; // 防御性兜底,不应该发生 + } + if (month == 2) { + // 闰年判断:2000~2099 区间内,能被 4 整除即为闰年 (期间没有能被 100 整除的年份需要排除) + if ((year % 4) == 0) { + return 29; + } + } + return days_in_month_table[month - 1]; +} + +void Clock_RecalcWeekday(void) +{ + // Zeller 公式:h = (q + [13(m+1)/5] + K + [K/4] + [J/4] - 2J) mod 7 + // h: 0=周六,1=周日,2=周一,...,6=周五;用 "+5J" 代替 "-2J" 避免无符号数下溢 (-2 ≡ 5 (mod 7)) + // 1、2 月按上一年的 13、14 月处理 + u8 m = current_month; + u8 y = current_year; + u8 j = 20; // 世纪数 (2000~2099 固定为 20) + u16 h; + + if (m < 3) { + m += 12; + if (y == 0) { + y = 99; // 2000 年 1/2 月,按 1999 年处理的边界情况 + j = 19; + } else { + y -= 1; + } + } + + h = (current_day + (13UL * (m + 1)) / 5 + y + y / 4 + j / 4 + 5UL * j) % 7; + + // 把 Zeller 原始约定 (0=周六) 转换成常用的 0=周日, 1=周一, ..., 6=周六 + current_weekday = (u8)((h + 6) % 7); +} + /** * @brief 系统软时钟毫秒递增更新 (在 1ms 中断中调用) - * @details 负责由定时器 1ms 驱动递增软时钟,并在分钟发生跳变时将 clock_updated 置位 + * @details 负责由定时器 1ms 驱动递增软时钟,并在分钟/日期发生跳变时做相应处理 */ void Clock_IncMS(void) { @@ -29,7 +76,20 @@ void Clock_IncMS(void) current_min = 0; // 1小时时间到 current_hour++; if (current_hour >= 24) { - current_hour = 0; // 小时循环 + current_hour = 0; // 小时循环,日期进位 + current_day++; + if (current_day > Days_In_Month(current_year, current_month)) { + current_day = 1; // 月份进位 + current_month++; + if (current_month > 12) { + current_month = 1; // 年份进位 (不处理世纪) + current_year++; + if (current_year > 99) { + current_year = 0; + } + } + } + Clock_RecalcWeekday(); // 日期变化,重新计算星期 } } clock_updated = 1; // 分钟跳变,标志位置位,前台待机 App 会捕获以刷新 UI diff --git a/App/clock.h b/App/clock.h index 74840e8..8621fba 100644 --- a/App/clock.h +++ b/App/clock.h @@ -7,6 +7,10 @@ extern u8 current_hour; extern u8 current_min; extern u8 current_sec; +extern u8 current_day; // 日 (1~31) +extern u8 current_month; // 月 (1~12) +extern u8 current_year; // 年 (0~99,代表 2000~2099,不处理世纪) +extern u8 current_weekday; // 星期 (0=周日, 1=周一, ..., 6=周六) extern volatile u8 clock_updated; extern volatile bit second_tick_flag; // 整秒滴答标志位,每次 current_sec 递增时置 1,供主循环转换为事件 @@ -15,4 +19,18 @@ extern volatile bit second_tick_flag; // 整秒滴答标志位,每次 current_ */ void Clock_IncMS(void); +/** + * @brief 返回指定年月的天数上限 (处理闰年 2 月) + * @param year 年 (0~99,代表 2000~2099) + * @param month 月 (1~12) + */ +u8 Days_In_Month(u8 year, u8 month); + +/** + * @brief 根据年月日重新计算星期,并写回 current_weekday + * @details 采用 Zeller 公式现算,不依赖 RTC 自身的星期寄存器, + * 避免手动改日期后星期跟着错乱;日期发生变化(自然进位或手动调节)时都要调用一次。 + */ +void Clock_RecalcWeekday(void); + #endif // __CLOCK_H__