Modularize main.c into rgb, event, database, ui, system, and app_manager modules

This commit is contained in:
Antigravity IDE
2026-07-15 16:14:30 +08:00
parent 30a2e1680b
commit 7ebe802410
15 changed files with 2216 additions and 2124 deletions

783
App/app_manager.c Normal file
View File

@@ -0,0 +1,783 @@
#include "app_manager.h"
#include "rgb.h"
#include "event.h"
#include "database.h"
#include "ui.h"
#include "system.h"
#include "../Drivers/rf.h"
#include "../Drivers/lcd.h"
#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
// 定义菜单项定义
char *code menu_items[5] = {
"DETEC. PORTE",
"DETEC. PIR",
"DETEC. FUMEE",
"DETEC. GAZ",
"DETEC. EAU"
};
/* 回调函数前置声明 */
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;
}

16
App/app_manager.h Normal file
View File

@@ -0,0 +1,16 @@
#ifndef __APP_MANAGER_H__
#define __APP_MANAGER_H__
#include "config.h"
// AppManager 核心接口
void AppManager_Init(void);
void AppManager_StartApp(AppID app_id);
void AppManager_DispatchEvent(SystemEvent evt);
void AppManager_RunActiveApp(void);
AppID AppManager_GetActiveAppID(void);
// 时间编辑状态机接口 (由 MenuApp 触发)
void Start_Time_Edit(void);
#endif

View File

@@ -1,10 +1,28 @@
#ifndef __CONFIG_H__
#define __CONFIG_H__
#include "stc32g.h"
#include "intrins.h"
typedef unsigned char u8;
typedef unsigned int u16;
typedef unsigned long u32;
/* ====== 硬件引脚定义 (sbit) ====== */
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)
/* ====== 系统状态 ====== */
typedef enum {
STATE_NORMAL,
@@ -104,7 +122,38 @@ typedef struct {
unsigned char name_gbk[16];
} Sensor_Slot;
/* ====== 全局变量 extern 声明 ====== */
extern SystemState current_state;
extern u8 current_hour;
extern u8 current_min;
extern u8 current_sec;
extern volatile u8 clock_updated;
extern u8 menu_select;
extern u32 captured_addr;
extern u8 captured_type;
extern u8 alarm_sensor_slot;
extern Sensor_Slot xdata sensor_list[16];
extern char *code sensor_names_fr[5];
extern volatile u16 ms_tick;
extern volatile u16 inactivity_timer; // 无操作闲置计时器
extern volatile u32 pair_timeout_ms;
extern volatile u16 alarm_timer_ms;
extern volatile u16 motor_timer_ms;
extern volatile bit alarm_flash_flag;
extern volatile u16 key_scan_timer;
extern volatile u16 key_up_hold;
extern volatile u16 key_down_hold;
extern volatile u16 key_confirm_hold;
extern volatile u16 key_sos_hold;
extern volatile u16 comb_hold;
#define MAIN_Fosc 24000000UL
#define CLONED_ADDR 0x37A86UL
#endif

140
App/database.c Normal file
View File

@@ -0,0 +1,140 @@
#include "database.h"
#define IAP_CMD_READ 1
#define IAP_CMD_WRITE 2
#define IAP_CMD_ERASE 3
// 定义全局常量数组
char *code sensor_names_fr[5] = {
"PORTE",
"PIR",
"FUMEE",
"GAZ",
"EAU"
};
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;
}

18
App/database.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef __DATABASE_H__
#define __DATABASE_H__
#include "config.h"
// 底层 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_With_Zone(u32 addr, u8 type, u8 zone);
bit Check_Sensor_ID(u32 addr, u8 *out_slot_index);
#endif

309
App/event.c Normal file
View File

@@ -0,0 +1,309 @@
#include "event.h"
#include "app_manager.h"
#include "database.h"
#include "system.h"
#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;
}
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);
}
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;
}
}
}
}

19
App/event.h Normal file
View File

