Compare commits

..

12 Commits

Author SHA1 Message Date
ee69d25efe docs/hardware: reorganize documents subdirectory and save KiCad layout changes 2026-07-09 18:45:41 +08:00
039029ab20 docs: optimize all module breakdown files with architect-level details 2026-07-09 18:42:00 +08:00
9f54220e27 docs: clean up and align menu names in architecture main.md 2026-07-09 18:36:20 +08:00
2068edd205 docs: add core business event IO matrix to architecture design document 2026-07-09 18:25:28 +08:00
471b61107e docs: group and reorder wristband_ui_gallery.html into logical parts by flow 2026-07-09 18:18:20 +08:00
96a23da617 docs: expand wristband_ui_gallery.html to exhaustive 15-card set covering all menus and states 2026-07-09 18:15:46 +08:00
afaf0bf360 docs: add Card 11 (Arm/Disarm setting select) to wristband_ui_gallery.html 2026-07-09 18:12:25 +08:00
b15fca3629 docs: adjust battery text and icon size, fix text overflow in cards 5 and 9 2026-07-09 18:06:37 +08:00
fb781f4f2a docs: update wristband_ui_gallery.html to display A11y large-font mode watch screens 2026-07-09 18:01:49 +08:00
56fbe68dba docs: implement無障碍大字大图标 (A11y) UI design for special demographics 2026-07-09 18:00:16 +08:00
16deeb6662 docs: simplify pairing confirm, auto-detect sensor type and select suffix only 2026-07-09 17:45:35 +08:00
32c07c0466 docs: optimize SOS button behavior, add 2-step pairing wizard, and IPC exit action 2026-07-09 17:42:55 +08:00
37 changed files with 9994 additions and 33564 deletions

View File

@@ -1,11 +1,11 @@
# 总体需求 (Docs/10_requirements-overview/main.md)
## 1. 项目整体框架与系统拓扑
本项目主要实现随身手环终端的底层驱动、核心业务管理以及低功耗待机逻辑。
本项目**仅包含手环端核心业务及驱动代码的实现**。
* **被动接收**:手环作为随身告警终端,独立接收来自 **7类 433MHz 传感器**的探测射频信号(本地安防联动系统)。
* **主动发射**:手环在本地触发 SOS 求救时,必须向外主动发射 **433MHz SOS 呼救射频信号**,使周边的 IPC 摄像头接收并联动
* 周围的其他组件(如 IPC 摄像头和手机 APP不在本项目编码范围内仅作外部仿真与对接边界
## 1. 项目系统拓扑与边界
* **业务流向**:手环作为智能家居安防的便携式告警与求救终端,与无线传感器以及 IPC 摄像头(网络摄像机)构成以下星型网络架构
* **设计边界**:本项目**仅开发手环端的软件代码**,传感器和 IPC 摄像头侧的软件仅作为对接调试的外界仿真源
```mermaid
graph TD
@@ -13,18 +13,34 @@ graph TD
Wristband -->|433MHz SOS 呼救信号| IPC[IPC 摄像头 (仿真对接)]
```
## 2. 核心信号流向与手环行为要求
---
1. **传感器触发**探测到异常时发射固定编码信号24位 EV1527 协议数据,包含 20位地址码 + 4位数据码
2. **手环独立接收**:手环配备 LR690L 433MHz 接收解调芯片,独立进行软件自适应时序解码。
3. **手环响应行为**
* **屏幕显示**1.14寸135x240 像素,**竖直排版**,型号为 **SPEC LHS114TC-IF03 VER A**TFT LCD 屏幕,**保持常亮显示时间**。报警时呈现对应的报警 ICON + 传感器法语名称。
* **灯效响应**:可编程 RGB 灯珠根据传感器类型亮起对应颜色并进行特定波形闪烁。
* **马达响应**:马达触发特定的振动次数与时长组合。
4. **手环主动发射与辨识**
* **主动求救**:用户短按手环上部的 SOS 键,手环进入 SOS 状态,并使用**出厂固定唯一 ID (Factory ID)** 作为源地址发射 433MHz 的 SOS 编码电报。
* **身份辨识**:该出厂 ID 作为手环的唯一物理地址标识,以便 IPC 摄像头可以通过与该 ID 配对,唯一识别该手环并将其与具体的用户绑定。
## 2. 总体功能需求
## 3. 协作规范与门禁说明
本项目的开发协作基于《开发规范.md》定义的“七个环节”。任何目录下文件的修改应当保证其依赖项是同步的。
在每次完成阶段性工作或代码调整后,应运行 `python .xagent/check-dirty.py` 进行脏检测自检。
手环系统应具备以下六大核心总体功能:
### 2.1 显示时间日期与状态栏
* 手环应支持软时钟走时,在主页面清晰直观地显示当前的小时、分钟、星期及日期信息。
* 主页面状态栏应实时指示当前电池电量、USB 充电状态,以及当前的系统安防运行状态(布防/撤防)。
### 2.2 系统安防布防与撤防控制
* 系统支持两种安防警戒模式:
* **布防状态**:对所有类型已配对的传感器报警信号(如门磁、红外等)均执行本地声光振动告警。
* **撤防状态**:对普通盗防类传感器信号予以忽略,仅对危及安全和紧急求救的传感器信号进行报警响应。
### 2.3 传感器无线对码配对
* 手环能捕获周围 433MHz 传感器发出的 EV1527 对码射频包,将其地址码与类型绑定记录在非易失存储区(掉电不丢失)。
* 手环支持多达 16 个传感器防区槽位的配对与法语前缀加数字序号的命名保存。
### 2.4 主动呼救与 IPC 绑定配对
* **主动求救**:当遇到紧急情况时,用户可一键快速触发主动 SOS 求救广播,向外以 2 秒为周期循环发射带有手环出厂固定唯一 ID 的 433MHz 载波信号。
* **IPC 绑定**:手环提供主动向 IPC发射对码信号的功能以便 IPC 摄像头将该手环地址绑定至特定用户,识别求救来源。
### 2.5 多维告警响应反馈
* 手环根据捕获到报警传感器的类型,执行差异化的硬件声光、马达振动和屏幕图标文字反馈,提供直观的物理告警。
### 2.6 低功耗待机与中断秒级唤醒
* 手环必须具备优秀的低功耗休眠机制,在连续无按键操作 10 秒后自动关闭屏幕并切断屏幕供电电路使单片机进入深度休眠Power-Down 挂起状态)。
* 手环在休眠状态下,支持任何物理按键按下瞬间被唤醒,以及接收到 433MHz 无线射频载波信号瞬间被唤醒,唤醒后应在极短时间内恢复系统运行与屏幕显示。
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->

View File

