Files
stc32g128k/App/ui.c

397 lines
17 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

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

#include "ui.h"
#include "../Drivers/lcd.h"
// 引入外部主菜单对码项名称定义 (例如 "DETEC. PORTE" 等,法语表达防区类型名称)
extern char *code menu_items[5];
/**
* @brief 渲染并显示系统待机时钟主页面
* @param state 当前的系统布防状态 (STATE_ARMED: 已布防, 否则为正常或撤防)
* @param hour 当前待显示的小时数 (0~23)
* @param min 当前待显示的分钟数 (0~59)
* @details 该页面被划分为三大功能视觉区:
* 1. 顶部状态栏:左侧绘制布撤防指示锁,右侧显示 "85%" 剩余电量以及精细电池单色位图。
* 2. 中部核心栏:垂直居中以 16x32 大号粗字体绘制格式化时间 "HH:MM",在其下方标注 AM/PM 标识。
* 3. 底部信息栏:使用灰色小字模绘制法语星期日期 "VEN 10-07" (星期五 7月10日)。
*/
void UI_ShowClockPage(SystemState state, u8 hour, u8 min)
{
char time_str[6];
LCD_Clear(COLOR_BLACK); // 使用纯黑色底色,充分利用 AMOLED 黑色像素不发光的省电特性
// ====== 绘制左上角布撤防锁头状态图标 ======
if (state == STATE_ARMED)
{
// 状态: 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
{
// 状态: NORMAL / DISARMED (非布防) -> 绘制绿色开启锁头
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); // 静态测试显示 85% 字符
LCD_DrawMonoBitmap(92, 27, 20, 10, bmp_battery_20x10, COLOR_GRAY, COLOR_BLACK); // 渲染 20x10 电池外廓和格数
// ====== 绘制中部时间 "HH:MM" ======
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); // 使用 16x32 特大字体居中绘制时间
// ====== 绘制 AM/PM 状态 ======
LCD_ShowStringCentered(150, "AM", COLOR_WHITE, COLOR_BLACK);
// ====== 绘制日期星期栏 ======
LCD_ShowStringCentered(195, "VEN 10-07", COLOR_GRAY, COLOR_BLACK);
}
/**
* @brief 渲染显示传感器对码二级菜单页面
* @param selected_index 当前聚焦选中的菜单行索引 (0~4分别对应 5 种传感器)
* @details 采用典型的纵向菜单滚轮特效:
* - 中间 110 行高度为当前选中行,使用青色框高亮圈起并用青色绘制名称。
* - 上下邻近行显示为暗灰色,且无边框保护,营造层次感。
*/
void UI_ShowPairMenuPage(u8 selected_index)
{
LCD_Clear(COLOR_BLACK);
// 绘制标题 "MENU APPAI." (法语: 配对/对码设置菜单)
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);
}
}
/**
* @brief 渲染正在搜索配对信号的雷达扫描等待页面
* @param frame_index 当前的雷达波纹帧索引号 (0~2 帧循环)
* @details 在中央以青色渲染雷达同心圆扩散动图,并在底部提示 "DECLENCH. CAPT" (请触发传感器发射)
*/
void UI_ShowPairWaitPage(u8 frame_index)
{
LCD_Clear(COLOR_BLACK);
// 绘制头部 "APPAIRAGE" (对码中)
LCD_ShowStringCentered(35, "APPAIRAGE", COLOR_GRAY, COLOR_BLACK);
// 根据 Timer1 节拍算得的帧号,切换渲染雷达的 3 种点阵图形,形成扩散动态视觉
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); // 法语: 请触发传感器以发射射频信号
}
/**
* @brief 渲染捕获到传感器地址后的微调确认页面
* @param type 已解码出的传感器硬件类型 (0:门磁, 1:PIR, 2:烟感, 3:燃气, 4:水浸)
* @param zone_index 当前配置的防区编号 (1~99),由用户上下键修改微调
*/
void UI_ShowPairConfirmPage(u8 type, u8 zone_index)
{
char zone_str[3];
LCD_Clear(COLOR_BLACK);
// 顶部显示捕获回馈标志 "v RECU" (法语: 信号已接收)
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);
// 绘制一个青色边框,在中间以 16x32 大号字体展示当前防区号 (如 "01", "12")
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); // 法语: 短按以确认保存
}
/**
* @brief 渲染显示配对成功反馈页
*/
void UI_ShowPairSuccessPage(void)
{
LCD_Clear(COLOR_BLACK);
// 渲染 32x32 对勾图标
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); // 法语: 配置已保存
}
/**
* @brief 渲染显示配对失败页面
* @param is_timeout 1表示超时退出的失败0表示其他对码错误
*/
void UI_ShowPairFailPage(u8 is_timeout)
{
LCD_Clear(COLOR_BLACK);
// 渲染 32x32 红色叉号图标
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); // 法语: 传感器类型不符
}
}
/**
* @brief 渲染显示传感器入侵警报页面
* @param type 触发报警的传感器类型 (0~4)
* @param zone_index 报警传感器的防区号 (1~99, 0)
* @details 在界面外侧四周圈起醒目的双重红色警告边框 (COLOR_RED)
* 中间绘制设备对应的大警告图标,并在下方用特大红字标明防区及设备类型(如 "PORTE 02"
* 最下方渲染白色粗体 "INTRUSION!" (入侵警告) 提示。
*/
void UI_ShowAlarmPage(u8 type, u8 zone_index)
{
char zone_info[20];
LCD_Clear(COLOR_BLACK);
// 绘制双重红色爆闪粗边界框 (配合 ms_tick 在 AlarmApp 中亮灭闪烁)
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); // 白字入侵提示
}
/**
* @brief 渲染显示 SOS 主动求救发送状态页面
* @details 绘制红、白双色三层粗边界警告,中央绘制细致的警徽警灯图案,
* 绘制红色闪烁求助状态字样 "SOS ENVOI" (求救发送中...) 和 "APPEL..." (呼叫中)。
*/
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); // 法语: 紧急呼叫中
}
/**
* @brief 渲染显示与网络摄像机 (IPC) 进行一键绑定配对的进度画面
* @param addr 手环自身的 24 位克隆出厂硬件地址
*/
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); // 镜头固定翼 1
LCD_FillRect(72, 52, 2, 14, COLOR_CYAN); // 镜头固定翼 2
LCD_ShowStringCentered(120, "LIAISON", COLOR_WHITE, COLOR_BLACK); // 法语: 连接/绑定
LCD_ShowStringCentered(155, "ENVOI EN COURS", COLOR_CYAN, COLOR_BLACK); // 法语: 发送对码帧中
// 拼接要对外广播的手环 24 位克隆物理 ID 十六进制字符串并显示在最下方
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);
}
/**
* @brief 渲染显示设置主菜单界面
* @param selected_index 当前菜单高亮行索引 (0:时钟修改, 1:对码匹配, 2:摄像机绑定)
* @details 在顶部和底部绘制向上/向下箭头提示滑轮,中部绘制选中项的大几何图形,
* 并在下方打印对应的法文字符串描述。
*/
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)
{
// 菜单 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)
{
// 菜单 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)
{
// 菜单 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);
}
/**
* @brief 渲染修改时间闪烁调节页面 (调节小时或分钟)
* @param hour 修改状态中的小时
* @param min 修改状态中的分钟
* @param selection 当前正在编辑聚焦的属性位 (1: 修改小时, 2: 修改分钟)
* @param blink_on 当前 500ms 控制隐现的闪烁周期标志位 (1-绘制正常字0-以空格消隐该数值字符)
*/
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);
}