@@ -0,0 +1,19 @@
#ifndef __EVENT_H__
#define __EVENT_H__
#include "config.h"
// 事件队列初始化与操作
void EventQueue_Init(void);
bit EventQueue_Push(SystemEvent evt);
bit EventQueue_InsertFront(SystemEvent evt);
SystemEvent EventQueue_Pop(void);
bit EventQueue_IsEmpty(void);
bit EventQueue_IsFull(void);
u8 EventQueue_GetCount(void);
// 事件分发与定时器/按键扫描初始化
void Event_Dispatcher_Loop(void);
void Timer1_Init(void);
#endif

2136
App/main.c

File diff suppressed because it is too large Load Diff

84
App/rgb.c Normal file
View File

@@ -0,0 +1,84 @@
#include "rgb.h"
/* ====== SPI 寄存器定义 (如果 stc32g.h 没定义) ====== */
#ifndef P_SW1
sfr P_SW1 = 0xA2;
sfr SPCTL = 0xCE;
sfr SPSTAT = 0xCD;
sfr SPDAT = 0xCF;
#endif
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; // 开中断
}

12
App/rgb.h Normal file
View File

@@ -0,0 +1,12 @@
#ifndef __RGB_H__
#define __RGB_H__
#include "config.h"
// SPI 及 RGB 发送初始化
void SPI_Init(void);
// 向 FCOB 幻彩灯条发送两颗 LED 的 GRB 数据
void RGB_Send(u8 g1, u8 r1, u8 b1, u8 g2, u8 r2, u8 b2);
#endif

327
App/system.c Normal file
View File

@@ -0,0 +1,327 @@
#include "system.h"
#include "rgb.h"
#include "ui.h"
#include "../Drivers/lcd.h"
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));
}
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;
}
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';
}
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 为 低电平触发 唤醒
P0IM1 |= 0x0E;
P0IM0 &= ~0x0E;
P0INTE |= 0x0E;
// 8. 配置 P2.6 (KEY_SOS) 为 低电平触发 唤醒
P2IM1 |= 0x40;
P2IM0 &= ~0x40;
P2INTE |= 0x40;
// ===== 配置端口掉电唤醒使能寄存器 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 挂起状态
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");
}

31
App/system.h Normal file
View File

@@ -0,0 +1,31 @@
#ifndef __SYSTEM_H__
#define __SYSTEM_H__
#include "config.h"
// GPIO 与系统外设初始化
void GPIO_Init(void);
void Uart1_Init(void);
// 延时与时间工具函数
void Delay_ms(u16 ms);
void Delay10us(void);
void Delay_us(u16 us);
u16 GetTimer0_Safe(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_SendHex16(u16 val);
void Uart_SendHex20(u32 val);
void Uart_SendHex32(u32 val);
void FormatHex(u32 val, char *buf);
// 电源管理与休眠唤醒
void Enter_Low_Power_Sleep(void);
void Wakeup_Restore(void);
#endif

290
App/ui.c Normal file
View File

@@ -0,0 +1,290 @@
#include "ui.h"
#include "../Drivers/lcd.h"
// 引入外部菜单项定义
extern char *code menu_items[5];
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);
}

18
App/ui.h Normal file
View File

@@ -0,0 +1,18 @@
#ifndef __UI_H__
#define __UI_H__
#include "config.h"
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);
#endif

View File

@@ -329,6 +329,36 @@
<FileType>1</FileType>
<FilePath>.\App\main.c</FilePath>
</File>
<File>
<FileName>rgb.c</FileName>
<FileType>1</FileType>
<FilePath>.\App\rgb.c</FilePath>
</File>
<File>
<FileName>event.c</FileName>
<FileType>1</FileType>
<FilePath>.\App\event.c</FilePath>
</File>
<File>
<FileName>app_manager.c</FileName>
<FileType>1</FileType>
<FilePath>.\App\app_manager.c</FilePath>
</File>
<File>
<FileName>database.c</FileName>
<FileType>1</FileType>
<FilePath>.\App\database.c</FilePath>
</File>
<File>
<FileName>ui.c</FileName>
<FileType>1</FileType>
<FilePath>.\App\ui.c</FilePath>
</File>
<File>
<FileName>system.c</FileName>
<FileType>1</FileType>
<FilePath>.\App\system.c</FilePath>
</File>
</Files>
</Group>
<Group>