2243 lines
68 KiB
C
2243 lines
68 KiB
C
#include "stc32g.h"
|
||
#include "intrins.h"
|
||
#include "config.h"
|
||
#include "../Drivers/lcd.h"
|
||
#include "../Drivers/rf.h"
|
||
|
||
/* =========================================================================
|
||
* 硬件引脚定义
|
||
* ========================================================================= */
|
||
|
||
sbit MOTOR = P2^5; // 振动马达控制端 (推挽输出,与 SPI SCLK 物理共享引脚)
|
||
sbit KEY_UP = P0^1; // 侧边按键 ▲ (高阻输入 + 上拉)
|
||
sbit KEY_CONFIRM = P0^2; // 侧边按键 ■ (高阻输入 + 上拉)
|
||
sbit KEY_DOWN = P0^3; // 侧边按键 ▼ (高阻输入 + 上拉)
|
||
sbit KEY_SOS = P2^6; // 物理 SOS 求救按键 (高阻输入 + 上拉)
|
||
sbit RGB_DIN = P2^3; // FCOB 幻彩灯条 DIN 控制线
|
||
sbit SHUT = P2^2; // LR690L 射频芯片休眠/工作控制引脚 (低使能开启工作)
|
||
sbit SGM_CTRL = P3^4; // SGM3833 PMIC 屏幕负压供电使能脚
|
||
sbit LCD_RST = P1^0; // Truly AMOLED 屏幕复位脚 (RESET)
|
||
sbit LCD_DCX = P1^1; // Truly AMOLED 屏幕数据/指令选择脚 (D/C)
|
||
sbit LCD_SDI = P1^4; // Truly AMOLED 屏幕 SPI 数据脚 (MOSI)
|
||
sbit LCD_SCL = P1^5; // Truly AMOLED 屏幕 SPI 时钟脚 (SCLK)
|
||
sbit LCD_CS = P1^6; // Truly AMOLED 屏幕 SPI 片选脚 (CS)
|
||
|
||
/* ====== 硬件 SPI 寄存器定义 ====== */
|
||
sfr P_SW1 = 0xA2;
|
||
sfr SPCTL = 0xCE;
|
||
sfr SPSTAT = 0xCD;
|
||
sfr SPDAT = 0xCF;
|
||
|
||
/* ====== RGB 幻彩灯条驱动函数 ====== */
|
||
void SPI_Init(void)
|
||
{
|
||
P_SW1 = (P_SW1 & ~0x0C) | 0x04; // 映射 SPI 至第二组 (P2.2~P2.5)
|
||
SPCTL = 0xD1; // SSIG=1, SPEN=1, MSTR=1, SPR0=1 (3.0 MHz)
|
||
SPSTAT = 0xC0; // 清除标志
|
||
}
|
||
|
||
void Encode_Byte(u8 val, u8 *out)
|
||
{
|
||
u32 spi_val = 0;
|
||
u8 i;
|
||
for (i = 0; i < 8; i++)
|
||
{
|
||
spi_val <<= 3;
|
||
if (val & (0x80 >> i))
|
||
{
|
||
spi_val |= 6; // 1码 -> 110
|
||
}
|
||
else
|
||
{
|
||
spi_val |= 4; // 0码 -> 100
|
||
}
|
||
}
|
||
out[0] = (u8)(spi_val >> 16);
|
||
out[1] = (u8)(spi_val >> 8);
|
||
out[2] = (u8)spi_val;
|
||
}
|
||
|
||
void RGB_Send(u8 g1, u8 r1, u8 b1, u8 g2, u8 r2, u8 b2)
|
||
{
|
||
u8 idata spi_buf[18];
|
||
u8 i;
|
||
|
||
// 转码 3-bit 编码
|
||
Encode_Byte(g1, &spi_buf[0]);
|
||
Encode_Byte(r1, &spi_buf[3]);
|
||
Encode_Byte(b1, &spi_buf[6]);
|
||
Encode_Byte(g2, &spi_buf[9]);
|
||
Encode_Byte(r2, &spi_buf[12]);
|
||
Encode_Byte(b2, &spi_buf[15]);
|
||
|
||
EA = 0; // 关中断
|
||
|
||
// 临时将 P2.5 (MOTOR) 配置为高阻输入以隔离时钟信号
|
||
P2M1 |= (1 << 5);
|
||
P2M0 &= ~(1 << 5);
|
||
|
||
SPI_Init(); // 启动 SPI
|
||
|
||
for (i = 0; i < 18; i++)
|
||
{
|
||
SPDAT = spi_buf[i];
|
||
while (!(SPSTAT & 0x80)); // 等待发送完毕
|
||
SPSTAT = 0xC0;
|
||
}
|
||
|
||
SPCTL = 0; // 关闭 SPI
|
||
|
||
// 配置 P2.3 (RGB_DIN) 为推挽输出并拉低 100us 做 Reset
|
||
P2M1 &= ~(1 << 3);
|
||
P2M0 |= (1 << 3);
|
||
RGB_DIN = 0;
|
||
for (i = 0; i < 200; i++)
|
||
{
|
||
_nop_(); _nop_(); _nop_();
|
||
}
|
||
|
||
// 恢复 P2.5 (MOTOR) 为推挽输出以进行振动控制
|
||
P2M1 &= ~(1 << 5);
|
||
P2M0 |= (1 << 5);
|
||
MOTOR = 0;
|
||
|
||
EA = 1; // 开中断
|
||
}
|
||
|
||
/* =========================================================================
|
||
* 系统全局变量
|
||
* ========================================================================= */
|
||
|
||
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;
|
||
u32 captured_addr = 0;
|
||
u8 captured_type = 0;
|
||
u8 alarm_sensor_slot = 0;
|
||
|
||
Sensor_Slot xdata sensor_list[16];
|
||
|
||
volatile u16 ms_tick = 0;
|
||
volatile u16 inactivity_timer = 0; // 无操作闲置计时器
|
||
volatile u32 pair_timeout_ms = 0;
|
||
volatile u16 alarm_timer_ms = 0;
|
||
volatile u16 motor_timer_ms = 0;
|
||
volatile bit alarm_flash_flag = 0;
|
||
|
||
volatile u16 key_scan_timer = 0;
|
||
volatile u16 key_up_hold = 0;
|
||
volatile u16 key_down_hold = 0;
|
||
volatile u16 key_confirm_hold = 0;
|
||
volatile u16 key_sos_hold = 0;
|
||
volatile u16 comb_hold = 0;
|
||
|
||
/* =========================================================================
|
||
* 辅助映射表及前置声明
|
||
* ========================================================================= */
|
||
|
||
char *code sensor_names_fr[5] = {
|
||
"PORTE",
|
||
"PIR",
|
||
"FUMEE",
|
||
"GAZ",
|
||
"EAU"
|
||
};
|
||
|
||
char *code default_names[5] = {
|
||
"PORTE",
|
||
"PIR",
|
||
"FUMEE",
|
||
"GAZ",
|
||
"EAU"
|
||
};
|
||
|
||
u8 code default_zones[5] = {1, 1, 0, 0, 0};
|
||
|
||
char *code menu_items[5] = {
|
||
"DETEC. PORTE",
|
||
"DETEC. PIR",
|
||
"DETEC. FUMEE",
|
||
"DETEC. GAZ",
|
||
"DETEC. EAU"
|
||
};
|
||
|
||
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 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_With_Zone(u32 addr, u8 type, u8 zone);
|
||
bit Check_Sensor_ID(u32 addr, u8 *out_slot_index);
|
||
void UI_ShowClockPage(SystemState state, u8 hour, u8 min);
|
||
void UI_ShowPairMenuPage(u8 selected_index);
|
||
void UI_ShowPairWaitPage(u8 frame_index);
|
||
void UI_ShowPairConfirmPage(u8 type, u8 zone_index);
|
||
void UI_ShowPairSuccessPage(void);
|
||
void UI_ShowPairFailPage(u8 is_timeout);
|
||
void UI_ShowAlarmPage(u8 type, u8 zone_index);
|
||
void UI_ShowSosPage(void);
|
||
void UI_ShowBindingPage(u32 addr);
|
||
void UI_ShowMainMenu(u8 selected_index);
|
||
void UI_ShowSetTimePage(u8 hour, u8 min, u8 selection, u8 blink_on);
|
||
void Start_Time_Edit(void);
|
||
bit EV1527_Decode(u32 *out_addr, u8 *out_data);
|
||
void RF_SetMode(u8 mode);
|
||
void EV1527_Transmit(u32 addr, u8 dat);
|
||
void EventQueue_Init(void);
|
||
bit EventQueue_Push(SystemEvent evt);
|
||
bit EventQueue_InsertFront(SystemEvent evt);
|
||
SystemEvent EventQueue_Pop(void);
|
||
bit EventQueue_IsEmpty(void);
|
||
void AppManager_Init(void);
|
||
void AppManager_StartApp(AppID app_id);
|
||
void AppManager_DispatchEvent(SystemEvent evt);
|
||
void AppManager_RunActiveApp(void);
|
||
AppID AppManager_GetActiveAppID(void);
|
||
void Add_Sensor_With_Zone(u32 addr, u8 type, u8 zone);
|
||
|
||
/* =========================================================================
|
||
* 步骤1+4: 事件环形队列 (SystemEvent 环形缓冲区, 深度8)
|
||
* ========================================================================= */
|
||
|
||
#define EVENT_QUEUE_DEPTH 8
|
||
|
||
static SystemEvent idata event_queue[EVENT_QUEUE_DEPTH];
|
||
static u8 queue_head = 0;
|
||
static u8 queue_tail = 0;
|
||
static u8 queue_count = 0;
|
||
|
||
void EventQueue_Init(void)
|
||
{
|
||
queue_head = 0;
|
||
queue_tail = 0;
|
||
queue_count = 0;
|
||
}
|
||
|
||
bit EventQueue_Push(SystemEvent evt)
|
||
{
|
||
if (queue_count >= EVENT_QUEUE_DEPTH) return 0;
|
||
EA = 0;
|
||
event_queue[queue_tail] = evt;
|
||
queue_tail = (queue_tail + 1) % EVENT_QUEUE_DEPTH;
|
||
queue_count++;
|
||
EA = 1;
|
||
return 1;
|
||
}
|
||
|
||
bit EventQueue_InsertFront(SystemEvent evt)
|
||
{
|
||
if (queue_count >= EVENT_QUEUE_DEPTH) return 0;
|
||
EA = 0;
|
||
queue_head = (queue_head - 1 + EVENT_QUEUE_DEPTH) % EVENT_QUEUE_DEPTH;
|
||
event_queue[queue_head] = evt;
|
||
queue_count++;
|
||
EA = 1;
|
||
return 1;
|
||
}
|
||
|
||
SystemEvent EventQueue_Pop(void)
|
||
{
|
||
SystemEvent evt;
|
||
evt.key_event = KEY_EVENT_NONE;
|
||
evt.priority = EVENT_PRIORITY_LOW;
|
||
evt.extra_data = 0;
|
||
evt.extra_size = 0;
|
||
if (queue_count == 0) return evt;
|
||
EA = 0;
|
||
evt = event_queue[queue_head];
|
||
queue_head = (queue_head + 1) % EVENT_QUEUE_DEPTH;
|
||
queue_count--;
|
||
EA = 1;
|
||
return evt;
|
||
}
|
||
|
||
bit EventQueue_IsEmpty(void)
|
||
{
|
||
return (queue_count == 0) ? 1 : 0;
|
||
}
|
||
|
||
bit EventQueue_IsFull(void)
|
||
{
|
||
return (queue_count >= EVENT_QUEUE_DEPTH) ? 1 : 0;
|
||
}
|
||
|
||
u8 EventQueue_GetCount(void)
|
||
{
|
||
return queue_count;
|
||
}
|
||
|
||
/* =========================================================================
|
||
* 步骤2+5: App 框架与 AppManager
|
||
* ========================================================================= */
|
||
|
||
#define APP_ONRUN_MAX_TICKS 5
|
||
|
||
static WristbandApp app_registry[APP_ID_MAX];
|
||
static WristbandApp *active_app = NULL;
|
||
#define DEFAULT_APP_ID APP_ID_CLOCK
|
||
|
||
/* 回调函数前置声明 */
|
||
void ClockApp_OnStart(void);
|
||
void ClockApp_onRun(void);
|
||
void ClockApp_onClose(void);
|
||
EventResult ClockApp_onEvent(SystemEvent *evt);
|
||
void MenuApp_OnStart(void);
|
||
void MenuApp_onRun(void);
|
||
void MenuApp_onClose(void);
|
||
EventResult MenuApp_onEvent(SystemEvent *evt);
|
||
void PairApp_OnStart(void);
|
||
void PairApp_onRun(void);
|
||
void PairApp_onClose(void);
|
||
EventResult PairApp_onEvent(SystemEvent *evt);
|
||
void AlarmApp_OnStart(void);
|
||
void AlarmApp_onRun(void);
|
||
void AlarmApp_onClose(void);
|
||
EventResult AlarmApp_onEvent(SystemEvent *evt);
|
||
void SOSApp_OnStart(void);
|
||
void SOSApp_onRun(void);
|
||
void SOSApp_onClose(void);
|
||
EventResult SOSApp_onEvent(SystemEvent *evt);
|
||
|
||
/* ====== AppManager 核心接口 ====== */
|
||
|
||
void AppManager_Init(void)
|
||
{
|
||
u8 i;
|
||
/* 清零注册表 */
|
||
for (i = 0; i < APP_ID_MAX; i++) {
|
||
app_registry[i].app_id = APP_ID_MAX;
|
||
app_registry[i].app_type = APP_TYPE_FOREGROUND;
|
||
app_registry[i].app_name = "";
|
||
app_registry[i].OnStart = NULL;
|
||
app_registry[i].onRun = NULL;
|
||
app_registry[i].onClose = NULL;
|
||
app_registry[i].onEvent = NULL;
|
||
app_registry[i].is_active = 0;
|
||
app_registry[i].is_running = 0;
|
||
}
|
||
|
||
/* 注册 ClockApp */
|
||
app_registry[APP_ID_CLOCK].app_id = APP_ID_CLOCK;
|
||
app_registry[APP_ID_CLOCK].app_type = APP_TYPE_FOREGROUND;
|
||
app_registry[APP_ID_CLOCK].app_name = "Clock";
|
||
app_registry[APP_ID_CLOCK].OnStart = ClockApp_OnStart;
|
||
app_registry[APP_ID_CLOCK].onRun = ClockApp_onRun;
|
||
app_registry[APP_ID_CLOCK].onClose = ClockApp_onClose;
|
||
app_registry[APP_ID_CLOCK].onEvent = ClockApp_onEvent;
|
||
|
||
/* 注册 MenuApp */
|
||
app_registry[APP_ID_MENU].app_id = APP_ID_MENU;
|
||
app_registry[APP_ID_MENU].app_type = APP_TYPE_FOREGROUND;
|
||
app_registry[APP_ID_MENU].app_name = "Menu";
|
||
app_registry[APP_ID_MENU].OnStart = MenuApp_OnStart;
|
||
app_registry[APP_ID_MENU].onRun = MenuApp_onRun;
|
||
app_registry[APP_ID_MENU].onClose = MenuApp_onClose;
|
||
app_registry[APP_ID_MENU].onEvent = MenuApp_onEvent;
|
||
|
||
/* 注册 PairApp */
|
||
app_registry[APP_ID_PAIR].app_id = APP_ID_PAIR;
|
||
app_registry[APP_ID_PAIR].app_type = APP_TYPE_FOREGROUND;
|
||
app_registry[APP_ID_PAIR].app_name = "Pair";
|
||
app_registry[APP_ID_PAIR].OnStart = PairApp_OnStart;
|
||
app_registry[APP_ID_PAIR].onRun = PairApp_onRun;
|
||
app_registry[APP_ID_PAIR].onClose = PairApp_onClose;
|
||
app_registry[APP_ID_PAIR].onEvent = PairApp_onEvent;
|
||
|
||
/* 注册 AlarmApp */
|
||
app_registry[APP_ID_ALARM].app_id = APP_ID_ALARM;
|
||
app_registry[APP_ID_ALARM].app_type = APP_TYPE_FOREGROUND;
|
||
app_registry[APP_ID_ALARM].app_name = "Alarm";
|
||
app_registry[APP_ID_ALARM].OnStart = AlarmApp_OnStart;
|
||
app_registry[APP_ID_ALARM].onRun = AlarmApp_onRun;
|
||
app_registry[APP_ID_ALARM].onClose = AlarmApp_onClose;
|
||
app_registry[APP_ID_ALARM].onEvent = AlarmApp_onEvent;
|
||
|
||
/* 注册 SOSApp */
|
||
app_registry[APP_ID_SOS].app_id = APP_ID_SOS;
|
||
app_registry[APP_ID_SOS].app_type = APP_TYPE_FOREGROUND;
|
||
app_registry[APP_ID_SOS].app_name = "SOS";
|
||
app_registry[APP_ID_SOS].OnStart = SOSApp_OnStart;
|
||
app_registry[APP_ID_SOS].onRun = SOSApp_onRun;
|
||
app_registry[APP_ID_SOS].onClose = SOSApp_onClose;
|
||
app_registry[APP_ID_SOS].onEvent = SOSApp_onEvent;
|
||
|
||
AppManager_StartApp(DEFAULT_APP_ID);
|
||
}
|
||
|
||
void AppManager_StartApp(AppID app_id)
|
||
{
|
||
WristbandApp *target;
|
||
if (app_id >= APP_ID_MAX) return;
|
||
target = &app_registry[app_id];
|
||
if (target->OnStart == NULL) return;
|
||
if (active_app == target) return;
|
||
|
||
Uart_SendString("[APP] StartApp: ");
|
||
Uart_SendString(target->app_name);
|
||
Uart_SendString("\r\n");
|
||
|
||
/* 关闭当前活跃应用 */
|
||
if (active_app != NULL && active_app->onClose != NULL) {
|
||
active_app->is_active = 0;
|
||
active_app->onClose();
|
||
}
|
||
|
||
/* 切换并启动新应用 */
|
||
active_app = target;
|
||
if (active_app->OnStart != NULL) {
|
||
active_app->is_active = 1;
|
||
active_app->is_running = 1;
|
||
active_app->OnStart();
|
||
}
|
||
}
|
||
|
||
void AppManager_DispatchEvent(SystemEvent evt)
|
||
{
|
||
EventResult result = EVENT_IGNORED;
|
||
if (active_app != NULL && active_app->is_active && active_app->onEvent != NULL) {
|
||
result = active_app->onEvent(&evt);
|
||
if (result == EVENT_HANDLED) return;
|
||
}
|
||
}
|
||
|
||
void AppManager_RunActiveApp(void)
|
||
{
|
||
u16 start_tick;
|
||
u16 elapsed;
|
||
if (active_app == NULL || !active_app->is_active) return;
|
||
if (!active_app->is_running) return;
|
||
if (active_app->onRun == NULL) return;
|
||
start_tick = ms_tick;
|
||
active_app->onRun();
|
||
elapsed = ms_tick - start_tick;
|
||
if (elapsed > APP_ONRUN_MAX_TICKS) {
|
||
active_app->is_running = 0;
|
||
} else {
|
||
active_app->is_running = 1;
|
||
}
|
||
}
|
||
|
||
AppID AppManager_GetActiveAppID(void)
|
||
{
|
||
if (active_app == NULL) return APP_ID_MAX;
|
||
return active_app->app_id;
|
||
}
|
||
|
||
/* =========================================================================
|
||
* ClockApp 回调实现与时间编辑状态机
|
||
* ========================================================================= */
|
||
|
||
static u8 time_edit_active = 0;
|
||
static u8 time_edit_field = 1;
|
||
static u8 time_edit_blink_on = 1;
|
||
|
||
void Start_Time_Edit(void)
|
||
{
|
||
time_edit_active = 1;
|
||
time_edit_field = 1;
|
||
time_edit_blink_on = 1;
|
||
current_state = STATE_SET_TIME;
|
||
|
||
// FCOB 幻彩灯条蓝色常亮 (R=0, G=0, B=180)
|
||
RGB_Send(0, 0, 180, 0, 0, 180);
|
||
|
||
// 马达短振 50ms
|
||
MOTOR = 1;
|
||
Delay_ms(50);
|
||
MOTOR = 0;
|
||
|
||
UI_ShowSetTimePage(current_hour, current_min, time_edit_field, 1);
|
||
}
|
||
|
||
void ClockApp_OnStart(void)
|
||
{
|
||
time_edit_active = 0;
|
||
current_state = STATE_NORMAL;
|
||
RF_SetMode(1);
|
||
UI_ShowClockPage(current_state, current_hour, current_min);
|
||
|
||
// 启动时熄灭灯条
|
||
RGB_Send(0, 0, 0, 0, 0, 0);
|
||
|
||
Uart_SendString("[ClockApp] Started\r\n");
|
||
}
|
||
|
||
void ClockApp_onRun(void)
|
||
{
|
||
u32 rx_addr;
|
||
u8 rx_data;
|
||
|
||
if (time_edit_active)
|
||
{
|
||
// 500ms 周期控制闪烁
|
||
u8 blink = (ms_tick / 500) % 2;
|
||
if (blink != time_edit_blink_on)
|
||
{
|
||
time_edit_blink_on = blink;
|
||
UI_ShowSetTimePage(current_hour, current_min, time_edit_field, time_edit_blink_on);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (clock_updated) {
|
||
clock_updated = 0;
|
||
UI_ShowClockPage(current_state, current_hour, current_min);
|
||
}
|
||
if (EV1527_Decode(&rx_addr, &rx_data)) {
|
||
u8 slot;
|
||
if (Check_Sensor_ID(rx_addr, &slot)) {
|
||
u8 sensor_type = sensor_list[slot].type;
|
||
if (sensor_type < 5) {
|
||
if (sensor_list[slot].zone == 0 || current_state == STATE_ARMED) {
|
||
SystemEvent rf_evt;
|
||
rf_evt.key_event = KEY_EVENT_NONE;
|
||
rf_evt.priority = EVENT_PRIORITY_HIGH;
|
||
rf_evt.extra_data = rx_addr;
|
||
rf_evt.extra_size = 4;
|
||
EventQueue_InsertFront(rf_evt);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
void ClockApp_onClose(void)
|
||
{
|
||
time_edit_active = 0;
|
||
RGB_Send(0, 0, 0, 0, 0, 0);
|
||
Uart_SendString("[ClockApp] Closed\r\n");
|
||
}
|
||
|
||
EventResult ClockApp_onEvent(SystemEvent *evt)
|
||
{
|
||
if (time_edit_active)
|
||
{
|
||
if (evt->key_event == KEY_EVENT_UP_CLICK)
|
||
{
|
||
if (time_edit_field == 1) {
|
||
current_hour = (current_hour + 1) % 24;
|
||
} else {
|
||
current_min = (current_min + 1) % 60;
|
||
}
|
||
UI_ShowSetTimePage(current_hour, current_min, time_edit_field, 1);
|
||
return EVENT_HANDLED;
|
||
}
|
||
if (evt->key_event == KEY_EVENT_DOWN_CLICK)
|
||
{
|
||
if (time_edit_field == 1) {
|
||
current_hour = (current_hour == 0) ? 23 : (current_hour - 1);
|
||
} else {
|
||
current_min = (current_min == 0) ? 59 : (current_min - 1);
|
||
}
|
||
UI_ShowSetTimePage(current_hour, current_min, time_edit_field, 1);
|
||
return EVENT_HANDLED;
|
||
}
|
||
if (evt->key_event == KEY_EVENT_CONFIRM_LONG)
|
||
{
|
||
// 长按 Confirm 键跳转字段
|
||
if (time_edit_field == 1) {
|
||
time_edit_field = 2;
|
||
} else {
|
||
time_edit_field = 1;
|
||
}
|
||
UI_ShowSetTimePage(current_hour, current_min, time_edit_field, 1);
|
||
return EVENT_HANDLED;
|
||
}
|
||
if (evt->key_event == KEY_EVENT_CONFIRM_CLICK)
|
||
{
|
||
// 短按 Confirm 键保存退出
|
||
time_edit_active = 0;
|
||
current_state = STATE_NORMAL;
|
||
RGB_Send(0, 0, 0, 0, 0, 0); // 熄灭
|
||
|
||
// 退出短振
|
||
MOTOR = 1; Delay_ms(50); MOTOR = 0;
|
||
|
||
UI_ShowClockPage(current_state, current_hour, current_min);
|
||
return EVENT_HANDLED;
|
||
}
|
||
return EVENT_HANDLED; // 吞掉其他按键
|
||
}
|
||
|
||
if (evt->key_event == KEY_EVENT_CONFIRM_LONG) {
|
||
AppManager_StartApp(APP_ID_MENU);
|
||
return EVENT_HANDLED;
|
||
}
|
||
if (evt->key_event == KEY_EVENT_UP_LONG) {
|
||
if (current_state == STATE_ARMED) {
|
||
current_state = STATE_DISARMED;
|
||
} else {
|
||
current_state = STATE_ARMED;
|
||
}
|
||
// 切换状态短震确认
|
||
MOTOR = 1; Delay_ms(100); MOTOR = 0;
|
||
UI_ShowClockPage(current_state, current_hour, current_min);
|
||
return EVENT_HANDLED;
|
||
}
|
||
if (evt->key_event == KEY_EVENT_SOS_CLICK || evt->key_event == KEY_EVENT_SOS_LONG) {
|
||
AppManager_StartApp(APP_ID_SOS);
|
||
return EVENT_HANDLED;
|
||
}
|
||
return EVENT_IGNORED;
|
||
}
|
||
|
||
/* =========================================================================
|
||
* MenuApp 回调实现
|
||
* ========================================================================= */
|
||
|
||
static u8 menu_level = 0; // 0:主菜单, 1:传感器对码二级菜单, 2:IPC绑定中
|
||
static u16 last_bind_tx_ms = 0;
|
||
|
||
void MenuApp_OnStart(void)
|
||
{
|
||
menu_level = 0;
|
||
menu_select = 0;
|
||
current_state = STATE_PAIR_MENU;
|
||
RF_SetMode(0);
|
||
UI_ShowMainMenu(menu_select);
|
||
RGB_Send(0, 0, 0, 0, 0, 0); // 熄灭灯条
|
||
Uart_SendString("[MenuApp] Started (Main Menu)\r\n");
|
||
}
|
||
|
||
void MenuApp_onRun(void)
|
||
{
|
||
if (menu_level == 2)
|
||
{
|
||
// 绑定中:控制青色呼吸灯 (周期1000ms,双灯同步)
|
||
u16 breath = ms_tick % 1000;
|
||
u8 val = (breath < 500) ? (breath * 2 / 5) : ((1000 - breath) * 2 / 5); // 范围 0~200
|
||
RGB_Send(0, val, val, 0, val, val);
|
||
|
||
// 每 500ms 发射一次绑定信号 (出厂 Factory ID)
|
||
if (ms_tick - last_bind_tx_ms >= 500 || ms_tick < last_bind_tx_ms)
|
||
{
|
||
last_bind_tx_ms = ms_tick;
|
||
RF_SetMode(2);
|
||
EV1527_Transmit(CLONED_ADDR, 0x01);
|
||
RF_SetMode(0);
|
||
}
|
||
}
|
||
}
|
||
|
||
void MenuApp_onClose(void)
|
||
{
|
||
menu_level = 0;
|
||
RGB_Send(0, 0, 0, 0, 0, 0);
|
||
RF_SetMode(1);
|
||
Uart_SendString("[MenuApp] Closed\r\n");
|
||
}
|
||
|
||
EventResult MenuApp_onEvent(SystemEvent *evt)
|
||
{
|
||
if (menu_level == 0)
|
||
{
|
||
// 主菜单层 (0-HORLOGE, 1-APPAIRAGE, 2-LIAISON)
|
||
if (evt->key_event == KEY_EVENT_UP_CLICK) {
|
||
if (menu_select > 0) {
|
||
menu_select--;
|
||
UI_ShowMainMenu(menu_select);
|
||
}
|
||
return EVENT_HANDLED;
|
||
}
|
||
if (evt->key_event == KEY_EVENT_DOWN_CLICK) {
|
||
if (menu_select < 2) {
|
||
menu_select++;
|
||
UI_ShowMainMenu(menu_select);
|
||
}
|
||
return EVENT_HANDLED;
|
||
}
|
||
if (evt->key_event == KEY_EVENT_CONFIRM_CLICK) {
|
||
if (menu_select == 0) {
|
||
// 启动时钟 App 并立即进入编辑模式
|
||
AppManager_StartApp(APP_ID_CLOCK);
|
||
Start_Time_Edit();
|
||
} else if (menu_select == 1) {
|
||
// 进入二级传感器对码菜单
|
||
menu_level = 1;
|
||
menu_select = 0;
|
||
UI_ShowPairMenuPage(menu_select);
|
||
} else if (menu_select == 2) {
|
||
// 进入摄像机绑定
|
||
menu_level = 2;
|
||
last_bind_tx_ms = ms_tick;
|
||
current_state = STATE_IPC_BIND;
|
||
UI_ShowBindingPage(CLONED_ADDR);
|
||
}
|
||
return EVENT_HANDLED;
|
||
}
|
||
if (evt->key_event == KEY_EVENT_CONFIRM_LONG) {
|
||
AppManager_StartApp(APP_ID_CLOCK);
|
||
return EVENT_HANDLED;
|
||
}
|
||
}
|
||
else if (menu_level == 1)
|
||
{
|
||
// 传感器对码选择菜单 (0-门磁, 1-PIR, 2-烟感, 3-气感, 4-水浸)
|
||
if (evt->key_event == KEY_EVENT_UP_CLICK) {
|
||
if (menu_select > 0) {
|
||
menu_select--;
|
||
UI_ShowPairMenuPage(menu_select);
|
||
}
|
||
return EVENT_HANDLED;
|
||
}
|
||
if (evt->key_event == KEY_EVENT_DOWN_CLICK) {
|
||
if (menu_select < 4) {
|
||
menu_select++;
|
||
UI_ShowPairMenuPage(menu_select);
|
||
}
|
||
return EVENT_HANDLED;
|
||
}
|
||
if (evt->key_event == KEY_EVENT_CONFIRM_CLICK) {
|
||
// 保存当前选定的类型到 captured_type,短震 100ms 并跳转到 PairApp 对码搜索
|
||
captured_type = menu_select;
|
||
MOTOR = 1;
|
||
Delay_ms(100);
|
||
MOTOR = 0;
|
||
AppManager_StartApp(APP_ID_PAIR);
|
||
return EVENT_HANDLED;
|
||
}
|
||
if (evt->key_event == KEY_EVENT_DOWN_LONG) {
|
||
// 返回主菜单
|
||
menu_level = 0;
|
||
menu_select = 1;
|
||
UI_ShowMainMenu(menu_select);
|
||
return EVENT_HANDLED;
|
||
}
|
||
}
|
||
else if (menu_level == 2)
|
||
{
|
||
// 绑定中:长按 Confirm 键 (3秒) 或 长按 DOWN 键 (2秒) 退出返回主菜单
|
||
if (evt->key_event == KEY_EVENT_CONFIRM_LONG || evt->key_event == KEY_EVENT_DOWN_LONG) {
|
||
menu_level = 0;
|
||
menu_select = 2;
|
||
current_state = STATE_PAIR_MENU;
|
||
RGB_Send(0, 0, 0, 0, 0, 0); // 熄灭
|
||
UI_ShowMainMenu(menu_select);
|
||
return EVENT_HANDLED;
|
||
}
|
||
}
|
||
return EVENT_IGNORED;
|
||
}
|
||
|
||
/* =========================================================================
|
||
* PairApp 回调实现
|
||
* ========================================================================= */
|
||
|
||
enum {
|
||
PAIR_STATE_WAIT,
|
||
PAIR_STATE_CONFIRM,
|
||
PAIR_STATE_SUCCESS,
|
||
PAIR_STATE_FAIL
|
||
};
|
||
|
||
static u8 pair_state;
|
||
static u8 last_radar_sec;
|
||
static u8 captured_zone = 1;
|
||
static u32 success_start_ms = 0;
|
||
static u32 fail_start_ms = 0;
|
||
|
||
void PairApp_OnStart(void)
|
||
{
|
||
pair_state = PAIR_STATE_WAIT;
|
||
last_radar_sec = 99;
|
||
current_state = STATE_PAIR_WAIT;
|
||
pair_timeout_ms = 0;
|
||
captured_zone = 1;
|
||
RF_SetMode(1);
|
||
|
||
// 初始化对码搜寻状态显示
|
||
UI_ShowPairWaitPage(0);
|
||
|
||
// 快闪白色 RGB (双灯)
|
||
RGB_Send(150, 150, 150, 150, 150, 150);
|
||
|
||
Uart_SendString("[PairApp] Started (WAIT)\r\n");
|
||
}
|
||
|
||
void PairApp_onRun(void)
|
||
{
|
||
u32 rx_addr;
|
||
u8 rx_data;
|
||
u8 flash;
|
||
u8 frame;
|
||
|
||
if (pair_state == PAIR_STATE_WAIT) {
|
||
// 白色 3Hz 快闪 (周期 333ms, 亮 166ms)
|
||
flash = (ms_tick / 166) % 2;
|
||
if (flash) RGB_Send(150, 150, 150, 150, 150, 150);
|
||
else RGB_Send(0, 0, 0, 0, 0, 0);
|
||
|
||
frame = (u8)((ms_tick / 250) % 3);
|
||
if (frame != last_radar_sec) {
|
||
last_radar_sec = frame;
|
||
UI_ShowPairWaitPage(frame);
|
||
}
|
||
if (pair_timeout_ms >= 30000UL) {
|
||
pair_timeout_ms = 0;
|
||
pair_state = PAIR_STATE_FAIL;
|
||
current_state = STATE_PAIR_FAIL;
|
||
fail_start_ms = ms_tick;
|
||
UI_ShowPairFailPage(1);
|
||
|
||
// 红色常亮
|
||
RGB_Send(200, 0, 0, 200, 0, 0);
|
||
|
||
Uart_SendString("[PairApp] Timeout\r\n");
|
||
return;
|
||
}
|
||
if (EV1527_Decode(&rx_addr, &rx_data)) {
|
||
// 排除 SOS 数据码 (0x08 代表 SOS 求救,不用于常规传感器配对)
|
||
if (rx_data != 0x08)
|
||
{
|
||
captured_addr = rx_addr;
|
||
pair_state = PAIR_STATE_CONFIRM;
|
||
current_state = STATE_PAIR_CONFIRM;
|
||
RF_SetMode(0);
|
||
captured_zone = 1; // 初始为防区 1
|
||
UI_ShowPairConfirmPage(captured_type, captured_zone);
|
||
Uart_SendString("[PairApp] Sensor captured\r\n");
|
||
}
|
||
}
|
||
} else if (pair_state == PAIR_STATE_CONFIRM) {
|
||
// 橙色慢闪 (1.5Hz, 周期 666ms)
|
||
u8 flash = (ms_tick / 333) % 2;
|
||
if (flash) RGB_Send(100, 180, 0, 100, 180, 0); // 橙色
|
||
else RGB_Send(0, 0, 0, 0, 0, 0);
|
||
} else if (pair_state == PAIR_STATE_SUCCESS) {
|
||
// 2 秒后自动返回二级菜单
|
||
if (ms_tick - success_start_ms >= 2000 || ms_tick < success_start_ms)
|
||
{
|
||
AppManager_StartApp(APP_ID_MENU);
|
||
}
|
||
} else if (pair_state == PAIR_STATE_FAIL) {
|
||
// 5 秒后自动返回二级菜单
|
||
if (ms_tick - fail_start_ms >= 5000 || ms_tick < fail_start_ms)
|
||
{
|
||
AppManager_StartApp(APP_ID_MENU);
|
||
}
|
||
}
|
||
}
|
||
|
||
void PairApp_onClose(void)
|
||
{
|
||
RGB_Send(0, 0, 0, 0, 0, 0); // 关闭灯光
|
||
MOTOR = 0;
|
||
RF_SetMode(1);
|
||
Uart_SendString("[PairApp] Closed\r\n");
|
||
}
|
||
|
||
EventResult PairApp_onEvent(SystemEvent *evt)
|
||
{
|
||
if (pair_state == PAIR_STATE_CONFIRM) {
|
||
if (evt->key_event == KEY_EVENT_UP_CLICK) {
|
||
if (captured_zone < 99) {
|
||
captured_zone++;
|
||
UI_ShowPairConfirmPage(captured_type, captured_zone);
|
||
}
|
||
return EVENT_HANDLED;
|
||
}
|
||
if (evt->key_event == KEY_EVENT_DOWN_CLICK) {
|
||
if (captured_zone > 1) {
|
||
captured_zone--;
|
||
UI_ShowPairConfirmPage(captured_type, captured_zone);
|
||
}
|
||
return EVENT_HANDLED;
|
||
}
|
||
if (evt->key_event == KEY_EVENT_CONFIRM_CLICK) {
|
||
// 保存防区配对并显示成功反馈
|
||
Add_Sensor_With_Zone(captured_addr, captured_type, captured_zone);
|
||
pair_state = PAIR_STATE_SUCCESS;
|
||
current_state = STATE_PAIR_SUCCESS;
|
||
success_start_ms = ms_tick;
|
||
UI_ShowPairSuccessPage();
|
||
|
||
// 绿色常亮且马达长振 400ms
|
||
RGB_Send(0, 200, 0, 0, 200, 0);
|
||
MOTOR = 1;
|
||
Delay_ms(400);
|
||
MOTOR = 0;
|
||
return EVENT_HANDLED;
|
||
}
|
||
if (evt->key_event == KEY_EVENT_CONFIRM_LONG) {
|
||
// 长按 Confirm 取消返回主菜单
|
||
RGB_Send(0, 0, 0, 0, 0, 0);
|
||
AppManager_StartApp(APP_ID_MENU);
|
||
return EVENT_HANDLED;
|
||
}
|
||
}
|
||
|
||
// 搜寻状态或确认状态下长按 DOWN 退出
|
||
if (evt->key_event == KEY_EVENT_DOWN_LONG) {
|
||
RGB_Send(0, 0, 0, 0, 0, 0);
|
||
AppManager_StartApp(APP_ID_MENU);
|
||
return EVENT_HANDLED;
|
||
}
|
||
return EVENT_IGNORED;
|
||
}
|
||
|
||
/* =========================================================================
|
||
* AlarmApp 回调实现
|
||
* ========================================================================= */
|
||
|
||
void AlarmApp_OnStart(void)
|
||
{
|
||
u8 slot;
|
||
u8 type = 0;
|
||
u8 zone = 1;
|
||
|
||
current_state = STATE_ALARM;
|
||
alarm_timer_ms = 0;
|
||
alarm_flash_flag = 0;
|
||
|
||
if (Check_Sensor_ID(captured_addr, &slot)) {
|
||
type = sensor_list[slot].type;
|
||
zone = sensor_list[slot].zone;
|
||
alarm_sensor_slot = slot;
|
||
}
|
||
|
||
// 初始化显示警报界面
|
||
UI_ShowAlarmPage(type, zone);
|
||
|
||
// 首次亮起防区对应颜色
|
||
if (type == 0) RGB_Send(0, 180, 0, 0, 180, 0); // 门磁: 红色 (R=180)
|
||
else if (type == 1) RGB_Send(100, 180, 0, 100, 180, 0); // PIR: 黄色
|
||
else if (type == 2) RGB_Send(180, 0, 0, 180, 0, 0); // 烟感: 绿色 (G=180)
|
||
else if (type == 3) RGB_Send(0, 180, 180, 0, 180, 180); // 气感: 紫色
|
||
else RGB_Send(0, 0, 180, 0, 0, 180); // 水浸: 蓝色
|
||
|
||
Uart_SendString("[AlarmApp] Started\r\n");
|
||
}
|
||
|
||
void AlarmApp_onRun(void)
|
||
{
|
||
u8 type = sensor_list[alarm_sensor_slot].type;
|
||
u8 flash = (ms_tick / 250) % 2; // 250ms 亮灭周期
|
||
|
||
// 同步控制 RGB 幻彩灯闪烁
|
||
if (flash)
|
||
{
|
||
if (type == 0) RGB_Send(0, 180, 0, 0, 180, 0);
|
||
else if (type == 1) RGB_Send(100, 180, 0, 100, 180, 0);
|
||
else if (type == 2) RGB_Send(180, 0, 0, 180, 0, 0);
|
||
else if (type == 3) RGB_Send(0, 180, 180, 0, 180, 180);
|
||
else RGB_Send(0, 0, 180, 0, 0, 180);
|
||
}
|
||
else
|
||
{
|
||
RGB_Send(0, 0, 0, 0, 0, 0);
|
||
}
|
||
|
||
// Truly AMOLED 粗红框闪烁
|
||
if (alarm_flash_flag) {
|
||
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED);
|
||
LCD_DrawRectBorder(6, 6, 108, 228, COLOR_RED);
|
||
} else {
|
||
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_BLACK);
|
||
LCD_DrawRectBorder(6, 6, 108, 228, COLOR_BLACK);
|
||
}
|
||
}
|
||
|
||
void AlarmApp_onClose(void)
|
||
{
|
||
RGB_Send(0, 0, 0, 0, 0, 0); // 关闭灯条
|
||
MOTOR = 0;
|
||
current_state = STATE_NORMAL;
|
||
Uart_SendString("[AlarmApp] Closed\r\n");
|
||
}
|
||
|
||
EventResult AlarmApp_onEvent(SystemEvent *evt)
|
||
{
|
||
// 任意键按下或长按 ➔ 清除报警返回时钟
|
||
if (evt->key_event != KEY_EVENT_NONE) {
|
||
AppManager_StartApp(APP_ID_CLOCK);
|
||
return EVENT_HANDLED;
|
||
}
|
||
return EVENT_IGNORED;
|
||
}
|
||
|
||
/* =========================================================================
|
||
* SOSApp 回调实现
|
||
* ========================================================================= */
|
||
|
||
static u8 sos_last_phase;
|
||
|
||
void SOSApp_OnStart(void)
|
||
{
|
||
current_state = STATE_SOS_EMITTED;
|
||
sos_last_phase = 99;
|
||
alarm_timer_ms = 0;
|
||
UI_ShowSosPage();
|
||
|
||
// 立即启动双灯交替爆闪
|
||
RGB_Send(0, 200, 0, 0, 0, 0);
|
||
|
||
Uart_SendString("[SOSApp] Started\r\n");
|
||
}
|
||
|
||
void SOSApp_onRun(void)
|
||
{
|
||
// 每 500ms 发射一次求救射频信号 (CLONED_ADDR 伴随 SOS 数据码 0x08)
|
||
u8 sec_phase = (u8)((ms_tick / 500) % 2);
|
||
if (sec_phase != sos_last_phase) {
|
||
sos_last_phase = sec_phase;
|
||
RF_SetMode(2);
|
||
EV1527_Transmit(CLONED_ADDR, 0x08);
|
||
RF_SetMode(0);
|
||
}
|
||
|
||
// 50ms 周期控制双灯交替红色闪烁
|
||
{
|
||
u8 flash = (ms_tick / 50) % 2;
|
||
if (flash) {
|
||
RGB_Send(0, 200, 0, 0, 0, 0); // 灯1亮红,灯2灭
|
||
} else {
|
||
RGB_Send(0, 0, 0, 0, 200, 0); // 灯1灭,灯2亮红
|
||
}
|
||
}
|
||
|
||
// Truly AMOLED 边框闪烁 (红粗边框 + 白细边框)
|
||
if (alarm_flash_flag) {
|
||
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED);
|
||
LCD_DrawRectBorder(5, 5, 110, 230, COLOR_RED);
|
||
LCD_DrawRectBorder(8, 8, 104, 224, COLOR_WHITE);
|
||
} else {
|
||
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_BLACK);
|
||
LCD_DrawRectBorder(5, 5, 110, 230, COLOR_BLACK);
|
||
LCD_DrawRectBorder(8, 8, 104, 224, COLOR_BLACK);
|
||
}
|
||
}
|
||
|
||
void SOSApp_onClose(void)
|
||
{
|
||
RGB_Send(0, 0, 0, 0, 0, 0); // 关闭灯条
|
||
MOTOR = 0;
|
||
current_state = STATE_NORMAL;
|
||
Uart_SendString("[SOSApp] Closed\r\n");
|
||
}
|
||
|
||
EventResult SOSApp_onEvent(SystemEvent *evt)
|
||
{
|
||
// 短按或长按任意键 ➔ 退出求救状态返回待机时钟
|
||
if (evt->key_event != KEY_EVENT_NONE) {
|
||
AppManager_StartApp(APP_ID_CLOCK);
|
||
return EVENT_HANDLED;
|
||
}
|
||
return EVENT_IGNORED;
|
||
}
|
||
|
||
/* =========================================================================
|
||
* 延时与时间获取辅助函数
|
||
* ========================================================================= */
|
||
|
||
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;
|
||
while (--i);
|
||
}
|
||
|
||
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;
|
||
while (GetTimer0_Safe() < ticks);
|
||
TR0 = 0;
|
||
}
|
||
|
||
/* =========================================================================
|
||
* STC32G 闪存 IAP (EEPROM) 读写
|
||
* ========================================================================= */
|
||
|
||
#define IAP_CMD_READ 1
|
||
#define IAP_CMD_WRITE 2
|
||
#define IAP_CMD_ERASE 3
|
||
|
||
void IAP_Disable(void)
|
||
{
|
||
IAP_CONTR = 0;
|
||
IAP_CMD = 0;
|
||
IAP_TRIG = 0;
|
||
IAP_ADDRH = 0xff;
|
||
IAP_ADDRL = 0xff;
|
||
}
|
||
|
||
u8 IAP_ReadByte(u16 addr)
|
||
{
|
||
IAP_CONTR = 0x80;
|
||
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;
|
||
}
|
||
|
||
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();
|
||
}
|
||
|
||
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();
|
||
}
|
||
|
||
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++);
|
||
}
|
||
}
|
||
|
||
void Save_Database(void)
|
||
{
|
||
u16 addr = 0;
|
||
u8 *ptr = (u8 *)&sensor_list;
|
||
u16 size = sizeof(sensor_list);
|
||
u16 i;
|
||
IAP_EraseSector(0);
|
||
for (i = 0; i < size; i++) {
|
||
IAP_WriteByte(addr++, ptr[i]);
|
||
}
|
||
}
|
||
|
||
void Add_Sensor_With_Zone(u32 addr, u8 type, u8 zone)
|
||
{
|
||
u8 i;
|
||
u8 slot_to_use = 15;
|
||
for (i = 0; i < 16; i++) {
|
||
if (sensor_list[i].is_used == 0x01) {
|
||
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 == addr) {
|
||
slot_to_use = i;
|
||
break;
|
||
}
|
||
} else {
|
||
slot_to_use = i;
|
||
break;
|
||
}
|
||
}
|
||
sensor_list[slot_to_use].is_used = 0x01;
|
||
sensor_list[slot_to_use].addr[0] = (u8)(addr >> 16);
|
||
sensor_list[slot_to_use].addr[1] = (u8)(addr >> 8);
|
||
sensor_list[slot_to_use].addr[2] = (u8)addr;
|
||
sensor_list[slot_to_use].type = type;
|
||
sensor_list[slot_to_use].zone = zone;
|
||
|
||
for (i = 0; i < 15; i++) {
|
||
sensor_list[slot_to_use].name_gbk[i] = sensor_names_fr[type][i];
|
||
if (sensor_names_fr[type][i] == '\0') break;
|
||
}
|
||
sensor_list[slot_to_use].name_gbk[15] = '\0';
|
||
Save_Database();
|
||
}
|
||
|
||
bit Check_Sensor_ID(u32 addr, u8 *out_slot_index)
|
||
{
|
||
u8 i;
|
||
for (i = 0; i < 16; i++) {
|
||
if (sensor_list[i].is_used == 0x01) {
|
||
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 == addr) {
|
||
*out_slot_index = i;
|
||
return 1;
|
||
}
|
||
}
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
/* =========================================================================
|
||
* 串口调试驱动
|
||
* ========================================================================= */
|
||
|
||
void Uart1_Init(void)
|
||
{
|
||
u16 reload = (u16)(65536UL - (MAIN_Fosc / 4 / 115200UL));
|
||
SCON = 0x50;
|
||
AUXR |= 0x01;
|
||
AUXR |= 0x04;
|
||
T2L = (u8)reload;
|
||
T2H = (u8)(reload >> 8);
|
||
AUXR |= 0x10;
|
||
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';
|
||
}
|
||
|
||
/* =========================================================================
|
||
* UI 页面渲染函数
|
||
* ========================================================================= */
|
||
|
||
void UI_ShowClockPage(SystemState state, u8 hour, u8 min)
|
||
{
|
||
char time_str[6];
|
||
LCD_Clear(COLOR_BLACK);
|
||
|
||
// 左上角锁图标
|
||
if (state == STATE_ARMED)
|
||
{
|
||
LCD_DrawRectBorder(10, 23, 20, 15, COLOR_RED);
|
||
LCD_FillRect(15, 15, 2, 8, COLOR_RED);
|
||
LCD_FillRect(17, 15, 10, 2, COLOR_RED);
|
||
LCD_FillRect(25, 15, 2, 8, COLOR_RED);
|
||
}
|
||
else
|
||
{
|
||
LCD_DrawRectBorder(10, 23, 20, 15, COLOR_GREEN);
|
||
LCD_FillRect(15, 15, 2, 8, COLOR_GREEN);
|
||
LCD_FillRect(17, 15, 8, 2, COLOR_GREEN);
|
||
LCD_FillRect(25, 17, 2, 6, COLOR_GREEN);
|
||
}
|
||
|
||
// 右上角电量
|
||
LCD_ShowString(62, 24, "85%", COLOR_GRAY, COLOR_BLACK);
|
||
LCD_DrawMonoBitmap(92, 27, 20, 10, bmp_battery_20x10, COLOR_GRAY, COLOR_BLACK);
|
||
|
||
// 中间时间
|
||
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, COLOR_WHITE, COLOR_BLACK);
|
||
|
||
// AM/PM
|
||
LCD_ShowStringCentered(150, "AM", COLOR_WHITE, COLOR_BLACK);
|
||
|
||
// 日期 (法语)
|
||
LCD_ShowStringCentered(195, "VEN 10-07", COLOR_GRAY, 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)
|
||
{
|
||
LCD_Clear(COLOR_BLACK);
|
||
LCD_ShowStringCentered(35, "APPAIRAGE", COLOR_GRAY, COLOR_BLACK);
|
||
|
||
if (frame_index == 0)
|
||
LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_radar_32x32_f1, COLOR_CYAN, COLOR_BLACK);
|
||
else if (frame_index == 1)
|
||
LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_radar_32x32_f2, COLOR_CYAN, COLOR_BLACK);
|
||
else
|
||
LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_radar_32x32_f3, COLOR_CYAN, COLOR_BLACK);
|
||
|
||
LCD_ShowStringCentered(165, "RECHERCHE", COLOR_WHITE, COLOR_BLACK);
|
||
LCD_ShowStringCentered(200, "DECLENCH. CAPT", COLOR_CYAN, COLOR_BLACK);
|
||
}
|
||
|
||
void UI_ShowPairConfirmPage(u8 type, u8 zone_index)
|
||
{
|
||
char zone_str[3];
|
||
LCD_Clear(COLOR_BLACK);
|
||
|
||
LCD_ShowStringCentered(32, "v RECU", COLOR_GREEN, COLOR_BLACK);
|
||
|
||
if (type == 0) {
|
||
LCD_DrawMonoBitmap(44, 55, 32, 32, bmp_door_32x32, COLOR_WHITE, COLOR_BLACK);
|
||
} else if (type == 2 || type == 3) {
|
||
LCD_DrawMonoBitmap(44, 55, 32, 32, bmp_siren_32x32, COLOR_WHITE, COLOR_BLACK);
|
||
} else {
|
||
LCD_DrawMonoBitmap(44, 55, 32, 32, bmp_confirm_lock_32x32, COLOR_WHITE, COLOR_BLACK);
|
||
}
|
||
|
||
LCD_ShowStringCentered(105, sensor_names_fr[type], COLOR_WHITE, COLOR_BLACK);
|
||
|
||
LCD_DrawRectBorder(15, 140, 90, 44, COLOR_CYAN);
|
||
zone_str[0] = '0' + (zone_index / 10);
|
||
zone_str[1] = '0' + (zone_index % 10);
|
||
zone_str[2] = '\0';
|
||
LCD_ShowString16x32(44, 146, zone_str, COLOR_CYAN, COLOR_BLACK);
|
||
|
||
LCD_ShowStringCentered(215, "CONFIRMER", COLOR_GRAY, COLOR_BLACK);
|
||
}
|
||
|
||
void UI_ShowPairSuccessPage(void)
|
||
{
|
||
LCD_Clear(COLOR_BLACK);
|
||
LCD_DrawMonoBitmap(44, 75, 32, 32, bmp_checkmark_32x32, COLOR_GREEN, COLOR_BLACK);
|
||
LCD_ShowStringCentered(145, "SUCCES", COLOR_GREEN, COLOR_BLACK);
|
||
LCD_ShowStringCentered(180, "ENREGISTRE", 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);
|
||
LCD_ShowStringCentered(145, "ECHEC", COLOR_RED, COLOR_BLACK);
|
||
if (is_timeout) {
|
||
LCD_ShowStringCentered(180, "DELAI DEPASSE", COLOR_GRAY, COLOR_BLACK);
|
||
} else {
|
||
LCD_ShowStringCentered(180, "TYPE INCORRECT", COLOR_GRAY, COLOR_BLACK);
|
||
}
|
||
}
|
||
|
||
void UI_ShowAlarmPage(u8 type, u8 zone_index)
|
||
{
|
||
char zone_info[20];
|
||
LCD_Clear(COLOR_BLACK);
|
||
|
||
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED);
|
||
LCD_DrawRectBorder(6, 6, 108, 228, COLOR_RED);
|
||
|
||
if (type == 0) {
|
||
LCD_DrawMonoBitmap(44, 50, 32, 32, bmp_door_32x32, COLOR_RED, COLOR_BLACK);
|
||
} else if (type == 2 || type == 3) {
|
||
LCD_DrawMonoBitmap(44, 50, 32, 32, bmp_siren_32x32, COLOR_RED, COLOR_BLACK);
|
||
} else {
|
||
LCD_DrawMonoBitmap(44, 50, 32, 32, bmp_confirm_lock_32x32, COLOR_RED, COLOR_BLACK);
|
||
}
|
||
|
||
{
|
||
u8 len = 0;
|
||
char *p = sensor_names_fr[type];
|
||
while(*p) {
|
||
zone_info[len++] = *p++;
|
||
}
|
||
zone_info[len++] = ' ';
|
||
zone_info[len++] = '0' + (zone_index / 10);
|
||
zone_info[len++] = '0' + (zone_index % 10);
|
||
zone_info[len] = '\0';
|
||
}
|
||
LCD_ShowStringCentered(135, zone_info, COLOR_RED, COLOR_BLACK);
|
||
LCD_ShowStringCentered(175, "INTRUSION!", COLOR_WHITE, COLOR_BLACK);
|
||
}
|
||
|
||
void UI_ShowSosPage(void)
|
||
{
|
||
LCD_Clear(COLOR_BLACK);
|
||
LCD_DrawRectBorder(4, 4, 112, 232, COLOR_RED);
|
||
LCD_DrawRectBorder(5, 5, 110, 230, COLOR_RED);
|
||
LCD_DrawRectBorder(8, 8, 104, 224, COLOR_WHITE);
|
||
|
||
// 警灯组合绘制
|
||
LCD_FillRect(44, 45, 32, 2, COLOR_RED);
|
||
LCD_FillRect(48, 47, 24, 12, COLOR_RED);
|
||
LCD_FillRect(40, 59, 40, 4, COLOR_GRAY);
|
||
LCD_FillRect(57, 51, 6, 6, COLOR_WHITE);
|
||
LCD_FillRect(58, 36, 4, 6, COLOR_RED);
|
||
LCD_FillRect(46, 38, 4, 4, COLOR_RED);
|
||
LCD_FillRect(70, 38, 4, 4, COLOR_RED);
|
||
|
||
LCD_ShowStringCentered(120, "SOS", COLOR_RED, COLOR_BLACK);
|
||
LCD_ShowStringCentered(150, "ENVOI", COLOR_RED, COLOR_BLACK);
|
||
LCD_ShowStringCentered(195, "APPEL...", COLOR_WHITE, COLOR_BLACK);
|
||
}
|
||
|
||
void UI_ShowBindingPage(u32 addr)
|
||
{
|
||
char id_str[20];
|
||
LCD_Clear(COLOR_BLACK);
|
||
|
||
// 摄像机组合绘制
|
||
LCD_DrawRectBorder(36, 46, 32, 26, COLOR_CYAN);
|
||
LCD_FillRect(44, 56, 6, 6, COLOR_CYAN);
|
||
LCD_FillRect(68, 48, 2, 22, COLOR_CYAN);
|
||
LCD_FillRect(70, 50, 2, 18, COLOR_CYAN);
|
||
LCD_FillRect(72, 52, 2, 14, COLOR_CYAN);
|
||
|
||
LCD_ShowStringCentered(120, "LIAISON", COLOR_WHITE, COLOR_BLACK);
|
||
LCD_ShowStringCentered(155, "ENVOI EN COURS", COLOR_CYAN, COLOR_BLACK);
|
||
|
||
id_str[0] = 'I'; id_str[1] = 'D'; id_str[2] = ':'; id_str[3] = ' ';
|
||
id_str[4] = '0'; id_str[5] = 'x';
|
||
{
|
||
u8 val1 = (u8)(addr >> 16);
|
||
u8 val2 = (u8)(addr >> 8);
|
||
u8 val3 = (u8)addr;
|
||
u8 nibble = val1 >> 4;
|
||
id_str[6] = (nibble < 10) ? ('0' + nibble) : ('A' + (nibble - 10));
|
||
nibble = val1 & 0x0F;
|
||
id_str[7] = (nibble < 10) ? ('0' + nibble) : ('A' + (nibble - 10));
|
||
nibble = val2 >> 4;
|
||
id_str[8] = (nibble < 10) ? ('0' + nibble) : ('A' + (nibble - 10));
|
||
nibble = val2 & 0x0F;
|
||
id_str[9] = (nibble < 10) ? ('0' + nibble) : ('A' + (nibble - 10));
|
||
nibble = val3 >> 4;
|
||
id_str[10] = (nibble < 10) ? ('0' + nibble) : ('A' + (nibble - 10));
|
||
nibble = val3 & 0x0F;
|
||
id_str[11] = (nibble < 10) ? ('0' + nibble) : ('A' + (nibble - 10));
|
||
id_str[12] = '\0';
|
||
}
|
||
LCD_ShowStringCentered(205, id_str, COLOR_GRAY, COLOR_BLACK);
|
||
}
|
||
|
||
void UI_ShowMainMenu(u8 selected_index)
|
||
{
|
||
LCD_Clear(COLOR_BLACK);
|
||
LCD_FillRect(58, 20, 4, 2, COLOR_GRAY);
|
||
LCD_FillRect(56, 22, 8, 2, COLOR_GRAY);
|
||
LCD_FillRect(54, 24, 12, 2, COLOR_GRAY);
|
||
|
||
if (selected_index == 0)
|
||
{
|
||
LCD_DrawRectBorder(44, 70, 32, 32, COLOR_CYAN);
|
||
LCD_FillRect(59, 76, 2, 10, COLOR_CYAN);
|
||
LCD_FillRect(59, 85, 8, 2, COLOR_CYAN);
|
||
LCD_ShowStringCentered(150, "1. HORLOGE", COLOR_WHITE, COLOR_BLACK);
|
||
}
|
||
else if (selected_index == 1)
|
||
{
|
||
LCD_DrawMonoBitmap(44, 70, 32, 32, bmp_radar_32x32_f3, COLOR_CYAN, COLOR_BLACK);
|
||
LCD_ShowStringCentered(150, "2. APPAIRAGE", COLOR_WHITE, COLOR_BLACK);
|
||
}
|
||
else if (selected_index == 2)
|
||
{
|
||
LCD_DrawRectBorder(44, 74, 20, 16, COLOR_CYAN);
|
||
LCD_FillRect(52, 80, 4, 4, COLOR_CYAN);
|
||
LCD_FillRect(64, 76, 2, 12, COLOR_CYAN);
|
||
LCD_FillRect(66, 78, 2, 8, COLOR_CYAN);
|
||
LCD_FillRect(68, 80, 2, 4, COLOR_CYAN);
|
||
LCD_ShowStringCentered(150, "3. LIAISON", COLOR_WHITE, COLOR_BLACK);
|
||
}
|
||
|
||
LCD_FillRect(54, 210, 12, 2, COLOR_GRAY);
|
||
LCD_FillRect(56, 212, 8, 2, COLOR_GRAY);
|
||
LCD_FillRect(58, 214, 4, 2, COLOR_GRAY);
|
||
}
|
||
|
||
void UI_ShowSetTimePage(u8 hour, u8 min, u8 selection, u8 blink_on)
|
||
{
|
||
char time_str[6];
|
||
LCD_Clear(COLOR_BLACK);
|
||
|
||
LCD_DrawRectBorder(10, 23, 20, 15, COLOR_GREEN);
|
||
LCD_FillRect(15, 15, 2, 8, COLOR_GREEN);
|
||
LCD_FillRect(17, 15, 8, 2, COLOR_GREEN);
|
||
LCD_FillRect(25, 17, 2, 6, COLOR_GREEN);
|
||
|
||
LCD_ShowString(62, 24, "85%", COLOR_GRAY, COLOR_BLACK);
|
||
LCD_DrawMonoBitmap(92, 27, 20, 10, bmp_battery_20x10, COLOR_GRAY, COLOR_BLACK);
|
||
|
||
if (selection == 1 && !blink_on)
|
||
{
|
||
time_str[0] = ' ';
|
||
time_str[1] = ' ';
|
||
}
|
||
else
|
||
{
|
||
time_str[0] = '0' + (hour / 10);
|
||
time_str[1] = '0' + (hour % 10);
|
||
}
|
||
time_str[2] = '\0';
|
||
LCD_ShowString16x32(12, 100, time_str, COLOR_CYAN, COLOR_BLACK);
|
||
|
||
LCD_ShowString16x32(48, 98, ":", COLOR_WHITE, COLOR_BLACK);
|
||
|
||
if (selection == 2 && !blink_on)
|
||
{
|
||
time_str[0] = ' ';
|
||
time_str[1] = ' ';
|
||
}
|
||
else
|
||
{
|
||
time_str[0] = '0' + (min / 10);
|
||
time_str[1] = '0' + (min % 10);
|
||
}
|
||
time_str[2] = '\0';
|
||
LCD_ShowString16x32(64, 100, time_str, COLOR_CYAN, COLOR_BLACK);
|
||
LCD_ShowStringCentered(150, "AM", COLOR_WHITE, COLOR_BLACK);
|
||
LCD_ShowStringCentered(195, "VEN 10-07", COLOR_GRAY, COLOR_BLACK);
|
||
}
|
||
|
||
void Wakeup_Restore(void);
|
||
|
||
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. 向屏幕发送 Display OFF (0x28) 彻底关闭像素发光显示
|
||
WriteComm(0x28);
|
||
Delay_ms(20);
|
||
|
||
// 3. AMOLED 屏控制器写入 Sleep In (0x10) 睡眠指令
|
||
WriteComm(0x10);
|
||
Delay_ms(20);
|
||
|
||
// 4. 拉低 SGM_CTRL 彻底断开 SGM3833 负压升压芯片的供电
|
||
SGM_CTRL = 0;
|
||
|
||
// 5. 将 AMOLED 控制总线所有 IO 拉低,以防由于 IO 寄生二极管对屏幕倒灌电导致常亮
|
||
LCD_CS = 0;
|
||
LCD_RST = 0;
|
||
LCD_DCX = 0;
|
||
LCD_SCL = 0;
|
||
LCD_SDI = 0;
|
||
|
||
// 6. 休眠状态下不能关闭接收芯片,保持 SHUT 为高电平 (1) 工作状态
|
||
SHUT = 1;
|
||
|
||
// 确保开启扩展寄存器访问 (EAXFR = 1),以便能正确配置端口唤醒使能扩展寄存器
|
||
P_SW2 |= 0x80;
|
||
|
||
// 7. 配置 P0.1, P0.2, P0.3 为 低电平触发 唤醒
|
||
// PxIM1=1, PxIM0=0 → 低电平触发
|
||
P0IM1 |= 0x0E;
|
||
P0IM0 &= ~0x0E;
|
||
P0INTE |= 0x0E;
|
||
|
||
// 8. 配置 P2.6 (KEY_SOS) 为 低电平触发 唤醒
|
||
P2IM1 |= 0x40;
|
||
P2IM0 &= ~0x40;
|
||
P2INTE |= 0x40;
|
||
|
||
// ===== 关键新增:配置端口掉电唤醒使能寄存器 PxWKUE =====
|
||
// STC32G 在进入 Power-Down (STOP) 模式后,除了开启端口中断外,
|
||
// 必须使能对应的 PxWKUE 寄存器,硬件唤醒通路才能生效!
|
||
P0WKUE |= 0x0E; // 使能 P0.1/P0.2/P0.3 掉电唤醒
|
||
P2WKUE |= 0x40; // 使能 P2.6 掉电唤醒
|
||
|
||
// 9. 不允许外部中断 2 (RF_RX_DATA) 沿触发唤醒源
|
||
EX2 = 0;
|
||
|
||
// 10. 清除端口中断的悬挂/标志位,防误触
|
||
P0INTF = 0x00;
|
||
P2INTF = 0x00;
|
||
|
||
// 11. 关闭 Timer1 中断,保持 EA=1 让端口中断能唤醒
|
||
ET1 = 0;
|
||
EA = 1;
|
||
|
||
// 12. 写入 PCON 掉电模式位,使 MCU 进入深度 Power-Down 挂起状态
|
||
// CPU 在此停止,直到端口低电平中断唤醒
|
||
Uart_SendString("[SYS] Entering Power-Down mode...\r\n");
|
||
PCON |= 0x02; // PD = 1
|
||
_nop_();
|
||
_nop_();
|
||
_nop_();
|
||
_nop_();
|
||
|
||
// 13. 唤醒并执行完 ISR 后,CPU 从这里继续执行
|
||
Wakeup_Restore();
|
||
|
||
}
|
||
void Wakeup_Restore(void)
|
||
{
|
||
u8 p0_flag;
|
||
u8 p2_flag;
|
||
|
||
// 确保 EAXFR=1 才能访问扩展 SFR (P0INTF/P2INTF/P0INTE/P2INTE)
|
||
EAXFR = 1;
|
||
|
||
// [诊断] 在清除标志位之前先读取,判断唤醒来源
|
||
p0_flag = P0INTF;
|
||
p2_flag = P2INTF;
|
||
|
||
// 1. 立即禁用端口中断和唤醒,防止继续触发
|
||
P0INTE = 0x00;
|
||
P2INTE = 0x00;
|
||
P0WKUE = 0x00; // 清除 P0 唤醒允许
|
||
P2WKUE = 0x00; // 清除 P2 唤醒允许
|
||
P0INTF = 0x00;
|
||
P2INTF = 0x00;
|
||
EX2 = 0;
|
||
|
||
// 1b. 关闭掉电唤醒定时器
|
||
WKTCH = 0x00;
|
||
WKTCL = 0x00;
|
||
|
||
// 2. 重新开启全局中断与 Timer1 中断
|
||
EA = 1;
|
||
ET1 = 1;
|
||
|
||
// [诊断] 打印唤醒来源
|
||
if (p0_flag & 0x0E) {
|
||
Uart_SendString("[WR] Woken by KEY P0 (P0INTF=0x");
|
||
Uart_SendHex8(p0_flag);
|
||
Uart_SendString(")\r\n");
|
||
} else if (p2_flag & 0x40) {
|
||
Uart_SendString("[WR] Woken by KEY P2 (P2INTF=0x");
|
||
Uart_SendHex8(p2_flag);
|
||
Uart_SendString(")\r\n");
|
||
} else {
|
||
Uart_SendString("[WR] Woken by TIMER (no key flag)\r\n");
|
||
}
|
||
|
||
// 3. 保持 SHUT 开启射频接收芯片工作
|
||
SHUT = 1;
|
||
|
||
// 4. 拉高 SGM_CTRL 并发送脉冲使能 SGM3833 升压
|
||
SGM_CTRL = 1;
|
||
SGM_SendPulse(27);
|
||
|
||
// 5. 等待负压电轨充分稳定
|
||
Delay_ms(200);
|
||
|
||
// 6. 重做屏控制器 RM69310 寄存器组的初始化
|
||
LCD_Init();
|
||
|
||
// 7. 重置闲置倒计时
|
||
inactivity_timer = 0;
|
||
|
||
// 8. 直接绘制时钟界面(绕过 AppManager 早期返回保护)
|
||
current_state = STATE_NORMAL;
|
||
UI_ShowClockPage(current_state, current_hour, current_min);
|
||
Uart_SendString("[SYS] Wakeup restored!\r\n");
|
||
}
|
||
|
||
|
||
|
||
// 移除了冲突的中断服务函数,采用更健壮的 EA=0 挂起直接继续执行唤醒序列
|
||
|
||
|
||
/* =========================================================================
|
||
* 步骤3: 事件分发路由器 (Event_Dispatcher_Loop)
|
||
* ========================================================================= */
|
||
|
||
void Event_Dispatcher_Loop(void)
|
||
{
|
||
SystemEvent evt = EventQueue_Pop();
|
||
if (evt.key_event == KEY_EVENT_NONE && evt.extra_size == 0) {
|
||
return;
|
||
}
|
||
inactivity_timer = 0; // 收到有效按键或无线事件,重置闲置计时器
|
||
Uart_SendString("[EVENT] key=");
|
||
Uart_SendHex8((u8)evt.key_event);
|
||
Uart_SendString(", pri=");
|
||
Uart_SendHex8((u8)evt.priority);
|
||
Uart_SendString("\r\n");
|
||
|
||
// SOS 按键:仅在当前不处于 SOS 求救模式时触发进入 SOS 状态,否则作为普通按键进行事件分发
|
||
if ((evt.key_event == KEY_EVENT_SOS_CLICK || evt.key_event == KEY_EVENT_SOS_LONG) &&
|
||
AppManager_GetActiveAppID() != APP_ID_SOS) {
|
||
AppManager_StartApp(APP_ID_SOS);
|
||
return;
|
||
}
|
||
|
||
// 传感器入侵警报:当前不处于 SOS 求救状态时方可跳转到报警界面
|
||
if (evt.priority == EVENT_PRIORITY_HIGH && evt.extra_size >= 4) {
|
||
u8 slot;
|
||
if (Check_Sensor_ID(evt.extra_data, &slot)) {
|
||
if (AppManager_GetActiveAppID() != APP_ID_SOS) {
|
||
captured_addr = evt.extra_data;
|
||
alarm_sensor_slot = slot;
|
||
AppManager_StartApp(APP_ID_ALARM);
|
||
return;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 无线对码捕获:处于 PAIR 模式下时,将射频事件分发给 PairApp 处理并退出分发
|
||
if (evt.priority == EVENT_PRIORITY_NORMAL && evt.extra_size >= 4) {
|
||
captured_addr = evt.extra_data;
|
||
captured_type = menu_select;
|
||
if (AppManager_GetActiveAppID() == APP_ID_PAIR) {
|
||
AppManager_DispatchEvent(evt);
|
||
return;
|
||
}
|
||
}
|
||
|
||
AppManager_DispatchEvent(evt);
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
|
||
/* =========================================================================
|
||
* 定时器 1 (1ms 中断服务 + 按键消抖状态机)
|
||
* ========================================================================= */
|
||
|
||
void Timer1_Init(void)
|
||
{
|
||
AUXR |= 0x40;
|
||
TMOD &= 0x0F;
|
||
{
|
||
u16 reload = (u16)(65536UL - (MAIN_Fosc / 1000UL));
|
||
TL1 = (u8)reload;
|
||
TH1 = (u8)(reload >> 8);
|
||
}
|
||
ET1 = 1;
|
||
TR1 = 1;
|
||
EA = 1;
|
||
}
|
||
|
||
void Timer1_Isr(void) interrupt 3
|
||
{
|
||
SystemEvent evt;
|
||
|
||
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;
|
||
}
|
||
clock_updated = 1;
|
||
}
|
||
}
|
||
|
||
if (current_state == STATE_PAIR_WAIT) {
|
||
pair_timeout_ms++;
|
||
}
|
||
|
||
/* 振动马达定时状态机(仅控制马达,不再控制已被挪用/废弃的 LED) */
|
||
if (current_state == STATE_SOS_EMITTED) {
|
||
alarm_timer_ms++;
|
||
if (alarm_timer_ms >= 1200) alarm_timer_ms = 0;
|
||
if (alarm_timer_ms < 1000) MOTOR = 1; else MOTOR = 0;
|
||
} else if (current_state == STATE_ALARM) {
|
||
u8 stype = sensor_list[alarm_sensor_slot].type;
|
||
alarm_timer_ms++;
|
||
if (alarm_timer_ms >= 5000) alarm_timer_ms = 0;
|
||
if (stype == 0) {
|
||
// 门磁警报振动波形
|
||
if (alarm_timer_ms < 300) MOTOR = 1; else MOTOR = 0;
|
||
} else if (stype == 1) {
|
||
// PIR 警报振动波形
|
||
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) {
|
||
// 烟感警报振动波形
|
||
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) {
|
||
// 气感警报振动波形
|
||
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) {
|
||
// 水浸警报振动波形
|
||
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 {
|
||
if (alarm_timer_ms < 300) MOTOR = 1; else MOTOR = 0;
|
||
}
|
||
} else {
|
||
MOTOR = 0;
|
||
alarm_timer_ms = 0;
|
||
motor_timer_ms = 0;
|
||
}
|
||
|
||
alarm_flash_flag = (ms_tick / 100) % 2;
|
||
|
||
/* 按键消抖扫描 (每10ms) */
|
||
key_scan_timer++;
|
||
if (key_scan_timer >= 10) {
|
||
u8 raw_up = !KEY_UP;
|
||
u8 raw_down = !KEY_DOWN;
|
||
u8 raw_confirm = !KEY_CONFIRM;
|
||
u8 raw_sos = !KEY_SOS;
|
||
key_scan_timer = 0;
|
||
|
||
// 如果检测到有任何按键按下,清零闲置计时器;否则在非休眠非警报下累加
|
||
if (raw_up || raw_down || raw_confirm || raw_sos) {
|
||
inactivity_timer = 0;
|
||
} else {
|
||
if (current_state != STATE_SLEEP && current_state != STATE_ALARM && current_state != STATE_SOS_EMITTED) {
|
||
inactivity_timer++;
|
||
}
|
||
}
|
||
|
||
evt.key_event = KEY_EVENT_NONE;
|
||
evt.extra_data = 0;
|
||
evt.extra_size = 0;
|
||
|
||
if (raw_up && raw_down) {
|
||
comb_hold++;
|
||
if (comb_hold == 300) {
|
||
evt.key_event = KEY_EVENT_UP_DOWN_COMB;
|
||
evt.priority = EVENT_PRIORITY_NORMAL;
|
||
EventQueue_Push(evt);
|
||
}
|
||
key_up_hold = 0;
|
||
key_down_hold = 0;
|
||
} else {
|
||
comb_hold = 0;
|
||
|
||
if (raw_up) {
|
||
key_up_hold++;
|
||
if (key_up_hold == 300) {
|
||
evt.key_event = KEY_EVENT_UP_LONG;
|
||
evt.priority = EVENT_PRIORITY_NORMAL;
|
||
EventQueue_Push(evt);
|
||
}
|
||
} else {
|
||
if (key_up_hold >= 2 && key_up_hold < 300) {
|
||
evt.key_event = KEY_EVENT_UP_CLICK;
|
||
evt.priority = EVENT_PRIORITY_NORMAL;
|
||
EventQueue_Push(evt);
|
||
}
|
||
key_up_hold = 0;
|
||
}
|
||
|
||
if (raw_down) {
|
||
key_down_hold++;
|
||
if (key_down_hold == 300) {
|
||
evt.key_event = KEY_EVENT_DOWN_LONG;
|
||
evt.priority = EVENT_PRIORITY_NORMAL;
|
||
EventQueue_Push(evt);
|
||
}
|
||
} else {
|
||
if (key_down_hold >= 2 && key_down_hold < 300) {
|
||
evt.key_event = KEY_EVENT_DOWN_CLICK;
|
||
evt.priority = EVENT_PRIORITY_NORMAL;
|
||
EventQueue_Push(evt);
|
||
}
|
||
key_down_hold = 0;
|
||
}
|
||
|
||
if (raw_confirm) {
|
||
key_confirm_hold++;
|
||
if (key_confirm_hold == 300) {
|
||
evt.key_event = KEY_EVENT_CONFIRM_LONG;
|
||
evt.priority = EVENT_PRIORITY_NORMAL;
|
||
EventQueue_Push(evt);
|
||
}
|
||
} else {
|
||
if (key_confirm_hold >= 2 && key_confirm_hold < 300) {
|
||
evt.key_event = KEY_EVENT_CONFIRM_CLICK;
|
||
evt.priority = EVENT_PRIORITY_NORMAL;
|
||
EventQueue_Push(evt);
|
||
}
|
||
key_confirm_hold = 0;
|
||
}
|
||
|
||
if (raw_sos) {
|
||
key_sos_hold++;
|
||
if (key_sos_hold == 300) {
|
||
evt.key_event = KEY_EVENT_SOS_LONG;
|
||
evt.priority = EVENT_PRIORITY_URGENT;
|
||
EventQueue_InsertFront(evt);
|
||
}
|
||
} else {
|
||
if (key_sos_hold >= 2 && key_sos_hold < 300) {
|
||
evt.key_event = KEY_EVENT_SOS_CLICK;
|
||
evt.priority = EVENT_PRIORITY_URGENT;
|
||
EventQueue_InsertFront(evt);
|
||
}
|
||
key_sos_hold = 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* =========================================================================
|
||
* 主函数 (事件驱动框架)
|
||
* ========================================================================= */
|
||
|
||
void main(void)
|
||
{
|
||
u8 last_led_r = 1;
|
||
u8 last_led_g = 1;
|
||
u8 last_led_b = 1;
|
||
u8 last_motor = 0;
|
||
char cmd;
|
||
|
||
WTST = 0;
|
||
EAXFR = 1;
|
||
CKCON = 0;
|
||
WDT_CONTR = 0x00;
|
||
|
||
GPIO_Init();
|
||
RF_Init();
|
||
Delay_ms(500);
|
||
SGM_SendPulse(27);
|
||
Delay_ms(50);
|
||
LCD_Init();
|
||
Uart1_Init();
|
||
Uart_SendString("Wristband System Initialized!\r\n");
|
||
Load_Database();
|
||
|
||
/* 步骤1: 初始化事件队列 */
|
||
EventQueue_Init();
|
||
|
||
/* 步骤2+5: 初始化应用管理器 */
|
||
AppManager_Init();
|
||
|
||
/* 启动1ms定时器中断 */
|
||
Timer1_Init();
|
||
|
||
Uart_SendString("[SYS] Event-driven framework ready\r\n");
|
||
|
||
while (1) {
|
||
/* 串口调试命令 → 以事件方式入队 */
|
||
cmd = Uart_RxChar();
|
||
if (cmd != '\0') {
|
||
SystemEvent debug_evt;
|
||
inactivity_timer = 0; // 调试命令重置闲置计时器
|
||
debug_evt.extra_data = 0;
|
||
debug_evt.extra_size = 0;
|
||
|
||
if (cmd == 'u') {
|
||
debug_evt.key_event = KEY_EVENT_UP_CLICK;
|
||
debug_evt.priority = EVENT_PRIORITY_NORMAL;
|
||
EventQueue_Push(debug_evt);
|
||
Uart_SendString("[UART] KEY_UP Clicked\r\n");
|
||
} else if (cmd == 'U') {
|
||
debug_evt.key_event = KEY_EVENT_UP_LONG;
|
||
debug_evt.priority = EVENT_PRIORITY_NORMAL;
|
||
EventQueue_Push(debug_evt);
|
||
Uart_SendString("[UART] KEY_UP Long Pressed\r\n");
|
||
} else if (cmd == 'd') {
|
||
debug_evt.key_event = KEY_EVENT_DOWN_CLICK;
|
||
debug_evt.priority = EVENT_PRIORITY_NORMAL;
|
||
EventQueue_Push(debug_evt);
|
||
Uart_SendString("[UART] KEY_DOWN Clicked\r\n");
|
||
} else if (cmd == 'D') {
|
||
debug_evt.key_event = KEY_EVENT_DOWN_LONG;
|
||
debug_evt.priority = EVENT_PRIORITY_NORMAL;
|
||
EventQueue_Push(debug_evt);
|
||
Uart_SendString("[UART] KEY_DOWN Long Pressed\r\n");
|
||
} else if (cmd == 's') {
|
||
debug_evt.key_event = KEY_EVENT_SOS_CLICK;
|
||
debug_evt.priority = EVENT_PRIORITY_URGENT;
|
||
EventQueue_InsertFront(debug_evt);
|
||
Uart_SendString("[UART] KEY_SOS Clicked\r\n");
|
||
} else if (cmd == 'S') {
|
||
debug_evt.key_event = KEY_EVENT_SOS_LONG;
|
||
debug_evt.priority = EVENT_PRIORITY_URGENT;
|
||
EventQueue_InsertFront(debug_evt);
|
||
Uart_SendString("[UART] KEY_SOS Long Pressed\r\n");
|
||
} else if (cmd == 'c' || cmd == 'C') {
|
||
debug_evt.key_event = KEY_EVENT_UP_DOWN_COMB;
|
||
debug_evt.priority = EVENT_PRIORITY_NORMAL;
|
||
EventQueue_Push(debug_evt);
|
||
Uart_SendString("[UART] KEY_UP+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_With_Zone(mock_addr, 0, 1);
|
||
}
|
||
debug_evt.key_event = KEY_EVENT_NONE;
|
||
debug_evt.priority = EVENT_PRIORITY_HIGH;
|
||
debug_evt.extra_data = mock_addr;
|
||
debug_evt.extra_size = 4;
|
||
EventQueue_InsertFront(debug_evt);
|
||
Uart_SendString("[UART] Mock RF Alarm\r\n");
|
||
} else if (cmd == 'p' || cmd == 'P') {
|
||
if (AppManager_GetActiveAppID() == APP_ID_PAIR) {
|
||
debug_evt.key_event = KEY_EVENT_NONE;
|
||
debug_evt.priority = EVENT_PRIORITY_NORMAL;
|
||
debug_evt.extra_data = 0x789ABC;
|
||
debug_evt.extra_size = 4;
|
||
EventQueue_Push(debug_evt);
|
||
Uart_SendString("[UART] Mock RF Pair Signal\r\n");
|
||
} else {
|
||
Uart_SendString("[UART] Not in PAIR mode\r\n");
|
||
}
|
||
} else if (cmd == '1') {
|
||
if (AppManager_GetActiveAppID() == APP_ID_CLOCK) {
|
||
current_state = STATE_NORMAL;
|
||
UI_ShowClockPage(current_state, current_hour, current_min);
|
||
}
|
||
Uart_SendString("[UART] State: NORMAL\r\n");
|
||
} else if (cmd == '2') {
|
||
if (AppManager_GetActiveAppID() == APP_ID_CLOCK) {
|
||
current_state = STATE_ARMED;
|
||
UI_ShowClockPage(current_state, current_hour, current_min);
|
||
}
|
||
Uart_SendString("[UART] State: ARMED\r\n");
|
||
} else if (cmd == '3') {
|
||
if (AppManager_GetActiveAppID() == APP_ID_CLOCK) {
|
||
current_state = STATE_DISARMED;
|
||
UI_ShowClockPage(current_state, current_hour, current_min);
|
||
}
|
||
Uart_SendString("[UART] State: DISARMED\r\n");
|
||
} else if (cmd == 'z' || cmd == 'Z') {
|
||
inactivity_timer = 1000; // 模拟闲置超时,强制触发休眠
|
||
Uart_SendString("[UART] Force Entering Sleep mode!\r\n");
|
||
}
|
||
}
|
||
|
||
/* 步骤3: 事件分发 — 循环处理队列中所有事件 */
|
||
while (!EventQueue_IsEmpty()) {
|
||
Event_Dispatcher_Loop();
|
||
}
|
||
|
||
/* 步骤5: 运行活跃应用 (含CPU占用控制) */
|
||
AppManager_RunActiveApp();
|
||
|
||
/* 步骤6: 超时自动进入深度休眠 */
|
||
if (inactivity_timer >= 1000) {
|
||
inactivity_timer = 0;
|
||
current_state = STATE_SLEEP;
|
||
Enter_Low_Power_Sleep();
|
||
}
|
||
}
|
||
}
|
||
|
||
/* =========================================================================
|
||
* GPIO 初始化
|
||
* ========================================================================= */
|
||
|
||
void GPIO_Init(void)
|
||
{
|
||
IE = 0x00;
|
||
IE2 = 0x00;
|
||
TCON = 0x00;
|
||
TMOD &= 0xF0;
|
||
TR0 = 0;
|
||
ET0 = 0;
|
||
AUXR = 0x00;
|
||
INTCLKO = 0x00;
|
||
P_SW1 = 0x00;
|
||
P_SW2 = 0x80; // 开启 EAXFR=1,允许访问 P0PU/P2PU/P0INTE/P2INTE 等扩展 SFR 寄存器
|
||
P_SW3 = 0x00;
|
||
|
||
// 配置 P0.0 (BAT_ADC) 为高阻输入模式
|
||
P0M1 |= (1 << 0);
|
||
P0M0 &= ~(1 << 0);
|
||
|
||
// 配置 P0.1 (KEY_UP), P0.2 (KEY_CONFIRM), P0.3 (KEY_DOWN) 为准双向口模式并置 1
|
||
P0M1 &= ~((1 << 1) | (1 << 2) | (1 << 3));
|
||
P0M0 &= ~((1 << 1) | (1 << 2) | (1 << 3));
|
||
KEY_UP = 1;
|
||
KEY_CONFIRM = 1;
|
||
KEY_DOWN = 1;
|
||
|
||
// 配置 P2.3 (RGB_DIN) 为推挽输出模式
|
||
P2M1 &= ~(1 << 3);
|
||
P2M0 |= (1 << 3);
|
||
RGB_DIN = 0;
|
||
|
||
// 配置 P2.5 (MOTOR) 为推挽输出模式
|
||
P2M1 &= ~(1 << 5);
|
||
P2M0 |= (1 << 5);
|
||
MOTOR = 0;
|
||
|
||
// 配置 P2.6 (KEY_SOS) 为准双向口模式并置 1
|
||
P2M1 &= ~(1 << 6);
|
||
P2M0 &= ~(1 << 6);
|
||
KEY_SOS = 1;
|
||
|
||
// 配置 P2.2 (SHUT) 为推挽输出模式
|
||
P2M1 &= ~(1 << 2);
|
||
P2M0 |= (1 << 2);
|
||
SHUT = 1; // 默认拉高开启射频接收芯片工作
|
||
|
||
// 开启按键引脚 P0.1, P0.2, P0.3 和 P2.6 的内部上拉电阻,避免引脚抖动误唤醒
|
||
P0PU |= 0x0E;
|
||
P2PU |= 0x40;
|
||
|
||
// 配置 P2.7 (DET) 为高阻输入模式
|
||
P2M1 |= (1 << 7);
|
||
P2M0 &= ~(1 << 7);
|
||
|
||
// 配置 P3.0 (TXD), P3.1 (RXD) 和 P3.4 (SGM_CTRL)
|
||
P3M1 &= ~((1 << 0) | (1 << 1) | (1 << 4));
|
||
P3M0 &= ~(1 << 0);
|
||
P3M0 |= ((1 << 1) | (1 << 4));
|
||
}
|
||
|
||
// =========================================================================
|
||
// STC32G 端口中断 ISR
|
||
// 说明:STC32G 的 Port0 (向量 37) / Port2 (向量 39) 超过 Keil C251
|
||
// 支持的 0~31 范围,无法直接用 interrupt 37/39 声明。
|
||
// 因此通过 App/isr_jump.asm 在硬件绝对向量地址处放置 LJMP,
|
||
// 将 Port0 向量 → interrupt 13 入口 (0x006BH)
|
||
// 将 Port2 向量 → interrupt 15 入口 (0x007BH)
|
||
// 以此实现 Keil C251 可正确编译链接的中断服务函数。
|
||
// =========================================================================
|
||
|
||
// Port0 中断服务函数 (由 isr_jump.asm 从向量 37 @ 0x012BH 跳转至此)
|
||
void Port0_Isr(void) interrupt 13
|
||
{
|
||
// 关键:P0INTF/P0INTE 是 far 扩展 SFR,写入前必须确保 EAXFR=1
|
||
EAXFR = 1;
|
||
P0INTF = 0x00; // 清除 P0 端口中断悬挂标志
|
||
P0INTE = 0x00; // 立即禁用,防止低电平保持期间反复触发
|
||
}
|
||
|
||
// Port2 中断服务函数 (由 isr_jump.asm 从向量 39 @ 0x013BH 跳转至此)
|
||
void Port2_Isr(void) interrupt 15
|
||
{
|
||
// 关键:P2INTF/P2INTE 是 far 扩展 SFR,写入前必须确保 EAXFR=1
|
||
EAXFR = 1;
|
||
P2INTF = 0x00; // 清除 P2 端口中断悬挂标志
|
||
P2INTE = 0x00; // 立即禁用,防止低电平保持期间反复触发
|
||
}
|