Files
stc32g128k/App/system.c

484 lines
13 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

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 "../Drivers/adc.h"
#include "../Drivers/pcf8563.h"
#include "intrins.h"
// 引用 main.c 中的全局唤醒与运行状态变量
extern volatile u8 p0_wakeup_flag;
extern volatile u8 p2_wakeup_flag;
extern volatile u8 p3_wakeup_flag;
extern volatile u8 rf_wakeup_flag;
/**
* @brief 系统 GPIO 端口模式初始化
* @details 配置各引脚的推挽输出、高阻输入及准双向模式,建立系统初始电气环境。
*/
void GPIO_Init(void)
{
// 使能访问扩展特殊功能寄存器 XSFR
EAXFR = 1;
// 1. 配置 PWR_HOLD (P0.0) 为推挽输出模式 (P0M1 bit0=0, P0M0 bit0=1)
P0M1 &= ~(1 << 0);
P0M0 |= (1 << 0);
PWR_HOLD = 1; // 上电第一时间拉高,锁存电源保持供电
// 2. 配置 KEY_UP (P0.1), KEY_CONFIRM (P0.2) 为高阻输入/带上拉准双向
P0M1 &= ~((1 << 1) | (1 << 2));
P0M0 &= ~((1 << 1) | (1 << 2));
KEY_UP = 1;
KEY_CONFIRM = 1;
// 3. 初始化 STC32G 硬件 ADC 模块 (其中自动配置 P0.3 和 P1.3 为高阻输入模式)
ADC_Init();
// 4. 配置 PCF8563 I2C 管脚 P3.2 (SCL) 与 P3.3 (SDA) 为准双向口模式,并开上拉
P3M1 &= ~((1 << 2) | (1 << 3));
P3M0 &= ~((1 << 2) | (1 << 3));
P3PU |= (1 << 2) | (1 << 3); // 开启 P3.2 与 P3.3 的内部 4.7K 上拉电阻
RTC_SCL = 1;
RTC_SDA = 1;
// 5. 配置推挽输出引脚:
// MOTOR (P2.5), RGB_DIN (P2.3) -> P2M1 bit5,3=0; P2M0 bit5,3=1
P2M1 &= ~((1 << 5) | (1 << 3));
P2M0 |= ((1 << 5) | (1 << 3));
MOTOR = 0;
RGB_DIN = 0; // P2.3 初始拉低,防止上电期间 WS2812B 误触发
// SGM_CTRL (P1.7), LCD 控制管脚 (P1.0, P1.1, P1.4, P1.5, P1.6) 置为推挽输出
P1M1 &= ~((1 << 7) | (1 << 0) | (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6));
P1M0 |= ((1 << 7) | (1 << 0) | (1 << 1) | (1 << 4) | (1 << 5) | (1 << 6));
// SHUT (P3.5) 射频/外设开启使能脚置为推挽输出
P3M1 &= ~(1 << 5);
P3M0 |= (1 << 5);
SHUT = 0; // 低电平开启工作
// 7. 配置 CHRG_DET (P1.2) 为高阻输入模式,适配 DET 模拟 ADC 通道 2
// 由于是精密模拟电平检测,切勿开启引脚内部上拉电阻
P1M1 |= (1 << 2);
P1M0 &= ~(1 << 2);
// P1PU 在扩展 XSFR 空间中没有被定义的话可以用 P1 端口寄存器或者是关闭上拉,
// STC32G 中端口上拉寄存器是 P1PU (FE11H),因为是 XSFR 我们在 config.h 里有 extern 或直接操作
// 扩展 XSFR P1PU 地址为 0xFE11
(*(unsigned char volatile xdata *)0xFE11) &= ~(1 << 2);
}
/**
* @brief 初始化 UART1 串口通信
* @details 设定波特率 115200 (使用 Timer2 作为发生器24.0MHz 晶振)
*/
void Uart1_Init(void)
{
SCON = 0x50; // 8 位数据, 可变波特率
AUXR |= 0x01; // 串口 1 选择 Timer2 做波特率发生器
AUXR |= 0x04; // Timer2 时钟为 1T 模式
T2L = 0xCC; // 24MHz / 115200 = 208 (0xD0), 对应 reload 值 65536 - 52 = 0xFFCC
T2H = 0xFF;
AUXR |= 0x10; // 启动 Timer2 运行
}
/**
* @brief 串口 1 发送单字节数据
* @param dat 待发送的字节
*/
void Uart_SendByte(u8 dat)
{
SBUF = dat;
while (!TI);
TI = 0;
}
/**
* @brief 串口 1 发送字符串
* @param s 字符串指针
*/
void Uart_SendString(char *s)
{
while (*s) {
Uart_SendByte(*s++);
}
}
/**
* @brief 串口 1 非阻塞接收字符
* @return char 接收到的字符,若无接收数据则返回 '\0'
*/
char Uart_RxChar(void)
{
if (RI) {
RI = 0;
return SBUF;
}
return '\0';
}
/**
* @brief 阻塞毫秒延时
* @param ms 毫秒数
*/
void Delay_ms(u16 ms)
{
u16 i, j;
for (i = 0; i < ms; i++) {
for (j = 0; j < 2000; j++) {
_nop_();
}
}
}
/**
* @brief 阻塞微秒延时
* @param us 微秒数
*/
void Delay_us(u16 us)
{
u16 i;
for (i = 0; i < us; i++) {
_nop_(); _nop_(); _nop_(); _nop_();
}
}
/**
* @brief 系统开机上电硬件自检函数
* @details 检测 PWR_HOLD 锁存状态、ADC 采样基准与串口通讯,输出完整自检 report
*/
void Self_Test(void)
{
u16 key_down_adc;
u16 vbat_mv;
u16 batt_adc_raw;
u8 percent;
Uart_SendString("\r\n========================================\r\n");
Uart_SendString("[SELF-TEST] System Power-On Self Test...\r\n");
// 执行 I2C 总线器件扫描,查找 RTC 是否物理响应
PCF8563_ScanDiagnostic();
// 0. I2C 硬件脚电平诊断测试 (验证引脚可控性与短路)
Uart_SendString("[I2C-DIAG] Pin Level Test Starting...\r\n");
// 写 1 测试 (准双向+上拉)
P3M1 &= ~((1 << 2) | (1 << 3));
P3M0 &= ~((1 << 2) | (1 << 3));
P3PU |= (1 << 2) | (1 << 3);
RTC_SCL = 1;
RTC_SDA = 1;
Delay_ms(10);
Uart_SendString("[I2C-DIAG] Write SCL=1, SDA=1 -> Real Level: SCL=");
Uart_SendByte((u8)(RTC_SCL ? '1' : '0'));
Uart_SendString(", SDA=");
Uart_SendByte((u8)(RTC_SDA ? '1' : '0'));
Uart_SendString("\r\n");
// 写 0 测试
RTC_SCL = 0;
RTC_SDA = 0;
Delay_ms(10);
Uart_SendString("[I2C-DIAG] Write SCL=0, SDA=0 -> Real Level: SCL=");
Uart_SendByte((u8)(RTC_SCL ? '1' : '0'));
Uart_SendString(", SDA=");
Uart_SendByte((u8)(RTC_SDA ? '1' : '0'));
Uart_SendString("\r\n");
// 0.2 执行 RTC 底层单独试读测试
PCF8563_TryReadDiagnostic();
// 1. 验证 PWR_HOLD 锁存状态
if (PWR_HOLD == 1) {
Uart_SendString("[SELF-TEST] PWR_HOLD Pin State: HIGH [OK]\r\n");
} else {
Uart_SendString("[SELF-TEST] PWR_HOLD Pin State: LOW [FAIL]\r\n");
}
// 2. 检查 P0.3 ADC 按键基准常态电压
key_down_adc = ADC_ReadFiltered(ADC_CHANNEL_KEY_DOWN);
Uart_SendString("[SELF-TEST] KEY_DOWN (P0.3) ADC Raw Value: ");
Uart_SendByte((u8)('0' + (key_down_adc / 1000) % 10));
Uart_SendByte((u8)('0' + (key_down_adc / 100) % 10));
Uart_SendByte((u8)('0' + (key_down_adc / 10) % 10));
Uart_SendByte((u8)('0' + (key_down_adc % 10)));
Uart_SendString(" [OK]\r\n");
// 3. 检查 P1.3 电池电压检测与百分比
vbat_mv = ADC_GetBatteryVoltage_mV();
percent = ADC_GetBatteryPercent();
batt_adc_raw = ADC_ReadFiltered(ADC_CHANNEL_BATTERY);
Uart_SendString("[SELF-TEST] Battery Raw ADC: ");
Uart_SendByte((u8)('0' + (batt_adc_raw / 1000) % 10));
Uart_SendByte((u8)('0' + (batt_adc_raw / 100) % 10));
Uart_SendByte((u8)('0' + (batt_adc_raw / 10) % 10));
Uart_SendByte((u8)('0' + (batt_adc_raw % 10)));
Uart_SendString(" -> Voltage: ");
Uart_SendByte((u8)('0' + (vbat_mv / 1000) % 10));
Uart_SendByte('.');
Uart_SendByte((u8)('0' + (vbat_mv / 100) % 10));
Uart_SendByte((u8)('0' + (vbat_mv / 10) % 10));
Uart_SendString(" V, Capacity: ");
Uart_SendByte((u8)('0' + (percent / 100) % 10));
Uart_SendByte((u8)('0' + (percent / 10) % 10));
Uart_SendByte((u8)('0' + (percent % 10)));
Uart_SendString("% [OK]\r\n");
// 4. 检查充电状态
{
u16 det_adc = ADC_ReadFiltered(ADC_CHANNEL_CHRG_DET);
u16 det_mv = (det_adc * 3300UL) / 4095UL;
Uart_SendString("[SELF-TEST] CHRG_DET (P1.2) ADC: ");
Uart_SendByte((u8)('0' + (det_adc / 1000) % 10));
Uart_SendByte((u8)('0' + (det_adc / 100) % 10));
Uart_SendByte((u8)('0' + (det_adc / 10) % 10));
Uart_SendByte((u8)('0' + (det_adc % 10)));
Uart_SendString(" -> Voltage: ");
Uart_SendByte((u8)('0' + (det_mv / 1000) % 10));
Uart_SendByte('.');
Uart_SendByte((u8)('0' + (det_mv / 100) % 10));
Uart_SendByte((u8)('0' + (det_mv / 10) % 10));
Uart_SendString(" V -> ");
if (det_mv >= 800 && det_mv <= 1800) {
Uart_SendString("CHARGING [OK]\r\n");
} else if (det_mv >= 2200 && det_mv <= 2800) {
Uart_SendString("FULLY CHARGED [OK]\r\n");
} else {
Uart_SendString("NOT CHARGING [OK]\r\n");
}
}
Uart_SendString("[SELF-TEST] Self Test Completed Successfully!\r\n");
Uart_SendString("========================================\r\n\r\n");
}
/**
* @brief 执行关机断电流程
* @details 关断外设、震动提示并拉低 PWR_HOLD 引脚切断总电源
*/
void Power_Off(void)
{
Uart_SendString("[SYS] Executing Power Off sequence...\r\n");
// 马达震动反馈 100ms
MOTOR = 1;
Delay_ms(100);
MOTOR = 0;
// 关断屏幕背光与 RGB 灯光
SGM_CTRL = 1; // 高电平断电,关闭背光灭屏
RGB_Send(0, 0, 0, 0, 0, 0);
// 拉低 PWR_HOLD 切断电源锁存使总电源关闭
PWR_HOLD = 0;
// 进入死循环等待电源物理切断
while (1) {
PWR_HOLD = 0;
}
}
/**
* @brief 系统切入低功耗停机休眠模式
*/
void Enter_Low_Power_Sleep(void)
{
Uart_SendString("[SYS] Entering low power sleep...\r\n");
// 1. 关闭/熄灭 RGB 灯
RGB_Send(0, 0, 0, 0, 0, 0);
// 2. 向 LCD 芯片写入 Display OFF 指令 (0x28) 关显示
WriteComm(0x28);
Delay_ms(20);
// 3. 向 LCD 芯片写入 Sleep In 指令 (0x10) 关内部电荷泵
WriteComm(0x10);
Delay_ms(20);
// 4. 关闭背光(高电平断电,关闭背光灭屏)
SGM_CTRL = 1;
// 5. 屏幕接口防漏电处理
LCD_CS = 1;
LCD_RST = 1;
LCD_DCX = 0;
LCD_SCL = 0;
LCD_SDI = 0;
// 6. 射频设置为关闭接收模式低功耗SHUT=0, RF_SetMode(0)
RF_SetMode(0);
// 使能访问扩展特殊功能寄存器
P_SW2 |= 0x80;
// 7. 配置 Port0 中断唤醒源 (KEY_UP=P0.1, KEY_CONFIRM=P0.2, KEY_DOWN=P0.3)
P0IM1 |= 0x0E;
P0IM0 &= ~0x0E; // 下降沿触发
P0INTF = 0x00; // 清除挂起的中断标志
P0INTE |= 0x0E; // 开启 P0.1, P0.2, P0.3 中断允许
P0WKUE |= 0x0E; // 使能 P0.1, P0.2, P0.3 掉电唤醒
// 8. 配置 Port2 中断唤醒源 (SOS=P2.6)
P2IM1 |= 0x40;
P2IM0 &= ~0x40; // 下降沿触发
P2INTF = 0x00; // 清除挂起标志
P2INTE |= 0x40; // 开启 P2.6 中断允许
P2WKUE |= 0x40; // 使能 P2.6 掉电唤醒
// 9. 配置 Port3 中断唤醒源 (RF_RX_DATA=P3.6)
P3IM1 |= 0x40;
P3IM0 |= 0x40; // 双边沿触发
P3INTF = 0x00; // 清除挂起标志
P3INTE |= 0x40; // 开启 P3.6 中断允许
P3WKUE |= 0x40; // 使能 P3.6 掉电唤醒
// 确保引脚上拉和输入使能,防止因浮空产生的功耗泄漏
P2PU |= 0x40;
P2IE |= 0x40;
P3PU &= ~0x40;
P3IE |= 0x40;
// 清零各个唤醒检测标志位
P0INTF = 0x00;
P2INTF = 0x00;
P3INTF = 0x00;
p0_wakeup_flag = 0;
p2_wakeup_flag = 0;
p3_wakeup_flag = 0;
rf_wakeup_flag = 0;
// 10. 关闭系统滴答定时器 Timer1 中断,防止休眠时被定时器误唤醒
ET1 = 0;
EA = 1; // 开启全局中断
// 配置 PCON 挂起单片机,切入停机 (Power Down) 模式
PCON |= 0x02; // PD = 1
_nop_(); _nop_(); _nop_(); _nop_();
// ====== 被外部中断唤醒后,在此处继续运行 ======
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. 开启全局中断并重启系统滴答定时器
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. 恢复屏幕供电与背光(低电平上电,恢复背光亮屏)
SGM_CTRL = 0;
// 6. 延时 200ms 等待液晶稳压板上电稳定后,重新对 LCD 寄存器初始化
Delay_ms(200);
LCD_Init();
// 7. 充零闲置计时器
inactivity_timer = 0;
}
/**
* @brief 安全获取 Timer0 计数器值 (防高字节滚动跳变)
*/
u16 GetTimer0_Safe(void)
{
u8 high1, high2, low;
do {
high1 = TH0;
low = TL0;
high2 = TH0;
} while (high1 != high2);
return (((u16)high1 << 8) | low);
}
/**
* @brief 串口 1 发送 4 位十六进制字符 (0x0~0xF)
*/
void Uart_SendHex4(u8 val)
{
val &= 0x0F;
if (val < 10) {
Uart_SendByte('0' + val);
} else {
Uart_SendByte('A' + (val - 10));
}
}
/**
* @brief 串口 1 发送 8 位十六进制 (0x00~0xFF)
*/
void Uart_SendHex8(u8 val)
{
Uart_SendHex4(val >> 4);
Uart_SendHex4(val);
}
/**
* @brief 串口 1 发送 16 位十六进制 (0x0000~0xFFFF)
*/
void Uart_SendHex16(u16 val)
{
Uart_SendHex8((u8)(val >> 8));
Uart_SendHex8((u8)val);
}
/**
* @brief 串口 1 发送 20 位十六进制 (用于射频 5 位 ID 打印)
*/
void Uart_SendHex20(u32 val)
{
Uart_SendHex4((u8)(val >> 16));
Uart_SendHex16((u16)val);
}
/**
* @brief 串口 1 发送 32 位十六进制
*/
void Uart_SendHex32(u32 val)
{
Uart_SendHex16((u16)(val >> 16));
Uart_SendHex16((u16)val);
}