docs: optimize SOS button behavior, add 2-step pairing wizard, and IPC exit action
This commit is contained in:
@@ -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 -->
|
||||
|
||||
@@ -2,24 +2,41 @@
|
||||
|
||||
本章节定义手环终端的物理按键交互、报警矩阵(可编程 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 秒进入。显示四个选项,通过 ▲/▼ 键进行滚动,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)** | **步骤1 (选类型)**:切换传感器类型<br>**步骤2 (选序号)**:自增后缀序号 (01->99) | **步骤1 (选类型)**:切换传感器类型<br>**步骤2 (选序号)**:自减后缀序号 (99->01) | **步骤1**:确认类型并进入步骤2 (选序号)<br>**步骤2**:确认保存配置并存入 Flash,返回时钟<br>*长按 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. **对码二步保存向导**:进入对码微调页面后,交互分为两个步骤:
|
||||
> * **第一步 (选择类型)**:屏幕上的类型字段(如 `[ PORTE ]`)带选择框,按 ▲/▼ 可手动修正所捕获的传感器类型。按 CONFIRM 键保存当前选择并切换到第二步。
|
||||
> * **第二步 (选择序号)**:屏幕上的序号选框(如 `[ 01 ]`)高亮,按 ▲/▼ 键微调该类型传感器的序号(01~99)。按 CONFIRM 键完成最终写入并存入 Flash 数据库。
|
||||
> 4. **低功耗休眠与唤醒**:在待机、菜单或非报警页面下,若 **10秒内无任何按键操作**,系统将自动关闭 OLED 屏幕、切断 SGM3833 PMIC 电源,并将 MCU 挂起进入 Power-Down 停机模式。任何按键的按下或射频信号的接收都会通过中断将 CPU 瞬间唤醒。
|
||||
|
||||
## 2. 传感器对码与报警映射矩阵
|
||||
|
||||
@@ -43,9 +60,6 @@
|
||||
> * **数据码**:固定为 **`0x08`** (即 `1000`)。
|
||||
> * **目的**:使 IPC 摄像头能接收解码,将其与配对列表中该手环 of Factory ID 匹对,从而识别求救来源。
|
||||
|
||||
> [!IMPORTANT]
|
||||
> **屏蔽 SOS 被动报警**:为避免两个手环互发 SOS 相互触发造成死循环报警,手环**必须强行过滤**并排除对外界发射的 SOS 求救广播(数据码为 `0x08`,或其它手环发射源)的被动接收。
|
||||
|
||||
## 3. ZONE 防区划分
|
||||
|
||||
手环支持将配对的传感器划分至不同的防区:
|
||||
|
||||
@@ -1,107 +1,264 @@
|
||||
# 架构设计 (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. DATE/HEURE" + 确认键单击
|
||||
STATE_SET_TIME --> STATE_CLOCK : 保存退出 / 10秒无操作超时
|
||||
|
||||
STATE_MENU --> STATE_PAIR_SEARCH : 选中 "2. APPAIRAGE" + 确认键单击
|
||||
STATE_PAIR_SEARCH --> STATE_PAIR_CONFIRM : 捕获 433MHz 传感器数据
|
||||
STATE_PAIR_CONFIRM --> STATE_CLOCK : CONFIRM/SOS 确认并保存配置
|
||||
STATE_PAIR_SEARCH --> STATE_MENU : 长按 ▼ 键退出
|
||||
|
||||
STATE_MENU --> STATE_ARM_DISARM_SELECT : 选中 "3. ARM/DISARM" + 确认键单击
|
||||
STATE_ARM_DISARM_SELECT --> STATE_CLOCK : 选择完成并返回
|
||||
|
||||
STATE_MENU --> STATE_IPC_BIND : 选中 "4. LIAISON IPC" + 确认键单击
|
||||
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 接收到载波变化时,触发中断唤醒 MCU,ISR 立即拉高 `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;
|
||||
软件系统采用 **非阻塞** 的三层级模块化架构设计:
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph Layer3 [应用层 (APP)]
|
||||
MainScheduler[主循环轮询 & 喂狗]
|
||||
SysStateMachine[SystemState 业务状态机]
|
||||
SoftRTC[软件 RTC 计时器]
|
||||
end
|
||||
subgraph Layer2 [外设驱动层 (DRV)]
|
||||
AMOLED[RM69310 AMOLED 屏幕绘制]
|
||||
RF[LR690L 编解码 & 过滤 0x08 SOS 信号]
|
||||
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** 发送 `0x01` (IPC对码) 或 `0x08` (主动SOS)。 |
|
||||
| **显示模块 (LCD)** | 状态机重绘请求 `Redraw == 1` | SPI 串行像素显示数据流 | 驱动 **Truly AMOLED** 渲染当前状态所对应的时钟、主菜单、对码、时间设定或报警画面。 |
|
||||
| **供电管理 (POWER)**| 1. `inactivity_timer >= 1000` (10秒超时)<br>2. 中断唤醒信号 (GPIO/INT2) | 电源电平控制及停机状态 | 1. **进入休眠**:发出关屏指令 $
|
||||
ightarrow$ 拉低 `SGM_CTRL = 0` $
|
||||
ightarrow$ 执行停机挂起(主循环暂停在 `Enter_Low_Power_Sleep` 内);<br>2. **唤醒恢复**:拉高 `SGM_CTRL = 1` $
|
||||
ightarrow$ 延时 50ms $
|
||||
ightarrow$ 重新初始化 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 屏幕局部像素重绘]
|
||||
* 一共支持 **16** 个传感器槽位,通过 `Load_Database()` 和 `Save_Database()` 进行统一序列化读写。
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
# UI 设计 (Docs/40_ui-design/main.md)
|
||||
|
||||
本章节定义手环 135x240 TFT LCD 屏幕的画面布局、各页面坐标位置、中文字体/ASCII 字符对齐基线以及局部刷新逻辑。
|
||||
本章节定义手环 120x240 Truly AMOLED 屏幕的画面布局、各页面坐标位置、中文字体/ASCII 字符对齐基线以及局部刷新逻辑。
|
||||
|
||||
## 1. 屏幕物理参数与刷新机制
|
||||
|
||||
* **屏幕规格**:1.14寸 TFT LCD 屏,物理分辨率 **`135x240`**。
|
||||
* **竖向屏规范**:屏幕窗口配置为 **`135` 像素宽,`240` 像素高**。页面中的时钟、菜单、报警元素均采用竖屏垂直居中排版。
|
||||
* **坐标偏移修正**:TFT LCD 驱动芯片(ST7789V)的显示 RAM 大小为 240x320 像素。在控制 135x240 的竖屏物理面板时,为了将显示窗口完美对齐至显示区域,防止花屏或偏置错误,写屏窗口渲染必须应用 **X 轴偏移量 +52,Y 轴偏移量 +40**。即:`LCD_SetWindow(x1 + 52, x2 + 52, y1 + 40, y2 + 40)`。
|
||||
* **屏幕规格**:0.95寸 Truly AMOLED 屏,物理分辨率 **`120x240`**。
|
||||
* **竖向屏规范**:屏幕窗口配置为 **`120` 像素宽,`240` 像素高**。页面中的时钟、菜单、报警元素均采用竖屏垂直居中排版(以 `X=60` 为几何中线)。
|
||||
* **坐标偏移修正**: Truly AMOLED 屏幕驱动芯片(RM69310)控制的 GRAM 存在偏置。为了使画面对齐,写屏窗口渲染必须应用 **X 轴偏移量 +4,Y 轴偏移量 +0**。即:`LCD_SetWindow(x1 + 4, x2 + 4, y1, y2)`。
|
||||
* **局部刷新防抖机制**:清除重绘标志(`Redraw`)的动作必须在具体的刷新绘制块结束时重置,防止因为主循环高速轮询而产生屏幕闪烁。
|
||||
|
||||
---
|
||||
|
||||
## 2. 页面布局与坐标坐标规范
|
||||
|
||||
### 2.1 时钟待机界面 (STATE_NORMAL / STATE_ARMED / STATE_DISARMED)
|
||||
### 2.1 时钟待机界面 (STATE_CLOCK)
|
||||
|
||||
时钟页面作为常态显示,包括状态栏、数字时钟和日期行。
|
||||
|
||||
@@ -26,60 +26,82 @@
|
||||
| |
|
||||
| |
|
||||
| VEN. 07-10 | <- 底部日期 (Y=180)
|
||||
| |
|
||||
+-----------------------------------------+
|
||||
```
|
||||
|
||||
* **状态栏电量显示(极重要)**:
|
||||
* **电池图标** 绘制在 `X=107, Y=27`。
|
||||
* **电量百分比** 绘制在 `X=77, Y=24`。如果处于充电状态,追加 `+` 号后缀显示为 `85%+`,起始绘制点向左平移(如 `X=69`)以确保与电池图标之间依然有安全间距。
|
||||
* 两个控件在 `135` 像素宽的屏幕上保持 **6 像素安全间距**,彻底防范乱码和字符相撞。
|
||||
* **电池图标** 绘制在 `X=82, Y=27`。
|
||||
* **电量百分比** 绘制在 `X=76, Y=24`(以 `text-anchor="end"` 右对齐绘制)。如果处于充电状态,追加 `+` 号后缀显示为 `85%+`,起始绘制坐标向左偏置,以确保与电池图标之间依然有安全间距,不发生碰撞。
|
||||
* **安防状态显示**:
|
||||
* **布防状态 (ARMED)**:状态栏左侧显示红色闭锁 `🔒` 图标,显示 `ARME`。
|
||||
* **撤防状态 (DISARMED)**:状态栏左侧显示绿色开锁 `🔓` 图标,显示 `DESARME`。
|
||||
|
||||
### 2.2 传感器配对选择菜单 (STATE_PAIR_MENU)
|
||||
### 2.2 主选择菜单页面 (STATE_MENU)
|
||||
|
||||
* **顶部标题**:显示 `MODE PAIR P-XX`(其中 XX 为当前已配对的传感器数量)。
|
||||
* **列表滚动框**:采用 3 行滚动列表,可在 **7 种传感器类型**(门磁、红外、烟感、紧急、气体、水浸、振动)中上下循环滚动选中。当前行高亮并显示粗体外框,字号放大(如 `DETEC. PORTE`),非当前行暗淡显示。
|
||||
* **按键确认行为**:在配对菜单页面,短按侧边 **■ 确认键** 进入所选防区类型的对码等待状态。
|
||||
由时钟待机长按 CONFIRM 键 3 秒进入。显示四个功能条目,供滚动选择:
|
||||
```
|
||||
+-----------------------------------------+
|
||||
| - MENU - | <- 标题 (Y=30)
|
||||
| |
|
||||
| 1. DATE/HEURE | <- 条目 1 (Y=80)
|
||||
| > 2. APPAIRAGE < | <- 选中项高亮并加尖括号 (Y=115)
|
||||
| 3. ARM/DISARM | <- 条目 3 (Y=150)
|
||||
| 4. LIAISON IPC | <- 条目 4 (Y=185)
|
||||
+-----------------------------------------+
|
||||
```
|
||||
* 当前所选中的菜单项前后加上闪烁的 `> <` 箭头或以高亮粗体形式渲染,其余非选中项正常亮度显示。
|
||||
* 短按 ▲ 键向上滚动,▼ 键向下滚动。短按 CONFIRM 键进入该项。
|
||||
|
||||
### 2.3 报警触发与本地 SOS 界面 (STATE_ALARMING / STATE_SOS_EMITTED)
|
||||
### 2.3 功能设定与子界面设计
|
||||
|
||||
* **报警提示边框**:全屏闪烁的红色警告虚线/实线边框。
|
||||
* **中央图标**:根据触发的防区传感器类型,居中绘制对应的单色位图图标(32x32px):
|
||||
* 门磁:`🚪` 开门图标。
|
||||
* 红外:`👤` 人体辐射波纹图标。
|
||||
* 烟感:`🔥` 火苗图标。
|
||||
* 紧急按钮:`🆘` 求救图标。
|
||||
* 燃气:`💨` 毒气云雾图标。
|
||||
* 水浸:`💧` 滴水波纹图标。
|
||||
* 振动:`📳` 震动线框图标。
|
||||
* **主动 SOS 界面**:大字显示 `SOS EMIS`,居中显示 `🚨` 旋转警灯图标。
|
||||
* **绑定 IPC 界面 (STATE_BINDING)**:
|
||||
* 顶部标题:`LIAISON IPC` (法语:与 IPC 绑定)。
|
||||
* 中央:正在发送提示,显示 `ENVOI EN COURS`,配对确认锁图标。
|
||||
* **手环 Factory ID 显示**:底部居中显示手环自身出厂地址:`ID: 0xXXXXX`,以便用户配对及辨识。
|
||||
#### 2.3.1 时间日期修改界面 (STATE_SET_TIME)
|
||||
* **页面指示**:顶部大字 `REGLAGE HEURE`。
|
||||
* **数据列表**:居中分行显示当前编辑的 `HOUR`、`MIN`、`SEC`、`YEAR`、`MONTH`、`DAY`。
|
||||
* **光标与交互**:当前正在修改的数值行(如修改“小时”)闪烁或带有下划线高亮。按 ▲ 键增加数值,▼ 键减小数值,短按 CONFIRM 确认并跳转至下一项,全部设置完后自动保存返回时钟页。
|
||||
|
||||
### 2.4 对码确认与序号微调界面 (STATE_PAIR_CONFIRM)
|
||||
#### 2.3.2 传感器对码等待与配对界面 (STATE_PAIR_SEARCH)
|
||||
* **顶部标题**:显示 `APPAIRAGE`(配对)。
|
||||
* **中央图案**:绘制雷达搜索波纹动画 `SEARCHING...`,并闪烁提示 `DECLENCH. SENS`(触发传感器)。
|
||||
|
||||
#### 2.3.3 对码确认与二步微调界面 (STATE_PAIR_CONFIRM)
|
||||
当手环解码到传感器对码信号后,切入此确认页:
|
||||
```
|
||||
+-----------------------------------------+
|
||||
✓ RECU (对码成功) (Y=40)
|
||||
|
||||
[ 🚪 ICON ] (Y=80)
|
||||
|
||||
PORTE (Y=135)
|
||||
|
||||
[ 01 ] (Y=165) <- 序号微调框 (可按 ▲/▼ 修改)
|
||||
| ✓ RECU (Y=40)
|
||||
|
|
||||
| PORTE (Y=90) <- 步骤1: 传感器类型 (可按 ▲/▼ 修改)
|
||||
|
|
||||
| [ 🚪 ICON ] (Y=130) <- 对应位图图标 (32x32px)
|
||||
|
|
||||
| [ 01 ] (Y=180) <- 步骤2: 序号选框 (可按 ▲/▼ 修改)
|
||||
+-----------------------------------------+
|
||||
```
|
||||
* **元素定位**:
|
||||
* 顶部显示提示语 `✓ RECU`(法语:已接收),绿字。
|
||||
* 中央显示捕获传感器 32x32 对应位图图标。
|
||||
* 下方第一行展示传感器名称前缀(如 `PORTE`)。
|
||||
* 下方第二行展示序号选框 `[ 01 ]`:其中包含两个外括号,中间的数字即为 `selected_suffix_num`(01~99)。当用户按 ▲ 或 ▼ 键时,中间数字动态增减并实时局部刷新,外括号保持静止,增强可编辑交互感。
|
||||
* **确认行为**:短按 CONFIRM 键或 SOS 键保存退出。
|
||||
* **二步选择焦点逻辑**:
|
||||
* **步骤1 (编辑类型)**:焦点在类型文字处(例如类型名称前后有闪烁括号 `[ PORTE ]`),此时按 ▲/▼ 键可在门磁、红外、烟感等 7 种传感器类型间循环切换,图标实时更换。点击 CONFIRM 键保存当前类型并锁定,焦点切换到步骤2。
|
||||
* **步骤2 (编辑序号)**:焦点切换在底部的序号选框 `[ 01 ]`。此时按 ▲/▼ 键自增或自减数字序号(01~99)。点击 CONFIRM 键完成最终保存并存入数据库,返回时钟待机。
|
||||
* *长按 CONFIRM 键 3 秒放弃修改并直接返回时钟待机*。
|
||||
|
||||
#### 2.3.4 系统布撤防手动选择界面 (STATE_ARM_DISARM_SELECT)
|
||||
* **顶部标题**:显示 `REG. ALARME`。
|
||||
* **两个状态选项**:
|
||||
* `[ ARME ]`
|
||||
* `[ DESARME ]`
|
||||
* 按 ▲ / ▼ 键在这两者之间切换高亮。按 CONFIRM 键确认生效,系统状态转入 ARMED 或 DISARMED 并返回时钟页。
|
||||
|
||||
#### 2.3.5 IPC 绑定发射界面 (STATE_IPC_BIND)
|
||||
* **页面指示**:显示 `LIAISON IPC` (IPC 绑定)。
|
||||
* **发射中提示**:中央大字显示 `ENVOI EN COURS`(正在发送),绘制动态发射波形,系统使用 `bracelet_factory_id` 自动连续发射 EV1527 数据码为 `0x01` 的对码广播包。
|
||||
* **物理地址展示**:屏幕底部显示当前手环出厂唯一地址 `ID: 0xXXXXX`。
|
||||
* **退出/返回机制**:
|
||||
1. 自动退出:发射持续约 3 秒后,自动结束并退回时钟待机状态;
|
||||
2. 按键退出:**长按 CONFIRM 键 3 秒** 或 **长按 ▼ 键 2 秒** 立刻主动退出发射状态并返回时钟待机。
|
||||
|
||||
### 2.4 报警触发与本地 SOS 界面 (STATE_ALARMING / STATE_SOS_EMITTED)
|
||||
|
||||
* **报警提示边框**:全屏闪烁的红色警告虚线/实线边框。
|
||||
* **中央图标**:根据触发的防区传感器类型,居中绘制对应的单色位图图标(32x32px)。
|
||||
* **主动 SOS 界面**:大字显示 `SOS EMIS`,居中显示 `🚨` 旋转警灯图标。
|
||||
|
||||
---
|
||||
|
||||
@@ -87,6 +109,6 @@
|
||||
|
||||
为保证排版美观,[Drivers/lcd_font.h](file:///c:/workfile/105/stc32g12k128/Drivers/lcd_font.h) 内的标准字模点阵需要满足如下基线对齐:
|
||||
* **水平居中公式**:
|
||||
* 对于标准 8px 宽字符:X 轴绘制起点为 `(135 - len * 8) / 2`。
|
||||
* 对于双倍放大 16px 宽时钟字符:X 轴绘制起点为 `(135 - len * 16) / 2`。
|
||||
* 对于标准 8px 宽字符:X 轴绘制起点为 `(120 - len * 8) / 2`。
|
||||
* 对于双倍放大 16px 宽时钟字符:X 轴绘制起点为 `(120 - len * 16) / 2`。
|
||||
* **百分号(%)符号基线修正**:百分号字符在 `lcd_font.h` 数组的索引 `0x25` 中,点阵数据需**向上平移 2 像素**,以和 `0~9` 数字的基线保持严格水平对齐,避免百分号整体下沉。
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Wristband LCD UI Gallery</title>
|
||||
<title>Wristband AMOLED UI Gallery</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
@@ -70,7 +70,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 +105,7 @@
|
||||
}
|
||||
.screen-bezel {
|
||||
position: relative;
|
||||
width: 145px;
|
||||
width: 130px;
|
||||
height: 250px;
|
||||
background-color: #000000;
|
||||
border-radius: 25px;
|
||||
@@ -118,7 +118,7 @@
|
||||
|
||||
/* Inlined SVG Scaling */
|
||||
.screen-bezel svg {
|
||||
width: 135px;
|
||||
width: 120px;
|
||||
height: 240px;
|
||||
display: block;
|
||||
}
|
||||
@@ -149,7 +149,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>手环 LCD 屏幕 UI 效果图画册 (V2.0)</h1>
|
||||
<h1>手环 AMOLED 屏幕 UI 效果图画册 (V2.1)</h1>
|
||||
<div class="subtitle">所有 10 类显示界面在手环硬件上的 1:1 视觉仿真效果及按键映射说明</div>
|
||||
|
||||
<div class="grid">
|
||||
@@ -159,20 +159,20 @@
|
||||
<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>
|
||||
<rect x="82" y="27" width="20" height="10" rx="2" fill="none" stroke="#64748B" stroke-width="1.2"/>
|
||||
<rect x="84" y="29" width="13" height="6" rx="1" fill="#10B981"/>
|
||||
<rect x="103" y="30" width="1.5" height="4" rx="0.5" fill="#64748B"/>
|
||||
<text x="76" 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="#00E5FF" font-family="system-ui, sans-serif" font-size="24" font-weight="bold" text-anchor="middle">10:35</text>
|
||||
<text x="60" y="115" fill="#00E5FF" 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>
|
||||
<text x="60" y="165" fill="#64748B" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">THU 07-02</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
@@ -188,23 +188,23 @@
|
||||
<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>
|
||||
<rect x="82" y="27" width="20" height="10" rx="2" fill="none" stroke="#64748B" stroke-width="1.2"/>
|
||||
<rect x="84" y="29" width="13" height="6" rx="1" fill="#10B981"/>
|
||||
<rect x="103" y="30" width="1.5" height="4" rx="0.5" fill="#64748B"/>
|
||||
<text x="76" 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>
|
||||
<text x="60" 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>
|
||||
<text x="60" 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)">
|
||||
<g transform="translate(39, 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"/>
|
||||
@@ -225,23 +225,23 @@
|
||||
<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>
|
||||
<rect x="82" y="27" width="20" height="10" rx="2" fill="none" stroke="#64748B" stroke-width="1.2"/>
|
||||
<rect x="84" y="29" width="13" height="6" rx="1" fill="#10B981"/>
|
||||
<rect x="103" y="30" width="1.5" height="4" rx="0.5" fill="#64748B"/>
|
||||
<text x="76" 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>
|
||||
<text x="60" 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>
|
||||
<text x="60" 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)">
|
||||
<g transform="translate(27, 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>
|
||||
@@ -257,17 +257,21 @@
|
||||
|
||||
<!-- 4. Pairing Menu -->
|
||||
<div class="card">
|
||||
<div class="card-title">4. 滚动选择菜单</div>
|
||||
<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 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="#FFB74D" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">- MENU -</text>
|
||||
<text x="60" y="80" fill="#64748B" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">1. DATE/HEURE</text>
|
||||
|
||||
<!-- Highlight selection APPAIRAGE -->
|
||||
<rect x="10" y="102" width="100" height="24" rx="4" fill="none" stroke="#00E5FF" stroke-width="1.2"/>
|
||||
<text x="60" y="118" fill="#00E5FF" font-family="system-ui, sans-serif" font-size="10" font-weight="bold" text-anchor="middle">> 2. APPAIRAGE <</text>
|
||||
|
||||
<text x="60" y="155" fill="#64748B" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">3. ARM/DISARM</text>
|
||||
<text x="60" y="190" fill="#64748B" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">4. LIAISON IPC</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
@@ -283,14 +287,14 @@
|
||||
<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"/>
|
||||
<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"/>
|
||||
<path d="M 60 70 L 67.5 90 M 52 75 A 10 10 0 0 1 68 75 M 48 70 A 15 15 0 0 1 72 70 M 44 65 A 20 20 0 0 1 76 65" stroke="#00E5FF" stroke-width="2" fill="none" stroke-linecap="round"/>
|
||||
<circle cx="60" 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>
|
||||
<text x="60" y="145" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">SEARCHING...</text>
|
||||
<text x="60" y="170" fill="#00E5FF" font-family="system-ui, sans-serif" font-size="9" text-anchor="middle">TRIGGER SENSOR</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
@@ -306,24 +310,24 @@
|
||||
<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>
|
||||
<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="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)">
|
||||
<g transform="translate(47, 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"/>
|
||||
</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>
|
||||
<text x="60" 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>
|
||||
<rect x="30" y="160" width="60" height="22" rx="4" fill="none" stroke="#00E5FF" stroke-width="1.5"/>
|
||||
<text x="60" 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="215" fill="#64748B" font-family="system-ui, sans-serif" font-size="9" text-anchor="middle">ADDR: 0x37A86</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
@@ -339,15 +343,15 @@
|
||||
<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 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="145" fill="#10B981" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">PAIR SUCCESS</text>
|
||||
<text x="60" y="175" fill="#64748B" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">ADDR: 0x37A86</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
@@ -363,15 +367,15 @@
|
||||
<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="155" fill="#EF4444" font-family="system-ui, sans-serif" font-size="12" font-weight="bold" text-anchor="middle">PAIR FAILED</text>
|
||||
<text x="60" y="180" fill="#64748B" font-family="system-ui, sans-serif" font-size="9" text-anchor="middle">TYPE MISMATCH</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
@@ -387,15 +391,15 @@
|
||||
<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"/>
|
||||
<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="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"/>
|
||||
<rect x="52" y="55" width="16" height="30" rx="1" fill="none" stroke="#EF4444" stroke-width="2.5"/>
|
||||
<circle cx="64" cy="70" r="1.5" fill="#EF4444"/>
|
||||
</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>
|
||||
<text x="60" y="130" fill="#EF4444" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">FRONT DOOR</text>
|
||||
<text x="60" y="165" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">ZONE 0 | ALARM</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
@@ -411,26 +415,26 @@
|
||||
<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"/>
|
||||
<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="2.5" fill="none"/>
|
||||
<rect x="8" y="8" width="104" 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"/>
|
||||
<path d="M 60 48 L 60 54 M 45 52 L 50 57 M 75 52 L 70 57 M 40 65 L 46 67 M 80 65 L 74 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"/>
|
||||
<path d="M 46 82 C 46 64, 74 64, 74 82 Z" fill="#EF4444" stroke="#EF4444" stroke-width="1.5"/>
|
||||
<!-- Bulb inside -->
|
||||
<circle cx="67.5" cy="77" r="4.5" fill="#FFFFFF"/>
|
||||
<circle cx="60" cy="77" r="4.5" fill="#FFFFFF"/>
|
||||
<!-- Metallic Base -->
|
||||
<rect x="49.5" y="82" width="36" height="6" rx="1.5" fill="#64748B"/>
|
||||
<rect x="42" 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>
|
||||
<text x="60" y="138" fill="#EF4444" font-family="system-ui, sans-serif" font-size="11" font-weight="bold" text-anchor="middle">🆘 EMIT SOS</text>
|
||||
<text x="60" y="165" fill="#FFFFFF" font-family="system-ui, sans-serif" font-size="10" text-anchor="middle">BAND TRIGGERED</text>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 模块拆分 - 按键扫描与事件判定 (Docs/50_module-breakdown/mod-key.md)
|
||||
|
||||
本模块描述按键状态消抖、长按计时判定以及组合按键识别。支持 5 个物理按键到手环事件的映射。
|
||||
本模块描述按键状态消抖、长按和短按计时判定。支持 5 个物理按键到手环事件的映射。
|
||||
|
||||
## 1. 模块职责说明
|
||||
|
||||
@@ -11,7 +11,10 @@
|
||||
* 上部:`KEY_SOS1` (SOS 1), `KEY_SOS2` (SOS 2)
|
||||
3. 实现消抖检测(连续 20ms 检测到电平稳定才认定按键动作)。
|
||||
4. 对上部双 SOS 按键进行逻辑并联处理,保证按下任意一个均能触发求救。
|
||||
5. 对按住不放的行为进行计时累加,判定长按(如长按 SOS 达 5 秒触发对码;长按 ▲+▼ 达 3 秒触发配对模式)。
|
||||
5. 对按住不放的行为进行计时累加,判定长按:
|
||||
* 长按 **确认键 (CONFIRM / ■)** 达 3 秒触发主菜单选择界面或退出当前页面。
|
||||
* 侧边 **▼ 键** 长按达 2 秒产生长按退出/消除警报事件。
|
||||
* SOS 按键(SOS1 / SOS2)长按或短按,只要是非报警状态,一律触发 `KEY_SOS_ACTIVE` 本地报警呼救事件。
|
||||
6. 将按键判定结果转换为 `KeyEvent` 并注入按键事件缓冲区 `key_event_buf`。
|
||||
|
||||
## 2. 数据结构定义
|
||||
@@ -21,13 +24,11 @@
|
||||
typedef enum {
|
||||
KEY_EVENT_NONE,
|
||||
KEY_UP_SHORT,
|
||||
KEY_UP_LONG,
|
||||
KEY_DOWN_SHORT,
|
||||
KEY_DOWN_LONG,
|
||||
KEY_DOWN_LONG, // 长按 ▼ 2 秒解除警报/退出绑定
|
||||
KEY_CONFIRM_CLICK, // ■ 确认键短按
|
||||
KEY_SOS_SHORT, // SOS键短按 (SOS1 或 SOS2)
|
||||
KEY_SOS_LONG, // SOS键长按(5秒) (SOS1 或 SOS2)
|
||||
KEY_COMB_LONG // ▲ + ▼ 组合长按事件
|
||||
KEY_CONFIRM_LONG, // ■ 确认键长按(3秒进入/退出菜单/退出绑定)
|
||||
KEY_SOS_ACTIVE // SOS键活动 (短按或长按,跳转至发送 SOS 报警)
|
||||
} KeyEvent;
|
||||
```
|
||||
|
||||
@@ -39,9 +40,8 @@ typedef enum {
|
||||
* 计算并联 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`。
|
||||
* 检测到 `key_confirm_hold >= 300` 达 3 秒时,注入 `KEY_CONFIRM_LONG` 中断事件。
|
||||
* 检测到 `key_down_hold >= 200` 达 2 秒时,注入 `KEY_DOWN_LONG` 中断事件。
|
||||
* 检测到并联 SOS 被按下时,注入 `KEY_SOS_ACTIVE` 事件。
|
||||
|
||||
<!-- 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 -->
|
||||
|
||||
@@ -1,36 +1,38 @@
|
||||
# 模块拆分 - LCD 显示与字模驱动 (Docs/50_module-breakdown/mod-lcd.md)
|
||||
# 模块拆分 - AMOLED 显示与字模驱动 (Docs/50_module-breakdown/mod-lcd.md)
|
||||
|
||||
本模块管理 1.14寸 TFT LCD 屏幕引脚、电平控制、SPI 传输和界面图文渲染(分辨率为 135x240 像素)。
|
||||
本模块管理 0.95寸 Truly AMOLED 屏幕引脚、电平控制、SPI 传输和界面图文渲染(分辨率为 120x240 像素)。
|
||||
|
||||
## 1. 模块职责说明
|
||||
|
||||
* **职责范围**:
|
||||
1. 提供底层的 SPI 单向命令/数据通道。
|
||||
2. 控制 LCD_PWR_CTRL 供电管脚。
|
||||
2. 控制 `SGM_CTRL` 供电管脚以使能 SGM3833 PMIC。
|
||||
3. 支持屏幕窗口配置,实现底层清屏与局部像素写入。
|
||||
4. 读取 `lcd_font.h` 库,在指定坐标绘制 8x16 字符与自定义大小位图。
|
||||
5. 实现各业务状态屏幕(135x240 分辨率)画面绘制与文字对齐。
|
||||
5. 实现各业务状态屏幕(120x240 分辨率)及主选择菜单画面绘制与文字对齐。
|
||||
|
||||
## 2. 底层显示接口
|
||||
|
||||
* `void LCD_Init(void)`:初始化 LCD 引脚,置 `LCD_PWR_CTRL = 1` 使能背光与供电,并发送 **ST7789V** 屏幕控制器的初始化寄存器配置序列。
|
||||
* `void LCD_Init(void)`:初始化 LCD 引脚,置 `SGM_CTRL = 1` 使能 SGM3833 供电,并发送 **RM69310** AMOLED 控制器的初始化寄存器配置序列。
|
||||
* `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 轴偏移量 +52,Y 轴偏移量 +40**)。
|
||||
* `void LCD_SetWindow(u16 x1, u16 y1, u16 x2, u16 y2)`:设置局部像素绘制范围,以适配 Truly 120x240 竖屏的对齐(强制处理 **X 轴偏移量 +4**)。
|
||||
* `void LCD_Clear(u16 color)`:将整块屏幕刷写为特定背景色。
|
||||
|
||||
## 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_ShowStringCentered(u16 y, char *str, u16 fc, u16 bc)`:在 120 像素宽屏幕上水平居中绘制 8 像素宽的字符串,水平起始坐标算法为 `(120 - len * 8) / 2`。
|
||||
* `void LCD_ShowString16x32Centered(u16 y, char *str, u16 fc, u16 bc)`:在 120 像素宽屏幕上水平居中绘制双倍大小 (16x32px) 的时钟字符,水平起始坐标算法为 `(120 - 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_ShowClockPage(SystemState state, u8 hour, u8 min)`:渲染时钟待机页面(120 宽布局),并根据布撤防状态在状态栏绘制相应锁状态图标。
|
||||
* `void UI_ShowMenuPage(u8 selected_item)`:渲染主选择菜单(4个条目:`DATE/HEURE`、`APPAIRAGE`、`ARM/DISARM`、`LIAISON IPC`),高亮当前选中项。
|
||||
* `void UI_ShowSetTimePage(u8 edit_field, u8 hour, u8 min, u16 year, u8 month, u8 day)`:渲染日期时间微调设定界面。
|
||||
* `void UI_ShowArmDisarmSelectPage(u8 current_selection)`:渲染布撤防手动切换界面。
|
||||
* `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_ShowPairSearchPage(void)`:渲染对码等待搜索状态。
|
||||
* `void UI_ShowPairConfirmPage(u8 edit_focus, u8 type, u8 selected_suffix)`:渲染对码二步微调页。当 `edit_focus == 0` 时,类型字样显示为高亮选框形式;当 `edit_focus == 1` 时,后缀数字 `[ selected_suffix ]` 显示为高亮选框形式。
|
||||
* `void UI_ShowBindingPage(void)`:渲染与 IPC 绑定对码界面,并在屏幕底部居中显示手环自身的唯一 `Factory ID`。
|
||||
|
||||
<!-- Checked and verified with always-on screen and sleep removal changes -->
|
||||
|
||||
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
# 模块拆分 - 电源与供电管理 (Docs/50_module-breakdown/mod-power.md)
|
||||
|
||||
本模块描述手环的电源供电状态。根据系统设计,手环不启用任何关屏低功耗或停机(Power-Down)模式,以保证屏幕时钟常显与全时无线监听。
|
||||
本模块负责手环的供电管理以及低功耗休眠/唤醒逻辑。
|
||||
|
||||
## 1. 模块职责说明
|
||||
|
||||
* **职责范围**:
|
||||
1. 系统初始化时使能 LCD 屏幕与背光电源。
|
||||
2. 维持 `LCD_PWR_CTRL` 引脚输出高电平(1),确保屏幕不进入断电休眠。
|
||||
3. 不进行时钟挂起操作,使 CPU 保持在 active 状态以进行实时的 433MHz 射频信号捕获。
|
||||
1. 系统初始化时使能 SGM3833 PMIC 供电(拉高 `SGM_CTRL = 1`),使 OLED 屏正常工作。
|
||||
2. 维护低功耗状态机。当系统检测到连续 10 秒无任何按键操作时,执行关屏、拉低 `SGM_CTRL = 0` 彻底切断 **SGM3833** 供电、挂起外设,使单片机进入 Power-Down(停机模式)。
|
||||
3. 配置外部中断:将 5 个按键的 GPIO 口和 LR690L 433MHz 芯片的接收中断引脚配置为唤醒中断源,使手环在处于 Power-Down 挂起状态下能被瞬间唤醒。
|
||||
4. 唤醒后,第一时间拉高 `SGM_CTRL = 1` 恢复 OLED 电源,延时等待 PMIC 稳定后,重新驱动显示并恢复常规时钟计时。
|
||||
|
||||
## 2. 接口与函数设计
|
||||
|
||||
* `void Power_Init(void)`:
|
||||
1. 配置 `LCD_PWR_CTRL` 引脚为推挽输出模式。
|
||||
2. 设置 `LCD_PWR_CTRL = 1`(物理引脚 `P3.4`)开启 LCD 屏幕与背光供电。
|
||||
* `void Enter_Deep_Sleep(void)`:已作废。手环永不进入此函数,保持主循环及射频实时解调。
|
||||
1. 配置 `SGM_CTRL`(物理引脚 `P3.4`)为推挽输出模式。
|
||||
2. 设置 `SGM_CTRL = 1` 开启 SGM3833 OLED 供电芯片。
|
||||
* `void Enter_Low_Power_Sleep(void)`:
|
||||
1. 发送命令关闭 RM69310 AMOLED 屏幕显示。
|
||||
2. 拉低 `SGM_CTRL = 0` 关闭 SGM3833 升压/负压芯片。
|
||||
3. 允许按键引脚中断与 433MHz 射频接收中断。
|
||||
4. 写入 `PCON |= 0x02`(或 MCU 挂起控制位 `PD = 1`)使 CPU 进入深度睡眠 Power-Down 模式。
|
||||
* `void Wakeup_Restore(void)`:
|
||||
1. 拉高 `SGM_CTRL = 1` 开启 SGM3833 供电芯片。
|
||||
2. 延时约 50ms 等待电压稳定。
|
||||
3. 重新初始化 RM69310 屏幕控制器并恢复正常显示。
|
||||
|
||||
<!-- Checked and verified with always-on screen and sleep removal changes -->
|
||||
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V2 -->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 模块拆分 - 自适应 RF 解码与发射 (Docs/50_module-breakdown/mod-rf.md)
|
||||
|
||||
本模块管理射频芯片 GPIO 初始化、433MHz EV1527 解调接收(宽限时序比值算法)与射频打包主动发射。已确认此模块与新的 1.14寸屏幕/5按键物理变更兼容。
|
||||
本模块管理射频芯片 GPIO 初始化、433MHz EV1527 解调接收(宽限时序比值算法)与射频打包主动发射。
|
||||
|
||||
## 1. 模块职责说明
|
||||
|
||||
@@ -21,10 +21,10 @@
|
||||
* 首先通过计数检测大于 `1500us` 的同步低电平前导信号。
|
||||
* 自适应计算时钟基准 `T`。
|
||||
* 连续接收 24 位宽数据:如果某个数据位的高电平比低电平长,则认为该位为 `1`;反之为 `0`。
|
||||
* 解码成功将 20 位地址存入 `out_addr`,后 4 位数据存入 `out_type` 并返回 `1`;否则返回 `0`。
|
||||
* 正常解码成功将 20 位地址存入 `out_addr`,后 4 位数据存入 `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`。
|
||||
|
||||
<!-- 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 -->
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
# 模块拆分 - 系统核心控制与存储 (Docs/50_module-breakdown/mod-sys.md)
|
||||
|
||||
本模块定义手环系统的生命周期、时间计数器、定时中断调度器以及断电保存数据库模块。只涉及手环本地代码实现。
|
||||
本模块定义手环系统的生命周期、状态机、时间计数器、定时中断调度器以及断电保存数据库模块。
|
||||
|
||||
## 1. 模块职责说明
|
||||
|
||||
* **职责范围**:
|
||||
1. 驱动系统主状态机运转(`current_state`)。
|
||||
2. 提供 RTC 软时钟维护(`current_hour`, `current_min`, `current_sec`)。
|
||||
3. 利用 Timer 1 产生 1ms 定时器中断,维护毫秒滴答计数器并作为其他外设时效的基础。
|
||||
4. 实现 IAP 读写底层函数,对防区数据库进行存取与读取。
|
||||
5. 读取并管理手环出厂固定唯一的 `bracelet_factory_id`,作为主动射频发送的数据源地址。
|
||||
3. 利用 Timer 1 产生 1ms 定时器中断,作为按键消抖、LED闪烁、马达脉冲及超时休眠计数器的调度时基。
|
||||
4. 维护无按键操作计时器 `inactivity_timer`,计满 10 秒后触发系统进入 `STATE_SLEEP`。
|
||||
5. 实现 IAP 读写底层函数,对防区数据库进行存取与读取。
|
||||
6. 读取并管理手环出厂固定唯一的 `bracelet_factory_id`,作为主动射频发送的数据源地址。
|
||||
|
||||
## 2. 数据结构定义
|
||||
|
||||
@@ -24,19 +25,19 @@ typedef struct {
|
||||
} 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, // 低功耗休眠
|
||||
STATE_MENU, // 主选择菜单
|
||||
STATE_SET_TIME, // 时间日期设置
|
||||
STATE_PAIR_SEARCH, // 对码等待搜寻
|
||||
STATE_PAIR_CONFIRM, // 序号微调与保存确认
|
||||
STATE_ARM_DISARM_SELECT, // 手动选择布撤防设定
|
||||
STATE_IPC_BIND, // IPC 对码发射绑定
|
||||
STATE_ALARMING, // 被动防区报警响应
|
||||
STATE_SOS_EMITTED // 本地主动发射 SOS 呼救状态
|
||||
} SystemState;
|
||||
```
|
||||
|
||||
@@ -44,7 +45,7 @@ typedef enum {
|
||||
|
||||
### 3.1 定时与时钟函数
|
||||
* `void Timer1_Init(void)`:配置 Timer 1 作为 1ms 中断源,并使能全局中断。
|
||||
* `void Timer1_Isr(void) interrupt 3`:ISR 中断处理,自增时间滴答、维护 LED 闪烁周期、马达振动波形定时,以及维护软件 RTC(自增 `current_sec` 并在满 60 秒时触发主界面分钟刷新)。
|
||||
* `void Timer1_Isr(void) interrupt 3`:ISR 中断处理。自增时间滴答,并在主循环处于非休眠时自增 `inactivity_timer` 超时计时器。自增 RTC(时/分/秒/日/月/年)并维护其他任务定时。
|
||||
|
||||
### 3.2 IAP 数据库存储接口
|
||||
* `void Load_Database(void)`:从 Flash 扇区 `0xFE0000` 读取 16 组传感器配置载入内存的 `sensor_list`。
|
||||
@@ -55,13 +56,12 @@ typedef enum {
|
||||
### 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 状态。
|
||||
2. 读取 STC32G 硬件全球唯一 ID (UID) 生成 `bracelet_factory_id`。
|
||||
3. 载入数据库并配置 Timer1 和外设 GPIO 状态。
|
||||
|
||||
### 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`。
|
||||
* `u16 Get_ADC_Result(u8 channel)`:底层 ADC 数据采集函数。
|
||||
* `u8 Read_Battery_Percent(void)`:读取通道 8 (`P0.0`) 并返回 0%~100% 的电量。
|
||||
* `bit Is_Charging(void)`:读取 `P2.7` 返回是否处于充电状态。
|
||||
|
||||
<!-- 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 -->
|
||||
|
||||
@@ -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,134 @@ 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;
|
||||
}
|
||||
} else {
|
||||
comb_hold = 0;
|
||||
// 2. 按键长按与短按状态机扫描
|
||||
|
||||
// 单个按键:▲ 键 (UP)
|
||||
// ▲ 键 (UP) 处理
|
||||
if ((key_up_state & 0x07) == 0) {
|
||||
key_up_hold++;
|
||||
} else {
|
||||
if (key_up_hold > 2 && key_up_hold < 200) {
|
||||
if (key_up_hold > 2) {
|
||||
key_event_buf = KEY_UP_SHORT;
|
||||
}
|
||||
key_up_hold = 0;
|
||||
}
|
||||
|
||||
// 单个按键:■ 确认键 (CONFIRM)
|
||||
// ▼ 键 (DOWN) 处理 (支持长按 2 秒清除报警/退出)
|
||||
if ((key_down_state & 0x07) == 0) {
|
||||
key_down_hold++;
|
||||
if (key_down_hold == 200) { // 2秒
|
||||
key_event_buf = KEY_DOWN_LONG;
|
||||
}
|
||||
} else {
|
||||
if (key_down_hold > 2 && key_down_hold < 200) {
|
||||
key_event_buf = KEY_DOWN_SHORT;
|
||||
}
|
||||
key_down_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 < 200) {
|
||||
if (key_confirm_hold > 2 && key_confirm_hold < 300) {
|
||||
key_event_buf = KEY_CONFIRM_CLICK;
|
||||
}
|
||||
key_confirm_hold = 0;
|
||||
}
|
||||
|
||||
// 单个按键:并联 SOS 键
|
||||
// 并联 SOS 键
|
||||
if ((key_sos_state & 0x07) == 0) {
|
||||
key_sos_hold++;
|
||||
if (key_sos_hold == 500) { // 5秒
|
||||
key_event_buf = KEY_SOS_LONG;
|
||||
if (key_sos_hold == 3) {
|
||||
key_event_buf = KEY_SOS_ACTIVE;
|
||||
}
|
||||
} else {
|
||||
if (key_sos_hold > 2 && key_sos_hold < 500) {
|
||||
key_event_buf = KEY_SOS_SHORT;
|
||||
}
|
||||
key_sos_hold = 0;
|
||||
}
|
||||
// ▼ 键 (DOWN) 处理同上...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- 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) {
|
||||
// 长按 3 秒退出菜单返回时钟待机
|
||||
current_state = STATE_CLOCK;
|
||||
Redraw = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
// 执行具体事件动作,如在常态下短按 SOS 进入 SOS 警报发射状态等
|
||||
// ...
|
||||
case STATE_PAIR_CONFIRM:
|
||||
if (confirm_edit_focus == 0) {
|
||||
// 第一步:编辑传感器类型
|
||||
if (evt == KEY_UP_SHORT) {
|
||||
if (selected_type < 6) selected_type++;
|
||||
else selected_type = 0;
|
||||
Redraw = 1;
|
||||
} else if (evt == KEY_DOWN_SHORT) {
|
||||
if (selected_type > 0) selected_type--;
|
||||
else selected_type = 6;
|
||||
Redraw = 1;
|
||||
} else if (evt == KEY_CONFIRM_CLICK) {
|
||||
// 点击确认,锁定类型,跳入第二步选择序号
|
||||
confirm_edit_focus = 1;
|
||||
Redraw = 1;
|
||||
}
|
||||
} else if (confirm_edit_focus == 1) {
|
||||
// 第二步:编辑序号后缀
|
||||
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) {
|
||||
// 点击确认,完成最终写入保存,返回时钟
|
||||
Add_Sensor(captured_addr, selected_type, selected_suffix);
|
||||
current_state = STATE_CLOCK;
|
||||
Redraw = 1;
|
||||
}
|
||||
}
|
||||
// 长按 CONFIRM 3 秒退出不保存
|
||||
if (evt == KEY_CONFIRM_LONG) {
|
||||
current_state = STATE_CLOCK;
|
||||
Redraw = 1;
|
||||
}
|
||||
break;
|
||||
|
||||
case STATE_IPC_BIND:
|
||||
// IPC 绑定连发状态中支持物理长按退出
|
||||
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 -->
|
||||
|
||||
Binary file not shown.
@@ -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 -->
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
@@ -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,39 @@
|
||||
* **测试方法**:
|
||||
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.3V),AMOLED 重新点亮显示,无花屏或时钟错乱。
|
||||
|
||||
### 3.3 按键菜单交互与功能选择
|
||||
* **测试方法**:
|
||||
1. 在时钟待机页下,长按侧边 **■ 确认键** 达 3 秒。
|
||||
2. 在主菜单页面使用 ▲ 和 ▼ 键进行滚动,分别选中 `1. DATE/HEURE` 和 `2. APPAIRAGE`,按下 **■ 确认键** 进入。
|
||||
* **预期结果**:
|
||||
1. 长按 3 秒后,屏幕立刻切换显示 `- MENU -` 选项卡,且默认首项高亮。
|
||||
2. 滚动选择灵活响应,高亮行外围有 `> <` 箭头指示。按下确认键后能顺利切换至子修改界面或雷达对码界面。
|
||||
|
||||
### 3.4 对码二步保存与微调向导
|
||||
* **测试方法**:
|
||||
1. 同时长按 ▲+▼ 3秒进入对码,触发门磁信号对码成功。
|
||||
2. 观察屏幕,此时类型字样 `[ PORTE ]` 高亮,按 ▲/▼ 键确认类型名称是否可以修改切换。
|
||||
3. 点击 **■ 确认键**,查看屏幕焦点是否移入到序号 `[ 01 ]` 上。
|
||||
4. 此时按 ▲/▼ 键,观察序号数值是否可以进行增减微调。
|
||||
5. 最后点击 **■ 确认键**,手环是否将数据写入 Flash 数据库,并立刻退回到待机时钟页。
|
||||
* **预期结果**:
|
||||
对码成功进入微调后,系统按步骤顺序先选择传感器分类、后微调序号,点击确认后成功保存数据库退回时钟。整个过程按 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 -->
|
||||
|
||||
Reference in New Issue
Block a user