2026-07-15 16:14:30 +08:00
|
|
|
|
#include "system.h"
|
|
|
|
|
|
#include "rgb.h"
|
|
|
|
|
|
#include "ui.h"
|
|
|
|
|
|
#include "../Drivers/lcd.h"
|
2026-07-15 17:05:22 +08:00
|
|
|
|
#include "../Drivers/rf.h"
|
|
|
|
|
|
#include "app_manager.h"
|
2026-07-15 17:26:50 +08:00
|
|
|
|
#include "database.h"
|
2026-07-15 16:14:30 +08:00
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 外部声明的中断唤醒标志 (在 main.c 中定义,用于在中断中记录是谁唤醒了系统)
|
|
|
|
|
|
extern volatile u8 p0_wakeup_flag;
|
|
|
|
|
|
extern volatile u8 p2_wakeup_flag;
|
|
|
|
|
|
extern volatile u8 rf_wakeup_flag;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @brief 初始化 STC32G 单片机的 GPIO 管脚模式及初始电平
|
|
|
|
|
|
* @details 按照电路原理图对各个端口的输入输出模式、内部上拉、外设扩展功能进行配置。
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void GPIO_Init(void)
|
|
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
IE = 0x00; // 禁用 CPU 全局中断及常规中断
|
|
|
|
|
|
IE2 = 0x00; // 禁用扩展中断
|
|
|
|
|
|
TCON = 0x00; // 清零定时器控制寄存器,关闭 Timer0/1 的常规控制
|
|
|
|
|
|
TMOD &= 0xF0; // 清零 Timer0 工作模式
|
|
|
|
|
|
TR0 = 0; // 停止 Timer0 运行
|
|
|
|
|
|
ET0 = 0; // 禁用 Timer0 中断允许
|
|
|
|
|
|
AUXR = 0x00; // 辅助寄存器清零,时钟分频恢复默认
|
|
|
|
|
|
INTCLKO = 0x00; // 禁用时钟输出与固定外部中断触发
|
|
|
|
|
|
P_SW1 = 0x00; // 重置外设映射 1 (串口1、SPI、I2C等)
|
|
|
|
|
|
P_SW2 = 0x80; // 开启 EAXFR (Extended Access Flag Register),允许访问 P0PU/P2PU/P0INTE/P2INTE 等扩展 SFR 寄存器
|
|
|
|
|
|
P_SW3 = 0x00; // 重置外设映射 3
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 配置 P0.0 (BAT_ADC) 电池电量采集管脚为高阻输入模式 (M1=1, M0=0)
|
2026-07-15 16:14:30 +08:00
|
|
|
|
P0M1 |= (1 << 0);
|
|
|
|
|
|
P0M0 &= ~(1 << 0);
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 2. 配置 P0.1 (KEY_UP), P0.2 (KEY_CONFIRM), P0.3 (KEY_DOWN) 按键引脚为准双向口模式并置 1 (M1=0, M0=0)
|
2026-07-15 16:14:30 +08:00
|
|
|
|
P0M1 &= ~((1 << 1) | (1 << 2) | (1 << 3));
|
|
|
|
|
|
P0M0 &= ~((1 << 1) | (1 << 2) | (1 << 3));
|
|
|
|
|
|
KEY_UP = 1;
|
|
|
|
|
|
KEY_CONFIRM = 1;
|
|
|
|
|
|
KEY_DOWN = 1;
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 3. 配置 P2.3 (RGB_DIN) 幻彩灯条控制线为推挽输出模式 (M1=0, M0=1)
|
2026-07-15 16:14:30 +08:00
|
|
|
|
P2M1 &= ~(1 << 3);
|
|
|
|
|
|
P2M0 |= (1 << 3);
|
2026-07-16 17:52:52 +08:00
|
|
|
|
RGB_DIN = 0; // 默认拉低,保持复位时序状态
|
2026-07-15 16:14:30 +08:00
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 4. 配置 P2.5 (MOTOR) 振动马达控制端为推挽输出模式 (M1=0, M0=1)
|
2026-07-15 16:14:30 +08:00
|
|
|
|
P2M1 &= ~(1 << 5);
|
|
|
|
|
|
P2M0 |= (1 << 5);
|
2026-07-16 17:52:52 +08:00
|
|
|
|
MOTOR = 0; // 默认不震动
|
2026-07-15 16:14:30 +08:00
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 5. 配置 P2.6 (KEY_SOS) 物理求救按键引脚为准双向口模式并置 1 (M1=0, M0=0)
|
2026-07-15 16:14:30 +08:00
|
|
|
|
P2M1 &= ~(1 << 6);
|
|
|
|
|
|
P2M0 &= ~(1 << 6);
|
|
|
|
|
|
KEY_SOS = 1;
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 6. 配置 P2.2 (SHUT) 射频芯片使能引脚为推挽输出模式 (M1=0, M0=1)
|
2026-07-15 16:14:30 +08:00
|
|
|
|
P2M1 &= ~(1 << 2);
|
|
|
|
|
|
P2M0 |= (1 << 2);
|
2026-07-16 17:52:52 +08:00
|
|
|
|
SHUT = 1; // 默认拉高使能开启,允许无线芯片运行
|
2026-07-15 16:14:30 +08:00
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 7. 配置 P0.1, P0.2, P0.3 和 P2.6 的内部上拉电阻,避免引脚因浮空抖动误唤醒系统
|
|
|
|
|
|
P0PU |= 0x0E; // 开启 P0.1/P0.2/P0.3 上拉
|
|
|
|
|
|
P2PU |= 0x40; // 开启 P2.6 上拉
|
2026-07-15 16:14:30 +08:00
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 8. 配置 P2.7 (DET) 充电检测脚为高阻输入模式 (M1=1, M0=0)
|
2026-07-15 16:14:30 +08:00
|
|
|
|
P2M1 |= (1 << 7);
|
|
|
|
|
|
P2M0 &= ~(1 << 7);
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 9. 配置 P3.0 (UART1_RXD - 输入)、P3.1 (UART1_TXD - 输出) 以及 P3.4 (SGM_CTRL - 屏幕电源控制,输出)
|
|
|
|
|
|
P3M1 &= ~((1 << 0) | (1 << 1) | (1 << 4)); // 清除 M1 相应位
|
|
|
|
|
|
P3M0 &= ~(1 << 0); // 准双向 / 输入模式
|
|
|
|
|
|
P3M0 |= ((1 << 1) | (1 << 4)); // 推挽输出模式 (TXD 和 SGM_CTRL)
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 阻塞式毫秒级延时函数 (基于 CPU 频率估算环路次数)
|
|
|
|
|
|
* @param ms 延迟毫秒数
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Delay_ms(u16 ms)
|
|
|
|
|
|
{
|
|
|
|
|
|
u16 i, j;
|
|
|
|
|
|
for (i = 0; i < ms; i++)
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 24MHz 晶振下,双重循环实现约 1ms 延迟
|
2026-07-15 16:14:30 +08:00
|
|
|
|
for (j = 12000; j > 0; j--);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 精确的 10 微秒延时函数 (利用 8051 指令循环时间)
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Delay10us(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
unsigned char data i;
|
|
|
|
|
|
_nop_();
|
2026-07-16 17:52:52 +08:00
|
|
|
|
i = 30; // 循环常数
|
2026-07-15 16:14:30 +08:00
|
|
|
|
while (--i);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 规避 16 位寄存器在读取瞬间发生进位导致的读错问题
|
|
|
|
|
|
* @details 采用双重读取比对法,读取 TH0 -> TL0 -> 再读一次 TH0,如果两次高 8 位一致,代表没有发生高低字节间的溢出进位,是安全的。
|
|
|
|
|
|
* @return u16 当前 Timer0 的 16 位计数值
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
u16 GetTimer0_Safe(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
u8 h1, l, h2;
|
|
|
|
|
|
do {
|
|
|
|
|
|
h1 = TH0;
|
|
|
|
|
|
l = TL0;
|
|
|
|
|
|
h2 = TH0;
|
2026-07-16 17:52:52 +08:00
|
|
|
|
} while (h1 != h2); // 当两次 TH0 值相同时说明读取安全,退出循环
|
2026-07-15 16:14:30 +08:00
|
|
|
|
return ((u16)h1 << 8) | l;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 基于 Timer0 的精确微秒延时函数
|
|
|
|
|
|
* @param us 延时微秒数
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Delay_us(u16 us)
|
|
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 根据系统时钟频率及 T0 运行在 12T 模式下计算时钟 Tick 计数量
|
2026-07-15 16:14:30 +08:00
|
|
|
|
u16 ticks = (u16)((u32)us * (MAIN_Fosc / 1000000UL) / 12UL);
|
2026-07-16 17:52:52 +08:00
|
|
|
|
TL0 = 0; // 清零 Timer0 低字节
|
|
|
|
|
|
TH0 = 0; // 清零 Timer0 高字节
|
|
|
|
|
|
TR0 = 1; // 启动 Timer0 计数
|
|
|
|
|
|
while (GetTimer0_Safe() < ticks); // 等待计数达到目标值
|
|
|
|
|
|
TR0 = 0; // 停止 Timer0
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 初始化串口 1 (UART1) 为 115200 波特率,主要用于调试输出
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Uart1_Init(void)
|
|
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 计算 115200 波特率在定时器 2 (1T模式) 的重装载值
|
2026-07-15 16:14:30 +08:00
|
|
|
|
u16 reload = (u16)(65536UL - (MAIN_Fosc / 4 / 115200UL));
|
2026-07-16 17:52:52 +08:00
|
|
|
|
SCON = 0x50; // 8位数据位,允许串口接收 (REN=1)
|
|
|
|
|
|
AUXR |= 0x01; // 串口 1 波特率发生器选择定时器 2
|
|
|
|
|
|
AUXR |= 0x04; // 定时器 2 工作在 1T 模式 (无分频)
|
|
|
|
|
|
T2L = (u8)reload; // 载入重装载低字节
|
|
|
|
|
|
T2H = (u8)(reload >> 8);// 载入重装载高字节
|
|
|
|
|
|
AUXR |= 0x10; // 启动定时器 2
|
|
|
|
|
|
TI = 0; // 清除串口发送标志位
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 串口 1 发送单字节数据,并在发送时屏蔽接收以防自收自发杂波干扰
|
|
|
|
|
|
* @param dat 待发送的数据字节
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Uart_SendByte(u8 dat)
|
|
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
REN = 0; // 临时关闭串口接收,避免发送杂波反灌被接收中断误认
|
|
|
|
|
|
SBUF = dat; // 送入串口数据寄存器启动发送
|
|
|
|
|
|
while (!TI);// 阻塞等待 TI (Transmit Interrupt) 变为 1,表示发送完毕
|
|
|
|
|
|
TI = 0; // 清除发送中断标志位
|
|
|
|
|
|
RI = 0; // 顺便清零接收标志,确保没有误动作
|
|
|
|
|
|
REN = 1; // 重新开启接收允许
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 串口发送字符串,直到遇到 '\0' 结束
|
|
|
|
|
|
* @param s 字符串首地址
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Uart_SendString(char *s)
|
|
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
REN = 0; // 临时关闭接收
|
2026-07-15 16:14:30 +08:00
|
|
|
|
while (*s) {
|
|
|
|
|
|
SBUF = *s++;
|
2026-07-16 17:52:52 +08:00
|
|
|
|
while (!TI); // 等待发送结束
|
2026-07-15 16:14:30 +08:00
|
|
|
|
TI = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
RI = 0;
|
2026-07-16 17:52:52 +08:00
|
|
|
|
REN = 1; // 重新开启接收
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 非阻塞接收串口字符
|
|
|
|
|
|
* @return char 接收到的字符,若无则返回 0
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
char Uart_RxChar(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (RI) {
|
2026-07-16 17:52:52 +08:00
|
|
|
|
char c = SBUF; // 读取接收数据
|
|
|
|
|
|
RI = 0; // 手动清零接收标志位
|
|
|
|
|
|
return c; // 返回字符
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 串口输出 4 位十六进制数 (半字节,0x0~0xF)
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Uart_SendHex4(u8 val)
|
|
|
|
|
|
{
|
|
|
|
|
|
val &= 0x0F;
|
|
|
|
|
|
if (val < 10)
|
|
|
|
|
|
Uart_SendByte((u8)('0' + val));
|
|
|
|
|
|
else
|
|
|
|
|
|
Uart_SendByte((u8)('A' + (val - 10)));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 串口输出 8 位十六进制数 (单字节,0x00~0xFF)
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Uart_SendHex8(u8 val)
|
|
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
Uart_SendHex4(val >> 4); // 输出高四位
|
|
|
|
|
|
Uart_SendHex4(val); // 输出低四位
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 串口输出 20 位十六进制数 (射频 24 位地址的有效 20 位表达)
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Uart_SendHex20(u32 val)
|
|
|
|
|
|
{
|
|
|
|
|
|
Uart_SendHex4((u8)(val >> 16));
|
|
|
|
|
|
Uart_SendHex8((u8)(val >> 8));
|
|
|
|
|
|
Uart_SendHex8((u8)val);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 串口输出 32 位十六进制数
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Uart_SendHex32(u32 val)
|
|
|
|
|
|
{
|
|
|
|
|
|
Uart_SendHex8((u8)(val >> 24));
|
|
|
|
|
|
Uart_SendHex8((u8)(val >> 16));
|
|
|
|
|
|
Uart_SendHex8((u8)(val >> 8));
|
|
|
|
|
|
Uart_SendHex8((u8)val);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 串口输出 16 位十六进制数
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Uart_SendHex16(u16 val)
|
|
|
|
|
|
{
|
|
|
|
|
|
Uart_SendHex8((u8)(val >> 8));
|
|
|
|
|
|
Uart_SendHex8((u8)val);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 将数值格式化为 8 位十六进制形式的 "ADDR: 0xXXXXX" 字符串格式并清零尾部
|
|
|
|
|
|
* @param val 十六进制数值
|
|
|
|
|
|
* @param buf 输出接收缓冲区
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void FormatHex(u32 val, char *buf)
|
|
|
|
|
|
{
|
|
|
|
|
|
u8 i;
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 拷贝固定前缀 "ADDR: 0x"
|
2026-07-15 16:14:30 +08:00
|
|
|
|
buf[0] = 'A'; buf[1] = 'D'; buf[2] = 'D'; buf[3] = 'R'; buf[4] = ':';
|
|
|
|
|
|
buf[5] = ' '; buf[6] = '0'; buf[7] = 'x';
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 循环提取 20 位射频地址对应的 5 个半字节字符
|
2026-07-15 16:14:30 +08:00
|
|
|
|
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);
|
|
|
|
|
|
}
|
2026-07-16 17:52:52 +08:00
|
|
|
|
buf[13] = '\0'; // 尾部安全零字符
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 系统进入低功耗深度休眠 (Power-Down) 核心逻辑流程
|
|
|
|
|
|
* @details 实施对屏幕、电源芯片、以及各个总线管脚的低功耗关断,配置相应的端口外部唤醒中断,
|
|
|
|
|
|
* 然后将 MCU 的置入 PCON 的 PD 模式,直至被外部硬件动作唤醒。
|
|
|
|
|
|
*/
|
2026-07-15 16:14:30 +08:00
|
|
|
|
void Enter_Low_Power_Sleep(void)
|
|
|
|
|
|
{
|
|
|
|
|
|
Uart_SendString("[SYS] Entering low power sleep...\r\n");
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 1. 关闭/熄灭 FCOB 灯条
|
2026-07-15 16:14:30 +08:00
|
|
|
|
RGB_Send(0, 0, 0, 0, 0, 0);
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 2. 向 AMOLED 屏幕控制芯片 RM69310 写入 Display OFF 指令 (0x28),关闭屏幕发光
|
2026-07-15 16:14:30 +08:00
|
|
|
|
WriteComm(0x28);
|
|
|
|
|
|
Delay_ms(20);
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 3. 屏幕芯片写入 Sleep In 指令 (0x10),使其内部数字逻辑单元进入停机低损耗模式
|
2026-07-15 16:14:30 +08:00
|
|
|
|
WriteComm(0x10);
|
|
|
|
|
|
Delay_ms(20);
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 4. 将 P3.4 (SGM_CTRL) 拉低,切断 SGM3833 升压/负压电源芯片的使能,屏幕完全断电
|
2026-07-15 16:14:30 +08:00
|
|
|
|
SGM_CTRL = 0;
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 5. 【屏幕接口防漏电配置】:
|
|
|
|
|
|
// 当屏幕控制电源 (SGM3833) 切断后,屏幕内部处于无电状态。
|
|
|
|
|
|
// 如果单片机引脚 (SCL, SDA 等) 依然保持高电平,电流将通过屏幕保护二极管倒灌漏电,不仅会引发数毫安漏电,还会导致屏幕芯片损坏。
|
|
|
|
|
|
// - CS (片选) 必须拉高 (1) 以释放屏幕片选并禁用其输入缓冲器;
|
|
|
|
|
|
// - RST (复位) 必须保持高电平 (1),防止屏幕芯片被持续按在复位状态产生 ~130uA 的数字偏置电流;
|
|
|
|
|
|
// - 时钟 SCL、数据 SDI、命令/数据选择 DCX 拉低 (0) 以防浮空输入。
|
|
|
|
|
|
LCD_CS = 1;
|
|
|
|
|
|
LCD_RST = 1;
|
2026-07-15 16:14:30 +08:00
|
|
|
|
LCD_DCX = 0;
|
|
|
|
|
|
LCD_SCL = 0;
|
|
|
|
|
|
LCD_SDI = 0;
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 6. 休眠模式下关闭射频发射、接通接收 (SHUT=0, RF_RX=1),用来在休眠中监听射频警报。
|
|
|
|
|
|
// 此时屏幕和升压负压芯片已被切断,系统内部不存在高频开关纹波的噪底干扰,射频接收灵敏度达到最佳状态。
|
|
|
|
|
|
RF_SetMode(0);
|
2026-07-15 16:14:30 +08:00
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 开启扩展 SFR 访问权限 (EAXFR = 1)
|
2026-07-15 16:14:30 +08:00
|
|
|
|
P_SW2 |= 0x80;
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 7. 配置 P0.1, P0.2, P0.3 (KEY_UP/CONFIRM/DOWN) 端口下降沿低电平触发唤醒源
|
2026-07-15 16:14:30 +08:00
|
|
|
|
P0IM1 |= 0x0E;
|
|
|
|
|
|
P0IM0 &= ~0x0E;
|
2026-07-16 17:52:52 +08:00
|
|
|
|
P0INTE |= 0x0E; // 开启 P0 端口的中断允许
|
2026-07-15 16:14:30 +08:00
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 8. 配置 P2.6 (KEY_SOS) 为低电平触发;P2.1 (RF_RX_DATA) 为双沿跳变触发唤醒源
|
|
|
|
|
|
// P2.6: IM1=1, IM0=0 (低电平)
|
|
|
|
|
|
// P2.1: IM1=1, IM0=1 (双沿跳变触发,保障任何射频脉冲上升或下降沿都能立刻叫醒 MCU)
|
|
|
|
|
|
P2IM1 |= 0x42;
|
2026-07-15 16:14:30 +08:00
|
|
|
|
P2IM0 &= ~0x40;
|
2026-07-16 17:52:52 +08:00
|
|
|
|
P2IM0 |= 0x02;
|
|
|
|
|
|
P2INTE |= 0x42; // 开启 P2 端口中断允许
|
|
|
|
|
|
|
|
|
|
|
|
// 9. 【掉电唤醒核心扩展配置】:
|
|
|
|
|
|
P0WKUE |= 0x0E; // 使能 P0.1/P0.2/P0.3 的掉电唤醒 (WakeUp Enable)
|
|
|
|
|
|
P2WKUE |= 0x42; // 使能 P2.6 和 P2.1 的掉电唤醒
|
|
|
|
|
|
|
|
|
|
|
|
// 【漏电控制】:P2.1 (RF_RX_DATA) 接入的是射频芯片的数据引脚。在休眠中,
|
|
|
|
|
|
// 若开启了 P2.1 的上拉电阻,当射频芯片拉低引脚时,会产生高达 ~660uA 的强倒灌漏电流。
|
|
|
|
|
|
// 因此这里特地仅开启 P2.6 (SOS) 上拉,而彻底关闭 P2.1 上拉以实现极致微安级待机。
|
|
|
|
|
|
P2PU |= 0x40;
|
|
|
|
|
|
P2IE |= 0x42; // 强制使能 P2.6/P2.1 的内部数字输入通道,确保电平跳变能被硬件捕捉
|
|
|
|
|
|
|
|
|
|
|
|
// 10. 禁用无用的外部中断 2 (因为 STC32G 硬件 INT2 绑定在 P3.6,该物理引脚没有连接射频数据端,我们改由更灵活的 Port2 端口中断处理唤醒)
|
|
|
|
|
|
EX2 = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 11. 清除 P0 和 P2 的全部中断标志,防止进入休眠瞬间直接触发唤醒
|
2026-07-15 16:14:30 +08:00
|
|
|
|
P0INTF = 0x00;
|
|
|
|
|
|
P2INTF = 0x00;
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 清零内存暂存的唤醒源事件标志
|
|
|
|
|
|
p0_wakeup_flag = 0;
|
|
|
|
|
|
p2_wakeup_flag = 0;
|
|
|
|
|
|
rf_wakeup_flag = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 12. 关闭 Timer1 (系统滴答) 中断。全局中断保持开启 (EA=1) 用作掉电唤醒条件
|
2026-07-15 16:14:30 +08:00
|
|
|
|
ET1 = 0;
|
2026-07-15 17:26:50 +08:00
|
|
|
|
EA = 1;
|
2026-07-15 16:14:30 +08:00
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 13. 向电源控制寄存器 PCON 写入 0x02,使 MCU 核心进入 Power-Down 模式,CPU 停止运转
|
|
|
|
|
|
Uart_SendString("[SYS] Entering Power-Down mode...\r\n");
|
|
|
|
|
|
PCON |= 0x02; // 设定 PD 位,硬件立刻休眠挂起
|
|
|
|
|
|
|
|
|
|
|
|
// 唤醒后必须跟 _nop_() 空操作以占位和刷新指令流水线
|
|
|
|
|
|
_nop_();
|
|
|
|
|
|
_nop_();
|
|
|
|
|
|
_nop_();
|
|
|
|
|
|
_nop_();
|
|
|
|
|
|
|
|
|
|
|
|
// 14. 当有按键按下或无线信号到来时,CPU 被唤醒进入对应的 ISR (在 main.c 中执行中断标志保存),
|
|
|
|
|
|
// 中断处理完返回后,CPU 接着从本行执行,调用 Wakeup_Restore() 唤醒恢复流程。
|
|
|
|
|
|
Wakeup_Restore();
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 系统被中断唤醒后的运行环境恢复逻辑
|
|
|
|
|
|
* @details 负责重新开启屏幕供电、配置 GPIO、重新初始化 LCD 芯片寄存器、重置时间,最终回到 Clock 主界面。
|
|
|
|
|
|
*/
|
|
|
|
|
|
void Wakeup_Restore(void)
|
2026-07-15 16:14:30 +08:00
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
EAXFR = 1; // 确保允许访问扩展寄存区
|
2026-07-15 16:14:30 +08:00
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 1. 立即关断所有的唤醒源和端口中断,防止应用运行期间反复因电平抖动误触发中断服务程序
|
2026-07-15 16:14:30 +08:00
|
|
|
|
P0INTE = 0x00;
|
|
|
|
|
|
P2INTE = 0x00;
|
2026-07-16 17:52:52 +08:00
|
|
|
|
P0WKUE = 0x00; // 清除掉电唤醒源使能
|
|
|
|
|
|
P2WKUE = 0x00;
|
|
|
|
|
|
P0INTF = 0x00; // 清空标志位
|
2026-07-15 16:14:30 +08:00
|
|
|
|
P2INTF = 0x00;
|
2026-07-16 17:52:52 +08:00
|
|
|
|
EX2 = 0; // 禁用外部 INT2 中断
|
2026-07-15 16:14:30 +08:00
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 2. 清零掉电专用定时器 (防止定时唤醒)
|
2026-07-15 16:14:30 +08:00
|
|
|
|
WKTCH = 0x00;
|
|
|
|
|
|
WKTCL = 0x00;
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 3. 恢复 Timer1 定时器中断允许,重新开启系统基准时基轮询
|
2026-07-15 17:26:50 +08:00
|
|
|
|
EA = 1;
|
2026-07-16 17:52:52 +08:00
|
|
|
|
ET1 = 1;
|
2026-07-15 17:05:22 +08:00
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
// 4. 重置射频工作模式:进入模式 1 (接收模式) 并开启天线,恢复监听外部射频
|
|
|
|
|
|
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 ((p2_wakeup_flag & 0x02) || rf_wakeup_flag)
|
2026-07-15 17:26:50 +08:00
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
rf_wakeup_flag = 1; // 确认标记为射频解调唤醒
|
|
|
|
|
|
Uart_SendString("[WR] Woken by RF (P2.1)!\r\n");
|
2026-07-15 17:05:22 +08:00
|
|
|
|
}
|
2026-07-15 17:26:50 +08:00
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
Uart_SendString("[WR] Woken by TIMER/Other\r\n");
|
2026-07-15 17:26:50 +08:00
|
|
|
|
}
|
2026-07-16 17:52:52 +08:00
|
|
|
|
|
|
|
|
|
|
// 5. 【屏幕供电软启动】:
|
|
|
|
|
|
// 重新拉高 P3.4 (SGM_CTRL),并为负压电源芯片发送 27 个脉冲将输出稳压开启。
|
|
|
|
|
|
SGM_CTRL = 1;
|
|
|
|
|
|
SGM_SendPulse(27);
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 阻塞延时 200ms。
|
|
|
|
|
|
// 这期间用于避开电源电磁浪涌冲击,等待屏幕正负电压电轨彻底稳定,防止屏幕在初始化时画面出现撕裂或逻辑卡死。
|
|
|
|
|
|
Delay_ms(200);
|
|
|
|
|
|
|
|
|
|
|
|
// 7. 重新调用 LCD_Init() 初始化序列,写入控制偏置、伽马等命令,唤醒 Truly OLED 屏幕控制 IC RM69310
|
|
|
|
|
|
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");
|
2026-07-15 17:26:50 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-16 17:52:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @brief 掉电休眠用外部中断 2 中断服务子程序
|
|
|
|
|
|
* @details 当配置为 INT2 (P3.6) 跳变触发唤醒时,本函数将捕获唤醒标志,随后迅速关闭中断,
|
|
|
|
|
|
* 防止运行时产生的噪底高频跳变引发中断堆栈溢出。
|
|
|
|
|
|
*/
|
2026-07-15 17:26:50 +08:00
|
|
|
|
void INT2_Isr(void) interrupt 10
|
|
|
|
|
|
{
|
2026-07-16 17:52:52 +08:00
|
|
|
|
rf_wakeup_flag = 1; // 中断唤醒标志置位,标明由无线射频触发唤醒
|
|
|
|
|
|
EX2 = 0; // 保护机制:进入 ISR 后立即切断中断通道
|
2026-07-15 16:14:30 +08:00
|
|
|
|
}
|