#include "system.h" #include "rgb.h" #include "ui.h" #include "clock.h" #include "../Drivers/lcd.h" #include "../Drivers/rf.h" #include "intrins.h" // 引用外部定义在 main.c 中的掉电外部唤醒事件状态暂存标志 extern volatile u8 p0_wakeup_flag; // P0 端口按键中断唤醒标志 extern volatile u8 p2_wakeup_flag; // P2 端口按键中断唤醒标志 extern volatile u8 p3_wakeup_flag; // P3 端口射频中断唤醒标志 extern volatile u8 rf_wakeup_flag; // 射频接收中断唤醒标志 /** * @brief 系统 GPIO 端口工作模式初始化配置 */ void GPIO_Init(void) { // EAXFR = 1 允许访问扩展特殊功能寄存器 (XSFR) EAXFR = 1; // 1. 配置 P0.1 (KEY_UP), P0.2 (KEY_CONFIRM), P0.3 (KEY_DOWN) 物理按键引脚为准双向口模式 (M1=0, M0=0) P0M1 &= ~0x0E; P0M0 &= ~0x0E; KEY_UP = 1; KEY_CONFIRM = 1; KEY_DOWN = 1; // 2. 配置 P1.3 (BAT_ADC) 电池电压采样引脚为高阻输入模式 (M1=1, M0=0) P1M1 |= (1 << 3); P1M0 &= ~(1 << 3); // 3. 配置 P5.4 (DET) 充电器插入检测引脚为高阻输入模式 (M1=1, M0=0) P5M1 |= (1 << 4); P5M0 &= ~(1 << 4); // 4. 配置 P2.5 (MOTOR) 振动马达驱动引脚为推挽输出模式 (M1=0, M0=1) P2M1 &= ~(1 << 5); P2M0 |= (1 << 5); MOTOR = 0; // 默认静止不震动 // 5. 配置 P2.6 (KEY_SOS) 物理求救按键引脚为准双向口模式并置 1 (M1=0, M0=0) P2M1 &= ~(1 << 6); P2M0 &= ~(1 << 6); KEY_SOS = 1; // 6. 配置 P3.5 (SHUT) 射频芯片使能引脚为推挽输出模式 (M1=0, M0=1) P3M1 &= ~(1 << 5); P3M0 |= (1 << 5); SHUT = 1; // 默认拉高使能开启,允许无线芯片运行 // 7. 配置 P1.7 (SGM_CTRL / LEDA) 为推挽输出模式 (M1=0, M0=1) // PMOS 背光控制:低电平 (0) 导通发光,高电平 (1) 截止熄灭 P1M1 &= ~(1 << 7); P1M0 |= (1 << 7); SGM_CTRL = 1; // 初始设为高电平 (关闭背光),由 LCD_Init 初始化完成后开启 // 8. 配置 P0.1, P0.2, P0.3 和 P2.6 的内部上拉电阻,避免引脚浮空抖动误唤醒 P0PU |= 0x0E; // 开启 P0.1/P0.2/P0.3 上拉 P2PU |= 0x40; // 开启 P2.6 上拉 // 9. 配置 P3.0 (UART1_RXD - 输入) 和 P3.1 (UART1_TXD - 输出) 为相应模式 P3M1 &= ~((1 << 0) | (1 << 1)); P3M0 &= ~(1 << 0); // RXD 设为准双向 P3M0 |= (1 << 1); // TXD 设为推挽输出 } /** * @brief 阻塞式毫秒级延时函数 (基于 CPU 频率估算环路次数) * @param ms 延迟毫秒数 */ void Delay_ms(u16 ms) { u16 i, j; for (i = 0; i < ms; i++) // 24MHz 晶振下,双重循环实现约 1ms 延迟 for (j = 12000; j > 0; j--); } /** * @brief 精确的 10 微秒延时函数 (利用 8051 指令循环时间) */ void Delay10us(void) { unsigned char data i; _nop_(); i = 30; // 循环常数 while (--i); } /** * @brief 阻塞式微秒级延时函数 * @param us 延迟微秒数 */ void Delay_us(u16 us) { u16 i; for(i = 0; i < us; i++) { _nop_(); _nop_(); _nop_(); _nop_(); } } /** * @brief 获取安全的 Timer0 计数器值 */ u16 GetTimer0_Safe(void) { u8 th, tl; do { th = TH0; tl = TL0; } while (th != TH0); // 消除高低字节溢出读取不同步误差 return ((u16)th << 8) | tl; } /** * @brief 初始化 UART1 (串口 1) 用于调试输出与指令接收 */ void Uart1_Init(void) { SCON = 0x50; // 8位数据, 可变波特率 T2L = 0xCC; // 1T模式,波特率 115200 对应的重载值底位 (24MHz) T2H = 0xFF; // 波特率重载值高位 AUXR |= 0x14; // 定时器 2 设为 1T 模式并启动定时器 2 AUXR |= 0x01; // 串口 1 选择定时器 2 作为波特率发生器 ES = 1; // 开启串口 1 中断允许 } /** * @brief 串口 1 发送 1 字节数据 */ void Uart_SendByte(u8 dat) { SBUF = dat; while (!TI); // 等待发送中断标志置位 TI = 0; // 清除发送标志 } /** * @brief 串口 1 发送以 '' 结尾的字符串 */ void Uart_SendString(char *s) { while (*s) { Uart_SendByte(*s++); } } /** * @brief 串口 1 非阻塞接收字符 * @return char 接收到的字符,若无数据则返回 '' */ char Uart_RxChar(void) { if (RI) { char c = SBUF; RI = 0; return c; } return '\0'; } /** * @brief 串口 1 打印输出 4 位十六进制数 */ void Uart_SendHex4(u8 val) { val &= 0x0F; if (val < 10) Uart_SendByte('0' + val); else Uart_SendByte('A' + (val - 10)); } /** * @brief 串口 1 打印输出 8 位十六进制数 */ void Uart_SendHex8(u8 val) { Uart_SendHex4(val >> 4); Uart_SendHex4(val); } /** * @brief 串口 1 打印输出 16 位十六进制数 */ void Uart_SendHex16(u16 val) { Uart_SendHex8((u8)(val >> 8)); Uart_SendHex8((u8)val); } /** * @brief 串口 1 打印输出 32 位十六进制数 */ void Uart_SendHex32(u32 val) { Uart_SendHex16((u16)(val >> 16)); Uart_SendHex16((u16)val); } /** * @brief 格式化输出 20 位射频物理地址为 "ADDR: 0xXXXXX" 字符串 */ void Format_Hex_Address(u32 val, char *buf) { u8 i; buf[0] = 'A'; buf[1] = 'D'; buf[2] = 'D'; buf[3] = 'R'; buf[4] = ':'; buf[5] = ' '; buf[6] = '0'; buf[7] = 'x'; for (i = 0; i < 5; i++) { u8 nibble = (val >> (4 * (4 - i))) & 0x0F; if (nibble < 10) buf[8 + i] = '0' + nibble; else buf[8 + i] = 'A' + (nibble - 10); } buf[13] = '\0'; } /** * @brief 系统进入低功耗深度休眠 (Power-Down) 核心逻辑流程 */ void Enter_Low_Power_Sleep(void) { Uart_SendString("[SYS] Entering low power sleep...\r\n"); // 1. 关闭/熄灭 FCOB 灯条 RGB_Send(0, 0, 0, 0, 0, 0); // 2. 向 LCD 屏幕控制芯片写入 Display OFF 指令 (0x28),关闭发光 WriteComm(0x28); Delay_ms(20); // 3. 屏幕芯片写入 Sleep In 指令 (0x10),使其数字逻辑单元停机进入低损耗模式 WriteComm(0x10); Delay_ms(20); // 4. PMOS 背光控制:休眠时拉高 P1.7 (SGM_CTRL) 至高电平以完全截止熄灭背光,杜绝漏电 SGM_CTRL = 1; // 5. 【屏幕接口防漏电配置】: // SCL, SDA 拉低,CS, RST 拉高,彻底切断物理通路灌电 LCD_CS = 1; LCD_RST = 1; LCD_DCX = 0; LCD_SCL = 0; LCD_SDI = 0; // 6. 休眠模式下关闭射频发射,保持天线接收状态 (SHUT=0, RF_RX=1),在休眠中监听射频警报 RF_SetMode(0); // 开启扩展 SFR 访问权限 (EAXFR = 1) P_SW2 |= 0x80; // 7. 配置 P0.1, P0.2, P0.3 (KEY_UP/CONFIRM/DOWN) 端口下降沿低电平触发唤醒源 P0IM1 |= 0x0E; P0IM0 &= ~0x0E; P0INTE |= 0x0E; // 开启 P0 端口的中断允许 // 8. 配置 P2.6 (KEY_SOS) 为低电平触发外部唤醒源 P2IM1 |= 0x40; P2IM0 &= ~0x40; P2INTE |= 0x40; // 开启 P2 端口中断允许 // 9. 配置 P3.6 (RF_RX_DATA 新管脚) 为双沿跳变触发外部唤醒源,保障脉冲瞬间唤醒 MCU P3IM1 |= 0x40; P3IM0 |= 0x40; P3INTE |= 0x40; // 开启 P3 端口中断允许 // 10. 【掉电唤醒核心扩展配置】: P0WKUE |= 0x0E; // 使能 P0.1/P0.2/P0.3 的掉电唤醒 P2WKUE |= 0x40; // 使能 P2.6 的掉电唤醒 P3WKUE |= 0x40; // 使能 P3.6 的掉电唤醒 // 【漏电控制】:仅开启 P2.6 (SOS) 上拉,而关闭 P3.6 (RF_RX_DATA) 上拉,防止产生强灌电 P2PU |= 0x40; P2IE |= 0x40; // 使能 P2.6 内部通道 P3PU &= ~0x40; // 禁用 P3.6 上拉 P3IE |= 0x40; // 强制使能 P3.6 内部数字通道,确保电平跳变能被硬件捕捉 // 11. 清除 P0, P2, P3 的全部悬挂中断标志,防止瞬间误唤醒 P0INTF = 0x00; P2INTF = 0x00; P3INTF = 0x00; // 清零内存暂存的唤醒源事件标志 p0_wakeup_flag = 0; p2_wakeup_flag = 0; p3_wakeup_flag = 0; rf_wakeup_flag = 0; // 12. 关闭 Timer1 (系统滴答) 中断。全局中断保持开启 (EA=1) 用作掉电唤醒条件 ET1 = 0; EA = 1; // 13. 向电源控制寄存器 PCON 写入 0x02,使 MCU 进入 Power-Down 模式,CPU 停止运转 Uart_SendString("[SYS] Entering Power-Down mode...\r\n"); PCON |= 0x02; // 设定 PD 位,硬件立刻休眠挂起 // 唤醒后刷新指令流水线 _nop_(); _nop_(); _nop_(); _nop_(); // 14. 唤醒后,自动接着从本行执行,调用 Wakeup_Restore() 唤醒恢复流程 Wakeup_Restore(); } /** * @brief 系统被中断唤醒后的运行环境恢复逻辑 */ void Wakeup_Restore(void) { EAXFR = 1; // 允许访问扩展寄存区 // 1. 立即关闭唤醒源和端口中断,防止按键抖动误触发 P0INTE = 0x00; P2INTE = 0x00; P3INTE = 0x00; P0WKUE = 0x00; // 清除掉电唤醒源使能 P2WKUE = 0x00; P3WKUE = 0x00; P0INTF = 0x00; // 清空标志位 P2INTF = 0x00; P3INTF = 0x00; // 2. 清零掉电专用定时器 WKTCH = 0x00; WKTCL = 0x00; // 3. 恢复 Timer1 定时器中断允许,重新开启系统基准时基轮询 EA = 1; ET1 = 1; // 4. 重置射频工作模式:进入接收模式并开启天线 RF_SetMode(1); // 打印日志,告知调试串口具体的唤醒源 if ((p0_wakeup_flag & 0x0E) || (p2_wakeup_flag & 0x40)) { Uart_SendString("[WR] Woken by KEY (P0="); Uart_SendHex8(p0_wakeup_flag); Uart_SendString(", P2="); Uart_SendHex8(p2_wakeup_flag); Uart_SendString(")\r\n"); } else if ((p3_wakeup_flag & 0x40) || rf_wakeup_flag) { rf_wakeup_flag = 1; // 确认标记为射频解调唤醒 Uart_SendString("[WR] Woken by RF (P3.6)!\r\n"); } else { Uart_SendString("[WR] Woken by TIMER/Other\r\n"); } // 5. 【屏幕背光开启】:PMOS 背光控制,拉低 P1.7 (SGM_CTRL) 至低电平以点亮背光 SGM_CTRL = 0; // 6. 阻塞延时 200ms 等待供电与背光电压稳压建立,避开浪涌 Delay_ms(200); // 7. 重新调用 LCD_Init() 初始化序列,写入控制偏置、伽马等命令,唤醒屏控制器 LCD_Init(); // 8. 重置用户的无操作闲置计时器为零 inactivity_timer = 0; // 9. 更新运行状态为普通时钟,并强制调用时钟界面函数刷屏,返回工作状态 current_state = STATE_NORMAL; UI_ShowClockPage(current_state, current_hour, current_min); Uart_SendString("[SYS] Wakeup restored!\r\n"); }