docs: optimize SOS button behavior, add 2-step pairing wizard, and IPC exit action

This commit is contained in:
2026-07-09 17:42:55 +08:00
parent 1168e747e6
commit 32c07c0466
16 changed files with 821 additions and 478 deletions

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 -->