123 lines
4.3 KiB
Markdown
123 lines
4.3 KiB
Markdown
# 编码实现 - 系统核心控制与存储 (Docs/60_coding/mod-sys.md)
|
||
|
||
本文件对应系统控制与存储的具体编码实现细节。只包含手环代码实现。
|
||
|
||
## 1. 对应代码源文件
|
||
|
||
* [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (系统主循环、定时器中断、IAP 存取)
|
||
* [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 寄存器控制读写。
|
||
```c
|
||
void IAP_EraseSector(u16 addr) {
|
||
IAP_CMD = 3; // 扇区擦除
|
||
IAP_ADDRL = addr & 0xFF;
|
||
IAP_ADDRH = addr >> 8;
|
||
IAP_TRIG = 0x5A; // 触发
|
||
IAP_TRIG = 0xA5;
|
||
_nop_();
|
||
}
|
||
```
|
||
- 主动擦除与写字节前必须先调用使能,写完后调用 `IAP_Disable()` 避免误写入损坏配置。
|
||
- 数据库保存在末尾扇区 `0xFE0000`(大小为 512 字节)。
|
||
|
||
### 2.3 获取出厂唯一 ID (Get_Bracelet_Factory_ID)
|
||
从 STC32G 的内置 IDATA RAM 区域 `0xF1~0xF7` 处读取 7 字节唯一硬件 ID,并将其转换为符合 EV1527 的 20 位地址码:
|
||
```c
|
||
u32 Get_Bracelet_Factory_ID(void) {
|
||
unsigned char idata *p_uid = (unsigned char idata *)0xF1;
|
||
u32 uid = 0;
|
||
|
||
// 组合后 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
|
||
|
||
if (uid == 0) {
|
||
uid = CLONED_ADDR; // 防错备份
|
||
}
|
||
return uid;
|
||
}
|
||
```
|
||
|
||
### 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 电池采样与充电状态读取实现
|
||
在系统模块中,通过硬件 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
|
||
ADC_CONTR |= 0x80; // 打开 ADC 电源 (ADC_POWER = 1)
|
||
}
|
||
```
|
||
|
||
2. **读取采样数据与电量百分比计算**:
|
||
```c
|
||
u16 Get_ADC_Result(u8 channel) {
|
||
ADC_CONTR = 0x80 | channel; // 开启电源并选择通道
|
||
_nop_(); _nop_();
|
||
ADC_CONTR |= 0x40; // 启动 ADC 转换 (ADC_START = 1)
|
||
while (!(ADC_CONTR & 0x20)); // 等待转换完成 (ADC_FLAG)
|
||
ADC_CONTR &= ~0x20; // 清除 ADC 标志
|
||
return ((u16)ADC_RES << 8) | ADC_RESL; // 返回 12 位 ADC 转换结果
|
||
}
|
||
|
||
u8 Read_Battery_Percent(void) {
|
||
u16 adc_val = Get_ADC_Result(8); // 读取通道 8 (P0.0)
|
||
|
||
// 线性百分比转换:3.3V (ADC=1279) 对应 0%, 4.2V (ADC=1628) 对应 100%
|
||
if (adc_val <= 1279) {
|
||
return 0;
|
||
} else if (adc_val >= 1628) {
|
||
return 100;
|
||
} else {
|
||
return (u8)((u32)(adc_val - 1279) * 100 / (1628 - 1279));
|
||
}
|
||
}
|
||
```
|
||
|
||
3. **充电状态读取**:
|
||
```c
|
||
// P2.7 在 System_Init 时需配置为高阻输入以进行充电状态检测
|
||
bit Is_Charging(void) {
|
||
return (P2 & (1 << 7)) ? 1 : 0; // 读取 P2.7 引脚高电平状态,1 表示 USB 接入
|
||
}
|
||
```
|
||
|
||
<!-- Checked and verified with always-on screen and sleep removal changes -->
|
||
```
|