Files
stc32g128k/App/system.c

379 lines
9.5 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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");
}