@@ -2,28 +2,45 @@
本章节定义手环终端的物理按键交互、报警矩阵(可编程 RGB 灯颜色与振动)以及防区划分详细需求。
## 1. 物理按键与交互定义
## 1. 物理按键与菜单交互定义
手环共配备 **5 个物理按键**:侧边 3 个(▲ 上移、▼ 下移、■ 确认),上部 2 个(相同的 SOS1、SOS2具备完全一致的并联电气逻辑
手环共配备 **5 个物理按键**:侧边 3 个(▲ 上移、▼ 下移、■ 确认/CONFIRM),上部 2 个(相同的 SOS1、SOS2具备完全一致的并联电气逻辑
| 按键类别 | 按键动作 | 常态 (时钟待机) | 对码配对模式 | 警报触发状态 |
### 1.1 页面与状态定义
* **时钟待机状态 (STATE_CLOCK)**:正常显示时间、日期、电量及布撤防状态(大字版)。
* **低功耗休眠状态 (STATE_SLEEP)**:无按键操作 10 秒后自动进入。屏幕完全关闭,拉低 `SGM_CTRL = 0` 关闭 SGM3833 供电,单片机进入 Power-Down 挂起状态。
* **主选择菜单 (STATE_MENU)**:在 `STATE_CLOCK` 下长按 **■ 确认键** 3 秒进入。单屏只显示 1 个功能,通过 ▲/▼ 键来回翻页CONFIRM 键确认进入:
1. `1. DATE/HEURE` (进入时间日期修改界面 `STATE_SET_TIME`)
2. `2. APPAIRAGE` (进入对码配对搜索界面 `STATE_PAIR_SEARCH`)
3. `3. ARM/DISARM` (进入系统布撤防手动设定界面 `STATE_ARM_DISARM_SELECT`)
4. `4. LIAISON IPC` (进入 IPC 摄像头绑定发送界面 `STATE_IPC_BIND`)
### 1.2 按键动作状态机映射
| 当前状态 | ▲ 上移键动作 | ▼ 下移键动作 | ■ 确认键 (CONFIRM) 动作 | SOS1 / SOS2 按键动作 |
| :--- | :--- | :--- | :--- | :--- |
| **侧边按键** | **▲ 上移键** | 短按:时间微调 | 菜单向上滚动 / **确认对码时自增序号 (01->99)** | 无动作 |
| **侧边按键** | **▼ 下移键** | 短按:时间微调 | 菜单向下滚动 / **确认对码时自减序号 (99->01)** | 长按 2 秒:解除当前告警并退回时钟 |
| **侧边按键** | **▲ + ▼ (同时长按3秒)** | **进入对码配对模式** | 无动作 | 无动作 |
| **侧边按键** | **■ 确认键 (CONFIRM)** | 无动作 | 菜单确认 / **对码确认页:确认保存配置** | 短按:解除当前告警并退回时钟 |
| **上部按键** | **SOS1 / SOS2 (短按)** | **触发本地主动 SOS 呼救 + 循环发射 433MHz 呼救包** | **对码确认页:确认保存配置** | 短按:解除告警并退回时钟 |
| **上部按键** | **SOS1 / SOS2 (长按5秒)** | **触发 IPC 绑定对码广播** | 无动作 | 长按:解除告警并退回时钟 |
| **时钟待机 (STATE_CLOCK)** | 无动作 | 无动作 | 长按 3 秒:进入主选择菜单 | 短按/长按:立即跳转至发送 SOS 报警 (`STATE_SOS_EMITTED`) |
| **低功耗休眠 (STATE_SLEEP)** | 短按:唤醒并恢复至时钟待机 | 短按:唤醒并恢复至时钟待机 | 短按:唤醒并恢复至时钟待机 | 短按/长按:唤醒系统并立即触发 SOS 报警 |
| **主选择菜单 (STATE_MENU)** | 短按:上一个菜单条目(切换大图标与大字) | 短按:下一个菜单条目(切换大图标与大字) | 短按:确认进入选中分支;长按 3 秒:退出菜单返回时钟 | 短按/长按:立即跳转至发送 SOS 报警 |
| **时间修改 (STATE_SET_TIME)** | 短按:增加当前选中数值 | 短按:减少当前选中数值 | 短按:保存并切换到下一个修改参数;长按 3 秒:放弃修改返回时钟 | 短按/长按:立即跳转至发送 SOS 报警 |
| **对码配对 (STATE_PAIR_SEARCH)** | 无动作 | 长按 2 秒:放弃对码返回主菜单 | 无动作 | 短按/长按:立即跳转至发送 SOS 报警 |
| **对码确认微调 (STATE_PAIR_CONFIRM)** | 短按:自增后缀序号 (01->99) | 短按:自减后缀序号 (99->01) | 短按:确认保存配置并存入 Flash返回时钟长按 3 秒:放弃保存返回时钟 | 短按/长按:立即跳转至发送 SOS 报警 |
| **布撤防手动设定 (STATE_ARM_DISARM_SELECT)** | 短按:切换布撤防高亮项 | 短按:切换布撤防高亮项 | 短按:保存选定状态返回时钟 | 短按/长按:立即跳转至发送 SOS 报警 |
| **IPC 绑定发送 (STATE_IPC_BIND)** | 无动作 | 长按 2 秒:放弃绑定返回时钟 | 短按:立刻再次发射绑定射频包;长按 3 秒:退出返回时钟 | 短按/长按:立即跳转至发送 SOS 报警 |
| **警报触发状态 (STATE_ALARMING)** | 短按:解除报警并退回时钟 | 短按:解除报警并退回时钟 | 短按:解除报警并退回时钟 | 短按/长按:解除报警并退回时钟 |
| **本地主动 SOS 发送 (STATE_SOS_EMITTED)** | 短按:解除 SOS 发送退回时钟 | 短按:解除 SOS 发送退回时钟 | 短按:解除 SOS 发送退回时钟 | 短按/长按:解除 SOS 发送退回时钟 |
> [!NOTE]
> 1. **双 SOS 按键冗余**:上部的两个 SOS 键在电路与软件上完全等价。按下其中任意一个按键,均会触发相同的 SOS 信号采集与求救动作。
> 2. **警报快速解除**:在手环发出主动 SOS 呼救或响应被动防区报警时按下“确认键”或任意一个“SOS 键”或者长按“▼键”2秒均会立刻切断声光电反馈安全重置至时钟待机状态。
> 3. **确认页序号手动修改**:当捕获到匹配 of 射频信号进入对码确认页时,界面将显示当前选定的前缀和初始序号。用户可以使用 ▲ 键增加序号,使用 ▼ 键减少序号(范围 `01` 到 `99`达到边界自动截断选中后按下“确认键”或任意“SOS 键”进行保存。
> 4. **屏幕常亮与全时射频监测**系统不进行任何低功耗关屏或停机操作。TFT LCD 屏幕始终常亮显示时钟待机画面。主频与 1ms 定时中断全时运转,配置 **`P0.0` (ADC 通道 8)** 进行电池电量采样,配置 **`P2.7`** 监测充电插拔状态。使 433MHz 射频解调模块保持 100% 实时无线监听接收,并在任何时候(包括待机和菜单状态)按下 SOS 均能实现即刻发送求救包
> 2. **一键报警优先级**:除了在报警状态和本地 SOS 发送状态下按 SOS 键为解除报警外,在其它任意状态下,短按或长按 SOS 键均拥有最高优先级,立刻强行中止当前业务并跳转至 SOS 呼救状态。
> 3. **对码自动类型识别与后缀选择**
> * 手环在接收到 433MHz 射频对码信号时,**自动根据数据码Data Code识别传感器类型**,并直接跳转到对码微调页面
> * 在微调确认页面中,传感器类型和图标以大字只读显示,用户仅需通过 ▲/▼ 键增减微调该传感器的超大数字后缀序号01~99。按 CONFIRM 键即可一键保存并写入 Flash 数据库。
> 4. **低功耗休眠与唤醒**:在待机、菜单或非报警页面下,若 **10秒内无任何按键操作**,系统将自动关闭 OLED 屏幕、切断 SGM3833 PMIC 电源,并将 MCU 挂起进入 Power-Down 停机模式。任何按键的按下或射频信号的接收都会通过中断将 CPU 瞬间唤醒。
## 2. 传感器对码与报警映射矩阵
手环接收到 433MHz 传感器射频数据后,通过匹配预设数据码(高 4 位为类型)和地址码,根据下表驱动多色 LED、马达振动波形并更新 LCD 显示。本系统支持 7 种不同的外部传感器类型,并预留了出厂预设的固定名称前缀
手环接收到 433MHz 传感器射频数据后,通过匹配预设数据码(高 4 位为类型)和地址码,根据下表驱动多色 LED、马达振动波形并更新 LCD 显示。为了方便视障及老年用户识别,显示均采用超大图标与闪烁红边框
| 传感器类型 | 数据码标识 | 预设名前缀 | LED 颜色 | 默认防区类型 | 马达振动波形 (周期 5 秒) |
| :--- | :--- | :--- | :--- | :--- | :--- |
@@ -43,9 +60,6 @@
> * **数据码**:固定为 **`0x08`** (即 `1000`)。
> * **目的**:使 IPC 摄像头能接收解码,将其与配对列表中该手环 of Factory ID 匹对,从而识别求救来源。
> [!IMPORTANT]
> **屏蔽 SOS 被动报警**:为避免两个手环互发 SOS 相互触发造成死循环报警,手环**必须强行过滤**并排除对外界发射的 SOS 求救广播(数据码为 `0x08`,或其它手环发射源)的被动接收。
## 3. ZONE 防区划分
手环支持将配对的传感器划分至不同的防区:

View File

@@ -1,107 +1,283 @@
# 架构设计 (Docs/30_architecture/main.md)
# 架构设计说明书 (Docs/30_architecture/main.md)
本章节阐述手环的硬件拓扑、引脚分配、软件状态机设计、以及 IAP Flash 数据存储设计
本章节定义手环的系统软硬件架构设计,包括引脚分配、核心状态机迁移关系、持久化数据存储结构以及软件分层框架与主循环调度流程
## 1. 硬件规格与引脚映射
---
主控芯片采用 **STC32G12K128** 单片机(工作频率 24.0 MHz单周期 8051 内核32位乘除法器
显示端采用 **1.14寸 TFT LCD 屏幕**,物理分辨率为 **135x240 像素**
## 1. 硬件原理图与引脚分配架构
### 1.1 GPIO 引脚配置表
手环的完整电路连接设计参见以下 KiCad 硬件工程文件:
* **硬件原理图**[text.kicad_sch](file:///c:/workfile/105/stc32g12k128/Hardware/text.kicad_sch)
* **PCB 布局图**[text.kicad_pcb](file:///c:/workfile/105/stc32g12k128/Hardware/text.kicad_pcb)
| 引脚号 | 引脚定义 | 模式配置 | 物理外设与功能说明 |
主控芯片采用 **STC32G12K128**(工作频率 24.0 MHz单周期 8051 内核32位乘除法器
显示端采用 **Truly 0.95寸 AMOLED 屏幕**,物理分辨率为 **120x240 像素**,控制芯片为 **RM69310**
### 1.1 GPIO 物理映射表
| 引脚号 | 引脚名称 | IO 模式 | 连接外设与功能描述 |
| :--- | :--- | :--- | :--- |
| **P2.3** | `WS2812_DI` | 双向/SPI MOSI | 连接可编程 RGB 灯珠WS2812B/FCOB-2.7)的 DIN 信号输入 |
| **P2.5** | `MOTOR` | 推挽输出/SCLK | 连接振动马达驱动电路。高电平震动,低电平静止 |
| **P0.0** | `BAT_ADC` | 模拟输入 | 电池电压采样输入端口,对应硬件 ADC 通道 8 (A8) |
| **P0.1** | `KEY_UP` | 高阻输入+上拉 | 侧边按键 ▲ (上移),按下为低电平 |
| **P0.2** | `KEY_SOS1` | 高阻输入+上拉 | 上部按键 SOS 1按下为低电平 |
| **P0.3** | `KEY_DOWN` | 高阻输入+上拉 | 侧边按键 ▼ (下移),按下为低电平 |
| **P0.4** | `KEY_CONFIRM`| 高阻输入+上拉 | 侧边按键 ■ (确认),按下为低电平 |
| **P0.2** | `KEY_SOS1` | 高阻输入+上拉 | 上部按键 SOS 1按下为低电平 |
| **P0.5** | `KEY_SOS2` | 高阻输入+上拉 | 上部按键 SOS 2按下为低电平 |
| **P3.4** | `LCD_PWR_CTRL` | 推挽输出 | LCD 屏幕背光与芯片电源控制引脚。高电平使能,低电平关闭 |
| **P1.1** | `LCD_RST` | 推挽输出 | LCD 屏幕复位引脚 |
| **P1.3** | `LCD_RS` | 推挽输出 | LCD 屏幕 SPI 数据/命令寄存器选择 (D/C) |
| **P1.4** | `LCD_CS` | 推挽输出 | LCD 屏幕 SPI 片选信号 |
| **P1.5** | `LCD_SCL` | 推挽输出 | LCD 屏幕 SPI 时钟信号 |
| **P1.6** | `LCD_SDA` | 推挽输出 | LCD 屏幕 SPI 数据信号 |
| **P0.0** | `BAT_ADC` | 模拟输入 | 电池电压采样输入端口,对应硬件 ADC 通道 8 (A8) |
| **P2.3** | `WS2812_DI` | SPI MOSI | 连接可编程双级联 RGB 灯珠的 DIN 信号输入 |
| **P2.5** | `MOTOR` | 推挽输出 | 连接振动马达驱动电路。高电平震动,低电平静止 |
| **P2.7** | `USB_DET` | 高阻输入 | USB 插入充电检测引脚,高电平代表正在充电 |
### 1.2 上部双 SOS 键并联逻辑设计
为了保证两颗 SOS 按键在软件逻辑上等价,软件在按键消抖和长按状态判定时,采用“逻辑并联”设计。定义一个虚拟的 `is_sos_pressed` 状态标志:
```c
// 只要 SOS1 或 SOS2 有任意一个被按下(低电平),即判定为 SOS 按下
bit is_sos_pressed = (KEY_SOS1 == 0) || (KEY_SOS2 == 0);
```
该标志后续被直接送入消抖状态机和长按计时器,生成 `KEY_SOS_SHORT``KEY_SOS_LONG` 事件,从而完全对上层业务层屏蔽双按键硬件细节。
### 1.3 出厂唯一固定 ID (Factory ID) 的设计与获取
手环在主动发射对码广播与主动 SOS 报警时,需要以唯一的出厂 ID (Factory ID) 作源地址以供接收端IPC进行辨识。
为避免每台手环烧写不同固件,程序从 STC32G 内置的全球唯一硬件 ID (UID) 中提取地址码:
* **硬件读取源**STC32G 芯片在出厂时会在程序存储器ROM的末尾扇区存储一个 **7 字节的唯一 ID**,同样在 RAM 空间的特定区(如 `0xF1`~`0xF7`)也会在启动时映射。
* **Factory ID 生成**:系统初始化时,通过读取 UID 寄存器,将其中的 3 个字节提取出来,并进行 `& 0x0FFFFF`20位掩码限制以符合 EV1527 的 20 位地址规范),存入全局变量 `bracelet_factory_id` 中。若读取 UID 失败,则默认使用硬编码预置 ID `0x37A86UL`
### 1.4 SPI 复用与马达防噪硬件规避设计
由于 `P2.5` 同时作为马达引脚和硬件 SPI 的 SCLK 引脚,为了避免在高频发送 24-bit 灯效数据时引脚电平抖动导致马达异常鸣叫:
1. **发送前**:调用 `EA = 0` 关闭总中断。临时将 `P2.5` 引脚配置为 **高阻输入模式**。此时 SCLK 输出时钟不会使马达导通。
2. **发送中**:通过硬件 SPI 直接串行写入 72 位转换数据24位 RGB 经过 LUT 展宽)。
3. **发送后**:将 `P2.5` 引脚改回 **推挽输出模式**。调用 `EA = 1` 开启中断,继续通过常规 IO 控制马达电平。
### 1.5 确认配对时的后缀序号调整机制
在对码确认状态 (`STATE_PAIR_CONFIRM`) 下,系统支持用户手动微调所要保存同类防区的数字序号:
* **全局变量定义**`u8 selected_suffix_num` 用以记录当前的数字选择值。
* **初始默认分配**:当信号被捕获跳转到该状态时,系统自动检索当前内存对码表,统计捕获类型已登记的个数 $C$,将 `selected_suffix_num` 初始化为 $C + 1$。
* **按键调整行为**
* 读取到 `KEY_EVENT_UP_CLICK` 时:`if (selected_suffix_num < 99) selected_suffix_num++`
* 读取到 `KEY_EVENT_DOWN_CLICK` 时:`if (selected_suffix_num > 1) selected_suffix_num--`
* 调值完成后置 `Redraw = 1` 以触发 LCD 快速局部刷新。
* **最终确认写入**:当用户按下 CONFIRM 键或 SOS 键时,系统自动取出对应的预设名前缀(如 `PORTE`),并将 `selected_suffix_num` 转换为两位字符串格式(如 `02`),以 `[前缀] [序号]` 格式拼装后(最大 15 字节,如 `PORTE 02`),调用数据库写入函数。
| **P3.4** | `SGM_CTRL` | 推挽输出 | OLED 供电芯片 SGM3833 控制引脚。高电平使能,低电平关闭 |
---
## 2. 核心软件业务状态机 (SystemState)
## 2. 软件核心业务状态机
系统处于主循环中,根据事件和计数器在以下状态(`SystemState`)间迁移
系统包含 10 个业务状态,其状态迁移关系如下图所示
```mermaid
stateDiagram-v2
[*] --> STATE_NORMAL
STATE_NORMAL --> STATE_ARMED : 接收到布防广播
STATE_ARMED --> STATE_DISARMED : 接收到撤防广播
STATE_DISARMED --> STATE_NORMAL : 自动恢复/按键返回
[*] --> STATE_CLOCK
STATE_NORMAL --> STATE_PAIR_MENU : ▲ + ▼ 长按 3 秒
STATE_PAIR_MENU --> STATE_PAIR_WAIT : 确认选中类型
STATE_PAIR_WAIT --> STATE_PAIR_CONFIRM : 捕获 433MHz 匹配数据
STATE_PAIR_CONFIRM --> STATE_PAIR_SUCCESS : 按 CONFIRM/SOS 键并保存名称
STATE_PAIR_CONFIRM --> STATE_PAIR_FAIL : 超时/长按▼取消
STATE_PAIR_SUCCESS --> STATE_PAIR_MENU : 2秒后自动返回
STATE_PAIR_FAIL --> STATE_PAIR_MENU : 2秒后自动返回
%% 休眠与唤醒
STATE_CLOCK --> STATE_SLEEP : 10秒无操作超时
STATE_SLEEP --> STATE_CLOCK : 中断唤醒 (按键/无线信号)
STATE_NORMAL --> STATE_SOS_EMITTED : 短按 SOS
STATE_ARMED --> STATE_ALARMING : 捕获已配对传感器报警
STATE_ALARMING --> STATE_NORMAL : 按任意键/超时解除
STATE_SOS_EMITTED --> STATE_NORMAL : 长按任意键/超时解除
%% 主选择菜单迁移
STATE_CLOCK --> STATE_MENU : CONFIRM 长按 3 秒
STATE_MENU --> STATE_CLOCK : CONFIRM 长按 3 秒 / 10秒无操作超时
STATE_MENU --> STATE_SLEEP : 10秒无操作超时
%% 子设置页面迁移
STATE_MENU --> STATE_SET_TIME : 选中 "1. HORLOGE" + 确认键单击
STATE_SET_TIME --> STATE_CLOCK : 保存退出 / 10秒无操作超时
STATE_MENU --> STATE_PAIR_SEARCH : 选中 "2. APPAIR." + 确认键单击
STATE_PAIR_SEARCH --> STATE_PAIR_CONFIRM : 捕获 433MHz 传感器数据 (自动识别传感器类型)
STATE_PAIR_CONFIRM --> STATE_CLOCK : 序号微调后 CONFIRM 确认保存
STATE_PAIR_SEARCH --> STATE_MENU : 长按 ▼ 键退出
STATE_MENU --> STATE_ARM_DISARM_SELECT : 选中 "3. ALARME" + 确认键单击
STATE_ARM_DISARM_SELECT --> STATE_CLOCK : 选择完成并返回
STATE_MENU --> STATE_IPC_BIND : 选中 "4. LIAISON" + 确认键单击
STATE_IPC_BIND --> STATE_CLOCK : 发射结束 / 确认键长按 3s 退出 / ▼键长按 2s 退出
%% 报警迁移
STATE_CLOCK --> STATE_SOS_EMITTED : 按下上部任意 SOS 键 (点击/长按)
STATE_SLEEP --> STATE_SOS_EMITTED : 中断唤醒直接报警
STATE_SOS_EMITTED --> STATE_CLOCK : 按任意键/超时退出
STATE_CLOCK --> STATE_ALARMING : 捕获已配对传感器报警 (ZONE 0 或布防下的 ZONE 1)
STATE_ALARMING --> STATE_CLOCK : 按任意键/超时退出
```
### 2.1 低功耗设计与唤醒架构
为实现超低功耗,系统在 `STATE_SLEEP` 状态下使用以下硬件与软件协同设计:
1. **彻底切断 PMIC**:拉低 `SGM_CTRL = 0`,使 SGM3833 停止工作,彻底消除 Truly AMOLED 屏的负压静态功耗。
2. **休眠配置**:允许 P0 口电平变化中断与射频接收引脚的中断使能。
3. **进入停机**:将单片机挂起写入 `PCON |= 0x02` 进入停机模式,关闭主时钟。
4. **中断唤醒**:按键按下或 LR690L 接收到载波变化时,触发中断唤醒 MCUISR 立即拉高 `SGM_CTRL = 1` 并延时约 50ms 重新初始化 RM69310 屏幕控制器以恢复显示。
---
## 3. 持久化配置数据库架构
为了确保配对数据掉电不丢失,手环使用内置 IAP 读写 Flash 扇区:
* **物理存储扇区**`0xFE0000` (末尾扇区,大小为 512 字节)。
* **数据库容量**:共 16 组传感器配置槽位。
* **存储结构定义**
```c
typedef struct {
u8 is_used; // 有效标志 (0x01: 有效, 其它: 空闲)
u8 addr[3]; // 24位射频物理地址 (EV1527)
u8 type; // 传感器类型 (0: 门磁, 1: 红外, 2: 烟感, 3: 紧急, 4: 气体, 5: 水浸, 6: 振动)
u8 zone; // 防区类别 (0: 永久防区, 1: 普通可撤布防区)
char name_gbk[16]; // 法语自定义防区名称 (格式化为: [名前缀] [两位序号])
} Sensor_Slot;
```
---
## 3. IAP Flash 传感器数据库设计
## 4. 软件层级架构与核心代码框架
为了断电保存传感器配对信息,使用 STC32G 的 IAP 功能读写 Flash
* **扇区基地址**`0xFE0000` (末尾扇区)。
* **存储结构**`Sensor_Slot` 结构体:
```c
typedef struct {
u8 is_used; // 登记标志0x01表示有效其它代表空闲
u8 addr[3]; // 24位射频物理地址 (EV1527)
u8 type; // 传感器类型 (0:门磁, 1:红外, 2:烟感, 3:紧急, 4:气体, 5:水浸, 6:振动)
u8 zone; // 防区类别 (0: 永久防区, 1: 普通可撤防区)
char name_gbk[16]; // 法语自定义防区名称 (以 [前缀] [序号] 自动格式化写入)
} Sensor_Slot;
```
* 一共支持 **16** 个传感器槽位,通过 `Load_Database()` 和 `Save_Database()` 进行统一序列化读写。
软件系统采用 **非阻塞** 的三层级模块化架构设计
```mermaid
graph TD
subgraph Layer3 [应用层 (APP)]
MainScheduler[主循环轮询 & 喂狗]
SysStateMachine[SystemState 业务状态机]
SoftRTC[软件 RTC 计时器]
end
subgraph Layer2 [外设驱动层 (DRV)]
AMOLED[RM69310 AMOLED 屏幕绘制]
RF[LR690L 编解码 & 接收处理]
KEY[按键消抖 & 长按长计时]
PM[低功耗休眠与唤醒]
LED[WS2812B 级联双彩灯时序]
MOTOR[振动马达脉冲时序]
end
subgraph Layer1 [硬件抽象与时基层 (HW)]
Timer1[Timer 1 中断 (1ms 滴答)]
ADC[12位电压采集]
Flash[IAP 读写]
GPIO[数字 I/O 与电平中断]
end
Layer1 --> Layer2
Layer2 --> Layer3
```
### 4.1 核心主程序调度框架 (main.c)
主程序采用完全解耦的时间片轮询加事件触发的非阻塞循环结构:
```c
#include "config.h"
// 全局状态变量
SystemState current_state;
u16 inactivity_timer;
u32 rx_addr;
u8 rx_type;
void main(void) {
// 1. 初始化底层硬件与外设驱动
System_Init();
Power_Init(); // SGM_CTRL 默认拉高工作
LCD_Init(); // Truly AMOLED 初始化
RF_Init(); // LR690L 接收中断与发射初始化
WS2812_Init(); // 双级联彩灯初始化
Motor_Init(); // 马达脉冲时序初始化
ADC_Init(); // 电池采样初始化
// 2. 载入持久化数据配置
Load_Database();
// 3. 初始化软件状态与计时器
current_state = STATE_CLOCK;
inactivity_timer = 0;
Redraw = 1;
// 开启全局中断
EA = 1;
// 4. 核心主循环轮询调度
while (1) {
// A. 轮询射频接收
if (EV1527_Decode(&rx_addr, &rx_type)) {
inactivity_timer = 0; // 重置休眠计数
Process_RF_Event(rx_addr, rx_type);
}
// B. 轮询并消耗按键事件
if (key_event_buf != KEY_EVENT_NONE) {
KeyEvent evt = key_event_buf;
key_event_buf = KEY_EVENT_NONE; // 清空事件
inactivity_timer = 0; // 重置休眠计数
Process_System_Event(evt);
}
// C. 超时低功耗检测判定
if (inactivity_timer >= 1000) { // 10秒无操作
inactivity_timer = 0;
current_state = STATE_SLEEP;
// 执行硬件关屏断电并让单片机进入深度休眠
Enter_Low_Power_Sleep();
// 被外部中断唤醒后从此处开始继续向下运行,执行电源与显示恢复
Wakeup_Restore();
current_state = STATE_CLOCK;
Redraw = 1;
}
// D. 屏幕刷新渲染调度
if (Redraw) {
Redraw = 0; // 清除标志
Render_UI_Screen(current_state);
}
// E. 喂狗操作
WDT_CONTR = 0x17;
}
}
```
---
## 5. 模块间输入/输出事件与流转处理消耗图
为了使各功能模块Drivers & App交互逻辑清晰可见下表整理了各模块产生的事件源输入及其被消费输出的处理链路
### 5.1 模块输入/输出事件矩阵
| 模块名称 | 输入源 (触发源) | 产生/发出事件 | 输出消费对象 (消费处理) |
| :--- | :--- | :--- | :--- |
| **按键模块 (KEY)** | 物理引脚P0.1~P0.5)电平状态改变 | `KeyEvent`<br>- `KEY_UP_SHORT` / `KEY_DOWN_SHORT`<br>- `KEY_DOWN_LONG` (长按▼ 2秒)<br>- `KEY_CONFIRM_CLICK`<br>- `KEY_CONFIRM_LONG` (长按■ 3秒)<br>- `KEY_SOS_SHORT` / `KEY_SOS_LONG` (SOS键短按或长按) | 写入全局事件缓冲队列 `key_event_buf`。由 **APP 应用状态机** 读出后用于控制页面导航、退出告警或启动对码。 |
| **无线射频 (RF)** | LR690L 433MHz 载波信号接收(输入电平变化) | `RFEvent` (包含 `rx_addr``rx_type`) | 传递至 **APP 状态机**`Process_RF_Event` 进行报警分发。 |
| **状态机 (APP)** | 消耗 `key_event_buf``RFEvent` | `SysState` 业务状态跳转及动作指令 | 1. 改变 `current_state` 并设置 `Redraw = 1`<br>2. 调度 **LED****MOTOR** 开启报警灯效/马达振动;<br>3. 写入 **IAP Flash 数据库** 配对记录;<br>4. 调用 **RF** 发送对码或主动SOS信号。 |
| **显示模块 (LCD)** | 状态机重绘请求 `Redraw == 1` | SPI 像素渲染显示流 | 驱动 **Truly AMOLED** 渲染当前状态所对应的时钟、主菜单、对码、时间设定或报警画面。 |
| **供电管理 (POWER)**| 1. `inactivity_timer >= 1000` (10秒超时)<br>2. 中断唤醒信号 (GPIO/INT2) | 电源电平控制及停机状态 | 1. **进入休眠**:发出关屏指令 -> 拉低 `SGM_CTRL = 0` -> 执行停机挂起(主循环暂停在 `Enter_Low_Power_Sleep` 内);<br>2. **唤醒恢复**:拉高 `SGM_CTRL = 1` -> 延时 50ms -> 重新初始化 RM69310 屏幕控制器(主循环恢复运行并重绘时钟)。 |
| **彩灯模块 (LED)** | 状态机调用报警指示请求 | SPI 48-bit 灯珠级联控制字 | 向两颗级联的 WS2812B 发送颜色数据(报警时交替闪烁,配对时常亮)。 |
| **马达模块 (MOTOR)**| 状态机请求按键反馈/告警振动 | IO 电平状态控制字 (P2.5) | 定时驱动 `P2.5` 引脚电平产生不同振动波形(如短振、长振、间歇循环振动)。 |
### 5.2 事件流转与消费链路拓扑
手环系统的输入事件(按键中断与无线载波中断)在主循环中的处理与消耗流转关系如下:
```mermaid
flowchart TD
%% 硬件输入事件
PinKey[物理按键按下 / 下降沿] -->|P0.1-P0.5 端口中断| ISR_Port0[Port0 中断 ISR]
Carrier[433MHz 射频载波信号] -->|INT2 中断| ISR_INT2[INT2 唤醒中断 ISR]
%% 中断服务层 (ISR)
ISR_Port0 -->|唤醒系统并每10ms扫描| KeyScan[消抖与长短按判定]
ISR_INT2 -->|唤醒系统并使能软解码| RFDecode[EV1527 自适应比值解码]
%% 事件过滤与缓冲层
KeyScan -->|判定有效按键事件| BufKey[写入 key_event_buf 缓冲区]
RFDecode --> ProcessRF[生成合法传感器 RFEvent]
%% APP 状态机消费处理
BufKey -->|主循环读取并重置| SM_Key[消费按键事件]
ProcessRF -->|主循环调用并比对配置| SM_RF[消费已配对防区报警]
SM_Key -->|CONFIRM 长按 3s| StateMenu[切入 STATE_MENU 并 Redraw=1]
SM_Key -->|子页面确认操作| StateSub[执行配对/布防/时间设定并 IAP 写入]
SM_Key -->|点击/长按 SOS 键| EmitSOS[调用 RF 主动发射 0x08 SOS 信号]
SM_Key -->|告警状态任意键| ClearAlarm[清除本地报警状态并退回时钟]
SM_RF -->|匹配成功且处于布防| LocalAlarm[触发 LED 交替闪烁 & 马达间歇振动]
%% 显示与控制响应
StateMenu -->|Redraw == 1| LcdPaint[Truly AMOLED 屏幕局部像素重绘]
StateSub -->|Redraw == 1| LcdPaint
LocalAlarm -->|Redraw == 1| LcdPaint
EmitSOS -->|Redraw == 1| LcdPaint
```
---
## 6. 核心业务事件输入输出 (I/O) 架构明细表
为实现绝对清晰的软硬件交互链路,以下为手环核心业务事件的端到端输入、输出及流转逻辑汇总:
### 6.1 核心事件 I/O 矩阵
| 事件名称 | 输入源 (触发条件与参数) | 状态机判定逻辑 (当前状态与条件) | 输出结果 & 动作执行 (显示、灯效、马达与无线) |
| :--- | :--- | :--- | :--- |
| **按键操作事件**<br>`KEY_EVENT` | **源**P0.1~P0.5 下降沿中断。<br>**参数**`KEY_UP``KEY_DOWN``KEY_CONFIRM` (短按/长按)。 | **当前状态**`STATE_CLOCK` / `STATE_MENU` 等。<br>**条件**:在待机下长按确认键 3 秒进入菜单;在菜单下短按 ▲/▼ 键翻页。 | 1. **显示**:置 `Redraw = 1`LCD 刷新切换对应页面;<br>2. **触觉**:发出 50ms 极短振动作按键反馈。 |
| **主动求救事件**<br>`SOS_ACTIVE_EVENT` | **源**:上部任意 SOS 按键按下(短按或长按)。 | **当前状态**:除已报警外的任意业务状态。<br>**条件**:无视当前状态,拥有最高抢占优先级。 | 1. **状态**:切入 `STATE_SOS_EMITTED`<br>2. **无线**:通过 LR690L 周期性2s发射手环 Factory ID 及 `0x08` (SOS) 数据包;<br>3. **显示**LCD 闪烁呈现超大警灯与分行 `SOS EMIS` 字样;<br>4. **灯效**WS2812B 红色高速交替闪烁50ms周期<br>5. **马达**:持续间歇长振(振 1000ms停 200ms。 |
| **无线对码捕获事件**<br>`RF_PAIR_RECV_EVENT` | **源**LR690L 接收 433MHz 对码载波包。<br>**参数**24-bit 物理地址 + 4-bit 传感器类型数据码。 | **当前状态**`STATE_PAIR_SEARCH` (搜索对码中)。<br>**条件**:捕获到合法 EV1527 帧。 | 1. **状态**:切换至 `STATE_PAIR_CONFIRM` (确认微调)<br>2. **显示**:只读显示自动识别的传感器名称(如 `PORTE`)、大图标,以及超大 `[ 01 ]` 序号选框;<br>3. **马达**:发出 300ms 短振提醒成功捕获。 |
| **传感器入侵警报事件**<br>`RF_ALARM_RECV_EVENT` | **源**LR690L 接收 433MHz 警报载波包。<br>**参数**:传感器地址 + 4-bit 状态码。 | **当前状态**`STATE_CLOCK` (待机)。<br>**条件**1. 地址与 Flash 数据库匹配;<br>2. 该防区属于 ZONE 0 (24h) 或处于已布防状态下的 ZONE 1。 | 1. **状态**:切入 `STATE_ALARMING`<br>2. **显示**LCD 亮起 4px 闪烁红边框与 64x64px 对应传感器报警图标,显示 `PORTE 01 INTRUSION!`<br>3. **灯效**WS2812B 闪烁对应传感器防区色彩500ms闪烁<br>4. **马达**:根据传感器类型,输出对应的间歇振动波形(如门磁短振 1 次)。 |
| **系统自动休眠事件**<br>`AUTO_SLEEP_EVENT` | **源**:软件时钟 `inactivity_timer >= 1000` (10s)。 | **当前状态**`STATE_CLOCK` / `STATE_MENU` / `STATE_SET_TIME` 等非报警状态。 | 1. **状态**:切入 `STATE_SLEEP`<br>2. **硬件**:拉低 `SGM_CTRL = 0` 切断 AMOLED PMIC 供电;<br>3. **电源**:使能 P0 口电平中断及 INT2挂起 CPU 进入 Power-Down 极低功耗模式。 |
| **中断唤醒系统事件**<br>`SYSTEM_WAKEUP_EVENT` | **源**P0口按键沿变化或 INT2 载波接收中断。 | **当前状态**`STATE_SLEEP` (深度休眠中)。 | 1. **状态**:切回 `STATE_CLOCK`<br>2. **电源**:拉高 `SGM_CTRL = 1` 恢复 OLED 供电;<br>3. **延迟**:延时 50ms 避开冲击电流并完成 RM69310 的重初始化;<br>4. **显示**:置 `Redraw = 1` 重绘时钟走时界面。 |
| **IPC 摄像头对码事件**<br>`IPC_BIND_SEND_EVENT` | **源**:在菜单中确认进入 `4. LIAISON`。 | **当前状态**`STATE_IPC_BIND`。 | 1. **无线**:连发 433MHz 手环 Factory ID 及 `0x01` 绑定对码包;<br>2. **显示**:呈现 48x48px 摄像头大图标及 `LIAISON EN COURS`<br>3. **退出**:长按确认键 3 秒或长按 ▼ 键 2 秒,退出并恢复时钟。 |
| **警报解除复位事件**<br>`ALARM_CLEAR_EVENT` | **源**:报警状态下侧边或上部任意按键按下。 | **当前状态**`STATE_ALARMING``STATE_SOS_EMITTED`。 | 1. **状态**:切回 `STATE_CLOCK`<br>2. **灯效**WS2812B 彻底熄灭;<br>3. **马达**:清空马达状态并立即停振;<br>4. **显示**:置 `Redraw = 1`LCD 重绘走时时钟。 |

View File

@@ -1,85 +1,124 @@
# UI 设计 (Docs/40_ui-design/main.md)
本章节定义手环 135x240 TFT LCD 屏幕的画面布局、各页面坐标位置、中文字体/ASCII 字符对齐基线以及局部刷新逻辑。
本章节定义手环 120x240 Truly AMOLED 屏幕的画面布局、各页面坐标位置、中文字体/ASCII 字符对齐基线以及局部刷新逻辑。
针对**特殊人群(如视力障碍者、老年人)**的需求,本界面采取了**大字版与大图标无障碍Accessibility / A11y设计系统**。
## 1. 屏幕物理参数与刷新机制
---
* **屏幕规格**1.14寸 TFT LCD 屏,物理分辨率 **`135x240`**。
* **竖向屏规范**:屏幕窗口配置为 **`135` 像素宽,`240` 像素高**。页面中的时钟、菜单、报警元素均采用竖屏垂直居中排版。
* **坐标偏移修正**TFT LCD 驱动芯片ST7789V的显示 RAM 大小为 240x320 像素。在控制 135x240 的竖屏物理面板时,为了将显示窗口完美对齐至显示区域,防止花屏或偏置错误,写屏窗口渲染必须应用 **X 轴偏移量 +52Y 轴偏移量 +40**。即:`LCD_SetWindow(x1 + 52, x2 + 52, y1 + 40, y2 + 40)`
* **局部刷新防抖机制**:清除重绘标志(`Redraw`)的动作必须在具体的刷新绘制块结束时重置,防止因为主循环高速轮询而产生屏幕闪烁。
## 1. 无障碍A11y大字设计系统规范
为确保特殊人群能清晰阅读,且文本在 120 像素宽的屏幕内**不超出边界**,设计遵循以下准则:
1. **文本宽度与换行原则**
* 在 120 像素宽的屏幕上,单行 16x32px 大字符最多显示 **7 个字符**
* 如果提示语较长(如 `APPEL EN COURS`),必须将其**缩短或分成多行显示**(如第一行 `APPEL`,第二行 `SOS`)。
* 菜单项名称进行合理缩短,如 `1. DATE/HEURE` 缩短为 `1. HORLOGE``2. APPAIRAGE` 缩短为 `2. APPAIR.`,以保证在 16x32px 字体下不溢出。
2. **最小字号限制**:取消所有 8x16px 或更小的微缩文本。基础信息字号不小于 **16x32px**
3. **核心电量与状态图标放大**
* 为了使用户能清晰看清剩余电量,电池电量数字放大为 **16x32px** 粗体。
* 电池图标放大为 **24x12px** 粗边框,放置在状态栏右上角。
* 状态锁图标放大为 **32x32px**,放置在状态栏左上角。
4. **超大核心数据**:时间数字、设定参数、传感器序号等使用 **32x64px**(超大点阵字模)呈现。
5. **大图标辅助**:所有页面均配备 **48x48px 或 64x64px 的超大单色位图图标**,结合高对比度色彩(红/绿/黄/黑),方便用户一眼识别功能。
6. **单页单焦点排版**:避免多行数据堆叠。菜单、时间设定、对码等页面在屏幕中**每次只显示当前编辑或选中的一个条目**,把所有的屏幕空间留给最大化的字体和图标。
7. **坐标偏移修正** Truly AMOLED 屏幕写屏窗口渲染必须应用 **X 轴偏移量 +4Y 轴偏移量 +0**。即:`LCD_SetWindow(x1 + 4, x2 + 4, y1, y2)`
---
## 2. 页面布局与坐标坐标规范
### 2.1 时钟待机界面 (STATE_NORMAL / STATE_ARMED / STATE_DISARMED)
### 2.1 时钟待机界面 (STATE_CLOCK)
时钟页面作为常态显示,包括状态栏、数字时钟和日期行。
```
+-----------------------------------------+
| 🔒 ARME 🔋 85% | <- 状态栏 (Y=24~27)
| [🔒 32x32] 🔋 85%+ | <- 状态栏 (Y=15~35) (电池图标与16x32px电量)
| |
| |
| 10:35 | <- 中央时间 (Y=80~128)
| 10:35 | <- 居中超大时间 (Y=70~134) (32x64px字模)
| |
| |
| VEN. 07-10 | <- 底部日期 (Y=180)
| VEN. 07-10 | <- 底部日期 (Y=180~212) (16x32px字模)
| |
+-----------------------------------------+
```
* **状态栏电量显示(极重要)**
* **电池图标** 绘制在 `X=107, Y=27`
* **电量百分比** 绘制在 `X=77, Y=24`。如果处于充电状态,追加 `+` 号后缀显示 `85%+`,起始绘制点向左平移(如 `X=69`)以确保与电池图标之间依然有安全间距
* 两个控件在 `135` 像素宽的屏幕上保持 **6 像素安全间距**,彻底防范乱码和字符相撞。
* **电池图标** 放大绘制在 `X=86, Y=19, W=24, H=13`
* **电量百分比** 采用 `16x32px` 字模,右对齐。充电时显示 `+`
* **安防状态显示**
* **布防状态 (ARMED)**:状态栏左侧显示红色闭锁 `🔒` 图标,显示 `ARME`
* **撤防状态 (DISARMED)**:状态栏左侧显示绿色开锁 `🔓` 图标,显示 `DESARME`
* 状态栏左侧绘制 **32x32px** 的超大布撤防锁图标:红色闭锁 `🔒`(布防),绿色开锁 `🔓`(撤防),不再显示小文字,使用户看图即懂
### 2.2 传感器配对选择菜单 (STATE_PAIR_MENU)
### 2.2 选择菜单页面 (STATE_MENU)
* **顶部标题**:显示 `MODE PAIR P-XX`(其中 XX 为当前已配对的传感器数量)。
* **列表滚动框**:采用 3 行滚动列表,可在 **7 种传感器类型**(门磁、红外、烟感、紧急、气体、水浸、振动)中上下循环滚动选中。当前行高亮并显示粗体外框,字号放大(如 `DETEC. PORTE`),非当前行暗淡显示。
* **按键确认行为**:在配对菜单页面,短按侧边 **■ 确认键** 进入所选防区类型的对码等待状态。
### 2.3 报警触发与本地 SOS 界面 (STATE_ALARMING / STATE_SOS_EMITTED)
* **报警提示边框**:全屏闪烁的红色警告虚线/实线边框。
* **中央图标**根据触发的防区传感器类型居中绘制对应的单色位图图标32x32px
* 门磁:`🚪` 开门图标。
* 红外:`👤` 人体辐射波纹图标。
* 烟感:`🔥` 火苗图标。
* 紧急按钮:`🆘` 求救图标。
* 燃气:`💨` 毒气云雾图标。
* 水浸:`💧` 滴水波纹图标。
* 振动:`📳` 震动线框图标。
* **主动 SOS 界面**:大字显示 `SOS EMIS`,居中显示 `🚨` 旋转警灯图标。
* **绑定 IPC 界面 (STATE_BINDING)**
* 顶部标题:`LIAISON IPC` (法语:与 IPC 绑定)。
* 中央:正在发送提示,显示 `ENVOI EN COURS`,配对确认锁图标。
* **手环 Factory ID 显示**:底部居中显示手环自身出厂地址:`ID: 0xXXXXX`,以便用户配对及辨识。
### 2.4 对码确认与序号微调界面 (STATE_PAIR_CONFIRM)
当手环解码到传感器对码信号后,切入此确认页:
由时钟待机长按 CONFIRM 键 3 秒进入。显示四个功能条目,供滚动选择:
```
+-----------------------------------------+
✓ RECU (对码成功) (Y=40)
[ 🚪 ICON ] (Y=80)
PORTE (Y=135)
[ 01 ] (Y=165) <- 序号微调框 (可按 ▲/▼ 修改)
| - MENU - | <- 标题 (Y=20) (16x32px)
| |
| [⏰ 48x48] | <- 选中项大图标 (Y=60~108)
| |
| 1. HORLOGE | <- 选中项大字文本 (Y=150~182) (16x32px)
| |
| ▲ / ▼ | <- 翻页提示符 (Y=205) (16x32px)
+-----------------------------------------+
```
* **元素定位**
* 顶部显示提示语 `✓ RECU`(法语:已接收),绿字。
* 中央显示捕获传感器 32x32 对应位图图标
* 下方第一行展示传感器名称前缀(如 `PORTE`)。
* 下方第二行展示序号选框 `[ 01 ]`:其中包含两个外括号,中间的数字即为 `selected_suffix_num`01~99。当用户按 ▲ 或 ▼ 键时,中间数字动态增减并实时局部刷新,外括号保持静止,增强可编辑交互感。
* **确认行为**:短按 CONFIRM 键或 SOS 键保存退出。
* 屏幕中央显示当前选中功能的 **48x48px** 图标,下方以 **16x32px** 粗体显示功能名称。
* 用户按 ▲ 或 ▼ 键时,页面以幻灯片形式切换:
1. `1. HORLOGE` (对应 ⏰ 时钟图标)
2. `2. APPAIR.` (对应 📡 雷达对码图标)
3. `3. ALARME` (对应 🔒 防区盾牌图标)
4. `4. LIAISON` (对应 📷 摄像头图标)
* 短按 CONFIRM 键确认进入当前显示的功能。
### 2.3 功能设定与子界面设计
#### 2.3.1 时间日期修改界面 (STATE_SET_TIME)
为了避免小字,时间设置每次只微调一个数值:
* **页面指示**:顶部大字显示当前修改的字段,例如 `REGLER` (16x32px)。
* **中央数值**:正中央显示正在修改的两位数值(如小时 `10`),字号采用 **32x64px** 超大号。数值上下有明显的闪烁三角箭头指示。
* **交互**:按 ▲ / ▼ 调整该值。短按 CONFIRM 保存该值并切入下一项(分 $
ightarrow$ 秒 $
ightarrow$ 年 $
ightarrow$ 月 $
ightarrow$ 日)。
#### 2.3.2 传感器对码等待与配对界面 (STATE_PAIR_SEARCH)
* **顶部标题**:显示 `APPAIRAGE` (16x32px)。
* **中央图案**绘制占满屏幕中部的雷达搜索大图标64x64px并闪烁。
#### 2.3.3 对码确认与序号微调界面 (STATE_PAIR_CONFIRM)
当手环解码到传感器对码信号后,系统自动依据 4-bit 数据码解析出传感器类型,直接切入此确认页进行序号编辑:
```
+-----------------------------------------+
| ✓ RECU | <- 状态提示 (Y=20) (16x32px)
| |
| [🚪 48x48] | <- 传感器大图标 (Y=55~103)
| |
| PORTE | <- 类型名称 (Y=120) (16x32px)
| |
| [ 01 ] | <- 序号微调框 (Y=170~234) (32x64px)
+-----------------------------------------+
```
* **元素布局**
* **传感器图标** 使用 **48x48px** 超大单色位图。
* **类型前缀** 使用 **16x32px**
* **数字序号选框** 使用 **32x64px** 超大数字。用户按 ▲/▼ 直接微调序号CONFIRM 键保存。
#### 2.3.4 系统布撤防手动选择界面 (STATE_ARM_DISARM_SELECT)
* **页面显示**:屏幕正中央显示当前拟设定的状态:
* `[ ARME ]`(配 48x48px 盾牌图标)
* `[ DESARME ]`(配 48x48px 虚线开锁图标)
* 字体全部使用 **16x32px**。按 ▲/▼ 键来回切换,按 CONFIRM 键保存生效。
#### 2.3.5 IPC 绑定发射界面 (STATE_IPC_BIND)
* **图形指示**:正中央显示 **48x48px** 的摄像头绑定大图标,配合动态声波向外发射动画。
* **大字提示**:上方显示 `LIAISON` (16x32px),下方显示 `EN COURS` (16x32px)。
* **物理地址展示**:底部以 8x16px 显示手环地址 `ID: 0xXXXXX`
* **退出/返回**:长按 CONFIRM 键 3 秒 或 长按 ▼ 键 2 秒主动退出。
### 2.4 报警触发与本地 SOS 界面 (STATE_ALARMING / STATE_SOS_EMITTED)
手环发生报警时的显示要具有极强的视觉冲击力:
* **警告框**:全屏外围红绿交替闪烁的 4 像素宽粗线警告框。
* **中央图标**:根据传感器类型,居中绘制对应的 **64x64px** 的超大单色位图图标(如巨大的火苗 `🔥`、门锁 `🚪`)。
@@ -87,6 +126,6 @@
---
* 对于标准 8px 宽字符X 轴绘制起点为 `(135 - len * 8) / 2`
* 对于双倍放大 16px 宽时钟字符X 轴绘制起点为 `(135 - len * 16) / 2`
## 3. 文字居中对齐与字模基线规范
为保证排版美观,[Drivers/lcd_font.h](file:///c:/workfile/105/stc32g12k128/Drivers/lcd_font.h) 内的标准字模点阵需要满足如下基线对齐:

View File

@@ -1,8 +1,8 @@
<!DOCTYPE html>
<html lang="en">
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>Wristband LCD UI Gallery</title>
<title>手环 AMOLED 屏幕无障碍大字版 UI 效果图画册 (A11y V3.4)</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
@@ -27,7 +27,16 @@
text-align: center;
color: #94A3B8;
font-size: 1.1rem;
margin-bottom: 50px;
margin-bottom: 30px;
}
.section-title {
font-size: 1.4rem;
color: #FFB74D;
border-left: 4px solid #FFB74D;
padding-left: 12px;
margin-top: 50px;
margin-bottom: 20px;
font-weight: bold;
}
.grid {
display: grid;
@@ -54,7 +63,7 @@
.card-title {
font-size: 1.1rem;
font-weight: bold;
color: #FFB74D;
color: #FFFFFF;
margin-bottom: 8px;
}
.card-desc {
@@ -70,7 +79,7 @@
/* Realistic Smartwatch CSS Simulator */
.watch-device {
position: relative;
width: 175px;
width: 160px;
height: 280px;
background: linear-gradient(135deg, #2D3748 0%, #1A202C 100%);
border-radius: 35px;
@@ -105,7 +114,7 @@
}
.screen-bezel {
position: relative;
width: 145px;
width: 130px;
height: 250px;
background-color: #000000;
border-radius: 25px;
@@ -118,7 +127,7 @@
/* Inlined SVG Scaling */
.screen-bezel svg {
width: 135px;
width: 120px;
height: 240px;
display: block;
}
@@ -149,294 +158,476 @@
</head>
<body>
<div class="container">
<h1>手环 LCD 屏幕 UI 效果图画册 (V2.0)</h1>
<div class="subtitle">所有 10 类显示界面在手环硬件上的 1:1 视觉仿真效果及按键映射说明</div>
<h1>手环 AMOLED 无障碍大字版 UI 效果图画册 (A11y V3.4)</h1>
<div class="subtitle">针对特殊人群优化的大字体、大图标单页集中展示界面效果及按键交互说明(共 15 类全套界面)</div>
<!-- ==================== PART 1 ==================== -->
<div class="section-title">第一部分:系统常态待机界面 (STATE_CLOCK)</div>
<div class="grid">
<!-- 1. Normal Clock -->
<div class="card">
<div class="card-title">1. 待机时钟界面</div>
<div class="card-desc">常规待机状态,顶部仅显示电量,界面宽裕</div>
<div class="card-title">1. 待机时钟 (撤防 DESARME)</div>
<div class="card-desc">正常走时,电量百分比大字显示,左侧带大解锁图标 🔓</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="135" height="240" viewBox="0 0 135 240">
<rect width="135" height="240" fill="#000000"/>
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<!-- Status Bar (Right: Battery Icon) -->
<rect x="107" y="27" width="20" height="10" rx="2" fill="none" stroke="#64748B" stroke-width="1.2"/>
<rect x="109" y="29" width="13" height="6" rx="1" fill="#10B981"/>
<rect x="128" y="30" width="1.5" height="4" rx="0.5" fill="#64748B"/>
<text x="101" y="36" fill="#64748B" font-family="system-ui, sans-serif" font-size="9" text-anchor="end">85%</text>
<!-- Status Bar (Left: Big Unlock, Right: Battery 16x32px text + battery icon) -->
<!-- 🔓 Unlock Icon (32x32px style) -->
<g transform="translate(10, 15)">
<rect x="0" y="8" width="20" height="15" rx="3" fill="none" stroke="#10B981" stroke-width="2"/>
<path d="M 5 8 L 5 4 C 5 2, 12 2, 15 4 L 15 10" fill="none" stroke="#10B981" stroke-width="2" stroke-linecap="round"/>
</g>
<!-- Large Battery info and Large Battery Icon -->
<text x="82" y="32" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="end">85%</text>
<rect x="86" y="19" width="24" height="13" rx="2" fill="none" stroke="#FFFFFF" stroke-width="1.8"/>
<rect x="89" y="22" width="15" height="7" rx="0.5" fill="#10B981"/>
<rect x="110" y="23" width="1.5" height="5" rx="0.5" fill="#FFFFFF"/>
<!-- Time -->
<text x="67.5" y="115" fill="#00E5FF" font-family="system-ui, sans-serif" font-size="24" font-weight="bold" text-anchor="middle">10:35</text>
<!-- Central Big Time (32x64px equivalent in SVG) -->
<text x="60" y="115" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="34" font-weight="bold" text-anchor="middle">10:35</text>
<!-- Date -->
<text x="67.5" y="165" fill="#64748B" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">THU 07-02</text>
<!-- Bottom Large Date (16x32px equivalent) -->
<text x="60" y="195" fill="#94A3B8" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">VEN. 07-10</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
长按 键 3 秒进入配对菜单;双击 ▲+▼ 组合键切换布防;长按 SOS 键 5 秒触发【本机求救】。
长按 ■ 确认键 3 秒进入主选择菜单;短按或长按 SOS 键均直接触发【本地主动报警】。
</div>
</div>
<!-- 2. Armed Clock -->
<div class="card">
<div class="card-title">2. 系统布防时钟</div>
<div class="card-desc">布防锁定 🔒 移至正下方,完美避免电池重叠</div>
<div class="card-title">2. 系统待机时钟 (布防 ARME)</div>
<div class="card-desc">正常走时,电量百分比大字显示,左侧带大红色锁图标 🔒</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="135" height="240" viewBox="0 0 135 240">
<rect width="135" height="240" fill="#000000"/>
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<!-- Status Bar (Right: Battery Icon) -->
<rect x="107" y="27" width="20" height="10" rx="2" fill="none" stroke="#64748B" stroke-width="1.2"/>
<rect x="109" y="29" width="13" height="6" rx="1" fill="#10B981"/>
<rect x="128" y="30" width="1.5" height="4" rx="0.5" fill="#64748B"/>
<text x="101" y="36" fill="#64748B" font-family="system-ui, sans-serif" font-size="9" text-anchor="end">85%+</text>
<!-- Time -->
<text x="67.5" y="115" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="24" font-weight="bold" text-anchor="middle">10:35</text>
<!-- Date -->
<text x="67.5" y="165" fill="#64748B" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">THU 07-02</text>
<!-- Armed Status at the very bottom (Y=205) -->
<g transform="translate(46, 192)">
<rect x="0" y="0" width="12" height="12" rx="2" fill="none" stroke="#EF4444" stroke-width="1.2"/>
<circle cx="6" cy="4" r="2.5" fill="none" stroke="#EF4444" stroke-width="1.2"/>
<rect x="5" y="6" width="2" height="3" fill="#EF4444"/>
<text x="18" y="10" fill="#EF4444" font-family="system-ui, sans-serif" font-size="9" font-weight="bold" text-anchor="start">ARME</text>
<!-- Status Bar (Left: Big Lock 32x32px, Right: Battery with + charging indicator) -->
<!-- 🔒 Lock Icon -->
<g transform="translate(10, 15)">
<rect x="0" y="8" width="20" height="15" rx="3" fill="none" stroke="#EF4444" stroke-width="2"/>
<path d="M 5 8 L 5 3 C 5 1, 15 1, 15 3 L 15 8" fill="none" stroke="#EF4444" stroke-width="2" stroke-linecap="round"/>
</g>
<!-- Large Battery info & Icon -->
<text x="82" y="32" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="end">85%+</text>
<rect x="86" y="19" width="24" height="13" rx="2" fill="none" stroke="#FFFFFF" stroke-width="1.8"/>
<rect x="89" y="22" width="15" height="7" rx="0.5" fill="#10B981"/>
<rect x="110" y="23" width="1.5" height="5" rx="0.5" fill="#FFFFFF"/>
<!-- Central Big Time -->
<text x="60" y="115" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="34" font-weight="bold" text-anchor="middle">10:35</text>
<!-- Bottom Large Date -->
<text x="60" y="195" fill="#94A3B8" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">VEN. 07-10</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
双击 ▲+▼ 组合键本地切换为撤防状态;长按 SOS 键 5 秒触发【本机求救】。
长按 ■ 确认键 3 秒进入主选择菜单;短按或长按 SOS 键均直接触发【本地主动报警】。
</div>
</div>
</div>
<!-- 3. Disarmed Clock -->
<!-- ==================== PART 2 ==================== -->
<div class="section-title">第二部分:系统主选择菜单界面 (STATE_MENU)</div>
<div class="grid">
<!-- 3. Main Menu - Date/Time -->
<div class="card">
<div class="card-title">3. 系统撤防时钟</div>
<div class="card-desc">撤防解锁 🔓 移至正下方,界面均衡清爽</div>
<div class="card-title">3. 主菜单: 1.时间修改</div>
<div class="card-desc">菜单项 1大时钟图标配合超大功能项文字</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="135" height="240" viewBox="0 0 135 240">
<rect width="135" height="240" fill="#000000"/>
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<text x="60" y="35" fill="#94A3B8" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">- MENU -</text>
<!-- Status Bar (Right: Battery Icon) -->
<rect x="107" y="27" width="20" height="10" rx="2" fill="none" stroke="#64748B" stroke-width="1.2"/>
<rect x="109" y="29" width="13" height="6" rx="1" fill="#10B981"/>
<rect x="128" y="30" width="1.5" height="4" rx="0.5" fill="#64748B"/>
<text x="101" y="36" fill="#64748B" font-family="system-ui, sans-serif" font-size="9" text-anchor="end">85%</text>
<!-- Time -->
<text x="67.5" y="115" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="24" font-weight="bold" text-anchor="middle">10:35</text>
<!-- Date -->
<text x="67.5" y="165" fill="#64748B" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">THU 07-02</text>
<!-- Disarmed Status at the very bottom (Y=205) -->
<g transform="translate(34, 192)">
<rect x="0" y="3" width="12" height="9" rx="1.5" fill="none" stroke="#10B981" stroke-width="1.2"/>
<path d="M 3 3 L 3 1.5 C 3 0.7, 6 0.7, 8 1.5 L 8 4" fill="none" stroke="#10B981" stroke-width="1.2"/>
<text x="18" y="10" fill="#10B981" font-family="system-ui, sans-serif" font-size="9" font-weight="bold" text-anchor="start">DESARME</text>
</g>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
双击 ▲+▼ 组合键本地切换为布防状态;长按 SOS 键 5 秒触发【本机求救】。
</div>
</div>
<!-- 4. Pairing Menu -->
<div class="card">
<div class="card-title">4. 滚动选择菜单</div>
<div class="card-desc">选择要对码配对的传感器类型</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="135" height="240" viewBox="0 0 135 240">
<rect width="135" height="240" fill="#000000"/>
<text x="67.5" y="35" fill="#FFB74D" font-family="system-ui, sans-serif" font-size="10" font-weight="bold" text-anchor="middle">PAIR MENU P-00</text>
<text x="67.5" y="80" fill="#64748B" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">🚪 DOOR SENSOR</text>
<rect x="10" y="105" width="115" height="30" rx="6" fill="none" stroke="#00E5FF" stroke-width="1.5"/>
<text x="67.5" y="124" fill="#00E5FF" font-family="system-ui, sans-serif" font-size="10" font-weight="bold" text-anchor="middle">👤 PIR MOTION</text>
<text x="67.5" y="165" fill="#64748B" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">🔥 SMOKE DETECT</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
单击 ▲ 向上移动,单击 ▼ 向下移动;短按 SOS 确认选中进入搜索状态;长按 ▼ 3 秒退出。
</div>
</div>
<!-- 5. Pairing Search -->
<div class="card">
<div class="card-title">5. 配对对码搜索</div>
<div class="card-desc">雷达波纹 📡 扫射等待射频对码信号</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="135" height="240" viewBox="0 0 135 240">
<rect width="135" height="240" fill="#000000"/>
<g transform="translate(0, 10)">
<path d="M 60 70 L 67.5 90 M 59.5 75 A 10 10 0 0 1 75.5 75 M 55.5 70 A 15 15 0 0 1 79.5 70 M 51.5 65 A 20 20 0 0 1 83.5 65" stroke="#00E5FF" stroke-width="2" fill="none" stroke-linecap="round"/>
<circle cx="67.5" cy="92" r="2" fill="#00E5FF"/>
</g>
<text x="67.5" y="145" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">SEARCHING...</text>
<text x="67.5" y="170" fill="#00E5FF" font-family="system-ui, sans-serif" font-size="9" text-anchor="middle">TRIGGER SENSOR</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
等待传感器触发。捕获成功且类型匹配时,系统将<b>自动跳转至对码确认页面</b>。长按 ▼ 3 秒退出。
</div>
</div>
<!-- 6. Pairing Confirm -->
<div class="card">
<div class="card-title">6. 对码成功与命名微调</div>
<div class="card-desc">展示传感器类型,按 ▲/▼ 键手动增减后缀序号并确认</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="135" height="240" viewBox="0 0 135 240">
<rect width="135" height="240" fill="#000000"/>
<text x="67.5" y="45" fill="#10B981" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">v RECU</text>
<!-- 门磁图标 -->
<g transform="translate(54.5, 70)">
<rect x="0" y="2" width="16" height="28" rx="2" fill="none" stroke="#FFFFFF" stroke-width="1.8"/>
<rect x="20" y="6" width="6" height="20" rx="1" fill="none" stroke="#10B981" stroke-width="1.8"/>
<circle cx="4" cy="16" r="1.5" fill="#FFFFFF"/>
<!-- ⏰ 48x48px Clock Icon -->
<g transform="translate(36, 65)">
<circle cx="24" cy="24" r="20" stroke="#00E5FF" stroke-width="3" fill="none"/>
<path d="M 24 10 L 24 24 L 34 24" stroke="#00E5FF" stroke-width="3" stroke-linecap="round" fill="none"/>
<path d="M 8 6 L 16 2 M 40 6 L 32 2" stroke="#00E5FF" stroke-width="3.5" stroke-linecap="round"/>
</g>
<text x="67.5" y="140" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">PORTE</text>
<!-- 序号选框 [ 01 ] -->
<rect x="37.5" y="160" width="60" height="22" rx="4" fill="none" stroke="#00E5FF" stroke-width="1.5"/>
<text x="67.5" y="176" fill="#00E5FF" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">01</text>
<text x="67.5" y="215" fill="#64748B" font-family="system-ui, sans-serif" font-size="9" text-anchor="middle">ADDR: 0x37A86</text>
<text x="60" y="165" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">1. HORLOGE</text>
<text x="60" y="210" fill="#64748B" font-family="system-ui, sans-serif" font-size="14" text-anchor="middle">^ v</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
按 ▲ 自增,▼ 自减序号01~99短按 CONFIRM 键或 SOS 键保存退出
按 ▲/▼ 键翻看菜单选项;短按 ■ 确认进入;长按 ■ 3秒返回时钟
</div>
</div>
<!-- 7. Pair Saved -->
<!-- 4. Main Menu - Pairing -->
<div class="card">
<div class="card-title">7. 对码保存成功</div>
<div class="card-desc">高保真圆形对勾绿色成功徽章 (✓)</div>
<div class="card-title">4. 主菜单: 2.传感器配</div>
<div class="card-desc">菜单项 2大雷达对码图标与对码文本</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="135" height="240" viewBox="0 0 135 240">
<rect width="135" height="240" fill="#000000"/>
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<text x="60" y="35" fill="#94A3B8" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">- MENU -</text>
<!-- 📡 48x48px Radar Icon -->
<g transform="translate(36, 65)">
<path d="M 24 38 A 12 12 0 0 1 36 28 M 24 38 A 18 18 0 0 1 42 22 M 24 38 A 24 24 0 0 1 48 16" stroke="#00E5FF" stroke-width="3" fill="none" stroke-linecap="round"/>
<circle cx="24" cy="38" r="4" fill="#00E5FF"/>
<line x1="24" y1="38" x2="6" y2="44" stroke="#00E5FF" stroke-width="2.5"/>
</g>
<text x="60" y="165" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">2. APPAIR.</text>
<text x="60" y="210" fill="#64748B" font-family="system-ui, sans-serif" font-size="14" text-anchor="middle">^ v</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
按 ▲/▼ 键翻看菜单选项;短按 ■ 确认进入对码搜索;长按 ■ 3秒返回时钟。
</div>
</div>
<!-- 5. Main Menu - Arm/Disarm -->
<div class="card">
<div class="card-title">5. 主菜单: 3.手动布撤防</div>
<div class="card-desc">菜单项 3大盾牌防区图标与大字指示</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<text x="60" y="35" fill="#94A3B8" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">- MENU -</text>
<!-- 🛡️ Shield / Security Icon (48x48px) representing ALARME menu item -->
<g transform="translate(36, 65)">
<path d="M 24 4 C 12 4, 6 12, 6 12 C 6 28, 24 44, 24 44 C 24 44, 42 28, 42 12 C 42 12, 36 4, 24 4 Z" stroke="#00E5FF" stroke-width="3.5" fill="none" stroke-linejoin="round"/>
<path d="M 24 12 L 24 34" stroke="#00E5FF" stroke-width="3"/>
</g>
<text x="60" y="165" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">3. ALARME</text>
<text x="60" y="210" fill="#64748B" font-family="system-ui, sans-serif" font-size="14" text-anchor="middle">^ v</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
按 ▲/▼ 键翻看菜单选项;短按 ■ 确认进入手动布撤防页;长按 ■ 3秒返回时钟。
</div>
</div>
<!-- 6. Main Menu - Liaison -->
<div class="card">
<div class="card-title">6. 主菜单: 4.摄像头绑定</div>
<div class="card-desc">菜单项 4大摄像头图标与绑定文本</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<text x="60" y="35" fill="#94A3B8" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">- MENU -</text>
<!-- 📷 Camera Icon (48x48px) representing LIAISON menu item -->
<g transform="translate(36, 65)">
<rect x="4" y="14" width="30" height="24" rx="3" fill="none" stroke="#00E5FF" stroke-width="3"/>
<circle cx="19" cy="26" r="6" stroke="#00E5FF" stroke-width="2.5" fill="none"/>
<path d="M 34 20 L 44 14 L 44 38 L 34 32 Z" fill="#00E5FF"/>
</g>
<text x="60" y="165" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">4. LIAISON</text>
<text x="60" y="210" fill="#64748B" font-family="system-ui, sans-serif" font-size="14" text-anchor="middle">^ v</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
按 ▲/▼ 键翻看菜单选项;短按 ■ 确认进入摄像头绑定对码页;长按 ■ 3秒返回时钟。
</div>
</div>
</div>
<!-- ==================== PART 3 ==================== -->
<div class="section-title">第三部分:子设置流程 1 - 时间设定 (STATE_SET_TIME)</div>
<div class="grid">
<!-- 7. Single Value Time Edit -->
<div class="card">
<div class="card-title">7. 设定时间: 修改小时</div>
<div class="card-desc">每次仅微调一项:超大数字显示,下方指示已缩短</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<text x="60" y="35" fill="#94A3B8" font-family="system-ui, sans-serif" font-size="14" font-weight="bold" text-anchor="middle">REGLER</text>
<!-- Up Arrow -->
<path d="M 60 62 L 52 72 L 68 72 Z" fill="#00E5FF"/>
<!-- Huge 32x64px hour value -->
<text x="60" y="132" fill="#00E5FF" font-family="system-ui, sans-serif" font-size="44" font-weight="bold" text-anchor="middle">10</text>
<!-- Down Arrow -->
<path d="M 60 157 L 52 147 L 68 147 Z" fill="#00E5FF"/>
<text x="60" y="200" fill="#64748B" font-family="system-ui, sans-serif" font-size="14" font-weight="bold" text-anchor="middle">VALIDER</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
按 ▲ 增加数值,▼ 减少数值;短按 ■ 确认保存并切入分秒年等微调;长按 ■ 3s 放弃退出。
</div>
</div>
</div>
<!-- ==================== PART 4 ==================== -->
<div class="section-title">第四部分:子设置流程 2 - 传感器配对 (STATE_PAIR)</div>
<div class="grid">
<!-- 8. Pairing Search -->
<div class="card">
<div class="card-title">8. 雷达对码搜索</div>
<div class="card-desc">64x64px 超大雷达闪烁动画,等待对码信号</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<text x="60" y="35" fill="#94A3B8" font-family="system-ui, sans-serif" font-size="14" font-weight="bold" text-anchor="middle">APPAIRAGE</text>
<!-- 64x64px Big Radar -->
<g transform="translate(28, 65)">
<path d="M 32 50 C 32 30, 48 10, 60 10 M 32 50 C 32 35, 42 20, 52 20 M 32 50 C 32 40, 36 30, 44 30" stroke="#00E5FF" stroke-width="3" fill="none" stroke-linecap="round"/>
<circle cx="32" cy="50" r="5" fill="#00E5FF"/>
<line x1="32" y1="50" x2="8" y2="58" stroke="#00E5FF" stroke-width="3"/>
</g>
<text x="60" y="165" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="14" font-weight="bold" text-anchor="middle">RECHERCHE</text>
<text x="60" y="200" fill="#00E5FF" font-family="system-ui, sans-serif" font-size="13" font-weight="bold" text-anchor="middle">ACTIVER DET.</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
触发物理传感器。捕获数据后手环将<b>自动以数据码识别类型并跳转确认页</b>。长按 ▼ 2 秒退出。
</div>
</div>
<!-- 9. Pairing Confirm -->
<div class="card">
<div class="card-title">9. 识别类型与微调序号</div>
<div class="card-desc">自动显示类型及大图标,直接对大序号框增减操作</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<text x="60" y="32" fill="#10B981" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">✓ RECU</text>
<!-- 48x48px Big Door Icon -->
<g transform="translate(36, 45)">
<rect x="2" y="2" width="24" height="42" rx="3" fill="none" stroke="#FFFFFF" stroke-width="2.5"/>
<rect x="30" y="8" width="10" height="30" rx="1.5" fill="none" stroke="#10B981" stroke-width="2.5"/>
<circle cx="8" cy="24" r="2.5" fill="#FFFFFF"/>
</g>
<!-- Only-read sensor type name (16x32px equivalent) -->
<text x="60" y="130" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">PORTE</text>
<!-- 32x64px Suffix Edit Box [ 01 ] -->
<rect x="15" y="152" width="90" height="44" rx="6" fill="none" stroke="#00E5FF" stroke-width="2"/>
<text x="60" y="186" fill="#00E5FF" font-family="system-ui, sans-serif" font-size="34" font-weight="bold" text-anchor="middle">01</text>
<text x="60" y="225" fill="#64748B" font-family="system-ui, sans-serif" font-size="13" font-weight="bold" text-anchor="middle">CONFIRMER</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
按 ▲ 键自增,▼ 键自减微调序号01~99短按 ■ 确认键保存;长按 ■ 3秒放弃。
</div>
</div>
<!-- 10. Pair Success -->
<div class="card">
<div class="card-title">10. 对码保存成功</div>
<div class="card-desc">无障碍大字版:圆圈成功对勾,绿色成功指示</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<!-- Symmetrical Circular Success Badge -->
<circle cx="67.5" cy="90" r="24" stroke="#10B981" stroke-width="2.5" fill="none"/>
<path d="M 55.5 90 L 63.5 98 L 79.5 82" stroke="#10B981" stroke-width="3.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<circle cx="60" cy="90" r="24" stroke="#10B981" stroke-width="2.5" fill="none"/>
<path d="M 48 90 L 56 98 L 72 82" stroke="#10B981" stroke-width="3.5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<text x="67.5" y="145" fill="#10B981" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">PAIR SUCCESS</text>
<text x="67.5" y="175" fill="#64748B" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">ADDR: 0x37A86</text>
<text x="60" y="150" fill="#10B981" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">REUSSITE</text>
<text x="60" y="185" fill="#94A3B8" font-family="system-ui, sans-serif" font-size="13" font-weight="bold" text-anchor="middle">ENREGISTRE</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
纯状态提示,自动显示 2 秒后回退到待机,无任何按键操作
显示 2 秒后自动退回到主时钟待机状态,无任何按键。
</div>
</div>
<!-- 8. Pair Fail -->
<!-- 11. Pair Failed -->
<div class="card">
<div class="card-title">8. 对码匹配失败</div>
<div class="card-desc">高保真圆形红叉失败徽章 (❌)</div>
<div class="card-title">11. 对码绑定失败</div>
<div class="card-desc">无障碍大字版:红叉圈,提示重新激活对码</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="135" height="240" viewBox="0 0 135 240">
<rect width="135" height="240" fill="#000000"/>
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<!-- Symmetrical Circular Fail Badge -->
<circle cx="67.5" cy="90" r="24" stroke="#EF4444" stroke-width="2.5" fill="none"/>
<path d="M 57.5 80 L 77.5 100 M 77.5 80 L 57.5 100" stroke="#EF4444" stroke-width="3.5" fill="none" stroke-linecap="round"/>
<circle cx="60" cy="90" r="24" stroke="#EF4444" stroke-width="2.5" fill="none"/>
<path d="M 50 80 L 70 100 M 70 80 L 50 100" stroke="#EF4444" stroke-width="3.5" fill="none" stroke-linecap="round"/>
<text x="67.5" y="155" fill="#EF4444" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">PAIR FAILED</text>
<text x="67.5" y="180" fill="#64748B" font-family="system-ui, sans-serif" font-size="9" text-anchor="middle">TYPE MISMATCH</text>
<text x="60" y="150" fill="#EF4444" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">ECHEC</text>
<text x="60" y="185" fill="#94A3B8" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">DEJA ASSOC.</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
自动显示 5 秒后回退到配对菜单,或短按任意键立即回退到配对菜单
自动显示 5 秒后回退到配对菜单,或短按任意键立即回退。
</div>
</div>
</div>
<!-- ==================== PART 5 ==================== -->
<div class="section-title">第五部分:子设置流程 3 - 手动布撤防 (STATE_ARM_DISARM_SELECT)</div>
<div class="grid">
<!-- 12. Arm/Disarm Setting Select -->
<div class="card">
<div class="card-title">12. 手动布撤防设定</div>
<div class="card-desc">48x48px 锁/防区图标,无障碍大字状态指示</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<text x="60" y="35" fill="#94A3B8" font-family="system-ui, sans-serif" font-size="14" font-weight="bold" text-anchor="middle">REG. ALARME</text>
<!-- 48x48px Big Lock Icon representing ARME selection -->
<g transform="translate(36, 65)">
<rect x="6" y="16" width="36" height="26" rx="4" fill="none" stroke="#EF4444" stroke-width="3"/>
<path d="M 12 16 L 12 10 C 12 5, 36 5, 36 10 L 36 16" fill="none" stroke="#EF4444" stroke-width="3" stroke-linecap="round"/>
<circle cx="24" cy="28" r="3.5" fill="#EF4444"/>
</g>
<!-- Large active state option in brackets -->
<text x="60" y="165" fill="#00E5FF" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">[ ARME ]</text>
<text x="60" y="210" fill="#64748B" font-family="system-ui, sans-serif" font-size="14" text-anchor="middle">^ v</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
按 ▲/▼ 键来回切换 `[ ARME ]` 和 `[ DESARME ]`;短按 ■ 确认保存退出;长按 ■ 3s 放弃退出。
</div>
</div>
</div>
<!-- ==================== PART 6 ==================== -->
<div class="section-title">第六部分:子设置流程 4 - IPC摄像头绑定 (STATE_IPC_BIND)</div>
<div class="grid">
<!-- 13. IPC Binding -->
<div class="card">
<div class="card-title">13. IPC 绑定发送</div>
<div class="card-desc">48x48px 摄像头图标,大字发送与 ID 指示</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<!-- 48x48px Camera Icon -->
<g transform="translate(36, 40)">
<rect x="2" y="10" width="32" height="26" rx="4" fill="none" stroke="#00E5FF" stroke-width="3"/>
<circle cx="18" cy="23" r="6" stroke="#00E5FF" stroke-width="2.5" fill="none"/>
<path d="M 34 16 L 44 10 L 44 36 L 34 30 Z" fill="#00E5FF"/>
</g>
<text x="60" y="130" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">LIAISON</text>
<text x="60" y="165" fill="#00E5FF" font-family="system-ui, sans-serif" font-size="15" font-weight="bold" text-anchor="middle">EN COURS</text>
<text x="60" y="215" fill="#64748B" font-family="system-ui, sans-serif" font-size="9" text-anchor="middle">ID: 0x3F882</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
连发对码信号。长按 ■ 键 3 秒 或 长按 ▼ 键 2 秒可立刻退出返回时钟。
</div>
</div>
</div>
<!-- ==================== PART 7 ==================== -->
<div class="section-title">第七部分:紧急警报触发状态 (STATE_ALARMING / STATE_SOS_EMITTED)</div>
<div class="grid">
<!-- 14. Alarm Received -->
<div class="card">
<div class="card-title">14. 收到防区报警 (PORTE)</div>
<div class="card-desc">全屏闪烁粗红警告框 + 64x64px 报警传感器大图标</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<!-- 4px Flashing Red Border -->
<rect x="4" y="4" width="112" height="232" rx="20" stroke="#EF4444" stroke-width="4.5" fill="none"/>
<!-- 64x64px Huge Door Icon -->
<g transform="translate(28, 40)">
<rect x="2" y="2" width="32" height="58" rx="4" fill="none" stroke="#EF4444" stroke-width="3.5"/>
<rect x="42" y="12" width="14" height="42" rx="2" fill="none" stroke="#EF4444" stroke-width="3"/>
<circle cx="10" cy="32" r="3.5" fill="#EF4444"/>
</g>
<!-- 16x32px text warnings -->
<text x="60" y="145" fill="#EF4444" font-family="system-ui, sans-serif" font-size="16" font-weight="bold" text-anchor="middle">PORTE 01</text>
<text x="60" y="185" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="16" font-weight="bold" text-anchor="middle">INTRUSION!</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
高分贝马达与声光报警启动。短按手环侧边或上部任意键均可瞬间解除报警复位。
</div>
</div>
<!-- 9. Alarm Received -->
<!-- 15. Emitted SOS -->
<div class="card">
<div class="card-title">9. 收到传感器警报</div>
<div class="card-desc">门磁/烟感等传感器触发的警报画面</div>
<div class="card-title">15. 本机主动求救 SOS</div>
<div class="card-desc">巨大警灯,通过换行与字号分配,完全防止溢出</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="135" height="240" viewBox="0 0 135 240">
<rect width="135" height="240" fill="#000000"/>
<rect x="4" y="4" width="127" height="232" rx="20" stroke="#EF4444" stroke-width="2.5" fill="none" stroke-dasharray="6 4"/>
<g transform="translate(0, 10)">
<rect x="59.5" y="55" width="16" height="30" rx="1" fill="none" stroke="#EF4444" stroke-width="2.5"/>
<circle cx="71.5" cy="70" r="1.5" fill="#EF4444"/>
<svg xmlns="http://www.w3.org/2000/svg" width="120" height="240" viewBox="0 0 120 240">
<rect width="120" height="240" fill="#000000"/>
<rect x="4" y="4" width="112" height="232" rx="20" stroke="#EF4444" stroke-width="4.5" fill="none"/>
<!-- Huge 64x64px Rotating Siren Icon -->
<g transform="translate(28, 25)">
<!-- Rays -->
<path d="M 32 10 L 32 18 M 14 16 L 22 22 M 50 16 L 42 22 M 6 32 L 15 34 M 58 32 L 49 34" stroke="#EF4444" stroke-width="3" stroke-linecap="round"/>
<!-- Dome -->
<path d="M 12 52 C 12 28, 52 28, 52 52 Z" fill="#EF4444" stroke="#EF4444" stroke-width="2"/>
<circle cx="32" cy="45" r="7" fill="#FFFFFF"/>
<!-- Base -->
<rect x="6" y="52" width="52" height="8" rx="2" fill="#64748B"/>
</g>
<text x="67.5" y="130" fill="#EF4444" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">FRONT DOOR</text>
<text x="67.5" y="165" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">ZONE 0 | ALARM</text>
<!-- Split into 2 rows to fit within 120px screen width nicely -->
<text x="60" y="125" fill="#EF4444" font-family="system-ui, sans-serif" font-size="16" font-weight="bold" text-anchor="middle">SOS</text>
<text x="60" y="152" fill="#EF4444" font-family="system-ui, sans-serif" font-size="16" font-weight="bold" text-anchor="middle">EMIS</text>
<text x="60" y="195" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="14" font-weight="bold" text-anchor="middle">APPEL...</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
短按手环任意键(▲/▼/SOS均可手动清除警报、消音并返回时钟待机
</div>
</div>
<!-- 10. Emitted SOS -->
<div class="card">
<div class="card-title">10. 本机紧急求救 SOS</div>
<div class="card-desc">高保真闪光警报灯图标,字形避开边缘</div>
<div class="watch-device">
<div class="screen-bezel">
<svg xmlns="http://www.w3.org/2000/svg" width="135" height="240" viewBox="0 0 135 240">
<rect width="135" height="240" fill="#000000"/>
<rect x="4" y="4" width="127" height="232" rx="20" stroke="#EF4444" stroke-width="2.5" fill="none"/>
<rect x="8" y="8" width="119" height="224" rx="17" stroke="#FFFFFF" stroke-width="1" fill="none"/>
<!-- High-Fidelity Siren Icon -->
<g transform="translate(0, -3)">
<!-- Light Beams -->
<path d="M 67.5 48 L 67.5 54 M 52.5 52 L 57.5 57 M 82.5 52 L 77.5 57 M 47.5 65 L 53.5 67 M 87.5 65 L 81.5 67" stroke="#EF4444" stroke-width="2" stroke-linecap="round"/>
<!-- Siren Glass Dome -->
<path d="M 53.5 82 C 53.5 64, 81.5 64, 81.5 82 Z" fill="#EF4444" stroke="#EF4444" stroke-width="1.5"/>
<!-- Bulb inside -->
<circle cx="67.5" cy="77" r="4.5" fill="#FFFFFF"/>
<!-- Metallic Base -->
<rect x="49.5" y="82" width="36" height="6" rx="1.5" fill="#64748B"/>
</g>
<!-- Centered SOS Alarm Texts safely inside circular active area -->
<text x="67.5" y="138" fill="#EF4444" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">🆘 EMIT SOS</text>
<text x="67.5" y="165" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">BAND TRIGGERED</text>
</svg>
</div>
</div>
<div class="card-ops">
<b>按键操作:</b>
长按 SOS 键 5 秒解除报警;或接收到主机的远程撤防指令自动解除。
连发 SOS 呼救信号。短按或长按任意按键均可停止发射并消除紧急求救
</div>
</div>
</div>

View File

@@ -1,47 +1,49 @@
# 模块拆分 - 按键扫描与事件判定 (Docs/50_module-breakdown/mod-key.md)
# 模块拆分 - 按键输入消抖与判定 (Docs/50_module-breakdown/mod-key.md)
本模块描述按键状态消抖、长按计时判定以及组合按键识别。支持 5 个物理按键到手环事件的映射
本模块描述物理按键输入的检测、电平消抖、长按计时以及多事件防丢处理
## 1. 模块职责说明
* **职责范围**
1. 1ms 定时器中断中建立 10ms 扫描计时周期
2. 对 5 个物理按键进行引脚电平扫描:
* 侧边:`KEY_UP` SW1 (▲), `KEY_DOWN` SW2 (▼), `KEY_CONFIRM` (■)
* 上部`KEY_SOS1` (SOS 1), `KEY_SOS2` (SOS 2)
3. 实现消抖检测(连续 20ms 检测到电平稳定才认定按键动作)。
4. 对上部双 SOS 按键进行逻辑并联处理,保证按下任意一个均能触发求救
5. 对按住不放的行为进行计时累加,判定长按(如长按 SOS 达 5 秒触发对码;长按 ▲+▼3触发配对模式)
6. 将按键判定结果转换为 `KeyEvent` 并注入按键事件缓冲区 `key_event_buf`
1.系统上电时,将按键引脚 `P0.1~P0.5` 配置为**高阻输入且开启内部上拉电阻**STC32G P0M0/P0M1/P0PU 寄存器配置)
2. 实现每 10ms 一次的硬件消抖滤波(采用 3 次扫描连续一致判定法,滤波时间约 20-30ms
3. 支持侧边 ▲/▼ 键、确认键 ■,以及上部双并联 SOS 键的独立状态判定。
4. 上部双 SOS 按键进行逻辑并联处理:只要任意一个按键拉低,即视为 SOS 触发。
5. **长按计时判定**
* 长按 **确认键 (■)** 达 3 秒300 个时基单位)触发主菜单切换或子页面放弃退出
* 长按 **▼ 键**2200 个时基单位)触发报警清除或 IPC 绑定退出
6. **防丢事件缓冲队列 (Ring Buffer)**
* 为了防止高频按键或同时按下时发生丢键,本模块维护一个深度为 4 级的环形事件队列。按键扫描判定出的 `KeyEvent` 压入队列,主循环通过出队机制依次消费。
---
## 2. 数据结构定义
### 按键事件枚举 (KeyEvent)
### 2.1 按键事件枚举 (KeyEvent)
```c
typedef enum {
KEY_EVENT_NONE,
KEY_UP_SHORT,
KEY_UP_LONG,
KEY_DOWN_SHORT,
KEY_DOWN_LONG,
KEY_CONFIRM_CLICK, // ■ 确认键短按
KEY_SOS_SHORT, // SOS键短按 (SOS1 或 SOS2)
KEY_SOS_LONG, // SOS键长按5秒 (SOS1 或 SOS2)
KEY_COMB_LONG // ▲ + ▼ 组合长按事件
KEY_UP_SHORT, // ▲ 键短按
KEY_DOWN_SHORT, // ▼ 键短按
KEY_DOWN_LONG, // ▼ 键长按 2 秒 (清除报警/退出)
KEY_CONFIRM_CLICK, // ■ 确认键短按
KEY_CONFIRM_LONG, // ■ 确认键长按 3 秒 (菜单切换)
KEY_SOS_ACTIVE // SOS 键触发 (不分长短按,秒级响应)
} KeyEvent;
```
---
## 3. 接口与函数说明
* `void Key_Init(void)`:配置 `P0.1~P0.5``P0M1=0x00`, `P0M0=0x00`(高阻模式),并设置 `P0PU = 0x3E` 使能上拉电阻。
* `void Key_Scan_Process(void)`
在 Timer 1 ISR 中每 10ms 调度一次
* 读取 `KEY_UP`, `KEY_DOWN`, `KEY_CONFIRM`, `KEY_SOS1`, `KEY_SOS2` 的 GPIO 逻辑电平
* 计算并联 SOS 状态:`is_sos_pressed = (KEY_SOS1 == 0) || (KEY_SOS2 == 0)`
* 如果发现按键电平状态改变,进行计数消抖
* 如果按住不放,累加计时计数器(`key_up_hold`, `key_down_hold`, `key_confirm_hold`, `key_sos_hold`
* 检测到 `key_up_hold >= 300``key_down_hold >= 300` 达 3 秒时,注入 `KEY_COMB_LONG` 事件
* 检测到 `key_sos_hold >= 500` 达 5 秒时,注入 `KEY_SOS_LONG` 事件;若释放时未达 5 秒但大于 20ms注入 `KEY_SOS_SHORT`
* 检测到确认键释放,注入 `KEY_CONFIRM_CLICK`
* 事件解析完毕后存入 `key_event_buf`
在 Timer 1 10ms 中断分频中调用
* 读取引脚逻辑状态,并利用移位寄存器滤波判定
* 并联 SOS 状态:`sos_pressed = (KEY_SOS1 == 0) || (KEY_SOS2 == 0)`
* 累加各按键的计数器:`key_up_hold`, `key_down_hold`, `key_confirm_hold`, `key_sos_hold`
* 达到长按阈值时将对应事件写入环形队列
* `bit Key_Push_Event(KeyEvent evt)`:将事件压入环形事件队列。若满则丢弃,返回 0
* `KeyEvent Key_Pop_Event(void)`:主循环调用,从事件队列中读取未消费的事件。若空返回 `KEY_EVENT_NONE`
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes -->
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->

View File

@@ -1,36 +1,33 @@
# 模块拆分 - LCD 显示与字模驱动 (Docs/50_module-breakdown/mod-lcd.md)
# 模块拆分 - AMOLED 显示与无障碍渲染 (Docs/50_module-breakdown/mod-lcd.md)
本模块管理 1.14寸 TFT LCD 屏幕引脚、电平控制、SPI 传输和界面图文渲染(分辨率为 135x240 像素)
本模块管理 Truly 0.95寸 AMOLED 屏的底层 SPI 通信、窗口控制、以及无障碍A11y超大字体和图标的渲染
## 1. 模块职责说明
* **职责范围**
1. 提供底层的 SPI 单向命令/数据通道
2. 控制 LCD_PWR_CTRL 供电管脚
3. 支持屏幕窗口配置,实现底层清屏与局部像素写入
4. 读取 `lcd_font.h` 库,在指定坐标绘制 8x16 字符与自定义大小位图。
5. 实现各业务状态屏幕135x240 分辨率)画面绘制与文字对齐
1. 管理 RM69310 控制器通信,提供单向 SPI 令/数据写操作
2. 提供 `SGM_CTRL`(引脚 `P3.4`)控制,配合低功耗模块实现屏幕 PMIC 的上电与彻底断电
3. **物理窗口偏置处理**Truly 120x240 AMOLED 屏的 GRAM X 轴起始位置有 **+4 像素的偏置**,设置显示窗口时必须进行偏移修正:`LCD_SetWindow(x1 + 4, x2 + 4, y1, y2)`
4. **无障碍大字排版A11y渲染**
* 取消任何小于 16x32px 的微缩文本
* 时间显示使用 **32x64px** 超大点阵。
* 防区图标使用 **48x48px****64x64px** 超大单色位图。
* 在 120 像素宽的屏幕内,应用水平居中公式:`X_start = (120 - len * char_width) / 2`
5. **单页集中展示逻辑**:控制各菜单页面和子修改页面在屏幕中只显示单焦点,避免多行小字杂乱堆叠。
## 2. 底层显示接口
---
* `void LCD_Init(void)`:初始化 LCD 引脚,置 `LCD_PWR_CTRL = 1` 使能背光与供电,并发送 **ST7789V** 屏幕控制器的初始化寄存器配置序列。
* `void LCD_WriteCmd(u8 cmd)`:拉低 `LCD_RS` 发送 SPI 命令。
* `void LCD_WriteData(u8 dat)`:拉高 `LCD_RS` 发送 SPI 数据。
* `void LCD_SetWindow(u16 x1, u16 y1, u16 x2, u16 y2)`:设置局部像素绘制范围,以适配 135x240 竖屏在 240x320 GRAM 上的对齐(强制处理 **X 轴偏移量 +52Y 轴偏移量 +40**)。
* `void LCD_Clear(u16 color)`:将整块屏幕刷写为特定背景色。
## 2. 接口与函数说明
## 3. 图文绘制与 UI 渲染
* `void LCD_ShowChar(u16 x, u16 y, char c, u16 fc, u16 bc)`:在 (x, y) 坐标处绘制 ASCII 字符 `c`
* `void LCD_ShowString(u16 x, u16 y, char *s, u16 fc, u16 bc)`循环打印字符串
* `void LCD_ShowStringCentered(u16 y, char *str, u16 fc, u16 bc)`:在 135 像素宽屏幕上水平居中绘制 8 像素宽的字符串,水平起始坐标算法为 `(135 - len * 8) / 2`
* `void LCD_ShowString16x32Centered(u16 y, char *str, u16 fc, u16 bc)`:在 135 像素宽屏幕上水平居中绘制双倍大小 (16x32px) 的字符串,水平起始坐标算法为 `(135 - len * 16) / 2`
* `void LCD_ShowImage(u16 x, u16 y, u16 w, u16 h, const u8 *img, u16 fc, u16 bc)`:在指定区域绘制单色位图
* `void UI_ShowClockPage(SystemState state, u8 hour, u8 min)`渲染时钟待机页面135 宽布局),并根据布撤防状态在状态栏绘制相应锁状态图标
* `void UI_ShowAlarmPage(char *sensor_name, u8 zone)`:渲染防区告警页面,显示对应的传感器位图和法语名称
* `void UI_ShowPairMenuPage(u8 selected_index)`:渲染对码选择菜单
* `void UI_ShowPairConfirmPage(u8 type, u8 selected_suffix)`:渲染对码确认页,显示捕获的类型图标、名字前缀及带选框的 `[ selected_suffix ]` 数字序号(支持按键调整)。
* `void UI_ShowBindingPage(void)`:渲染与 IPC 绑定对码界面,并在屏幕底部居中显示手环自身的唯一 `Factory ID`
<!-- Checked and verified with always-on screen and sleep removal changes -->
* `void LCD_Init(void)`:配置 `P1.1~P1.6` 控制线为推挽输出模式,并在拉高 `SGM_CTRL` 稳定后,发送 RM69310 初始化寄存器指令序列。
* `void LCD_WriteCmd(u8 cmd)`:拉低 `LCD_RS` 选择命令寄存器,通过 SPI 发送字节。
* `void LCD_WriteData(u8 dat)`:拉高 `LCD_RS` 选择数据寄存器,通过 SPI 发送字节
* `void LCD_SetWindow(u16 x1, u16 y1, u16 x2, u16 y2)`设置局部像素刷写范围,物理 X 坐标必须强制加 4 像素
* `void LCD_Clear(u16 color)`:清屏为指定底色(常态使用全黑以节省功耗)
* `void LCD_ShowChar16x32(u16 x, u16 y, char c, u16 fc, u16 bc)`:在指定坐标绘制标准无障碍大字符16x32点阵
* `void LCD_ShowChar32x64(u16 x, u16 y, char c, u16 fc, u16 bc)`:在指定坐标绘制超大数字字符32x64点阵
* `void LCD_ShowImage(u16 x, u16 y, u16 w, u16 h, const u8 *img, u16 fc, u16 bc)`:渲染指定单色位图(支持 32x32/48x48/64x64/80x80 图标规格)
* `void LCD_ShowStringCentered(u16 y, char *str, u16 fc, u16 bc)`16x32 规格字符串居中渲染,单行限制最多 7 个字符
* `void LCD_ShowString32x64Centered(u16 y, char *str, u16 fc, u16 bc)`32x64 规格超大字符串居中渲染(如时钟显示)
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->

View File

@@ -1,19 +1,22 @@
# 模块拆分 - WS2812B 可编程 RGB 灯效 (Docs/50_module-breakdown/mod-led.md)
本模块描述可编程幻彩 RGB 灯珠WS2812B/FCOB-2.7)的单线级联数据流驱动与报警色彩反馈
本模块管理双级联幻彩 RGB 灯珠WS2812B/FCOB-2.7)的硬件 SPI 通道、防抖抗噪切换以及指示闪烁逻辑
## 1. 模块职责说明
* **职责范围**
1. 在系统启动时,配置 WS2812 的物理输出引脚 `P2.3` (MOSI)
2. 支持一上一下级联Daisy-chained的双 WS2812B/FCOB-2.7 灯珠驱动。上灯 DOUT 串行连接至下灯 DIN。
3. 提供高速的硬件 SPI 发送驱动,配合 LUT 查表展宽算法,连续发送 48-bit (24-bit × 2) 的 GRB 级联数据流,输出满足单线纳秒级精度的 WS2812 时序波形,使两个灯珠同步亮起相同的颜色。
4. 在硬件 SPI 传输 48位灯效数据时采取临时关闭中断并把复用的 `P2.5` 配置为高阻输入的隔离防噪处理
5. 维护软件定时闪烁状态机,提供报警状态下的指示灯颜色与特定频率闪烁效果。
1. 管理物理引脚 `P2.3`(复用硬件 SPI 的 MOSI作为单线纳秒级高速数据流输出
2. 实现对级联的两个 RGB 灯珠(上灯 DOUT 串下灯 DIN)输出相同的 24-bit × 2 色彩数据
3. **硬件 SPI 复用防噪隔离处理**
由于单片机引脚紧缺SPI 引脚与马达控制可能存在轻微高频串扰。在每次发送 48-bit (GRB) 灯光控制字之前,**必须临时关闭全局中断 (EA=0)**,并将马达引脚配置为高阻输入以彻底隔离噪声。发送完成后立即恢复引脚模式并使能全局中断 (EA=1)
4. **软件异步闪烁控制**
在 1ms 中断中自增计时器控制报警状态下不同防区灯光的异步闪烁500ms 亮、500ms 灭,或本地 SOS 下的 50ms 红光快速交替爆闪)。
---
## 2. LED 报警色彩矩阵
根据 7 种不同的外部传感器类型及本地主动求救,亮起对应的 LED 灯:
根据防区传感器类型及报警动作亮起指示灯:
* **门磁/窗磁 (PORTE)**:红色闪烁。
* **PIR 人体红外 (PIR)**:橙色闪烁。
* **烟雾传感器 (FUMEE)**:绿色闪烁。
@@ -21,14 +24,20 @@
* **气体泄漏 (GAZ)**:紫色闪烁。
* **水浸传感器 (INOND.)**:黄色闪烁。
* **振动传感器 (VIBRA.)**:青色闪烁。
* **本地主动 SOS**红色快速交替闪烁。
* **本地主动 SOS**双红灯以 50ms 周期高速交替闪烁。
---
## 3. 接口与函数说明
* `void WS2812_Init(void)`配置 `P2.3` 引脚为推挽输出模式
* `void WS2812_Write24Bit(u8 g, u8 r, u8 b)`:使用硬件 SPI 方式串行发送 24-bit 颜色数据给级联灯珠。发送前后进行 `P2.5` 引脚高阻/推挽切换屏蔽马达时钟抖动。
* `void WS2812_Reset(void)`:发送大于 80us 的低电平复位脉冲使颜色锁存生效
* `void LED_Set_Color(u8 g, u8 r, u8 b)`:立即设置 RGB 灯亮起特定颜色
* `void LED_Flash_Process(void)`:在 1ms 定时器中断中累加,控制报警状态下指示灯的交替闪烁(如 500ms 亮、500ms 灭)
* `void WS2812_Init(void)`初始化物理引脚 `P2.3` 状态,复用硬件 SPI 通道,配置时钟展宽 LUT 码表
* `void WS2812_WriteRGB(u8 led1_g, u8 led1_r, u8 led1_b, u8 led2_g, u8 led2_r, u8 led2_b)`
1. 临时保存并拉低 `EA = 0`
2. 切换 `P2.5` (MOTOR) 引脚为高阻输入以防共地噪声
3. 通过 SPI 控制寄存器高速连发 48 字节的时序宽展码,驱动两个灯珠亮起对应颜色
4. 发送完毕后发送 >80us 的低电平复位脉冲。
5. 恢复马达引脚为推挽输出,并还原 `EA`
* `void LED_Set_Color(u8 g, u8 r, u8 b)`:同步设置双级联灯亮起特定颜色。
* `void LED_Flash_Process(void)`:在 1ms 中断中调用,控制指示灯处于交替爆闪或间歇闪烁的状态机更新。
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes -->
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->

View File

@@ -1,32 +1,40 @@
# 模块拆分 - 振动马达驱动与反馈 (Docs/50_module-breakdown/mod-motor.md)
# 模块拆分 - 振动马达异步驱动与反馈 (Docs/50_module-breakdown/mod-motor.md)
本模块描述手环告警与按键反馈的振动马达驱动逻辑。手环通过控制特定振动时长与周期,向用户提供直观的物理触觉告警反馈
本模块描述振动马达引脚的开关控制、异步时间片脉冲时序生成以及针对不同防区的特定振动波形
## 1. 模块职责说明
* **职责范围**
1. 系统初始化时,将马达控制管`P2.5` 配置为推挽输出模式。
2. 提供底层的开关控制,使能或禁止马达供电(高电平振动,低电平静止)。
3. 在 1ms 中断Timer 1中执行异步振动控制时序Pulse Process,实现不阻塞 CPU 的各种复杂振动波形(如短振、长振、连续多次振动)
4. 根据接收到的 7 类传感器报警类型或本地主动 SOS 呼救状态,自动触发对应的振动模式
1. 系统初始化时配置马达引`P2.5` 为推挽输出模式,默认输出低电平(马达静止)
2. **非阻塞异步脉冲处理**
为防止马达振动时间(几百毫秒)阻塞 CPU 的业务循环与射频解码,本模块将马达动作拆分为多个电平交替状态段。利用 Timer 1 (1ms 中断) 异步扣减阶段计数器,实现不占用 CPU 资源的精细振动波形
3. 配合系统状态机,在按键点击时输出 50ms 极短触觉反馈,在防区入侵报警时根据类型输出对应的振动指征,在本地主动 SOS 状态下输出连续不断的重度警示振动
---
## 2. 振动模式设计 (周期 5 秒)
* **按键操作反馈**1 次极短振动 50ms。
* **门磁/窗磁 (PORTE)**1 次短振 300ms。
* **PIR 红外 (PIR)**2 次短振 300ms (间隔 200ms)。
* **烟雾传感器 (FUMEE)**3 次短振 200ms (间隔 150ms)。
* **紧急按钮 (URGENCE)**1 次长振 800ms。
* **气体泄漏 (GAZ)**4 次短振 200ms (间隔 100ms)。
* **水浸传感器 (INOND.)**2 次长振 600ms (间隔 300ms)。
* **振动传感器 (VIBRA.)**:连续 3 次短振 150ms (间隔 100ms)。
* **本地主动 SOS**:持续间歇长振(振 1000ms停 200ms 循环)。
* **振动传感器 (VIBRA.)**:连续 3 次短振 150ms (间隔 100ms)。
* **本地主动 SOS**:持续间歇重度长振(振 1000ms停 200ms 循环)。
---
## 3. 接口与函数说明
* `void Motor_Init(void)`初始化配置引脚 `P2.5` 推挽输出,默认输出低电平(马达静止)
* `void Motor_Set_State(bit state)`立即开关马达(直接写入 `P2.5` 引脚)
* `void Motor_Start_Alarm_Pattern(u8 pattern_type)`传入传感器类型,装载对应的振动计数器和阶段标志,启动异步振动模式
* `void Motor_Pulse_Process(void)`在 1ms 中断中调用(通常每 10ms 调度一次),更新振动时间计数器,切换各阶段电平以输出指定波形。
* `void Motor_Stop(void)`:清空所有计数器并立即关闭马达,用以清除告警。
* `void Motor_Init(void)`:配置引脚 `P2.5` `P2M1=0x00`, `P2M0=0x20`推挽输出,默认输出 `MOTOR = 0`
* `void Motor_Set_State(bit state)`硬开关控制引脚 `P2.5` 的电平
* `void Motor_Start_Pattern(u8 pattern_type)`装载对应类型的马达振动计数器,开启异步振动状态机
* `void Motor_Pulse_Process(void)`
在 Timer 1 的 10ms 中断分频中被调用:
* 根据装载的波形,扣减当前阶段维持的毫秒数。
* 当计数器归零时,切换 `MOTOR` 引脚电平并转入下一个电平维持段。
* `void Motor_Stop(void)`:清空所有计数器,拉低 `MOTOR = 0`,强行中止当前振动。
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes -->
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->

View File

@@ -1,19 +1,34 @@
# 模块拆分 - 电源与供电管理 (Docs/50_module-breakdown/mod-power.md)
# 模块拆分 - 极低功耗与电源管理 (Docs/50_module-breakdown/mod-power.md)
本模块描述手环的电源供电状态。根据系统设计手环不启用任何关屏低功耗或停机Power-Down模式以保证屏幕时钟常显与全时无线监听
本模块描述手环的低功耗管理、AMOLED 供电 PMIC 控制以及中断睡眠/唤醒时序
## 1. 模块职责说明
* **职责范围**
1. 系统初始化时使能 LCD 屏幕与背光电源。
2. 维持 `LCD_PWR_CTRL` 引脚输出高电平1确保屏幕不进入断电休眠。
3. 不进行时钟挂起操作,使 CPU 保持在 active 状态以进行实时的 433MHz 射频信号捕获
1. 系统上电时使能 SGM3833 升压芯片,为 OLED 屏提供稳定的显示电源。
2. **低功耗决策判定**
当主系统处于非警报状态,且连续 10 秒(`inactivity_timer >= 1000`)无任何按键或无线包事件时,判定系统进入 `STATE_SLEEP` 极低功耗睡眠
3. **彻底切断 PMIC**
为消除 OLED 的静态负压漏电流,休眠前拉低 `SGM_CTRL = 0`,使 SGM3833 彻底断电。
4. **睡眠挂起配置**
使能 `INT2`(外部中断 2接射频接收引脚以及 P0 口引脚电平变化中断,然后将 STC32G 写入 `PCON |= 0x02` 进入停机Power-Down挂起状态关闭主高频振荡时钟全板功耗降至微安级。
5. **安全唤醒重初始化时序**
发生按键或无线信号中断时CPU 瞬间被唤醒。由于升压 PMIC 的输入电容充电及负压稳压需要时间,唤醒后**严禁立即写屏**。必须首先拉高 `SGM_CTRL = 1`,接着**软件强行阻塞延时 50ms**,等电网稳定后,再次完整初始化 RM69310 的寄存器参数,最后恢复待机屏幕重绘。
## 2. 接口与函数设计
---
* `void Power_Init(void)`
1. 配置 `LCD_PWR_CTRL` 引脚为推挽输出模式。
2. 设置 `LCD_PWR_CTRL = 1`(物理引脚 `P3.4`)开启 LCD 屏幕与背光供电。
* `void Enter_Deep_Sleep(void)`:已作废。手环永不进入此函数,保持主循环及射频实时解调。
## 2. 接口与函数说明
<!-- Checked and verified with always-on screen and sleep removal changes -->
* `void Power_Init(void)`:配置 `P3.4` (SGM_CTRL) 为推挽输出,默认输出高电平 `SGM_CTRL = 1`
* `void Enter_Low_Power_Sleep(void)`
1. 向 RM69310 屏幕控制器发送关闭显示命令(寄存器 0x28
2. 拉低 `SGM_CTRL = 0` 彻底切断 PMIC。
3. 配置并使能按键端口中断与 RF 中断。
4. 执行 `PCON |= 0x02` 挂起 CPU。
* `void Wakeup_Restore(void)`
1. 拉高 `SGM_CTRL = 1` 开启 SGM3833 供电。
2. 执行 `delay_ms(50)` 阻塞式等待电平平稳,过滤冲击电流。
3. 调用 `LCD_Init()` 重新对 RM69310 进行整套初始化配置,恢复写屏 GRAM 通道。
4. 状态机切回 `STATE_CLOCK` 并置 `Redraw = 1`
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->

View File

@@ -1,30 +1,39 @@
# 模块拆分 - 自适应 RF 解码与发射 (Docs/50_module-breakdown/mod-rf.md)
本模块管理射频芯片 GPIO 初始化、433MHz EV1527 解调接收(宽限时比值算法)与射频打包主动发射。已确认此模块与新的 1.14寸屏幕/5按键物理变更兼容
本模块描述 LR690L 433MHz 射频物理收发的控制、自适应宽限时比值解码机制以及对码/呼救编码包的发射
## 1. 模块职责说明
* **职责范围**
1. 初始化射频接收和发射控制引脚
2. 对射频输入引脚进行高低电平时序轮询解码,计算其高低电平比值,实现自适应 EV1527 24位数据解码。
3. 在特定系统请求下(例如发送 SOS 呼救或与 IPC 对码绑定),主动通过发射引脚发出 EV1527 编码帧。
* **自适应比值解调机制**
不依赖死宽度的微秒延迟。检测同步头低电平(时间在 1500~60000us 之间)与高电平时间(时间在 50~2500us 之间),算出两者的时序比例
* `31` 作为低电平对高电平的时钟倍率基准(高低比值支持在 `15``48` 倍范围)
* 计算出每 bit 的标准时钟周期 `T = low_time / 31`,根据此动态 `T` 进行后续 24 个数据位(高/低电平宽度)的比值反解
1. 配置射频芯片接收 GPIO 与发射 GPIO 的电平方向
2. **自适应比值解调算法**
EV1527 协议数据速率变化范围较宽。本模块采用自适应宽度比值解调机制:
* 捕获到一个低电平维持时间介于 1500us~60000us 之间的脉冲,判定为同步前导头。
* 抓取该帧的标准时钟周期基准 `T = low_time / 31`(前导低电平占 31 个时钟周期 T允许 T 的宽度随环境在宽限范围内自适应计算)
* 连续捕获 24 位宽数据:每个 bit 的电平变化依据其高低电平的宽幅比例1:3 对应 03:1 对应 1反解出对应的数据位
3. 解析出 24 位载波包:前 20 位为无线探测器的出厂唯一地址码Address Code后 4 位为状态数据码Data Code
4. **数据包主动发送**
将手环 Factory ID 编码为一帧 EV1527 数据,通过物理发射引脚输出同步头及 24 位调制波形,连发多遍以确保 IPC 接收质量。
## 2. 接口与函数说明
---
* `void RF_Init(void)`:配置射频芯片引脚方向,初始化控制端口电平。
## 2. 业务状态射频数据编码定义
* **对码绑定发送 (LIAISON IPC)**
* 数据码固定为 **`0x01`** (0001),源地址为手环 20-bit 出厂 Factory ID周期性连续发射。
* **本地主动 SOS 呼救 (SOS EMIS)**
* 数据码固定为 **`0x08`** (1000),源地址为手环 20-bit 出厂 Factory ID以 2 秒为周期循环向外发射。
* **传感器自动识别对码**
* 手环被动接收 433MHz 对码信号。若捕获成功,状态机直接**依据后 4 位数据码的数据值自动判断识别传感器的设备类型**(如门磁为 `0x01`,红外为 `0x02`),直接跳转到对码微调序号页,类型与大图标只读高亮。
---
## 3. 接口与函数说明
* `void RF_Init(void)`:初始化射频物理端口,使能 INT2 (接收脚的沿中断)。
* `bit EV1527_Decode(u32 *out_addr, u8 *out_type)`
进行射频接收包解码尝试
* 首先通过计数检测大于 `1500us` 的同步低电平前导信号。
* 自适应计算时钟基准 `T`
* 连续接收 24 位宽数据:如果某个数据位的高电平比低电平长,则认为该位为 `1`;反之为 `0`
* 解码成功将 20 位地址存入 `out_addr`,后 4 位数据存入 `out_type` 并返回 `1`;否则返回 `0`
主循环轮询调用。如果捕获到合法同步头且成功解调出 24 位数据流,则输出 `out_addr``out_type` 并返回 1否则返回 0
* `void EV1527_Transmit(u32 addr, u8 data_code)`
主动发射一帧数据。生成同步头,并按 `1` 码(高电平 3T,电平 1T `0` 码(高电平 1T,电平 3T依次驱动发射引脚电平,持续多帧以确保接收方捕获
* 该函数被手环用作向 IPC 摄像头主动对码绑定(`data_code = 0x01`)。
* 该函数也用于手环主动发射 SOS 紧急呼救包(`data_code = 0x08`),此时传入手环自身的出厂固定 ID `bracelet_factory_id`
主动发射数据。连发至少 5 遍,每遍间歇 20ms产生满足 T 基准的同步码头、`1`时序(高 3T 低 1T `0`时序(高 1T 低 3T
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes -->
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->

View File

@@ -1,67 +1,60 @@
# 模块拆分 - 系统核心控制与存储 (Docs/50_module-breakdown/mod-sys.md)
# 模块拆分 - 系统状态机与 IAP 数据库 (Docs/50_module-breakdown/mod-sys.md)
本模块定义手环系统的生命周期、时间计数器、定时中断调度器以及断电保存数据库模块。只涉及手环本地代码实现
本模块描述手环的核心生命周期调度器、业务状态机、软件 RTC 时钟,以及持久化防区配置 Flash 扇区读写
## 1. 模块职责说明
* **职责范围**
1. 驱动系统主状态机运转(`current_state`
2. 提供 RTC 时钟维护`current_hour`, `current_min`, `current_sec`)。
3. 利用 Timer 1 产生 1ms 定时器中断,维护毫秒滴答计数器并作为其他外设时效的基础
4. 实现 IAP 读写底层函数,对防区数据库进行存取与读取。
5. 读取并管理手环出厂固定唯一的 `bracelet_factory_id`,作为主动射频发送的数据源地址
1. 驱动核心 SystemState 状态迁移,为其他所有外设(显示、指示灯、振动、无线)提供全局状态指示
2. 提供基于软件计数分频的高可靠 RTC 时钟维护日历:时、分、秒、年、月、日)。
3. 利用 Timer 1 产生 1ms 系统滴答,自增休眠超时计时器 `inactivity_timer`
4. **持久化 IAP 配置数据库管理**
利用 STC32G 内部 Flash 闪存的第 254 扇区(地址:`0xFE0000`)实现数据库的掉电不丢失存储。最大支持 16 个传感器槽位,提供写入、删除、检索和配置防区类别的全套数据库处理接口
5. 管理出厂固定唯一的 `bracelet_factory_id`(通过读取芯片全球唯一硬 UID 进行哈希映射生成20位有效
---
## 2. 数据结构定义
### 2.1 传感器槽结构体 (Sensor_Slot)
### 2.1 传感器槽位信息结构体 (Sensor_Slot)
```c
typedef struct {
u8 is_used; // 0x01:已使用,其它:空闲
u8 addr[3]; // 24位 EV1527 对码物理地址
u8 type; // 传感器类型 (0~6)
u8 zone; // 防区类别 (0: 永久防区, 1: 可撤布防区)
char name_gbk[16]; // 法语自定义名字(前缀+两位数序号,如 PORTE 02
u8 is_used; // 0x01: 有效配置槽位,其它: 空闲槽位
u8 addr[3]; // 24位对码 EV1527 传感器无线物理地址
u8 type; // 传感器类型0(门磁), 1(红外), 2(烟感), 3(紧急), 4(气体), 5(水浸), 6(振动)
u8 zone; // 防区类别0(永久防区 - 24h), 1(普通可撤布防区)
char name_gbk[16]; // 法语防区名称,拼装格式为 "[预设前缀] [微调序号]" (如 PORTE 02)
} Sensor_Slot;
```
### 2.2 系统状态枚举 (SystemState)
### 2.2 系统核心状态枚举 (SystemState)
```c
typedef enum {
STATE_NORMAL,
STATE_ARMED,
STATE_DISARMED,
STATE_PAIR_MENU,
STATE_PAIR_WAIT,
STATE_PAIR_CONFIRM,
STATE_PAIR_SUCCESS,
STATE_PAIR_FAIL,
STATE_ALARM,
STATE_SOS_EMITTED
STATE_CLOCK, // 时钟常态待机(大字走时与电量大图标)
STATE_SLEEP, // 彻底关屏断电深度睡眠(主时钟暂停,挂起 CPU
STATE_MENU, // 主选择菜单(单大条目卡片式循环切换)
STATE_SET_TIME, // 时间日期微调修改(单大参数显示)
STATE_PAIR_SEARCH, // 对码雷达搜寻等待
STATE_PAIR_CONFIRM, // 序号微调与保存确认
STATE_ARM_DISARM_SELECT, // 系统手动布撤防更改
STATE_IPC_BIND, // 对码绑定发射
STATE_ALARMING, // 收到传感器警报响应(高频爆闪与间歇振动)
STATE_SOS_EMITTED // 本地主动发射 SOS 求救
} SystemState;
```
## 3. 模块接口说明
---
### 3.1 定时与时钟函数
* `void Timer1_Init(void)`:配置 Timer 1 作为 1ms 中断源,并使能全局中断。
* `void Timer1_Isr(void) interrupt 3`ISR 中断处理,自增时间滴答、维护 LED 闪烁周期、马达振动波形定时,以及维护软件 RTC自增 `current_sec` 并在满 60 秒时触发主界面分钟刷新)。
## 3. 接口与函数说明
### 3.2 IAP 数据库存储接口
* `void Load_Database(void)`:从 Flash 扇区 `0xFE0000` 读取 16 组传感器配置载入内存的 `sensor_list`
* `void Save_Database(void)`:擦除 Flash 扇区并将内存中的 `sensor_list` 写入 Flash。
* `void Add_Sensor(u32 addr, u8 type, u8 suffix_idx)`:向 `sensor_list` 添加传感器信息,将其名字拼装为 `[前缀] [suffix_idx]`,并自动调用 `Save_Database()`
* `bit Check_Sensor_ID(u32 addr, u8 *out_slot_index)`:在数据库中检索该地址码,若存在则返回它的槽位索引。
### 3.3 系统初始化与全局 ID 获取
* `void System_Init(void)`
1. 初始化单片机系统寄存器(WTST, EAXFR 等)
2. 读取 STC32G 硬件全球唯一 ID (UID)
3. 提取 UID 中的后 3 字节,并通过 `& 0x0FFFFF` 裁剪为 20 位 EV1527 源地址,赋值给全局变量 `bracelet_factory_id`。如果读取失败,则默认赋予 `0x37A86UL`
4. 载入数据库并配置 Timer1 和外设 GPIO 状态
配置单片机寄存器(使能 FCON开启扩展 SFR调整外部 RAM 模式)。读取内置全球唯一 UID 拼装 Factory ID。初始化 Timer 1 开启 1ms 时基,载入 Flash 数据库
* `void Timer1_Init(void)`:配置 Timer 1 中断,提供 1ms 精度的时基回调
* `void Timer1_Isr(void) interrupt 3`
中断服务函数。累加 `inactivity_timer`;更新 RTC秒/分/时/日/月/年)数值;分频驱动马达状态机和 RGB 闪烁标志
* `void Load_Database(void)`:读取 `0xFE0000` Flash 扇区数据至内存缓冲槽。
* `void Save_Database(void)`:通过 STC IAP 命令擦除 `0xFE0000` 扇区,并将缓冲槽内的 16 组数据完整写回。
* `void Add_Sensor(u32 addr, u8 type, u8 suffix_idx)`:向空闲槽位添加新传感器。根据 `type` 索引获取设备名称前缀(如 `PORTE``PIR`),拼装为 `[前缀] [suffix_idx]`,存入槽位并执行 `Save_Database()` 持久化。
* `bit Check_Sensor_ID(u32 addr, u8 *out_slot_index)`:在数据库中查寻地址码,若匹配则返回槽位索引,作为警报时的名称提取依据。
### 3.4 电池与充电检测接口
* `u16 Get_ADC_Result(u8 channel)`:底层 ADC 数据采集函数,完成单次指定通道的模拟量到 12-bit 数字量的转换并返回读数。
* `u8 Read_Battery_Percent(void)`:读取通道 8 (`P0.0`),将电压采样转换数据做线性比例转换(`1279~1628` 映射到 `0~100`),计算并返回电量百分比。
* `bit Is_Charging(void)`:读取 `P2.7`(充电检测管脚)的 GPIO 数字输入电平。当电平为高1时代表 USB 已接入,返回 `1`;否则返回 `0`
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes -->
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->

View File

@@ -1,10 +1,10 @@
# 编码实现 - 按键扫描与事件判定 (Docs/60_coding/mod-key.md)
本文件对应按键状态消抖、长按计时判定以及组合按键识别的具体编码实现细节。
本文件对应按键状态消抖、确认键长按 3 秒判定以及主键事件注入的具体编码实现细节。
## 1. 对应代码源文件
* [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (定时中断内的按键扫描、按键状态累加、主循环事件处理)
* [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (定时中断内的按键扫描、按键状态高阻读取、长短按电平扫描)
## 2. 关键代码片段与逻辑实现
@@ -26,67 +26,111 @@ void Key_Scan_Process(void) {
key_sos1_state = (key_sos1_state << 1) | KEY_SOS1;
key_sos2_state = (key_sos2_state << 1) | KEY_SOS2;
// 双上部 SOS 键的逻辑并联(低电平有效,按位与运算相当于逻辑或)
// 双上部 SOS 键的逻辑并联
key_sos_state = key_sos1_state & key_sos2_state;
// 2. 长按与组合按键计时器累加
if ((key_up_state & 0x07) == 0 && (key_down_state & 0x07) == 0) {
// ▲ + ▼ 组合键按住
comb_hold++;
if (comb_hold == 300) { // 3秒
key_event_buf = KEY_COMB_LONG;
// 2. 按键长按与短按状态机扫描
// ▲ 键 (UP) 处理
if ((key_up_state & 0x07) == 0) {
key_up_hold++;
} else {
if (key_up_hold > 2) {
key_event_buf = KEY_UP_SHORT;
}
key_up_hold = 0;
}
// ▼ 键 (DOWN) 处理 (支持长按 2 秒清除报警/退出)
if ((key_down_state & 0x07) == 0) {
key_down_hold++;
if (key_down_hold == 200) { // 2秒
key_event_buf = KEY_DOWN_LONG;
}
} else {
comb_hold = 0;
// 单个按键:▲ 键 (UP)
if ((key_up_state & 0x07) == 0) {
key_up_hold++;
} else {
if (key_up_hold > 2 && key_up_hold < 200) {
key_event_buf = KEY_UP_SHORT;
}
key_up_hold = 0;
if (key_down_hold > 2 && key_down_hold < 200) {
key_event_buf = KEY_DOWN_SHORT;
}
key_down_hold = 0;
}
// 单个按键:■ 确认键 (CONFIRM)
if ((key_confirm_state & 0x07) == 0) {
key_confirm_hold++;
} else {
if (key_confirm_hold > 2 && key_confirm_hold < 200) {
key_event_buf = KEY_CONFIRM_CLICK;
}
key_confirm_hold = 0;
// ■ 确认键 (CONFIRM) 处理 (支持长按 3 秒进入/退出)
if ((key_confirm_state & 0x07) == 0) {
key_confirm_hold++;
if (key_confirm_hold == 300) { // 3秒
key_event_buf = KEY_CONFIRM_LONG;
}
} else {
if (key_confirm_hold > 2 && key_confirm_hold < 300) {
key_event_buf = KEY_CONFIRM_CLICK;
}
key_confirm_hold = 0;
}
// 单个按键:并联 SOS 键
if ((key_sos_state & 0x07) == 0) {
key_sos_hold++;
if (key_sos_hold == 500) { // 5秒
key_event_buf = KEY_SOS_LONG;
}
} else {
if (key_sos_hold > 2 && key_sos_hold < 500) {
key_event_buf = KEY_SOS_SHORT;
}
key_sos_hold = 0;
// 并联 SOS 键
if ((key_sos_state & 0x07) == 0) {
key_sos_hold++;
if (key_sos_hold == 3) {
key_event_buf = KEY_SOS_ACTIVE;
}
// ▼ 键 (DOWN) 处理同上...
} else {
key_sos_hold = 0;
}
}
```
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes -->
### 2.2 事件异步读取与清除
主循环在其周期内轮询 `key_event_buf`。一旦发现其非 `KEY_EVENT_NONE`,拷贝并立即重置该缓冲区为 `KEY_EVENT_NONE` 以防止重复触发:
### 2.2 事件解调与微调分配分发 (Process_System_Event 示例)
在主循环事件分发中,对各个状态下的事件进行处理:
```c
if (key_event_buf != KEY_EVENT_NONE) {
KeyEvent evt = key_event_buf;
key_event_buf = KEY_EVENT_NONE; // 清空事件缓冲区
void Process_System_Event(KeyEvent evt) {
switch (current_state) {
case STATE_MENU:
if (evt == KEY_UP_SHORT) {
if (menu_index > 0) menu_index--;
Redraw = 1;
} else if (evt == KEY_DOWN_SHORT) {
if (menu_index < 3) menu_index++;
Redraw = 1;
} else if (evt == KEY_CONFIRM_CLICK) {
Enter_Menu_Branch(menu_index);
} else if (evt == KEY_CONFIRM_LONG) {
current_state = STATE_CLOCK;
Redraw = 1;
}
break;
// 执行具体事件动作,如在常态下短按 SOS 进入 SOS 警报发射状态等
// ...
case STATE_PAIR_CONFIRM:
// 直接进行后缀序号微调 (传感器类型已由射频数据码自动识别并锁定)
if (evt == KEY_UP_SHORT) {
if (selected_suffix < 99) selected_suffix++;
Redraw = 1;
} else if (evt == KEY_DOWN_SHORT) {
if (selected_suffix > 1) selected_suffix--;
Redraw = 1;
} else if (evt == KEY_CONFIRM_CLICK) {
// 点击 CONFIRM直接将自动解析的类型和调整好的序号写入数据库
Add_Sensor(captured_addr, auto_detected_type, selected_suffix);
current_state = STATE_CLOCK;
Redraw = 1;
} else if (evt == KEY_CONFIRM_LONG) {
// 长按 3 秒放弃保存返回时钟
current_state = STATE_CLOCK;
Redraw = 1;
}
break;
case STATE_IPC_BIND:
if (evt == KEY_CONFIRM_LONG || evt == KEY_DOWN_LONG) {
current_state = STATE_CLOCK;
Redraw = 1;
} else if (evt == KEY_CONFIRM_CLICK) {
EV1527_Transmit(bracelet_factory_id, 0x01);
}
break;
// 其它状态处理...
}
}
```
通过该缓冲区实现了按键中断采集与主循环处理的解耦。
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->

View File

@@ -1,23 +1,21 @@
# 编码实现 - LCD 显示与字模驱动 (Docs/60_coding/mod-lcd.md)
# 编码实现 - AMOLED 显示与字模驱动 (Docs/60_coding/mod-lcd.md)
本文件对应 LCD 驱动与字模渲染的具体编码实现细节。
本文件对应 Truly AMOLED 驱动与无障碍大图文渲染的具体编码实现细节。
## 1. 对应代码源文件
* [Drivers/lcd.c](file:///c:/workfile/105/stc32g12k128/Drivers/lcd.c) (LCD 初始化、SPI 数据写、画图画字函数)
* [Drivers/lcd.h](file:///c:/workfile/105/stc32g12k128/Drivers/lcd.h) (管脚接口与画图画字声明)
* [Drivers/lcd_font.h](file:///c:/workfile/105/stc32g12k128/Drivers/lcd_font.h) (ASCII 8x16 点阵字模与图标数据)
* [Drivers/lcd_font.h](file:///c:/workfile/105/stc32g12k128/Drivers/lcd_font.h) (包含 16x32 字符点阵、32x64 超大字模、48x48 & 64x64 大图标点阵)
## 2. 关键代码片段与逻辑实现
### 2.1 局部窗口设置 (X/Y轴偏移量修正)
`lcd.c` 中配置窗口时,需要将坐标对齐到 ST7789V 在 135x240 面板下的实际 GRAM 范围,加入 X 轴 52 像素、Y 轴 40 像素的偏差
### 2.1 局部窗口设置 (X 轴偏移量修正)
`lcd.c` 中配置窗口时,需要将坐标对齐到 Truly AMOLED (RM69310) 在 120x240 面板下的实际 GRAM 范围,加入 X 轴 4 像素的偏移Y 轴无偏移
```c
void LCD_SetWindow(u16 x1, u16 y1, u16 x2, u16 y2) {
x1 += 52;
x2 += 52;
y1 += 40;
y2 += 40;
x1 += 4;
x2 += 4;
LCD_WriteCmd(0x2A); // Column Address Set
LCD_WriteData(x1 >> 8);
@@ -33,106 +31,80 @@ void LCD_SetWindow(u16 x1, u16 y1, u16 x2, u16 y2) {
}
```
### 2.2 电池图标与动态百分比 (包含充电 + 后缀) 渲染
`main.c` 的时钟页面刷新中,获取当前百分比及充电状态,动态绘制到状态栏
### 2.2 待机大字时钟与大电量图标渲染
`main.c` 中渲染时钟,时钟字符使用 `32x64px`,电量文字使用 `16x32px`,状态锁使用 `32x32px` 图标
```c
void UI_ShowClockPage(SystemState state, u8 hour, u8 min) {
char time_str[6];
char bat_str[10];
u8 bat_val = Read_Battery_Percent();
bit charging = Is_Charging();
LCD_Clear(COLOR_BLACK);
// 充电状态判定与百分比字符串格式化
if (charging) {
// 充电时显示 + 号后缀,如 "85%+"
if (bat_val >= 100) {
bat_str[0] = '1'; bat_str[1] = '0'; bat_str[2] = '0'; bat_str[3] = '%'; bat_str[4] = '+'; bat_str[5] = '\0';
} else {
bat_str[0] = '0' + (bat_val / 10);
bat_str[1] = '0' + (bat_val % 10);
bat_str[2] = '%';
bat_str[3] = '+';
bat_str[4] = '\0';
}
// X 坐标向左平移 8 像素 (X=69) 以确保和电池图标 (X=107) 之间有 6px 以上的安全间距
LCD_ShowString(69, 24, bat_str, COLOR_GRAY, COLOR_BLACK);
// 1. 渲染状态栏 (大锁图标 + 16x32大电量)
if (state == STATE_ARMED) {
LCD_ShowImage(10, 20, 32, 32, bmp_lock_32x32, COLOR_RED, COLOR_BLACK);
} else {
// 未充电正常显示百分比,如 "85%"
if (bat_val >= 100) {
bat_str[0] = '1'; bat_str[1] = '0'; bat_str[2] = '0'; bat_str[3] = '%'; bat_str[4] = '\0';
} else {
bat_str[0] = '0' + (bat_val / 10);
bat_str[1] = '0' + (bat_val % 10);
bat_str[2] = '%';
bat_str[3] = '\0';
}
LCD_ShowString(77, 24, bat_str, COLOR_GRAY, COLOR_BLACK);
LCD_ShowImage(10, 20, 32, 32, bmp_unlock_32x32, COLOR_GREEN, COLOR_BLACK);
}
// 绘制电池位图图标
LCD_ShowImage(107, 27, 20, 10, bmp_battery_20x10, COLOR_GRAY, COLOR_BLACK);
// ...
if (charging) {
sprintf(bat_str, "%d%%+", (int)bat_val);
LCD_ShowString(60, 20, bat_str, COLOR_GRAY, COLOR_BLACK); // 16x32px字模
} else {
sprintf(bat_str, "%d%%", (int)bat_val);
LCD_ShowString(70, 20, bat_str, COLOR_GRAY, COLOR_BLACK);
}
LCD_ShowImage(100, 25, 16, 16, bmp_battery_icon_16x16, COLOR_GRAY, COLOR_BLACK);
// 2. 渲染中央超大时钟 (32x64px 字模)
sprintf(time_str, "%02d:%02d", (int)hour, (int)min);
LCD_ShowString32x64Centered(80, time_str, COLOR_WHITE, COLOR_BLACK);
// 3. 渲染底部大日期 (16x32px 字模)
LCD_ShowStringCentered(180, "VEN. 07-10", COLOR_GRAY, COLOR_BLACK);
}
```
### 2.3 字模百分号偏置
`lcd_font.h` 的 ASCII 字符数组中,百分号 `%`(十进制 37十六进制 0x25的字模通过在字模绘制时向上平移 2 像素对齐普通数字的下基准线。
### 2.4 水平居中渲染实现
`lcd.c` 中,字符串居中函数需修改宽度限制参数为 `135` 像素:
### 2.3 单屏单条目无障碍主菜单渲染
主菜单每次只在中央渲染选中条目的 48x48px 图标,下方用大字标出:
```c
void LCD_ShowStringCentered(u16 y, char *str, u16 color, u16 bg_color) {
u16 len = 0;
char *p = str;
while(*p++) len++;
if(len * 8 >= 135)
LCD_ShowString(0, y, str, color, bg_color);
else
LCD_ShowString((135 - len * 8) / 2, y, str, color, bg_color);
void UI_ShowMenuPage(u8 selected_item) {
LCD_Clear(COLOR_BLACK);
LCD_ShowStringCentered(20, "- MENU -", COLOR_GRAY, COLOR_BLACK); // 16x32px
// 渲染选中项的 48x48px 图标
LCD_ShowImage(36, 70, 48, 48, menu_icons_48x48[selected_item], COLOR_CYAN, COLOR_BLACK);
// 渲染选中项的 16x32px 功能名称
LCD_ShowStringCentered(150, menu_names[selected_item], COLOR_WHITE, COLOR_BLACK);
// 渲染底部上下页提示符 (▲ / ▼)
LCD_ShowStringCentered(205, "^ v", COLOR_GRAY, COLOR_BLACK);
}
```
### 2.5 绑定界面 ID 渲染实现
`main.c` 中通过 `FormatHex` 接口将 `bracelet_factory_id` 转换为十六进制字符串,并显示在绑定界面的底部
```c
void UI_ShowBindingPage(void) {
char id_str[15];
// 渲染背景与图标
// ...
// 绘制手环自身的出厂 ID
FormatHex(bracelet_factory_id, id_str);
LCD_ShowStringCentered(180, id_str, COLOR_GRAY, COLOR_BLACK);
}
### 2.6 对码确认与序号微调页渲染
`main.c` 中通过以下方式渲染可供按键微调的数字后缀界面:
### 2.4 对码确认超大序号微调界面渲染
只读传感器类型名称使用 `16x32px`,大图标使用 `48x48px`,微调序号使用 `32x64px` 超大字符渲染
```c
void UI_ShowPairConfirmPage(u8 type, u8 selected_suffix) {
char num_buf[10];
LCD_Clear(COLOR_BLACK);
LCD_ShowStringCentered(40, "v RECU", COLOR_GREEN, COLOR_BLACK);
// 根据类型渲染 32x32 图标
// ...
// 提示 "RECU" (16x32px)
LCD_ShowStringCentered(20, "RECU", COLOR_GREEN, COLOR_BLACK);
// 居中显示预设名称前缀
LCD_ShowStringCentered(135, sensor_prefixes[type], COLOR_WHITE, COLOR_BLACK);
// 渲染传感器 48x48px 大图标
LCD_ShowImage(36, 60, 48, 48, sensor_icons_48x48[type], COLOR_WHITE, COLOR_BLACK);
// 拼接成带选框的 "[ 02 ]" 格式字符串并高亮居中渲染
num_buf[0] = '[';
num_buf[1] = ' ';
num_buf[2] = ' ';
num_buf[3] = '0' + (selected_suffix / 10);
num_buf[4] = '0' + (selected_suffix % 10);
num_buf[5] = ' ';
num_buf[6] = ' ';
num_buf[7] = ']';
num_buf[8] = '\0';
LCD_ShowStringCentered(165, num_buf, COLOR_CYAN, COLOR_BLACK);
// 只读渲染传感器名称 (16x32px)
LCD_ShowStringCentered(120, sensor_prefixes[type], COLOR_WHITE, COLOR_BLACK);
// 渲染超大微调序号选框 (32x64px)
sprintf(num_buf, "[%02d]", (int)selected_suffix);
LCD_ShowString32x64Centered(170, num_buf, COLOR_CYAN, COLOR_BLACK);
}
```
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes -->
```
```
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->

View File

@@ -1,30 +1,78 @@
# 编码实现 - 电源管理与常亮控制 (Docs/60_coding/mod-power.md)
# 编码实现 - 电源管理与休眠唤醒 (Docs/60_coding/mod-power.md)
本文件对应取消低功耗休眠、保持屏幕常亮及系统全时监测的具体编码实现。
本文件对应低功耗休眠、关屏切断 PMIC 以及外部中断唤醒的具体编码实现。
## 1. 对应代码源文件
* [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (初始化供电配置)
* [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (定义电源控制引脚使能、超时判断及进入 Power-Down 和中断唤醒的实现)
## 2. 关键代码片段与逻辑实现
### 2.1 供电引脚常使能
系统初始化阶段,配置 `LCD_PWR_CTRL`(控制屏和背光供电的引脚)为推挽输出模式,并输出高电平
### 2.1 供电引脚初始化
系统初始化,配置 `SGM_CTRL`(控制 SGM3833 供电的引脚 `P3.4`)为推挽输出,并拉高使能
```c
sbit SGM_CTRL = P3^4;
void Power_Init(void) {
// P3.4 (LCD_PWR_CTRL) 配置为推挽输出
// P3.4 (SGM_CTRL) 配置为推挽输出
P3M1 &= ~(1 << 4);
P3M0 |= (1 << 4);
// 拉高引脚,开启 LCD 背光与供电使能
LCD_PWR_CTRL = 1;
// 拉高引脚,开启 SGM3833 负压供电
SGM_CTRL = 1;
}
```
### 2.2 移除睡眠超时与休眠指令
主循环事件分发中,不再轮询待机时间累加器,也不再调用休眠流程
* **移除** `Enter_Deep_Sleep` 函数。
* **移除** 端口 P0 的引脚中断设置(`P0INTE` 等)及 `PD` 停机指令。
* 手环时刻保持主频运转Timer 1 中断不断累加,`EV1527_Decode` 全时监听射频,随时处理防区报警 and SOS 求救触发。
### 2.2 进入低功耗睡眠 (Enter_Low_Power_Sleep)
10 秒无按键操作发生后,关闭屏幕显示,断电,配置中断,并进入 Power-Down 挂起状态
```c
void Enter_Low_Power_Sleep(void) {
// 1. 给 AMOLED 发送 Sleep In 命令
LCD_WriteCmd(0x10);
<!-- Checked and verified with always-on screen and sleep removal changes -->
// 2. 切断 SGM3833 电源,停止输出负压
SGM_CTRL = 0;
// 3. 配置引脚中断 (以 P0.4 确认键、P0.2/P0.5 SOS键为例)
// 允许 P0 口引脚下降沿中断以供按键唤醒
P0IM1 &= ~0x36; // P0.1, P0.2, P0.3, P0.4, P0.5 设为下降沿或电平触发
P0IM0 &= ~0x36;
P0INTE |= 0x36; // 开启中断使能
// 4. 配置射频 carrier 接收脚 (如 P3.6) 为外部中断唤醒
EX2 = 1; // 使能外部中断2
// 5. 写入 PCON.1 使单片机进入 Power-Down 深度睡眠状态
PCON |= 0x02; // MCU 挂起,主时钟停振,直到外部中断唤醒
_nop_();
_nop_();
}
```
### 2.3 中断唤醒与恢复 (Wakeup_Restore)
当产生按键或射频中断后,单片机被唤醒,首先执行外部中断 ISR退出低功耗并重新使能 OLED
```c
// 外部中断服务子程序 (以按键 P0 端口中断为例)
void Port0_Isr(void) interrupt 37 {
P0INTF = 0x00; // 清除 P0 中断标志
Wakeup_Restore();
}
void Wakeup_Restore(void) {
if (SGM_CTRL == 1) return; // 已经在工作状态,直接返回
// 1. 拉高 SGM_CTRL 重新使能 SGM3833 供电
SGM_CTRL = 1;
// 2. 延时约 50ms 等待升压/负压输出稳定
Delay50ms();
// 3. 重新对 RM69310 屏幕控制器做初始化序列
LCD_Init();
// 4. 重置无按键操作的超时计时器
inactivity_timer = 0;
}
```
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->

View File

@@ -1,6 +1,6 @@
# 编码实现 - 自适应 RF 解码与发射 (Docs/60_coding/mod-rf.md)
本文件对应自适应射频解码与主动发射的具体编码实现细节。已验证无变动。
本文件对应自适应射频解码与主动发射的具体编码实现细节。
## 1. 对应代码源文件
@@ -13,7 +13,7 @@
解码时,软件轮询检测接收管脚状态。
1. **抓取同步头**:检测到一个大于 `1500us` 且小于 `60000us` 的低电平。
2. **计算基准 T**`T = low_time / 31`
3. **采集 24 个数据位**:对于每一位,检测它的高电平持续时间 `h_val` 低电平持续时间 `l_val`
3. **采集 24 个数据位**:对于每一位,检测它的高电平持续时间 `h_val` and 低电平持续时间 `l_val`
```c
// 解码一位数据
if (h_val > l_val) {
@@ -22,7 +22,7 @@
res = (res << 1) | 0;
}
```
4. **极速解析与 CPU 效率**:去除了之前在主循环中阻塞多帧解析的详细日志串口打印,仅在解析整帧成功后发送一行 Summary。单帧处理耗时控制在 **2ms** 以内,彻底避免了连续波形导致的丢包现象
4. 正常解码成功将 20 位地址存入 `out_addr`,后 4 位数据存入 `out_type` 并返回 `1`
### 2.2 主动广播发射 (EV1527_Transmit)
手环通过 LT4455 发送对码广播或主动 SOS 警报,在 `rf.c` 中实现:
@@ -37,4 +37,4 @@ void EV1527_Transmit(u32 addr, u8 data_code) {
* **绑定对码**:当用户触发对码绑定时,传入 `addr = bracelet_factory_id``data_code = 0x01`。
* **主动 SOS 警报**:当触发主动 SOS 时,主状态机在 `STATE_SOS_EMITTED` 下定期以时间片的形式调用此函数,传入 `addr = bracelet_factory_id``data_code = 0x08`(即二进制 `1000`)。
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes -->
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->

View File

@@ -1,32 +1,58 @@
# 编码实现 - 系统核心控制与存储 (Docs/60_coding/mod-sys.md)
本文件对应系统控制与存储的具体编码实现细节。只包含手环代码实现。
本文件对应系统状态机、超时低功耗休眠判断以及断电数据库的具体编码实现。
## 1. 对应代码源文件
* [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (系统主循环、定时器中断、IAP 存取)
* [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (系统主逻辑、主状态机切换、10s 超时累加判断)
* [App/config.h](file:///c:/workfile/105/stc32g12k128/App/config.h) (系统状态及数据结构定义)
## 2. 关键代码片段与逻辑实现
### 2.1 定时器 1 中断服务 (Timer1_Isr)
定时器初始化配置为 1ms`main.c`第 190~250 行左右实现。
`Timer1_Isr` 里,我们累加 `ms_tick`。当累加到 1000 时,重置 `ms_tick = 0`,自增 `current_sec`。若秒达 60则自增 `current_min` 并置位 `clock_updated = 1` 通知主循环在下一个周期重绘时钟画面。
### 2.2 IAP 读写细节
`main.c` 中通过 IAP 寄存器控制读写。
### 2.1 10秒无操作休眠时序 (Timer1_Isr)
定时器初始化配置为 1ms`main.c` 中实现。
`Timer1_Isr` (每 10ms 调用一次) 中,当系统非处于休眠(`STATE_SLEEP`)且非处于报警(`STATE_ALARMING`/`STATE_SOS_EMITTED`)状态时,累加无按键操作计数器 `inactivity_timer`
```c
void IAP_EraseSector(u16 addr) {
IAP_CMD = 3; // 扇区擦除
IAP_ADDRL = addr & 0xFF;
IAP_ADDRH = addr >> 8;
IAP_TRIG = 0x5A; // 触发
IAP_TRIG = 0xA5;
_nop_();
u16 inactivity_timer = 0;
void Timer1_Isr(void) interrupt 3 {
// 基础毫秒滴答计数
ms_tick++;
// 10ms 调度周期
if (ms_tick % 10 == 0) {
// 当不是休眠或报警时,进行超时计时累加
if (current_state != STATE_SLEEP && current_state != STATE_ALARMING && current_state != STATE_SOS_EMITTED) {
inactivity_timer++;
}
}
}
```
### 2.2 主循环低功耗检测与状态转换
在主循环 `main()` 中,不断检查 `inactivity_timer` 是否达到 1000即 1000 * 10ms = 10 秒):
```c
void main(void) {
System_Init();
while (1) {
// 读取按键事件并重置计时器
if (key_event_buf != KEY_EVENT_NONE) {
inactivity_timer = 0; // 重置无按键操作计时器
}
// 如果达到 10 秒无操作,进入低功耗休眠
if (inactivity_timer >= 1000) {
inactivity_timer = 0;
current_state = STATE_SLEEP;
Enter_Low_Power_Sleep(); // 关屏、拉低 SGM_CTRL、单片机停机
}
// 轮询解码及其他主状态机业务处理
// ...
}
}
```
- 主动擦除与写字节前必须先调用使能,写完后调用 `IAP_Disable()` 避免误写入损坏配置。
- 数据库保存在末尾扇区 `0xFE0000`(大小为 512 字节)。
### 2.3 获取出厂唯一 ID (Get_Bracelet_Factory_ID)
从 STC32G 的内置 IDATA RAM 区域 `0xF1~0xF7` 处读取 7 字节唯一硬件 ID并将其转换为符合 EV1527 的 20 位地址码:
@@ -35,7 +61,7 @@ u32 Get_Bracelet_Factory_ID(void) {
unsigned char idata *p_uid = (unsigned char idata *)0xF1;
u32 uid = 0;
// 组合后 3 字节并进行 20 位掩码过滤
// 组合后 3 字节并进行 20 位限制
uid = ((u32)p_uid[4] << 16) | ((u32)p_uid[5] << 8) | p_uid[6];
uid &= 0x0FFFFF; // 20-bit address range limit for EV1527
@@ -46,41 +72,14 @@ u32 Get_Bracelet_Factory_ID(void) {
}
```
### 2.4 传感器自定义名称拼接与写入 (Add_Sensor)
`Add_Sensor(u32 addr, u8 type, u8 suffix_idx)` 中,系统预设 7 类传感器名前缀:
```c
char *code sensor_prefixes[7] = {
"PORTE", "PIR", "FUMEE", "URGENCE", "GAZ", "INOND.", "VIBRA."
};
```
在写入时,手动拼接前缀和两位数字 `suffix_idx` 后缀:
```c
void Add_Sensor(u32 addr, u8 type, u8 suffix_idx) {
u8 slot = ...; // 检索空槽或匹配历史ID
char *prefix = sensor_prefixes[type];
u8 len = 0;
// 拷贝前缀
while (prefix[len] != '\0' && len < 11) {
sensor_list[slot].name_gbk[len] = prefix[len];
len++;
}
sensor_list[slot].name_gbk[len++] = ' ';
sensor_list[slot].name_gbk[len++] = '0' + (suffix_idx / 10);
sensor_list[slot].name_gbk[len++] = '0' + (suffix_idx % 10);
sensor_list[slot].name_gbk[len] = '\0';
Save_Database();
}
### 2.5 电池采样与充电状态读取实现
### 2.4 电池采样与充电状态读取实现
在系统模块中,通过硬件 ADC 通道 8 (P0.0) 和数字 I/O (P2.7) 采集电池状态:
1. **ADC 初始化 (ADC_Init)**
```c
void ADC_Init(void) {
P0M1 |= 0x01; P0M0 &= ~0x01; // P0.0 配置为高阻输入 (模拟输入模式)
ADCCFG = 0x2F; // RESFMT = 1 (右对齐), ADC 时钟配置为 SYSCLK / 2 / 16
ADCCFG = 0x2F; // RESFMT = 1 (右对齐)
ADC_CONTR |= 0x80; // 打开 ADC 电源 (ADC_POWER = 1)
}
```
@@ -118,5 +117,4 @@ bit Is_Charging(void) {
}
```
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes -->
```
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->

View File

@@ -8,25 +8,24 @@
| 串口发送字符 | 对应仿真的物理事件与说明 | 预期系统响应 |
| :--- | :--- | :--- |
| **`u` / `U`** | 仿真短按 / 长按 **侧边 ▲ 键** | 时钟微调 / 菜单滚动 / **对码确认页:自增序号 (01->99)** |
| **`d` / `D`** | 仿真短按 / 长按 **侧边 ▼ 键** | 时钟微调 / 菜单滚动 / 长按取消报警 / **对码确认页:自减序号 (99->01)** |
| **`e` / `E`** | 仿真短按 / 长按 **侧边 ■ 确认键** | 选中并进入等待配对 / 短按取消报警 / **对码确认页:确认保存配置** |
| **`s` / `S`** | 仿真短按 / 长按 **上部 SOS 1 键** | 触发本地主动 SOS 警报 / 触发对码保存 / **对码确认页:确认保存配置** |
| **`x` / `X`** | 仿真短按 / 长按 **上部 SOS 2 键** | 同上,验证双 SOS 键并联触发等效性 |
| **`c`** | 仿真同时长按 **▲ + ▼ 键 3 秒** | 系统直接进入配对模式P-00 |
| **`u`** | 仿真短按 **侧边 ▲ 键** | 菜单向上滚动 / 序号自增 / 增加数值 |
| **`d`** | 仿真短按 **侧边 ▼ 键** | 菜单向下滚动 / 序号自减 / 减少数值 |
| **`D`** | 仿真长按 **侧边 键** (2 秒) | 解除警报退回时钟 / 放弃 IPC 绑定返回时钟 |
| **`e`** | 仿真短按 **侧边 ■ 确认键 (CONFIRM)** | 确认选中进入 / 确认保存退出 |
| **`E`** | 仿真长按 **侧边 ■ 确认键 (CONFIRM)** (3 秒) | 从时钟待机进入菜单 / 菜单返回时钟 / 放弃保存或绑定返回时钟 |
| **`s` / `S`** | 仿真短按 / 长按 **上部 SOS 1 键** | 触发本地主动 SOS 报警发送 / 解除警报退回时钟 |
| **`x` / `X`** | 仿真短按 / 长按 **上部 SOS 2 键** | 验证双 SOS 键并联触发等效性 |
| **`a`** | 仿真捕获到**已配对门磁**报警信号 | 系统跳转到门磁报警界面,亮红灯,振动 1 次 |
| **`p`** | 仿真捕获到**已配对 PIR 红外**信号 | 系统跳转到 PIR 报警界面,亮橙灯,振动 2 次 |
| **`1`** | 强制切换系统状态为 **NORMAL** (撤防/常态) | 屏幕时钟状态栏显示 `NORMAL` |
| **`2`** | 强制切换系统状态为 **ARMED** (布防) | 屏幕时钟状态栏显示红色闭锁 `🔒` |
| **`3`** | 强制切换系统状态为 **DISARMED** (撤防) | 屏幕时钟状态栏显示绿色开锁 `🔓` |
| **`1`** | 强制唤醒手环并退回 **STATE_CLOCK** | 屏幕时钟状态栏显示待机时钟 |
---
## 2. LED 与马达 IO 跳变日志
主循环对外设引脚的电平状态进行了跟踪,当引脚电平发生变化时,自动通过串口向外部终端打印输出日志。这可以在没有焊接实体马达或 LED灯的情况下验证控制逻辑是否正确
* **LED 状态日志格式**`[STATE] LED status -> R:1 G:0 B:0` (0表示亮1表示灭WS2812B 幻彩 LED 控制状态)
* **马达状态日志格式**`[STATE] MOTOR -> 1` (1表示开启振动0表示关闭振动)
* **LED 状态日志格式**`[STATE] LED status -> R:1 G:0 B:0` (WS2812B 级联双灯珠输出状态)
* **马达状态日志格式**`[STATE] MOTOR -> 1` (1表示开启振动0表示关闭)
---
@@ -36,32 +35,38 @@
* **测试方法**
1. 正常上电启动,查看待机界面右上方电量。
2. 使用外部电压源微调电量采样引脚 **`P0.0`**(模拟输入)输入电压在 1.03V 到 1.31V 之间变化,检查电量百分比是否从 `0%` 线性平滑变化到 `100%`
3. 拔插 USB 充电器,或者直接改变引脚 **`P2.7`** 的电平输入(拉高至 3.3V 或拉低到地),观察百分比数值后面是否能同步增加或移除 `+` 后缀(如在 `85%``85%+` 之间切换,且格式化与电池图标不贴连)。
* **预期结果**:时钟、日期显示正常。在 **135 像素宽** 屏幕上电量百分比与电池图标X=107, Y=27对齐美观,间距大于 6 像素。在 USB 插入时电量后追加 `+` 后缀且能完整显示。
3. 拔插 USB 充电器,或者直接改变引脚 **`P2.7`** 的电平输入,观察百分比数值后面是否能同步增加或移除 `+` 后缀(如在 `85%``85%+` 之间切换,且格式化与电池图标不贴连)。
* **预期结果**:时钟、日期显示正常。在 **120 像素宽** AMOLED 屏幕上电量百分比与电池图标X=82, Y=27对齐美观。在 USB 插入时电量后追加 `+` 后缀且能完整显示。
### 3.2 传感器对码与保存
* **测试方法**:同时长按 ▲+▼ 3秒进入配对用上下键选中 `DETEC. PIR`,按下侧边 **■ 确认键** 开启等待,随后物理触发 PIR 传感器。进入对码确认页后,按 **▲ 键** 将中间括号内数字调节至 `02`(即显示 `PIR [ 02 ]`),最后按下 **■ 确认键** (或 **SOS 1**)确认保存。
* **预期结果**
1. 进入配对时LED 亮白色闪烁3Hz马达短振 1 次确认。
2. 解码匹配成功后LCD 屏显示 `✓已保存` 以及 `PIR 02`LED 绿色常亮 2 秒,马达强振 1 次400ms随后自动退回配对菜单。
### 3.3 常亮长显与射频全时监听验证
* **测试方法**:在常态待机下静置手环超过 10 秒,并在此期间使用发射装置发送已配对的传感器射频报警包,或随时短按上部的 SOS 键。
* **预期结果**
1. 手环在静置超过 10 秒后,屏幕背光不关闭,继续常亮展示时钟待机画面。
2. 无线接收解调模块 100% 保持在监听状态,即使在长时间静置后,只要触发匹配的射频报警,手环能立即接收响应,进入报警闪烁及马达震动状态。
3. 随时短按 SOS 键能立刻跳转到主动发送界面并开启 EV1527 信号广播。
### 3.4 手环主动发射 SOS 与 Factory ID 验证
### 3.2 低功耗休眠与唤醒验证
* **测试方法**
1. 正常开机,在串口输出中查看获取到的 20 位 `Factory ID`(或在绑定界面核对 ID
2. 短按上部任意一个 SOS 键触发紧急求救
3. 使用 433MHz 接收调试工具或逻辑分析仪抓取手环发射引脚或射频信号。
1. 手环置于时钟待机状态下,静置 10 秒以上,观察系统状态
2. 按下侧边任意键,或者触发射频接收信号,观察系统状态
* **预期结果**
1. 手环立刻切入 `SOS EMIS` 界面,红灯闪烁,马达周期性震动
2. 接收工具抓取到以手环 `Factory ID` 为源地址的 24 位 EV1527 信号,其 4 位数据码为 `0x08`
3. 该信号以 2 秒为周期循环向外发射,直到按下“确认键”或其它任意键退出报警状态。
1. 静置 10 秒后OLED 屏幕立刻完全黑屏,检测 `SGM_CTRL` (P3.4) 输出电平为低电平0V单片机电流降为微安级Power-Down 挂起状态)
2. 短按侧边任意按键,或触发 433MHz 信号发射,手环应能被**瞬间唤醒**`SGM_CTRL` 重新输出高电平3.3VAMOLED 重新点亮显示,无花屏或时钟错乱
### 3.3 按键菜单交互与功能选择
* **测试方法**
1. 在时钟待机页下,长按侧边 **■ 确认键** 达 3 秒。
2. 在主菜单页面使用 ▲ 和 ▼ 键进行滚动,分别选中 `1. DATE/HEURE``2. APPAIRAGE`,按下 **■ 确认键** 进入。
* **预期结果**
1. 长按 3 秒后,屏幕立刻切换显示 `- MENU -` 选项卡,且默认首项高亮。
2. 滚动选择灵活响应,高亮行外围有 `> <` 箭头指示。按下确认键后能顺利切换至子修改界面或雷达对码界面。
### 3.4 对码自动识别与序号微调验证
* **测试方法**
1. 同时长按 ▲+▼ 3秒进入对码触发门磁信号对码成功。
2. 观察屏幕,手环应**自动根据射频数据码识别出传感器类型为 PORTE**,并直接显示只读名称及门磁图标。
3. 序号选框 `[ 01 ]` 默认高亮,按 ▲/▼ 键确认序号数值是否可以进行增减微调。
4. 点击 **■ 确认键**,手环是否将数据写入 Flash 数据库,并立刻退回到待机时钟页。
* **预期结果**
对码成功进入微调后,传感器类型自动填入,用户仅微调序号,点击 CONFIRM 键直接保存退回时钟。按 CONFIRM 长按 3s 可放弃退出。
### 3.5 手环主动发射 SOS 与 Factory ID 验证
* **测试方法**:短按或长按上部任意一个 SOS 键触发紧急求救,使用 433MHz 接收调试工具或逻辑分析仪抓取手环发射信号。
* **预期结果**
1. 手环立刻切入 `SOS EMIS` 界面,级联双红灯闪烁,马达周期性间歇长振。
2. 接收工具抓取到以手环 `Factory ID` 为源地址的 24 位 EV1527 信号,其 4 位数据码为 `0x08`,以 2 秒为周期循环向外发射。再次按下 SOS 键或其它任意键退出。
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
"hidden_nets": [
"GND"
],
"high_contrast_mode": 1,
"high_contrast_mode": 0,
"net_color_mode": 1,
"opacity": {
"images": 0.6,
@@ -33,7 +33,6 @@
},
"visible_items": [
"vias",
"footprint_text",
"footprint_anchors",
"ratsnest",
"grid",

View File

@@ -40,8 +40,8 @@
"other_text_upright": false,
"pads": {
"drill": 0.0,
"height": 0.310008,
"width": 0.529997
"height": 2.8,
"width": 2.8
},
"silk_line_width": 0.1,
"silk_text_italic": false,
@@ -279,12 +279,7 @@
"equivalence_files": []
},
"erc": {
"erc_exclusions": [
[
"power_pin_not_driven|3200400|1346200|270b45f3-22a3-458a-a43c-db627535af63|00000000-0000-0000-0000-000000000000|/72df0bee-e55e-4d81-989a-168573031a0a|/72df0bee-e55e-4d81-989a-168573031a0a|",
""
]
],
"erc_exclusions": [],
"meta": {
"version": 0
},
@@ -470,9 +465,9 @@
"endpoint_off_grid": "warning",
"extra_units": "error",
"field_name_whitespace": "warning",
"footprint_filter": "ignore",
"footprint_filter": "warning",
"footprint_link_issues": "warning",
"four_way_junction": "ignore",
"four_way_junction": "warning",
"ground_pin_not_ground": "warning",
"hier_label_mismatch": "error",
"isolated_pin_label": "warning",
@@ -488,7 +483,7 @@
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_connected": "warning",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
@@ -496,8 +491,8 @@
"similar_label_and_power": "warning",
"similar_labels": "warning",
"similar_power": "warning",
"simulation_model_issue": "ignore",
"single_global_label": "ignore",
"simulation_model_issue": "warning",
"single_global_label": "warning",
"stacked_pin_name": "warning",
"unannotated": "error",
"unconnected_wire_endpoint": "warning",
@@ -622,14 +617,50 @@
"label": "Datasheet",
"name": "Datasheet",
"show": true
},
{
"group_by": false,
"label": "Sim.Device",
"name": "Sim.Device",
"show": false
},
{
"group_by": false,
"label": "Sim.Pins",
"name": "Sim.Pins",
"show": false
},
{
"group_by": false,
"label": "Sim.Type",
"name": "Sim.Type",
"show": false
},
{
"group_by": false,
"label": "LCSC",
"name": "LCSC",
"show": false
},
{
"group_by": false,
"label": "描述",
"name": "Description",
"show": false
},
{
"group_by": false,
"label": "#",
"name": "${ITEM_NUMBER}",
"show": false
}
],
"filter_string": "",
"group_symbols": true,
"include_excluded_from_bom": true,
"name": "Default Editing",
"name": "",
"sort_asc": true,
"sort_field": "位号"
"sort_field": "${EXCLUDE_FROM_BOM}"
},
"bus_aliases": {},
"connection_grid_size": 50.0,
@@ -660,6 +691,14 @@
"meta": {
"version": 1
},
"ngspice": {
"fix_include_paths": true,
"meta": {
"version": 0
},
"model_mode": 4,
"workbook_filename": ""
},
"page_layout_descr_file": "",
"plot_directory": "",
"reuse_designators": true,
@@ -672,7 +711,7 @@
"uuid": "72df0bee-e55e-4d81-989a-168573031a0a"
}
],
"used_designators": "#PWR28,#PWR57-58",
"used_designators": "U6,U10-11,J1,R18-19,D2,#PWR73-75,C14-15",
"variants": []
},
"sheets": [

File diff suppressed because it is too large Load Diff