1338 lines
42 KiB
C
1338 lines
42 KiB
C
#include "stc32g.h"
|
||
#include "intrins.h"
|
||
#include "config.h"
|
||
#include "event.h"
|
||
#include "appmgr.h"
|
||
#include "../Drivers/lcd.h"
|
||
#include "../Drivers/rf.h"
|
||
|
||
/* =========================================================================
|
||
* 硬件引脚与全局变量定义
|
||
* ========================================================================= */
|
||
|
||
// 软件虚拟 LED 状态变量(0表示亮,1表示灭,兼容原有代码逻辑)
|
||
bit LED_R = 1;
|
||
bit LED_G = 1;
|
||
bit LED_B = 1;
|
||
|
||
bit last_hw_r = 1;
|
||
bit last_hw_g = 1;
|
||
bit last_hw_b = 1;
|
||
|
||
bit rgb_test_mode = 0;
|
||
|
||
// 实际 WS2812 物理控制引脚(接 P0.0)
|
||
sbit WS2812_DI = P2^3;
|
||
|
||
// 振动马达控制引脚(输出1开启振动,输出0停止)
|
||
sbit MOTOR = P2^5;
|
||
|
||
// 板载独立按键(按下时为低电平,利用内部上拉电阻)
|
||
sbit KEY_UP = P0^1; // SW1 按键
|
||
sbit KEY_SOS = P0^2; // SW3 按键
|
||
sbit KEY_DOWN = P0^3; // SW2 按键
|
||
|
||
// 系统运行状态变量与时钟
|
||
SystemState current_state = STATE_NORMAL; // 当前系统状态,默认是时钟待机
|
||
u8 current_hour = 10; // 系统小时时钟
|
||
u8 current_min = 35; // 系统分钟时钟
|
||
u8 current_sec = 0; // 系统秒时钟
|
||
volatile u8 clock_updated = 0; // 分钟变化刷新标志
|
||
|
||
u8 menu_select = 0; // 对码菜单选中的传感器类型索引 (0~4)
|
||
|
||
u32 captured_addr = 0; // 射频接收捕获到的临时24位地址码
|
||
u8 captured_type = 0; // 射频接收捕获到的临时传感器类型
|
||
u8 alarm_sensor_slot = 0; // 正在报警的传感器槽位索引
|
||
|
||
// 传感器配对记录表 (16组槽位,保存在 xdata 中,防内存溢出)
|
||
Sensor_Slot xdata sensor_list[16];
|
||
|
||
// 系统软件计数滴答(由定时器1 ms中断累加维护)
|
||
volatile u16 ms_tick = 0; // 毫秒计数器 (0~999)
|
||
volatile u32 pair_timeout_ms = 0; // 对码等待超时计数器 (最大 30000ms)
|
||
volatile u16 alarm_timer_ms = 0; // 警报声光闪烁定时器
|
||
volatile u16 motor_timer_ms = 0; // 马达振动波形定时器
|
||
volatile bit alarm_flash_flag = 0; // 闪烁标志状态位
|
||
|
||
// 独立按键消抖、长按与组合判定的软件计数器
|
||
volatile u16 key_scan_timer = 0; // 10ms按键扫描定时计数器
|
||
volatile u16 key_up_hold = 0; // SW1按键按下维持时间 (单位: 10ms)
|
||
volatile u16 key_down_hold = 0; // SW2按键按下维持时间 (单位: 10ms)
|
||
volatile u16 key_sos_hold = 0; // SW3按键按下维持时间 (单位: 10ms)
|
||
volatile u16 comb_hold = 0; // SW1+SW2组合键按下维持时间 (单位: 10ms)
|
||
volatile KeyEvent key_event_buf = KEY_EVENT_NONE; // 按键事件缓冲区
|
||
|
||
/* =========================================================================
|
||
* 辅助映射表及前置声明
|
||
* ========================================================================= */
|
||
|
||
// 各防区类型的默认法语防区名称 (供报警显示使用,最大15字符)
|
||
char *code default_names[5] = {
|
||
"PORTE ENTREE", // 0: 门磁
|
||
"SALON PIR", // 1: 红外
|
||
"CUISINE FUMEE", // 2: 烟感
|
||
"CUISINE GAZ", // 3: 燃气
|
||
"SDB EAU" // 4: 水浸
|
||
};
|
||
|
||
// 各防区类型的默认防区类别 (0: 24小时防区-撤防亦报警, 1: 普通防区-仅布防下报警)
|
||
u8 code default_zones[5] = {1, 1, 0, 0, 0};
|
||
|
||
// 对码选择菜单显示的各条目字符串 (法语,最大15字符)
|
||
char *code menu_items[5] = {
|
||
"DETEC. PORTE", // 0
|
||
"DETEC. PIR", // 1
|
||
"DETEC. FUMEE", // 2
|
||
"DETEC. GAZ", // 3
|
||
"DETEC. EAU" // 4
|
||
};
|
||
|
||
// 本地函数前置声明
|
||
void Delay10us(void);
|
||
void GPIO_Init(void);
|
||
void Timer1_Init(void);
|
||
u16 GetTimer0_Safe(void);
|
||
void Delay_us(u16 us);
|
||
void Delay_ms(u16 ms);
|
||
void Uart1_Init(void);
|
||
void Uart_SendByte(u8 dat);
|
||
void Uart_SendString(char *s);
|
||
char Uart_RxChar(void);
|
||
void Uart_SendHex4(u8 val);
|
||
void Uart_SendHex8(u8 val);
|
||
void Uart_SendHex20(u32 val);
|
||
void Uart_SendHex32(u32 val);
|
||
void Uart_SendHex16(u16 val);
|
||
void FormatHex(u32 val, char *buf);
|
||
void Format_Sensor_Name(u8 slot, char *buf);
|
||
|
||
// IAP 闪存读写底层声明
|
||
void IAP_Disable(void);
|
||
u8 IAP_ReadByte(u16 addr);
|
||
void IAP_WriteByte(u16 addr, u8 dat);
|
||
void IAP_EraseSector(u16 addr);
|
||
void Load_Database(void);
|
||
void Save_Database(void);
|
||
void Add_Sensor(u32 addr, u8 type);
|
||
bit Check_Sensor_ID(u32 addr, u8 *out_slot_index);
|
||
|
||
// UI 绘制层接口声明
|
||
void UI_ShowClockPage(SystemState state, u8 hour, u8 min);
|
||
void UI_ShowPairMenuPage(u8 selected_index);
|
||
void UI_ShowPairWaitPage(u8 frame_index);
|
||
void UI_ShowPairConfirmPage(u32 addr);
|
||
void UI_ShowPairSuccessPage(u32 addr);
|
||
void UI_ShowPairFailPage(u8 is_timeout);
|
||
void UI_ShowAlarmPage(char *sensor_name, u8 zone);
|
||
void UI_ShowSosPage(void);
|
||
|
||
/* =========================================================================
|
||
* WS2812B 幻彩 LED 单线驱动函数 (24MHz 1T 模式下精准时序控制)
|
||
* ========================================================================= */
|
||
|
||
const u16 code SPI_LUT[16] = {
|
||
0x924, 0x926, 0x934, 0x936, 0x9A4, 0x9A6, 0x9B4, 0x9B6,
|
||
0xD24, 0xD26, 0xD34, 0xD36, 0xDA4, 0xDA6, 0xDB4, 0xDB6
|
||
};
|
||
|
||
void WS2812_EncodeByte(u8 v, u8 *buf)
|
||
{
|
||
u16 p1 = SPI_LUT[v >> 4];
|
||
u16 p2 = SPI_LUT[v & 0x0F];
|
||
buf[0] = p1 >> 4;
|
||
buf[1] = ((p1 & 0x0F) << 4) | (p2 >> 8);
|
||
buf[2] = p2;
|
||
}
|
||
|
||
void WS2812_Write24Bit(u8 g, u8 r, u8 b)
|
||
{
|
||
u8 data buf[10];
|
||
u8 b0, b1, b2, b3, b4, b5, b6, b7, b8, b9;
|
||
|
||
buf[0] = 0x00; // Prepend dummy 0x00 to absorb SPI hardware startup delay/glitch
|
||
WS2812_EncodeByte(g, &buf[1]);
|
||
WS2812_EncodeByte(r, &buf[4]);
|
||
WS2812_EncodeByte(b, &buf[7]);
|
||
|
||
b0 = buf[0]; b1 = buf[1]; b2 = buf[2]; b3 = buf[3];
|
||
b4 = buf[4]; b5 = buf[5]; b6 = buf[6]; b7 = buf[7];
|
||
b8 = buf[8]; b9 = buf[9];
|
||
|
||
EA = 0;
|
||
|
||
// Ensure P2.3 (MOSI) and P2.5 (SCLK) are configured as push-pull output
|
||
P2M1 &= ~((1 << 3) | (1 << 5));
|
||
P2M0 |= ((1 << 3) | (1 << 5));
|
||
|
||
SPSTAT = 0xC0; // Clear flags first
|
||
SPDAT = b0; // Write data first!
|
||
SPCTL = 0xD0; // Enable SPI after writing data. Should start shifting b0 immediately!
|
||
|
||
// Unrolled 10-byte transmission with minimal gap (~300ns)
|
||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b1;
|
||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b2;
|
||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b3;
|
||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b4;
|
||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b5;
|
||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b6;
|
||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b7;
|
||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b8;
|
||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b9;
|
||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0;
|
||
|
||
SPCTL = 0x90; // Disable SPI
|
||
WS2812_DI = 0;
|
||
MOTOR = 0; // Ensure motor is off
|
||
|
||
EA = 1;
|
||
}
|
||
|
||
void WS2812_Reset(void)
|
||
{
|
||
SPCTL = 0x90;
|
||
WS2812_DI = 0;
|
||
{
|
||
u8 i;
|
||
for (i = 0; i < 10; i++)
|
||
{
|
||
Delay10us();
|
||
}
|
||
}
|
||
}
|
||
|
||
void Delay_ms(u16 ms)
|
||
{
|
||
u16 i, j;
|
||
for(i = 0; i < ms; i++)
|
||
for(j = 12000; j > 0; j--);
|
||
}
|
||
|
||
void Delay10us(void)
|
||
{
|
||
unsigned char data i;
|
||
_nop_();
|
||
i = 30; // 满足 24M 下时钟宽度
|
||
while(--i);
|
||
}
|
||
|
||
// 保证 16 位定时器 TH/TL 在读取时的原子性,防止在溢出瞬间读取出错
|
||
u16 GetTimer0_Safe(void)
|
||
{
|
||
u8 h1, l, h2;
|
||
do {
|
||
h1 = TH0;
|
||
l = TL0;
|
||
h2 = TH0;
|
||
} while (h1 != h2);
|
||
return ((u16)h1 << 8) | l;
|
||
}
|
||
|
||
void Delay_us(u16 us)
|
||
{
|
||
u16 ticks = (u16)((u32)us * (MAIN_Fosc / 1000000UL) / 12UL);
|
||
TL0 = 0;
|
||
TH0 = 0;
|
||
TR0 = 1; // 启动 T0 计数器
|
||
while (GetTimer0_Safe() < ticks);
|
||
TR0 = 0; // 停止计数
|
||
}
|
||
|
||
/* =========================================================================
|
||
* STC32G 闪存 IAP (EEPROM) 读写逻辑
|
||
* ========================================================================= */
|
||
|
||
#define IAP_CMD_READ 1
|
||
#define IAP_CMD_WRITE 2
|
||
#define IAP_CMD_ERASE 3
|
||
|
||
// 关闭 IAP 功能,恢复 SFR 安全状态
|
||
void IAP_Disable(void)
|
||
{
|
||
IAP_CONTR = 0;
|
||
IAP_CMD = 0;
|
||
IAP_TRIG = 0;
|
||
IAP_ADDRH = 0xff;
|
||
IAP_ADDRL = 0xff;
|
||
}
|
||
|
||
// 从指定 IAP 地址读取一个字节
|
||
u8 IAP_ReadByte(u16 addr)
|
||
{
|
||
IAP_CONTR = 0x80; // 启用 IAP 功能
|
||
IAP_TPS = (u8)(MAIN_Fosc / 1000000UL); // 设置操作等待时钟参数
|
||
IAP_CMD = IAP_CMD_READ;
|
||
IAP_ADDRL = (u8)addr;
|
||
IAP_ADDRH = (u8)(addr >> 8);
|
||
IAP_ADDRE = 0; // 高位物理地址清零
|
||
IAP_TRIG = 0x5a; // 发送硬件触发时序
|
||
IAP_TRIG = 0xa5;
|
||
_nop_();
|
||
_nop_();
|
||
_nop_();
|
||
_nop_();
|
||
IAP_Disable();
|
||
return IAP_DATA;
|
||
}
|
||
|
||
// 往指定 IAP 地址写入一个字节
|
||
void IAP_WriteByte(u16 addr, u8 dat)
|
||
{
|
||
IAP_CONTR = 0x80;
|
||
IAP_TPS = (u8)(MAIN_Fosc / 1000000UL);
|
||
IAP_CMD = IAP_CMD_WRITE;
|
||
IAP_ADDRL = (u8)addr;
|
||
IAP_ADDRH = (u8)(addr >> 8);
|
||
IAP_ADDRE = 0;
|
||
IAP_DATA = dat;
|
||
IAP_TRIG = 0x5a;
|
||
IAP_TRIG = 0xa5;
|
||
_nop_();
|
||
_nop_();
|
||
_nop_();
|
||
_nop_();
|
||
IAP_Disable();
|
||
}
|
||
|
||
// 擦除指定地址所在的 512 字节 IAP 扇区
|
||
void IAP_EraseSector(u16 addr)
|
||
{
|
||
IAP_CONTR = 0x80;
|
||
IAP_TPS = (u8)(MAIN_Fosc / 1000000UL);
|
||
IAP_CMD = IAP_CMD_ERASE;
|
||
IAP_ADDRL = (u8)addr;
|
||
IAP_ADDRH = (u8)(addr >> 8);
|
||
IAP_ADDRE = 0;
|
||
IAP_TRIG = 0x5a;
|
||
IAP_TRIG = 0xa5;
|
||
_nop_();
|
||
_nop_();
|
||
_nop_();
|
||
_nop_();
|
||
IAP_Disable();
|
||
}
|
||
|
||
// 从 IAP 的 Sector 0 载入配对列表数据
|
||
void Load_Database(void)
|
||
{
|
||
u16 addr = 0;
|
||
u8 *ptr = (u8 *)&sensor_list;
|
||
u16 size = sizeof(sensor_list);
|
||
u16 i;
|
||
|
||
for (i = 0; i < size; i++)
|
||
{
|
||
ptr[i] = IAP_ReadByte(addr++);
|
||
}
|
||
}
|
||
|
||
// 将配对列表保存到 IAP 的 Sector 0 中 (先擦除再逐字节写入)
|
||
void Save_Database(void)
|
||
{
|
||
u16 addr = 0;
|
||
u8 *ptr = (u8 *)&sensor_list;
|
||
u16 size = sizeof(sensor_list);
|
||
u16 i;
|
||
|
||
IAP_EraseSector(0); // 擦除物理 Sector 0
|
||
|
||
for (i = 0; i < size; i++)
|
||
{
|
||
IAP_WriteByte(addr++, ptr[i]);
|
||
}
|
||
}
|
||
|
||
// 添加或覆盖传感器对码数据并自动存入 EEPROM
|
||
void Add_Sensor(u32 addr, u8 type)
|
||
{
|
||
u8 i;
|
||
u8 slot_to_use = 15; // 若槽位已满,默认覆盖最后一个位置
|
||
u8 suffix = 1;
|
||
u32 db_addr = (addr << 4) | (type & 0x0F);
|
||
|
||
// 查找是否有空余位置,或者该 ID 已经配对过
|
||
for (i = 0; i < 16; i++)
|
||
{
|
||
if ((sensor_list[i].flags_suffix & 0x80) == 0x80)
|
||
{
|
||
u32 existing_addr = ((u32)sensor_list[i].addr[0] << 16) |
|
||
((u32)sensor_list[i].addr[1] << 8) |
|
||
sensor_list[i].addr[2];
|
||
if ((existing_addr >> 4) == addr)
|
||
{
|
||
slot_to_use = i;
|
||
break;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
slot_to_use = i;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 动态计算同类型传感器的后缀序号 (如果不是覆盖已有设备)
|
||
if ((sensor_list[slot_to_use].flags_suffix & 0x80) != 0x80)
|
||
{
|
||
for (i = 0; i < 16; i++)
|
||
{
|
||
if ((sensor_list[i].flags_suffix & 0x80) == 0x80)
|
||
{
|
||
u32 slot_addr = ((u32)sensor_list[i].addr[0] << 16) |
|
||
((u32)sensor_list[i].addr[1] << 8) |
|
||
sensor_list[i].addr[2];
|
||
if ((slot_addr & 0x0F) == type)
|
||
{
|
||
suffix++;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 覆盖已有设备时,保留原有后缀序号
|
||
suffix = sensor_list[slot_to_use].flags_suffix & 0x7F;
|
||
}
|
||
|
||
// 写入防区各项核心数据
|
||
sensor_list[slot_to_use].flags_suffix = 0x80 | (suffix & 0x7F);
|
||
sensor_list[slot_to_use].addr[0] = (u8)(db_addr >> 16);
|
||
sensor_list[slot_to_use].addr[1] = (u8)(db_addr >> 8);
|
||
sensor_list[slot_to_use].addr[2] = (u8)db_addr;
|
||
|
||
Save_Database(); // 立即同步存盘
|
||
}
|
||
|
||
// 检查某个接收到的 24 位地址是否已注册,若成功返回槽位索引
|
||
bit Check_Sensor_ID(u32 addr, u8 *out_slot_index)
|
||
{
|
||
u8 i;
|
||
for (i = 0; i < 16; i++)
|
||
{
|
||
if ((sensor_list[i].flags_suffix & 0x80) == 0x80)
|
||
{
|
||
u32 slot_addr = ((u32)sensor_list[i].addr[0] << 16) |
|
||
((u32)sensor_list[i].addr[1] << 8) |
|
||
sensor_list[i].addr[2];
|
||
if ((slot_addr >> 4) == addr)
|
||
{
|
||
*out_slot_index = i;
|
||
return 1; // 匹配成功
|
||
}
|
||
}
|
||
}
|
||
return 0; // 未在对码列表中找到
|
||
}
|
||
|
||
/* =========================================================================
|
||
* 串口调试驱动 (用于捕获输出及测试打印)
|
||
* ========================================================================= */
|
||
|
||
void Uart1_Init(void)
|
||
{
|
||
u16 reload = (u16)(65536UL - (MAIN_Fosc / 4 / 115200UL));
|
||
SCON = 0x50; // 8位可变波特率
|
||
AUXR |= 0x01; // 定时器2作为波特率发生器
|
||
AUXR |= 0x04; // 定时器2为 1T 模式
|
||
T2L = (u8)reload;
|
||
T2H = (u8)(reload >> 8);
|
||
AUXR |= 0x10; // 启动定时器2
|
||
TI = 0;
|
||
}
|
||
|
||
void Uart_SendByte(u8 dat)
|
||
{
|
||
REN = 0;
|
||
SBUF = dat;
|
||
while (!TI);
|
||
TI = 0;
|
||
RI = 0;
|
||
REN = 1;
|
||
}
|
||
|
||
void Uart_SendString(char *s)
|
||
{
|
||
REN = 0;
|
||
while (*s)
|
||
{
|
||
SBUF = *s++;
|
||
while (!TI);
|
||
TI = 0;
|
||
}
|
||
RI = 0;
|
||
REN = 1;
|
||
}
|
||
|
||
char Uart_RxChar(void)
|
||
{
|
||
if (RI)
|
||
{
|
||
char c = SBUF;
|
||
RI = 0;
|
||
return c;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
void Uart_SendHex4(u8 val)
|
||
{
|
||
val &= 0x0F;
|
||
if (val < 10)
|
||
Uart_SendByte((u8)('0' + val));
|
||
else
|
||
Uart_SendByte((u8)('A' + (val - 10)));
|
||
}
|
||
|
||
void Uart_SendHex8(u8 val)
|
||
{
|
||
Uart_SendHex4(val >> 4);
|
||
Uart_SendHex4(val);
|
||
}
|
||
|
||
void Uart_SendHex20(u32 val)
|
||
{
|
||
Uart_SendHex4((u8)(val >> 16));
|
||
Uart_SendHex8((u8)(val >> 8));
|
||
Uart_SendHex8((u8)val);
|
||
}
|
||
|
||
void Uart_SendHex32(u32 val)
|
||
{
|
||
Uart_SendHex8((u8)(val >> 24));
|
||
Uart_SendHex8((u8)(val >> 16));
|
||
Uart_SendHex8((u8)(val >> 8));
|
||
Uart_SendHex8((u8)val);
|
||
}
|
||
|
||
void Uart_SendHex16(u16 val)
|
||
{
|
||
Uart_SendHex8((u8)(val >> 8));
|
||
Uart_SendHex8((u8)val);
|
||
}
|
||
|
||
void FormatHex(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';
|
||
}
|
||
|
||
void Format_Sensor_Name(u8 slot, char *buf)
|
||
{
|
||
u8 i = 0;
|
||
u32 slot_addr = ((u32)sensor_list[slot].addr[0] << 16) |
|
||
((u32)sensor_list[slot].addr[1] << 8) |
|
||
sensor_list[slot].addr[2];
|
||
u8 type = slot_addr & 0x0F;
|
||
u8 suffix = sensor_list[slot].flags_suffix & 0x7F;
|
||
char *name_prefix = (type < 5) ? default_names[type] : "SENSOR";
|
||
|
||
while (name_prefix[i] != '\0' && i < 15)
|
||
{
|
||
buf[i] = name_prefix[i];
|
||
i++;
|
||
}
|
||
buf[i++] = ' ';
|
||
buf[i++] = '0' + (suffix / 10);
|
||
buf[i++] = '0' + (suffix % 10);
|
||
buf[i] = '\0';
|
||
}
|
||
|
||
/* =========================================================================
|
||
* UI 页面渲染函数
|
||
* ========================================================================= */
|
||
|
||
void UI_ShowClockPage(SystemState state, u8 hour, u8 min)
|
||
{
|
||
char time_str[6];
|
||
LCD_Clear(COLOR_BLACK);
|
||
|
||
// 右上角电量指示 (调整坐标防止贴在一起)
|
||
LCD_DrawMonoBitmap(92, 27, 20, 10, bmp_battery_20x10, COLOR_GRAY, COLOR_BLACK);
|
||
LCD_ShowString(62, 24, "85%", COLOR_GRAY, COLOR_BLACK);
|
||
|
||
// 2x 放大绘制当前时钟 (无显存双倍行/列插值算法)
|
||
time_str[0] = '0' + (hour / 10);
|
||
time_str[1] = '0' + (hour % 10);
|
||
time_str[2] = ':';
|
||
time_str[3] = '0' + (min / 10);
|
||
time_str[4] = '0' + (min % 10);
|
||
time_str[5] = '\0';
|
||
LCD_ShowString16x32Centered(100, time_str, (state == STATE_ARMED) ? COLOR_WHITE : ((state == STATE_DISARMED) ? COLOR_WHITE : COLOR_CYAN), COLOR_BLACK);
|
||
|
||
// 静态星期和日期 (法语 JEU)
|
||
LCD_ShowStringCentered(155, "JEU 07-02", COLOR_GRAY, COLOR_BLACK);
|
||
|
||
// 屏幕底端布撤防状态标识 (Y=202 避开屏幕下圆弧)
|
||
if(state == STATE_ARMED)
|
||
{
|
||
LCD_DrawMonoBitmap(32, 202, 16, 16, bmp_lock_16x16, COLOR_RED, COLOR_BLACK);
|
||
LCD_ShowString(52, 202, "ARME", COLOR_RED, COLOR_BLACK);
|
||
}
|
||
else if(state == STATE_DISARMED)
|
||
{
|
||
LCD_DrawMonoBitmap(22, 202, 16, 16, bmp_unlock_16x16, COLOR_GREEN, COLOR_BLACK);
|
||
LCD_ShowString(42, 202, "DESARME", COLOR_GREEN, COLOR_BLACK);
|
||
}
|
||
}
|
||
|
||
void UI_ShowPairMenuPage(u8 selected_index)
|
||
{
|
||
LCD_Clear(COLOR_BLACK);
|
||
LCD_ShowStringCentered(30, "MENU APPAI.", COLOR_AMBER, COLOR_BLACK);
|
||
|
||
// 绘制上一项
|
||
if (selected_index > 0)
|
||
{
|
||
LCD_ShowStringCentered(78, menu_items[selected_index - 1], COLOR_GRAY, COLOR_BLACK);
|
||
}
|
||
|
||
// 高亮框突出绘制当前选中的菜单项
|
||
LCD_DrawRectBorder(8, 110, 104, 28, COLOR_CYAN);
|
||
LCD_ShowStringCentered(118, menu_items[selected_index], COLOR_CYAN, COLOR_BLACK);
|
||
|
||
// 绘制下一项
|
||
if (selected_index < 4)
|
||
{
|
||
LCD_ShowStringCentered(158, menu_items[selected_index + 1], COLOR_GRAY, COLOR_BLACK);
|
||
}
|
||
}
|
||
|
||
void UI_ShowPairWaitPage(u8 frame_index)
|
||
{
|
||
// 雷达扫描:仅局部擦除并重新绘制雷达波纹,消除整屏清屏带来的闪烁
|
||
if(frame_index == 0)
|
||
LCD_DrawMonoBitmap(44, 56, 32, 32, bmp_radar_32x32_f1, COLOR_CYAN, COLOR_BLACK);
|
||
else if(frame_index == 1)
|
||
LCD_DrawMonoBitmap(44, 56, 32, 32, bmp_radar_32x32_f2, COLOR_CYAN, COLOR_BLACK);
|
||
else
|
||
LCD_DrawMonoBitmap(44, 56, 32, 32, bmp_radar_32x32_f3, COLOR_CYAN, COLOR_BLACK);
|
||
}
|
||
|
||
void UI_ShowPairConfirmPage(u32 addr)
|
||
{
|
||
char hex_str[15];
|
||
LCD_Clear(COLOR_BLACK);
|
||
|
||
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_GRAY);
|
||
LCD_DrawMonoBitmap(44, 60, 32, 32, bmp_confirm_lock_32x32, COLOR_AMBER, COLOR_BLACK);
|
||
|
||
LCD_ShowStringCentered(135, "CODE TROUVE", COLOR_WHITE, COLOR_BLACK);
|
||
LCD_ShowStringCentered(160, "MAINTENIR SOS", COLOR_AMBER, COLOR_BLACK);
|
||
|
||
FormatHex(addr, hex_str);
|
||
LCD_ShowStringCentered(185, hex_str, COLOR_GRAY, COLOR_BLACK);
|
||
}
|
||
|
||
void UI_ShowPairSuccessPage(u32 addr)
|
||
{
|
||
char hex_str[15];
|
||
LCD_Clear(COLOR_BLACK);
|
||
|
||
LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_checkmark_32x32, COLOR_GREEN, COLOR_BLACK);
|
||
LCD_ShowStringCentered(135, "SUCCES APPAI.", COLOR_GREEN, COLOR_BLACK);
|
||
|
||
FormatHex(addr, hex_str);
|
||
LCD_ShowStringCentered(165, hex_str, COLOR_GRAY, COLOR_BLACK);
|
||
}
|
||
|
||
void UI_ShowPairFailPage(u8 is_timeout)
|
||
{
|
||
LCD_Clear(COLOR_BLACK);
|
||
|
||
LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_cross_32x32, COLOR_RED, COLOR_BLACK);
|
||
if (is_timeout)
|
||
{
|
||
LCD_ShowStringCentered(145, "DELAI DEPASSE", COLOR_RED, COLOR_BLACK);
|
||
LCD_ShowStringCentered(170, "AUCUN SIGNAL", COLOR_GRAY, COLOR_BLACK);
|
||
}
|
||
else
|
||
{
|
||
LCD_ShowStringCentered(145, "ECHEC APPAI.", COLOR_RED, COLOR_BLACK);
|
||
LCD_ShowStringCentered(170, "TYPE INCORRECT", COLOR_GRAY, COLOR_BLACK);
|
||
}
|
||
}
|
||
|
||
void UI_ShowAlarmPage(char *sensor_name, u8 zone)
|
||
{
|
||
char zone_str[20];
|
||
LCD_Clear(COLOR_BLACK);
|
||
|
||
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED);
|
||
LCD_DrawMonoBitmap(44, 55, 32, 32, bmp_door_32x32, COLOR_RED, COLOR_BLACK);
|
||
|
||
LCD_ShowStringCentered(120, sensor_name, COLOR_RED, COLOR_BLACK);
|
||
|
||
zone_str[0] = 'Z'; zone_str[1] = 'O'; zone_str[2] = 'N'; zone_str[3] = 'E'; zone_str[4] = ' ';
|
||
zone_str[5] = '0' + zone;
|
||
zone_str[6] = ' '; zone_str[7] = '|'; zone_str[8] = ' '; zone_str[9] = 'A'; zone_str[10] = 'L';
|
||
zone_str[11] = 'A'; zone_str[12] = 'R'; zone_str[13] = 'M'; zone_str[14] = 'E'; zone_str[15] = '\0';
|
||
LCD_ShowStringCentered(155, zone_str, COLOR_WHITE, COLOR_BLACK);
|
||
}
|
||
|
||
void UI_ShowSosPage(void)
|
||
{
|
||
LCD_Clear(COLOR_BLACK);
|
||
|
||
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED);
|
||
LCD_DrawRectBorder(8, 8, 104, 224, COLOR_WHITE);
|
||
|
||
LCD_DrawMonoBitmap(44, 55, 32, 32, bmp_siren_32x32, COLOR_RED, COLOR_BLACK);
|
||
|
||
LCD_ShowStringCentered(125, "SOS EMIS", COLOR_RED, COLOR_BLACK);
|
||
LCD_ShowStringCentered(155, "BRACELET ACTIF", COLOR_WHITE, COLOR_BLACK);
|
||
}
|
||
|
||
void UI_ShowBindingPage(void)
|
||
{
|
||
LCD_Clear(COLOR_BLACK);
|
||
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_CYAN);
|
||
LCD_DrawMonoBitmap(44, 60, 32, 32, bmp_confirm_lock_32x32, COLOR_CYAN, COLOR_BLACK);
|
||
LCD_ShowStringCentered(135, "LIAISON IPC", COLOR_WHITE, COLOR_BLACK);
|
||
LCD_ShowStringCentered(160, "ENVOI EN COURS", COLOR_CYAN, COLOR_BLACK);
|
||
}
|
||
|
||
/* =========================================================================
|
||
* 定时器 1 (1ms 中断服务,包含消抖状态机)
|
||
* ========================================================================= */
|
||
|
||
void Timer1_Init(void)
|
||
{
|
||
AUXR |= 0x40; // 1T 模式,不分频
|
||
TMOD &= 0x0F; // 16位自动重装载
|
||
{
|
||
u16 reload = (u16)(65536UL - (MAIN_Fosc / 1000UL));
|
||
TL1 = (u8)reload;
|
||
TH1 = (u8)(reload >> 8);
|
||
}
|
||
ET1 = 1; // 使使能中断
|
||
TR1 = 1; // 启动定时器
|
||
EA = 1; // 开启全局总中断
|
||
}
|
||
|
||
// 1ms 时钟嘀嗒与按键扫描服务中断
|
||
void Timer1_Isr(void) interrupt 3
|
||
{
|
||
// 1. 累加时间滴答并进位,维护软件RTC
|
||
ms_tick++;
|
||
if (ms_tick >= 1000)
|
||
{
|
||
ms_tick = 0;
|
||
current_sec++;
|
||
if (current_sec >= 60)
|
||
{
|
||
current_sec = 0;
|
||
current_min++;
|
||
if (current_min >= 60)
|
||
{
|
||
current_min = 0;
|
||
current_hour++;
|
||
if (current_hour >= 24)
|
||
{
|
||
current_hour = 0;
|
||
}
|
||
}
|
||
// 通过事件队列通知时钟应用分钟变化
|
||
{
|
||
SystemEvent evt;
|
||
evt.key_event = KEY_EVENT_NONE;
|
||
evt.priority = EVENT_PRIORITY_LOW;
|
||
evt.extra_data = ((u32)current_hour << 8) | current_min;
|
||
evt.extra_size = 2;
|
||
EventQueue_Push(evt);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 2. 雷达对码扫描倒计时 (30秒超时自动回弹待机)
|
||
if (current_state == STATE_PAIR_WAIT)
|
||
{
|
||
pair_timeout_ms++;
|
||
}
|
||
|
||
// 3. 警报警示声光发生器逻辑
|
||
if (rgb_test_mode)
|
||
{
|
||
// RGB 测试模式下不更新虚拟 LED 状态,交由主循环串口命令控制
|
||
}
|
||
else if (current_state == STATE_SOS_EMITTED)
|
||
{
|
||
// SOS 特殊告警:LED 红色极速闪烁 (5Hz),马达持续进行 1200ms 循环长振
|
||
u8 sos_flash = (ms_tick / 100) % 2; // 5Hz -> 100ms 翻转一次
|
||
LED_R = sos_flash ? 0 : 1;
|
||
LED_G = 1;
|
||
LED_B = 1;
|
||
|
||
alarm_timer_ms++;
|
||
if (alarm_timer_ms >= 1200) alarm_timer_ms = 0;
|
||
|
||
if (alarm_timer_ms < 1000) MOTOR = 1; // 长振 1000ms
|
||
else MOTOR = 0; // 停 200ms
|
||
}
|
||
else if (current_state == STATE_ALARM)
|
||
{
|
||
// 传感器告警模式:根据不同传感器类型执行专属灯光颜色与特定振动模式
|
||
u8 stype = (((u32)sensor_list[alarm_sensor_slot].addr[0] << 16) |
|
||
((u32)sensor_list[alarm_sensor_slot].addr[1] << 8) |
|
||
sensor_list[alarm_sensor_slot].addr[2]) & 0x0F;
|
||
|
||
alarm_timer_ms++;
|
||
if (alarm_timer_ms >= 5000) alarm_timer_ms = 0; // 5秒周期循环
|
||
|
||
if (stype == 0) // 门磁/窗磁:红色,1次短振 300ms
|
||
{
|
||
LED_R = 0; LED_G = 1; LED_B = 1;
|
||
if (alarm_timer_ms < 300) MOTOR = 1;
|
||
else MOTOR = 0;
|
||
}
|
||
else if (stype == 1) // 人体感应 PIR:黄色/橙色,2次短振 300ms (间隔 200ms)
|
||
{
|
||
LED_R = 0; LED_G = 0; LED_B = 1;
|
||
if (alarm_timer_ms < 300) MOTOR = 1;
|
||
else if (alarm_timer_ms >= 300 && alarm_timer_ms < 500) MOTOR = 0;
|
||
else if (alarm_timer_ms >= 500 && alarm_timer_ms < 800) MOTOR = 1;
|
||
else MOTOR = 0;
|
||
}
|
||
else if (stype == 2) // 烟雾传感器:绿色,3次短振 200ms (间隔 150ms)
|
||
{
|
||
LED_R = 1; LED_G = 0; LED_B = 1;
|
||
if (alarm_timer_ms < 200) MOTOR = 1;
|
||
else if (alarm_timer_ms >= 200 && alarm_timer_ms < 350) MOTOR = 0;
|
||
else if (alarm_timer_ms >= 350 && alarm_timer_ms < 550) MOTOR = 1;
|
||
else if (alarm_timer_ms >= 550 && alarm_timer_ms < 700) MOTOR = 0;
|
||
else if (alarm_timer_ms >= 700 && alarm_timer_ms < 900) MOTOR = 1;
|
||
else MOTOR = 0;
|
||
}
|
||
else if (stype == 3) // 气体传感器:紫色,4次短振 200ms (间隔 100ms)
|
||
{
|
||
LED_R = 0; LED_G = 1; LED_B = 0;
|
||
if (alarm_timer_ms < 200) MOTOR = 1;
|
||
else if (alarm_timer_ms >= 200 && alarm_timer_ms < 300) MOTOR = 0;
|
||
else if (alarm_timer_ms >= 300 && alarm_timer_ms < 500) MOTOR = 1;
|
||
else if (alarm_timer_ms >= 500 && alarm_timer_ms < 600) MOTOR = 0;
|
||
else if (alarm_timer_ms >= 600 && alarm_timer_ms < 800) MOTOR = 1;
|
||
else if (alarm_timer_ms >= 800 && alarm_timer_ms < 900) MOTOR = 0;
|
||
else if (alarm_timer_ms >= 900 && alarm_timer_ms < 1100) MOTOR = 1;
|
||
else MOTOR = 0;
|
||
}
|
||
else if (stype == 4) // 水浸传感器:黄色,2次长振 600ms (间隔 300ms)
|
||
{
|
||
LED_R = 0; LED_G = 0; LED_B = 1;
|
||
if (alarm_timer_ms < 600) MOTOR = 1;
|
||
else if (alarm_timer_ms >= 600 && alarm_timer_ms < 900) MOTOR = 0;
|
||
else if (alarm_timer_ms >= 900 && alarm_timer_ms < 1500) MOTOR = 1;
|
||
else MOTOR = 0;
|
||
}
|
||
else
|
||
{
|
||
// 其他未知类型默认红色闪烁
|
||
u8 flash = (ms_tick / 500) % 2;
|
||
LED_R = flash ? 0 : 1; LED_G = 1; LED_B = 1;
|
||
if (alarm_timer_ms < 300) MOTOR = 1;
|
||
else MOTOR = 0;
|
||
}
|
||
}
|
||
else if (current_state == STATE_PAIR_WAIT || current_state == STATE_PAIR_CONFIRM)
|
||
{
|
||
// 白色快闪 (3Hz): 166ms 翻转一次 (仅在搜索和确认时开启)
|
||
u16 pair_flash_cnt = ms_tick % 333;
|
||
if (pair_flash_cnt < 166)
|
||
{
|
||
LED_R = 0;
|
||
LED_G = 0;
|
||
LED_B = 0; // 白色 (R+G+B 开启)
|
||
}
|
||
else
|
||
{
|
||
LED_R = 1;
|
||
LED_G = 1;
|
||
LED_B = 1; // 熄灭
|
||
}
|
||
MOTOR = 0;
|
||
}
|
||
else
|
||
{
|
||
MOTOR = 0;
|
||
LED_R = 1;
|
||
LED_G = 1;
|
||
LED_B = 1;
|
||
alarm_flash_flag = 0;
|
||
alarm_timer_ms = 0;
|
||
motor_timer_ms = 0;
|
||
}
|
||
|
||
// 4. 按键防抖与时间累加 (每 10ms 进行一次逻辑检测)
|
||
key_scan_timer++;
|
||
if (key_scan_timer >= 10)
|
||
{
|
||
// 物理引脚低电平有效,转换成逻辑高有效状态进行判定 (用 u8 代替 bit 作为局部变量,在块开头声明变量以符合 C90 规范)
|
||
u8 raw_up = !KEY_UP;
|
||
u8 raw_down = !KEY_DOWN;
|
||
u8 raw_sos = !KEY_SOS;
|
||
key_scan_timer = 0;
|
||
|
||
// 组合键检测:UP+DOWN 同时长按 3 秒 (300 * 10ms = 3000ms)
|
||
if (raw_up && raw_down)
|
||
{
|
||
comb_hold++;
|
||
if (comb_hold == 300)
|
||
{
|
||
key_event_buf = KEY_EVENT_UP_DOWN_COMB;
|
||
}
|
||
key_up_hold = 0;
|
||
key_down_hold = 0;
|
||
}
|
||
else
|
||
{
|
||
comb_hold = 0;
|
||
|
||
// 检测上键 UP
|
||
if (raw_up)
|
||
{
|
||
key_up_hold++;
|
||
if (key_up_hold == 300) // 达到 3.0 秒触发长按事件
|
||
{
|
||
key_event_buf = KEY_EVENT_UP_LONG;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (key_up_hold >= 2 && key_up_hold < 300) // 抬手时判定为单击事件
|
||
{
|
||
key_event_buf = KEY_EVENT_UP_CLICK;
|
||
}
|
||
key_up_hold = 0;
|
||
}
|
||
|
||
// 检测下键 DOWN
|
||
if (raw_down)
|
||
{
|
||
key_down_hold++;
|
||
if (key_down_hold == 300) // 达到 3.0 秒触发长按事件
|
||
{
|
||
key_event_buf = KEY_EVENT_DOWN_LONG;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (key_down_hold >= 2 && key_down_hold < 300) // 抬手时判定为单击事件
|
||
{
|
||
key_event_buf = KEY_EVENT_DOWN_CLICK;
|
||
}
|
||
key_down_hold = 0;
|
||
}
|
||
|
||
// 检测紧急 SOS 键
|
||
if (raw_sos)
|
||
{
|
||
key_sos_hold++;
|
||
if (key_sos_hold == 300) // 长按 3.0 秒
|
||
{
|
||
// 在对码确认状态下,长按 3 秒即可触发保存
|
||
if (current_state == STATE_PAIR_CONFIRM)
|
||
{
|
||
key_event_buf = KEY_EVENT_SOS_LONG;
|
||
}
|
||
}
|
||
else if (key_sos_hold == 500) // 长按 5.0 秒
|
||
{
|
||
// 在常态待机防区下,长按 5 秒触发求救发射
|
||
if (current_state == STATE_NORMAL || current_state == STATE_ARMED || current_state == STATE_DISARMED)
|
||
{
|
||
key_event_buf = KEY_EVENT_SOS_LONG;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (key_sos_hold >= 2 && key_sos_hold < 300) // 抬手时判定为短按事件
|
||
{
|
||
key_event_buf = KEY_EVENT_SOS_CLICK;
|
||
}
|
||
key_sos_hold = 0;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 5. WS2812B 实际硬件驱动刷新 (当虚拟 LED 寄存器发生变化时)
|
||
if (LED_R != last_hw_r || LED_G != last_hw_g || LED_B != last_hw_b)
|
||
{
|
||
last_hw_r = LED_R;
|
||
last_hw_g = LED_G;
|
||
last_hw_b = LED_B;
|
||
|
||
WS2812_Write24Bit(LED_G ? 0 : 255, LED_R ? 0 : 255, LED_B ? 0 : 255);
|
||
WS2812_Reset();
|
||
}
|
||
}
|
||
|
||
/* =========================================================================
|
||
* 外部声明:应用注册函数
|
||
* ========================================================================= */
|
||
|
||
extern void Apps_RegisterAll(void);
|
||
|
||
/* =========================================================================
|
||
* 时钟更新回调:在定时器中断中触发,通知时钟应用重绘
|
||
* ========================================================================= */
|
||
|
||
void ClockApp_NotifyUpdate(void)
|
||
{
|
||
// 通过事件队列通知时钟应用分钟变化
|
||
SystemEvent evt;
|
||
evt.key_event = KEY_EVENT_NONE;
|
||
evt.priority = EVENT_PRIORITY_LOW;
|
||
evt.extra_data = ((u32)current_hour << 8) | current_min;
|
||
evt.extra_size = 2;
|
||
EventQueue_Push(evt);
|
||
}
|
||
|
||
/* =========================================================================
|
||
* 系统主循环逻辑 (事件驱动框架核心)
|
||
* ========================================================================= */
|
||
|
||
void main(void)
|
||
{
|
||
SystemEvent evt;
|
||
char cmd;
|
||
u8 last_led_r = 1;
|
||
u8 last_led_g = 1;
|
||
u8 last_led_b = 1;
|
||
u8 last_motor = 0;
|
||
|
||
// 单片机时钟与看门狗基础初始化
|
||
WTST = 0;
|
||
EAXFR = 1;
|
||
CKCON = 0;
|
||
WDT_CONTR = 0x00;
|
||
|
||
// 外设 GPIO 及射频接口引脚初始化
|
||
GPIO_Init();
|
||
RF_Init();
|
||
|
||
Delay_ms(500);
|
||
|
||
// 唤醒 AMOLED 屏幕及供电调频芯片
|
||
SGM_SendPulse(27);
|
||
Delay_ms(50);
|
||
|
||
// 初始化显示屏驱动与命令
|
||
LCD_Init();
|
||
|
||
// 配置 Uart 用于后台诊断输出
|
||
Uart1_Init();
|
||
Uart_SendString("Wristband System Initialized!\r\n");
|
||
|
||
// 读入掉电保存的对码表数据,并启动 1ms 中断调度引擎
|
||
Load_Database();
|
||
Timer1_Init();
|
||
|
||
// 初始化事件队列和应用管理器
|
||
EventQueue_Init();
|
||
Apps_RegisterAll();
|
||
AppManager_Init();
|
||
|
||
Uart_SendString("[FRAMEWORK] Event-driven framework started\r\n");
|
||
|
||
while(1)
|
||
{
|
||
// 串口调试与物理引脚/器件仿真输入
|
||
cmd = Uart_RxChar();
|
||
if (cmd != '\0')
|
||
{
|
||
SystemEvent sys_evt;
|
||
sys_evt.priority = EVENT_PRIORITY_NORMAL;
|
||
sys_evt.extra_data = 0;
|
||
sys_evt.extra_size = 0;
|
||
|
||
if (cmd == 'u')
|
||
{
|
||
sys_evt.key_event = KEY_EVENT_UP_CLICK;
|
||
EventQueue_Push(sys_evt);
|
||
Uart_SendString("[UART SIM] KEY_UP Clicked\r\n");
|
||
}
|
||
else if (cmd == 'U')
|
||
{
|
||
sys_evt.key_event = KEY_EVENT_UP_LONG;
|
||
EventQueue_Push(sys_evt);
|
||
Uart_SendString("[UART SIM] KEY_UP Long Pressed\r\n");
|
||
}
|
||
else if (cmd == 'd')
|
||
{
|
||
sys_evt.key_event = KEY_EVENT_DOWN_CLICK;
|
||
EventQueue_Push(sys_evt);
|
||
Uart_SendString("[UART SIM] KEY_DOWN Clicked\r\n");
|
||
}
|
||
else if (cmd == 'D')
|
||
{
|
||
sys_evt.key_event = KEY_EVENT_DOWN_LONG;
|
||
EventQueue_Push(sys_evt);
|
||
Uart_SendString("[UART SIM] KEY_DOWN Long Pressed\r\n");
|
||
}
|
||
else if (cmd == 's')
|
||
{
|
||
sys_evt.key_event = KEY_EVENT_SOS_CLICK;
|
||
sys_evt.priority = EVENT_PRIORITY_URGENT;
|
||
EventQueue_EmergencyDispatch(sys_evt);
|
||
Uart_SendString("[UART SIM] KEY_SOS Clicked (Emergency)\r\n");
|
||
}
|
||
else if (cmd == 'S')
|
||
{
|
||
sys_evt.key_event = KEY_EVENT_SOS_LONG;
|
||
sys_evt.priority = EVENT_PRIORITY_URGENT;
|
||
EventQueue_EmergencyDispatch(sys_evt);
|
||
Uart_SendString("[UART SIM] KEY_SOS Long Pressed (Emergency)\r\n");
|
||
}
|
||
else if (cmd == 'c' || cmd == 'C')
|
||
{
|
||
sys_evt.key_event = KEY_EVENT_UP_DOWN_COMB;
|
||
EventQueue_Push(sys_evt);
|
||
Uart_SendString("[UART SIM] KEY_UP + KEY_DOWN Combined\r\n");
|
||
}
|
||
else if (cmd == 'a' || cmd == 'A')
|
||
{
|
||
// 模拟报警事件
|
||
u32 mock_addr = 0x123456;
|
||
u8 mock_slot;
|
||
if (!Check_Sensor_ID(mock_addr, &mock_slot))
|
||
{
|
||
Add_Sensor(mock_addr, 0);
|
||
}
|
||
captured_addr = mock_addr;
|
||
alarm_sensor_slot = mock_slot;
|
||
|
||
sys_evt.key_event = KEY_EVENT_NONE;
|
||
sys_evt.priority = EVENT_PRIORITY_HIGH;
|
||
sys_evt.extra_data = mock_addr;
|
||
sys_evt.extra_size = 4;
|
||
EventQueue_Push(sys_evt);
|
||
|
||
AppManager_StartApp(APP_ID_ALARM);
|
||
Uart_SendString("[UART SIM] Mock RF Alarm: ADDR 0x123456\r\n");
|
||
}
|
||
else if (cmd == 'p' || cmd == 'P')
|
||
{
|
||
// 模拟配对信号
|
||
captured_addr = 0x789ABC;
|
||
captured_type = 0;
|
||
|
||
sys_evt.key_event = KEY_EVENT_NONE;
|
||
sys_evt.priority = EVENT_PRIORITY_NORMAL;
|
||
sys_evt.extra_data = 0x789ABC;
|
||
sys_evt.extra_size = 4;
|
||
EventQueue_Push(sys_evt);
|
||
|
||
Uart_SendString("[UART SIM] Mock RF Pair Signal: ADDR 0x789ABC\r\n");
|
||
}
|
||
else if (cmd == 't' || cmd == 'T')
|
||
{
|
||
Uart_SendString("[UART SIM] SPI 3MHz Clock Test on P2.5 (SCLK) and P2.3 (MOSI) active. Press reset to exit.\r\n");
|
||
P2M1 &= ~((1 << 3) | (1 << 5));
|
||
P2M0 |= ((1 << 3) | (1 << 5));
|
||
SPCTL = 0xD0;
|
||
while(1)
|
||
{
|
||
SPSTAT = 0xC0;
|
||
SPDAT = 0xAA;
|
||
while (!(SPSTAT & 0x80));
|
||
}
|
||
}
|
||
else if (cmd == '1')
|
||
{
|
||
AppManager_StartApp(APP_ID_CLOCK);
|
||
Uart_SendString("[UART SIM] Set Active App to: Clock\r\n");
|
||
}
|
||
else if (cmd == '2')
|
||
{
|
||
current_state = STATE_ARMED;
|
||
Uart_SendString("[UART SIM] Set State to: ARMED\r\n");
|
||
}
|
||
else if (cmd == '3')
|
||
{
|
||
current_state = STATE_DISARMED;
|
||
Uart_SendString("[UART SIM] Set State to: DISARMED\r\n");
|
||
}
|
||
else if (cmd == '4')
|
||
{
|
||
rgb_test_mode = 1;
|
||
LED_R = 0; LED_G = 1; LED_B = 1;
|
||
Uart_SendString("[UART SIM] RGB Test -> RED\r\n");
|
||
}
|
||
else if (cmd == '5')
|
||
{
|
||
rgb_test_mode = 1;
|
||
LED_R = 1; LED_G = 0; LED_B = 1;
|
||
Uart_SendString("[UART SIM] RGB Test -> GREEN\r\n");
|
||
}
|
||
else if (cmd == '6')
|
||
{
|
||
rgb_test_mode = 1;
|
||
LED_R = 1; LED_G = 1; LED_B = 0;
|
||
Uart_SendString("[UART SIM] RGB Test -> BLUE\r\n");
|
||
}
|
||
else if (cmd == '7')
|
||
{
|
||
rgb_test_mode = 1;
|
||
LED_R = 0; LED_G = 0; LED_B = 1;
|
||
Uart_SendString("[UART SIM] RGB Test -> YELLOW\r\n");
|
||
}
|
||
else if (cmd == '8')
|
||
{
|
||
rgb_test_mode = 1;
|
||
LED_R = 0; LED_G = 1; LED_B = 0;
|
||
Uart_SendString("[UART SIM] RGB Test -> PURPLE\r\n");
|
||
}
|
||
else if (cmd == '9')
|
||
{
|
||
rgb_test_mode = 1;
|
||
LED_R = 1; LED_G = 0; LED_B = 0;
|
||
Uart_SendString("[UART SIM] RGB Test -> CYAN\r\n");
|
||
}
|
||
else if (cmd == '0')
|
||
{
|
||
rgb_test_mode = 1;
|
||
LED_R = 0; LED_G = 0; LED_B = 0;
|
||
Uart_SendString("[UART SIM] RGB Test -> WHITE\r\n");
|
||
}
|
||
else if (cmd == 'f' || cmd == 'F')
|
||
{
|
||
rgb_test_mode = 0;
|
||
LED_R = 1; LED_G = 1; LED_B = 1;
|
||
Uart_SendString("[UART SIM] RGB Test -> OFF\r\n");
|
||
}
|
||
}
|
||
|
||
// 1. 处理按键事件缓冲区(来自定时器中断)
|
||
if (key_event_buf != KEY_EVENT_NONE)
|
||
{
|
||
rgb_test_mode = 0;
|
||
|
||
SystemEvent sys_evt;
|
||
sys_evt.key_event = key_event_buf;
|
||
sys_evt.extra_data = 0;
|
||
sys_evt.extra_size = 0;
|
||
|
||
// 设置事件优先级
|
||
if (key_event_buf == KEY_EVENT_SOS_CLICK ||
|
||
key_event_buf == KEY_EVENT_SOS_LONG)
|
||
{
|
||
sys_evt.priority = EVENT_PRIORITY_URGENT;
|
||
// 紧急事件直接分发,跳过队列
|
||
EventQueue_EmergencyDispatch(sys_evt);
|
||
}
|
||
else
|
||
{
|
||
sys_evt.priority = EVENT_PRIORITY_NORMAL;
|
||
EventQueue_Push(sys_evt);
|
||
}
|
||
|
||
key_event_buf = KEY_EVENT_NONE;
|
||
}
|
||
|
||
// 2. 处理事件队列中的事件
|
||
while (!EventQueue_IsEmpty())
|
||
{
|
||
evt = EventQueue_Pop();
|
||
if (evt.key_event != KEY_EVENT_NONE || evt.priority != EVENT_PRIORITY_LOW)
|
||
{
|
||
Uart_SendString("[EVENT] Processing event: ");
|
||
Uart_SendHex8((u8)evt.key_event);
|
||
Uart_SendString(" Priority: ");
|
||
Uart_SendHex8((u8)evt.priority);
|
||
Uart_SendString("\r\n");
|
||
|
||
AppManager_DispatchEvent(evt);
|
||
}
|
||
}
|
||
|
||
// 3. 运行当前活跃应用的 onRun 回调(带CPU占用控制)
|
||
AppManager_RunActiveApp();
|
||
|
||
// 4. 仿真 LED/马达状态串口输出
|
||
if (LED_R != last_led_r || LED_G != last_led_g || LED_B != last_led_b)
|
||
{
|
||
last_led_r = LED_R;
|
||
last_led_g = LED_G;
|
||
last_led_b = LED_B;
|
||
Uart_SendString("[STATE] LED status -> R:");
|
||
Uart_SendByte(last_led_r ? '0' : '1');
|
||
Uart_SendString(" G:");
|
||
Uart_SendByte(last_led_g ? '0' : '1');
|
||
Uart_SendString(" B:");
|
||
Uart_SendByte(last_led_b ? '0' : '1');
|
||
Uart_SendString("\r\n");
|
||
}
|
||
if (MOTOR != last_motor)
|
||
{
|
||
last_motor = MOTOR;
|
||
Uart_SendString("[STATE] MOTOR status -> ");
|
||
if (last_motor)
|
||
{
|
||
Uart_SendString("VIBRATING (ON)\r\n");
|
||
}
|
||
else
|
||
{
|
||
Uart_SendString("SILENT (OFF)\r\n");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 芯片基础 IO 端口属性定义
|
||
void GPIO_Init(void)
|
||
{
|
||
IE = 0x00;
|
||
IE2 = 0x00;
|
||
TCON = 0x00;
|
||
TMOD &= 0xF0; // T0, T1 为模式 0 (16位自动重装载)
|
||
TR0 = 0;
|
||
ET0 = 0;
|
||
AUXR = 0x00; // T0, T1 为 12T 模式 (默认)
|
||
INTCLKO = 0x00;
|
||
P_SW1 = 0x04; // 串口引脚映射:P3.0 RxD, P3.1 TxD; SPI映射到第二组 (P2.2-P2.5)
|
||
P_SW2 = 0x00;
|
||
P_SW3 = 0x00;
|
||
|
||
// 配置 P0.1 (KEY_UP), P0.2 (KEY_SOS), P0.3 (KEY_DOWN) 为双向输入口
|
||
P0M1 &= ~((1 << 1) | (1 << 2) | (1 << 3));
|
||
P0M0 &= ~((1 << 1) | (1 << 2) | (1 << 3));
|
||
|
||
// 配置 P2.3 (WS2812_DI) 为推挽输出,初始化默认输出 0 (Reset/Idle)
|
||
P2M1 &= ~(1 << 3);
|
||
P2M0 |= (1 << 3);
|
||
WS2812_DI = 0;
|
||
|
||
// 配置 P2.5 (MOTOR) 震动马达、P2.6 (LED_B)、P2.7 (LED_G) 为推挽输出
|
||
P2M1 &= ~((1 << 5) | (1 << 6) | (1 << 7));
|
||
P2M0 |= ((1 << 5) | (1 << 6) | (1 << 7));
|
||
|
||
// 初始化虚拟 LED 与跟踪变量为熄灭状态 (1代表灭)
|
||
LED_R = 1;
|
||
LED_G = 1;
|
||
LED_B = 1;
|
||
last_hw_r = 1;
|
||
last_hw_g = 1;
|
||
last_hw_b = 1;
|
||
MOTOR = 0; // 默认马达静止
|
||
|
||
// 初始化 WS2812 灯珠为熄灭状态 (0, 0, 0)
|
||
WS2812_Write24Bit(0, 0, 0);
|
||
WS2812_Reset();
|
||
|
||
// 配置 P3.4 (SGM_CTRL) 为推挽输出,P3.1 (TxD) 为推挽输出,P3.0 (RxD) 为双向口输入
|
||
P3M1 &= ~((1 << 0) | (1 << 1) | (1 << 4));
|
||
P3M0 &= ~(1 << 0);
|
||
P3M0 |= ((1 << 1) | (1 << 4));
|
||
}
|
||
|
||
// Touched to align with doc updates V3
|