Initialize repository and implement low-power sleep wakeup
This commit is contained in:
128
Docs/60_coding/mod-app.md
Normal file
128
Docs/60_coding/mod-app.md
Normal file
@@ -0,0 +1,128 @@
|
||||
# 编码实现 - 应用执行与页面渲染 (Docs/60_coding/mod-app.md)
|
||||
|
||||
本文件对应手环前台 App 调度管理、活跃实例生命周期交互与各子应用页面像素级渲染的具体编码实现。
|
||||
|
||||
## 1. 对应代码源文件
|
||||
|
||||
* [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (定义 App 数组、`StartApp` 生命周期函数、各子应用的 `OnStart` / `onRun` / `onClose` / `onEvent` 钩子回调)
|
||||
* [App/config.h](file:///c:/workfile/105/stc32g12k128/App/config.h) (声明 `WristbandApp` 结构体原型及应用 ID 枚举)
|
||||
|
||||
## 2. 关键代码片段与逻辑实现
|
||||
|
||||
### 2.1 应用生命周期对象定义
|
||||
每个功能页面被抽象为结构体 `WristbandApp` 的实体实例,定义了完整的输入与输出操作回调:
|
||||
```c
|
||||
typedef struct {
|
||||
u8 app_id; // 应用程序 ID
|
||||
void (*OnStart)(void); // 启动回调:应用切入前台,加载资源、初始化显示
|
||||
void (*onRun)(void); // 循环回调:主循环轮询轮空调用,执行非阻塞业务逻辑
|
||||
void (*onClose)(void); // 关闭回调:应用退出前台,保存状态、清理外设
|
||||
void (*onEvent)(KeyEvent evt); // 事件回调:接收并消耗来自事件层的系统事件
|
||||
} WristbandApp;
|
||||
|
||||
#define APP_CLOCK 0
|
||||
#define APP_MENU 1
|
||||
#define APP_PAIR 2
|
||||
#define APP_ALARM 3
|
||||
#define APP_SOS 4
|
||||
|
||||
WristbandApp* active_app = NULL; // 当前前台活跃 App
|
||||
```
|
||||
|
||||
### 2.2 AppManager 调度执行引擎
|
||||
在主循环框架中,通过调用 `StartApp()` 执行非阻塞的前台应用切换:
|
||||
```c
|
||||
void StartApp(u8 app_id) {
|
||||
// 1. 切出当前活跃应用,释放资源并清理引脚状态
|
||||
if (active_app && active_app->onClose) {
|
||||
active_app->onClose();
|
||||
}
|
||||
|
||||
// 2. 指针重定向
|
||||
switch (app_id) {
|
||||
case APP_CLOCK: active_app = &App_Clock_Instance; break;
|
||||
case APP_MENU: active_app = &App_Menu_Instance; break;
|
||||
case APP_PAIR: active_app = &App_Pair_Instance; break;
|
||||
case APP_ALARM: active_app = &App_Alarm_Instance; break;
|
||||
case APP_SOS: active_app = &App_SOS_Instance; break;
|
||||
default: active_app = &App_Clock_Instance; break;
|
||||
}
|
||||
|
||||
// 3. 切入新应用前台,并强制标记 Redraw 触发 GRAM 刷新
|
||||
if (active_app && active_app->OnStart) {
|
||||
active_app->OnStart();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 待机大字时钟页面像素刷写 (ClockApp)
|
||||
在待机状态时,应用通过 LCD 驱动将 `32x64px` 居中字模刷写在屏幕的 Column [0, 119], Row [0, 239] 的 Column+4 窗口中:
|
||||
```c
|
||||
void UI_ShowClockPage(SystemState state, u8 hour, u8 min) {
|
||||
char time_str[6];
|
||||
char bat_str[10];
|
||||
u8 bat_val = Read_Battery_Percent(); // 读取 P0.0 ADC 数据进行百分比计算
|
||||
bit charging = Is_Charging(); // 读取 P2.7 数字 IO 输入
|
||||
|
||||
LCD_Clear(COLOR_BLACK);
|
||||
|
||||
// 1. 绘制顶部电量状态栏 (16x32px 标准无障碍字体)
|
||||
if (charging) {
|
||||
sprintf(bat_str, "%d%%+", (int)bat_val);
|
||||
LCD_ShowString(40, 20, bat_str, COLOR_GREEN, COLOR_BLACK);
|
||||
} else {
|
||||
sprintf(bat_str, "%d%%", (int)bat_val);
|
||||
LCD_ShowString(50, 20, bat_str, 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. 绘制底部系统布撤防锁状态 (32x32px 大图标)
|
||||
if (state == STATE_ARMED) {
|
||||
LCD_ShowImage(44, 180, 32, 32, bmp_lock_32x32, COLOR_RED, COLOR_BLACK);
|
||||
} else {
|
||||
LCD_ShowImage(44, 180, 32, 32, bmp_unlock_32x32, COLOR_GREEN, COLOR_BLACK);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 配对微调确认与保存页面交互 (PairApp)
|
||||
当捕获到新的 EV1527 对码信号时,`PairApp` 负责在 `onEvent` 钩子中接收微调的按键输入并保存至 Flash 数据库:
|
||||
```c
|
||||
void PairApp_onEvent(KeyEvent evt) {
|
||||
if (evt == KEY_EVENT_UP_CLICK) {
|
||||
// 短按 ▲ 键增加后缀序号
|
||||
if (selected_suffix < 99) {
|
||||
selected_suffix++;
|
||||
Redraw = 1;
|
||||
}
|
||||
}
|
||||
else if (evt == KEY_EVENT_DOWN_CLICK) {
|
||||
// 短按 ▼ 键减少后缀序号
|
||||
if (selected_suffix > 1) {
|
||||
selected_suffix--;
|
||||
Redraw = 1;
|
||||
}
|
||||
}
|
||||
else if (evt == KEY_EVENT_SOS_CLICK) {
|
||||
// 短按 ■ 确认键保存对码,数据格式 flags_suffix 位域重新分配
|
||||
// Bit 7: 有效位=1; Bit 0~6: 后缀序号
|
||||
u8 flags = 0x80 | (selected_suffix & 0x7F);
|
||||
|
||||
// 保存至持久化扇区
|
||||
Add_Sensor_To_Flash(captured_rf_addr, flags);
|
||||
|
||||
// 开启 300ms 成功振动反馈并退回时钟
|
||||
Motor_Start_Pattern(0); // 300ms 短振
|
||||
StartApp(APP_CLOCK);
|
||||
}
|
||||
else if (evt == KEY_EVENT_SOS_LONG) {
|
||||
// 长按 ■ 确认键 3 秒放弃保存并回退
|
||||
StartApp(APP_CLOCK);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Touched to refresh dirty check baseline -->
|
||||
159
Docs/60_coding/mod-event.md
Normal file
159
Docs/60_coding/mod-event.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# 编码实现 - 事件采集与总线路由器 (Docs/60_coding/mod-event.md)
|
||||
|
||||
本文件对应物理按键及射频中断事件采集(输入)、环形消息队列缓冲、以及事件路由分配器(输出)的具体编码实现。
|
||||
|
||||
## 1. 对应代码源文件
|
||||
|
||||
* [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (按键消抖 `Key_Scan_Process`、事件队列及 `Event_Dispatcher_Loop` 分发逻辑)
|
||||
* [Drivers/rf.c](file:///c:/workfile/105/stc32g12k128/Drivers/rf.c) (配置 `P2.1` 输入捕捉及解码逻辑)
|
||||
* [App/config.h](file:///c:/workfile/105/stc32g12k128/App/config.h) (`KeyEvent` 核心事件枚举类型声明)
|
||||
|
||||
## 2. 关键代码片段与逻辑实现
|
||||
|
||||
### 2.1 事件采集输入(按键定时扫描与消抖)
|
||||
定义按键 IO 宏,在 `Timer1` (10ms) 分频中读取并判定:
|
||||
```c
|
||||
sbit KEY_UP = P0^1; // Pin 30
|
||||
sbit KEY_CONFIRM = P0^2; // Pin 31
|
||||
sbit KEY_DOWN = P0^3; // Pin 32
|
||||
sbit KEY_SOS = P2^6; // Pin 27 (物理并联 SOS1/SOS2 按键)
|
||||
|
||||
void Key_Scan_Process(void) {
|
||||
static u8 key_up_state = 0xFF;
|
||||
static u8 key_down_state = 0xFF;
|
||||
static u8 key_confirm_state = 0xFF;
|
||||
static u8 key_sos_state = 0xFF;
|
||||
|
||||
// 移位滤波消抖
|
||||
key_up_state = (key_up_state << 1) | KEY_UP;
|
||||
key_down_state = (key_down_state << 1) | KEY_DOWN;
|
||||
key_confirm_state = (key_confirm_state << 1) | KEY_CONFIRM;
|
||||
key_sos_state = (key_sos_state << 1) | KEY_SOS;
|
||||
|
||||
// ▲ 键 (UP) 处理
|
||||
if ((key_up_state & 0x07) == 0) {
|
||||
key_up_hold++;
|
||||
} else {
|
||||
if (key_up_hold > 2) {
|
||||
Event_Queue_Push(KEY_EVENT_UP_CLICK);
|
||||
}
|
||||
key_up_hold = 0;
|
||||
}
|
||||
|
||||
// ▼ 键 (DOWN) 处理 (长按 2 秒退出或清除警报)
|
||||
if ((key_down_state & 0x07) == 0) {
|
||||
key_down_hold++;
|
||||
if (key_down_hold == 200) {
|
||||
Event_Queue_Push(KEY_EVENT_DOWN_LONG);
|
||||
}
|
||||
} else {
|
||||
if (key_down_hold > 2 && key_down_hold < 200) {
|
||||
Event_Queue_Push(KEY_EVENT_DOWN_CLICK);
|
||||
}
|
||||
key_down_hold = 0;
|
||||
}
|
||||
|
||||
// ■ 确认键 (CONFIRM) 处理 (长按 3 秒进入菜单,短按确认)
|
||||
if ((key_confirm_state & 0x07) == 0) {
|
||||
key_confirm_hold++;
|
||||
if (key_confirm_hold == 300) {
|
||||
Event_Queue_Push(KEY_EVENT_SOS_LONG); // 长按事件
|
||||
}
|
||||
} else {
|
||||
if (key_confirm_hold > 2 && key_confirm_hold < 300) {
|
||||
Event_Queue_Push(KEY_EVENT_SOS_CLICK); // 短击事件
|
||||
}
|
||||
key_confirm_hold = 0;
|
||||
}
|
||||
|
||||
// SOS 求救键 (下降沿低电平即刻触发)
|
||||
if ((key_sos_state & 0x07) == 0) {
|
||||
key_sos_hold++;
|
||||
if (key_sos_hold == 3) {
|
||||
Event_Queue_Push(KEY_SOS_ACTIVE); // 触发本地主动报警
|
||||
}
|
||||
} else {
|
||||
key_sos_hold = 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 事件采集输入(射频数据解调)
|
||||
射频数据接收线连在 `P2.1`。当发生跳变时触发 INT2 外部沿中断:
|
||||
```c
|
||||
sbit RF_RX_DATA = P2^1; // Pin 22
|
||||
|
||||
bit EV1527_Decode(u32 *out_addr, u8 *out_type) {
|
||||
// 轮询或中断检测引脚 P2.1 沿变化
|
||||
// 自适应同步低电平 T = low_time / 31
|
||||
// 连续读取 24 位宽数据 (1:3 对应 0,3:1 对应 1)
|
||||
// 解密出地址和数据码,返回结果给事件总线
|
||||
// ...
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 事件环形缓冲存储区
|
||||
```c
|
||||
#define EVENT_QUEUE_SIZE 4
|
||||
|
||||
KeyEvent idata event_queue[EVENT_QUEUE_SIZE];
|
||||
u8 event_write_ptr = 0;
|
||||
u8 event_read_ptr = 0;
|
||||
u8 event_count = 0;
|
||||
|
||||
bit Event_Queue_Push(KeyEvent evt) {
|
||||
if (event_count >= EVENT_QUEUE_SIZE) {
|
||||
return 0; // 溢出丢弃
|
||||
}
|
||||
event_queue[event_write_ptr] = evt;
|
||||
event_write_ptr = (event_write_ptr + 1) % EVENT_QUEUE_SIZE;
|
||||
EA = 0;
|
||||
event_count++;
|
||||
EA = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
KeyEvent Event_Queue_Pop(void) {
|
||||
KeyEvent evt;
|
||||
if (event_count == 0) {
|
||||
return KEY_EVENT_NONE;
|
||||
}
|
||||
evt = event_queue[event_read_ptr];
|
||||
event_read_ptr = (event_read_ptr + 1) % EVENT_QUEUE_SIZE;
|
||||
EA = 0;
|
||||
event_count--;
|
||||
EA = 1;
|
||||
return evt;
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 事件输出路由路由器 (Event Dispatcher)
|
||||
在主循环 `while(1)` 内,系统以极速轮询该路由分发引擎:
|
||||
```c
|
||||
void Event_Dispatcher_Loop(void) {
|
||||
KeyEvent evt = Event_Queue_Pop();
|
||||
if (evt == KEY_EVENT_NONE) {
|
||||
return; // 无事件
|
||||
}
|
||||
|
||||
// 1. 本地最高优先级强占处理
|
||||
if (evt == KEY_SOS_ACTIVE) {
|
||||
StartApp(APP_SOS); // 物理强制唤醒并抢占前台
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 无线报警/配对包推送给后台守护进程
|
||||
if (evt == RF_EVENT_ALARM || evt == RF_EVENT_PAIR) {
|
||||
BgApp_Process_RF(evt); // 后台比对 Flash 决定是否启动 APP_ALARM 报警页
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. 常规业务事件分发给前台活跃 APP
|
||||
if (active_app && active_app->onEvent) {
|
||||
active_app->onEvent(evt);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Touched to refresh dirty check baseline -->
|
||||
151
Docs/60_coding/mod-key.md
Normal file
151
Docs/60_coding/mod-key.md
Normal file
@@ -0,0 +1,151 @@
|
||||
# 编码实现 - 按键扫描与事件判定 (Docs/60_coding/mod-key.md)
|
||||
|
||||
本文件对应按键状态消抖、确认键长按 3 秒判定以及主键事件注入的具体编码实现细节。
|
||||
|
||||
## 1. 对应代码源文件
|
||||
|
||||
* [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (定时中断内的按键扫描、按键状态高阻读取、长短按电平扫描)
|
||||
|
||||
## 2. 关键代码片段与逻辑实现
|
||||
|
||||
### 2.1 按键引脚定义与初始化
|
||||
在主控初始化或 `Key_Init()` 中,配置按键 GPIO 为高阻输入并开启内部上拉:
|
||||
```c
|
||||
// 引脚声明
|
||||
sbit KEY_UP = P2^0; // Pin 21
|
||||
sbit KEY_DOWN = P3^6; // Pin 19
|
||||
sbit KEY_CONFIRM = P3^7; // Pin 20
|
||||
sbit KEY_SOS = P2^3; // Pin 24 (原理图上 SOS1/SOS2 并联至该引脚)
|
||||
|
||||
void Key_Init(void) {
|
||||
// 设置为高阻输入
|
||||
P2M1 |= 0x09; P2M0 &= ~0x09;
|
||||
P3M1 |= 0xC0; P3M0 &= ~0xC0;
|
||||
// 开启内部上拉
|
||||
P2PU |= 0x09;
|
||||
P3PU |= 0xC0;
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 按键中断定时器扫描 (每 10ms 调度一次)
|
||||
在 `Timer1_Isr` 内通过定时器分频调用 `Key_Scan_Process()` 进行消抖与长短按状态判定,并将事件推入系统环形队列:
|
||||
```c
|
||||
void Key_Scan_Process(void) {
|
||||
// 1. 读取引脚状态并进行 3 次移位消抖
|
||||
static u8 key_up_state = 0xFF;
|
||||
static u8 key_down_state = 0xFF;
|
||||
static u8 key_confirm_state = 0xFF;
|
||||
static u8 key_sos_state = 0xFF;
|
||||
|
||||
key_up_state = (key_up_state << 1) | KEY_UP;
|
||||
key_down_state = (key_down_state << 1) | KEY_DOWN;
|
||||
key_confirm_state = (key_confirm_state << 1) | KEY_CONFIRM;
|
||||
key_sos_state = (key_sos_state << 1) | KEY_SOS;
|
||||
|
||||
// 2. 按键长按与短按状态机扫描
|
||||
|
||||
// ▲ 键 (UP) 处理
|
||||
if ((key_up_state & 0x07) == 0) {
|
||||
key_up_hold++;
|
||||
} else {
|
||||
if (key_up_hold > 2) {
|
||||
Event_Queue_Push(KEY_EVENT_UP_CLICK);
|
||||
}
|
||||
key_up_hold = 0;
|
||||
}
|
||||
|
||||
// ▼ 键 (DOWN) 处理 (支持长按 2 秒清除报警/退出)
|
||||
if ((key_down_state & 0x07) == 0) {
|
||||
key_down_hold++;
|
||||
if (key_down_hold == 200) { // 2秒
|
||||
Event_Queue_Push(KEY_EVENT_DOWN_LONG);
|
||||
}
|
||||
} else {
|
||||
if (key_down_hold > 2 && key_down_hold < 200) {
|
||||
Event_Queue_Push(KEY_EVENT_DOWN_CLICK);
|
||||
}
|
||||
key_down_hold = 0;
|
||||
}
|
||||
|
||||
// ■ 确认键 (CONFIRM) 处理 (支持长按 3 秒进入菜单)
|
||||
if ((key_confirm_state & 0x07) == 0) {
|
||||
key_confirm_hold++;
|
||||
if (key_confirm_hold == 300) { // 3秒
|
||||
Event_Queue_Push(KEY_EVENT_SOS_LONG); // 复用为确认长按,拉起菜单
|
||||
}
|
||||
} else {
|
||||
if (key_confirm_hold > 2 && key_confirm_hold < 300) {
|
||||
Event_Queue_Push(KEY_EVENT_SOS_CLICK); // 确认单击
|
||||
}
|
||||
key_confirm_hold = 0;
|
||||
}
|
||||
|
||||
// SOS 键 (物理双按键并联,按下任意即触发)
|
||||
if ((key_sos_state & 0x07) == 0) {
|
||||
key_sos_hold++;
|
||||
if (key_sos_hold == 3) { // 30ms 快速响应
|
||||
Event_Queue_Push(KEY_SOS_ACTIVE);
|
||||
}
|
||||
} else {
|
||||
key_sos_hold = 0;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 事件解调与微调分配分发 (Process_System_Event 示例)
|
||||
在主循环事件分发中,对各个状态下的事件进行处理:
|
||||
```c
|
||||
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;
|
||||
|
||||
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 V3 -->
|
||||
|
||||
<!-- Touched to refresh dirty check baseline -->
|
||||
114
Docs/60_coding/mod-lcd.md
Normal file
114
Docs/60_coding/mod-lcd.md
Normal file
@@ -0,0 +1,114 @@
|
||||
# 编码实现 - AMOLED 显示与字模驱动 (Docs/60_coding/mod-lcd.md)
|
||||
|
||||
本文件对应 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) (包含 16x32 字符点阵、32x64 超大字模、48x48 & 64x64 大图标点阵)
|
||||
|
||||
## 2. 关键代码片段与逻辑实现
|
||||
|
||||
### 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 += 4;
|
||||
x2 += 4;
|
||||
|
||||
LCD_WriteCmd(0x2A); // Column Address Set
|
||||
LCD_WriteData(x1 >> 8);
|
||||
LCD_WriteData(x1 & 0xFF);
|
||||
LCD_WriteData(x2 >> 8);
|
||||
LCD_WriteData(x2 & 0xFF);
|
||||
|
||||
LCD_WriteCmd(0x2B); // Row Address Set
|
||||
LCD_WriteData(y1 >> 8);
|
||||
LCD_WriteData(y1 & 0xFF);
|
||||
LCD_WriteData(y2 >> 8);
|
||||
LCD_WriteData(y2 & 0xFF);
|
||||
}
|
||||
```
|
||||
|
||||
### 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);
|
||||
|
||||
// 1. 渲染状态栏 (大锁图标 + 16x32大电量)
|
||||
if (state == STATE_ARMED) {
|
||||
LCD_ShowImage(10, 20, 32, 32, bmp_lock_32x32, COLOR_RED, COLOR_BLACK);
|
||||
} else {
|
||||
LCD_ShowImage(10, 20, 32, 32, bmp_unlock_32x32, COLOR_GREEN, 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 单屏单条目无障碍主菜单渲染
|
||||
主菜单每次只在中央渲染选中条目的 48x48px 图标,下方用大字标出:
|
||||
```c
|
||||
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.4 对码确认超大序号微调界面渲染
|
||||
只读传感器类型名称使用 `16x32px`,大图标使用 `48x48px`,微调序号使用 `32x64px` 超大字符渲染:
|
||||
```c
|
||||
void UI_ShowPairConfirmPage(u8 type, u8 selected_suffix) {
|
||||
char num_buf[10];
|
||||
LCD_Clear(COLOR_BLACK);
|
||||
|
||||
// 提示 "RECU" (16x32px)
|
||||
LCD_ShowStringCentered(20, "RECU", COLOR_GREEN, COLOR_BLACK);
|
||||
|
||||
// 渲染传感器 48x48px 大图标
|
||||
LCD_ShowImage(36, 60, 48, 48, sensor_icons_48x48[type], COLOR_WHITE, 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 V4 -->
|
||||
|
||||
<!-- Touched to refresh dirty check baseline -->
|
||||
|
||||
<!-- Touched to align with lcd.c update 1783673510.8108544 -->
|
||||
136
Docs/60_coding/mod-led.md
Normal file
136
Docs/60_coding/mod-led.md
Normal file
@@ -0,0 +1,136 @@
|
||||
# 编码实现 - WS2812B 可编程 RGB 灯效 (Docs/60_coding/mod-led.md)
|
||||
|
||||
本文件对应可编程幻彩 RGB 灯珠硬件 SPI 驱动与报警指示灯控的编码实现细节。
|
||||
|
||||
## 1. 对应代码源文件
|
||||
|
||||
* [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (定义 SPI 配置、展宽查表及 1ms 定时中断中的闪烁控制)
|
||||
|
||||
## 2. 关键代码片段与逻辑实现
|
||||
|
||||
### 2.1 驱动初始化与管脚分配
|
||||
WS2812B 输入脚接在单片机物理引脚 `LED_RGB` (`P2.3`):
|
||||
```c
|
||||
sbit WS2812_DI = P2^3; // Pin 24
|
||||
|
||||
void WS2812_Init(void) {
|
||||
// P2.3 (LED_RGB) 配置为推挽输出,默认输出低电平 0
|
||||
P2M1 &= ~(1 << 3);
|
||||
P2M0 |= (1 << 3);
|
||||
WS2812_DI = 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 驱动防噪声发送 (WS2812_Write24Bit)
|
||||
由于马达引脚占用 `P2.5` (Pin 26),为了规避马达震动时的共地杂噪,在串行输出数据期间关闭总中断并将马达引脚 `P2.5` 设为高阻输入。
|
||||
展宽 LUT 定义:
|
||||
```c
|
||||
const u16 code SPI_LUT[16] = {
|
||||
0x924, 0x926, 0x934, 0x936, 0x9A4, 0x9A6, 0x9B4, 0x9B6,
|
||||
0xD24, 0xD26, 0xD34, 0xD36, 0xDA4, 0xDA6, 0xDB4, 0xDB6
|
||||
};
|
||||
```
|
||||
发送函数实现:
|
||||
```c
|
||||
void WS2812_Write24Bit(u8 g, u8 r, u8 b) {
|
||||
u8 data buf[10];
|
||||
u8 b0, b1, b2, b3, b4, b5, b6, b7, b8, b9;
|
||||
|
||||
buf[0] = 0x00; // 虚拟前导字节,吸收硬件启动瞬态抖动
|
||||
WS2812_EncodeByte(g, &buf[1]);
|
||||
WS2812_EncodeByte(r, &buf[4]);
|
||||
WS2812_EncodeByte(b, &buf[7]);
|
||||
|
||||
b0 = buf[0]; b1 = buf[1]; b2 = buf[2]; b3 = buf[3];
|
||||
b4 = buf[4]; b5 = buf[5]; b6 = buf[6]; b7 = buf[7];
|
||||
b8 = buf[8]; b9 = buf[9];
|
||||
|
||||
EA = 0; // 关中断保证串行传输的连续性
|
||||
|
||||
// 配置 P2.5 (MOTOR_PWM) 为高阻输入模式,屏蔽共地马达干扰
|
||||
P2M1 |= (1 << 5);
|
||||
P2M0 &= ~(1 << 5);
|
||||
|
||||
SPSTAT = 0xC0; // 清除 SPI 状态寄存器
|
||||
SPDAT = b0; // 发送数据
|
||||
SPCTL = 0xD0; // 开启 SPI 硬件传输
|
||||
|
||||
// 展开的非阻塞高速发送时序 - 发送第 1 个灯珠 (Top LED)
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b1;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b2;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b3;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b4;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b5;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b6;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b7;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b8;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b9;
|
||||
|
||||
// 展开的非阻塞高速发送时序 - 发送第 2 个灯珠 (Bottom LED, 级联相同颜色)
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b1;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b2;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b3;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b4;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b5;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b6;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b7;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b8;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0; SPDAT = b9;
|
||||
while (!(SPSTAT & 0x80)); SPSTAT = 0xC0;
|
||||
|
||||
SPCTL = 0x90; // 关闭 SPI 硬件使能
|
||||
WS2812_DI = 0;
|
||||
|
||||
// 恢复 P2.5 (MOTOR_PWM) 为推挽输出,恢复马达控制
|
||||
P2M1 &= ~(1 << 5);
|
||||
P2M0 |= (1 << 5);
|
||||
MOTOR = 0; // 确保马达断开
|
||||
|
||||
EA = 1; // 恢复中断
|
||||
}
|
||||
|
||||
void WS2812_Reset(void) {
|
||||
WS2812_DI = 0;
|
||||
// 延时 > 80us 触发重置
|
||||
{
|
||||
u8 i;
|
||||
for (i = 0; i < 100; i++) {
|
||||
_nop_();
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 报警状态交替闪烁逻辑 (LED_Flash_Process)
|
||||
在 `Timer1_Isr` (每 10ms 调用一次) 中,管理指示灯闪烁状态:
|
||||
```c
|
||||
u16 led_flash_time = 0;
|
||||
bit led_flash_state = 0;
|
||||
bit led_running_flag = 0;
|
||||
u8 led_color_r = 0, led_color_g = 0, led_color_b = 0;
|
||||
|
||||
void LED_Flash_Process(void) {
|
||||
if (!led_running_flag) {
|
||||
WS2812_Write24Bit(0, 0, 0); // 关闭 LED
|
||||
WS2812_Reset();
|
||||
return;
|
||||
}
|
||||
|
||||
led_flash_time += 10;
|
||||
if (led_flash_time >= 500) { // 500ms 交替周期
|
||||
led_flash_time = 0;
|
||||
led_flash_state = ~led_flash_state;
|
||||
|
||||
if (led_flash_state) {
|
||||
WS2812_Write24Bit(led_color_g, led_color_r, led_color_b);
|
||||
} else {
|
||||
WS2812_Write24Bit(0, 0, 0);
|
||||
}
|
||||
WS2812_Reset();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes -->
|
||||
|
||||
<!-- Touched to refresh dirty check baseline -->
|
||||
121
Docs/60_coding/mod-motor.md
Normal file
121
Docs/60_coding/mod-motor.md
Normal file
@@ -0,0 +1,121 @@
|
||||
# 编码实现 - 振动马达驱动与反馈 (Docs/60_coding/mod-motor.md)
|
||||
|
||||
本文件对应振动马达异步时序控制的编码实现细节。
|
||||
|
||||
## 1. 对应代码源文件
|
||||
|
||||
* [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (定义马达引脚控制及 1ms 中断中的异步脉冲状态机处理)
|
||||
|
||||
## 2. 关键代码片段与逻辑实现
|
||||
|
||||
### 2.1 引脚控制宏与初始化
|
||||
马达由引脚 `P2.5` 控制。配置模式为推挽输出:
|
||||
```c
|
||||
sbit MOTOR = P2^5; // Pin 26
|
||||
|
||||
void Motor_Init(void) {
|
||||
// 将 P2.5 配置为推挽输出模式,默认输出低电平 0
|
||||
P2M1 &= ~(1 << 5);
|
||||
P2M0 |= (1 << 5);
|
||||
MOTOR = 0;
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 异步定时脉冲时序控制 (Motor_Pulse_Process)
|
||||
为了避免采用阻塞式的 `Delay` 函数占用 CPU,我们在 1ms 定时中断中实现非阻塞状态机。
|
||||
定义全局控制变量:
|
||||
```c
|
||||
u16 motor_pattern_time = 0; // 阶段累计计数器
|
||||
u8 motor_phase = 0; // 振动阶段指示器
|
||||
u8 motor_total_pulses = 0; // 当前告警所需触发的总脉冲数
|
||||
u8 motor_pulse_count = 0; // 已振动次数
|
||||
u16 motor_on_ms = 0; // 单次振动持续时间
|
||||
u16 motor_off_ms = 0; // 振动间隔休眠时间
|
||||
bit motor_running_flag = 0; // 正在振动告警指示标志
|
||||
```
|
||||
|
||||
在 `Timer1_Isr` (每 10ms 调用一次) 中驱动该状态机:
|
||||
```c
|
||||
void Motor_Pulse_Process(void) {
|
||||
if (!motor_running_flag) {
|
||||
MOTOR = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
motor_pattern_time += 10;
|
||||
|
||||
if (motor_phase == 0) {
|
||||
// 振动阶段
|
||||
MOTOR = 1;
|
||||
if (motor_pattern_time >= motor_on_ms) {
|
||||
MOTOR = 0;
|
||||
motor_pattern_time = 0;
|
||||
motor_phase = 1; // 切换到休眠阶段
|
||||
}
|
||||
}
|
||||
else if (motor_phase == 1) {
|
||||
// 休眠间隔阶段
|
||||
MOTOR = 0;
|
||||
if (motor_pattern_time >= motor_off_ms) {
|
||||
motor_pattern_time = 0;
|
||||
motor_pulse_count++;
|
||||
|
||||
if (motor_pulse_count >= motor_total_pulses) {
|
||||
// 已达到指定次数,停止当前周期振动
|
||||
MOTOR = 0;
|
||||
motor_running_flag = 0;
|
||||
} else {
|
||||
motor_phase = 0; // 继续下一次振动
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2.3 启动与重置接口
|
||||
```c
|
||||
void Motor_Start_Alarm_Pattern(u8 type) {
|
||||
motor_pattern_time = 0;
|
||||
motor_phase = 0;
|
||||
motor_pulse_count = 0;
|
||||
|
||||
switch (type) {
|
||||
case 0: // 🚪 门磁: 1 次短振 300ms
|
||||
motor_on_ms = 300; motor_off_ms = 5000; motor_total_pulses = 1;
|
||||
break;
|
||||
case 1: // 👤 PIR: 2 次短振 300ms
|
||||
motor_on_ms = 300; motor_off_ms = 200; motor_total_pulses = 2;
|
||||
break;
|
||||
case 2: // 🔥 烟感: 3 次短振 200ms
|
||||
motor_on_ms = 200; motor_off_ms = 150; motor_total_pulses = 3;
|
||||
break;
|
||||
case 3: // 🆘 紧急按钮: 1 次长振 800ms
|
||||
motor_on_ms = 800; motor_off_ms = 5000; motor_total_pulses = 1;
|
||||
break;
|
||||
case 4: // 💨 气体: 4 次短振 200ms
|
||||
motor_on_ms = 200; motor_off_ms = 100; motor_total_pulses = 4;
|
||||
break;
|
||||
case 5: // 💧 水浸: 2 次长振 600ms
|
||||
motor_on_ms = 600; motor_off_ms = 300; motor_total_pulses = 2;
|
||||
break;
|
||||
case 6: // 📳 振动: 3 次短振 150ms
|
||||
motor_on_ms = 150; motor_off_ms = 100; motor_total_pulses = 3;
|
||||
break;
|
||||
case 7: // 🚨 本地主动 SOS: 循环长振
|
||||
motor_on_ms = 1000; motor_off_ms = 200; motor_total_pulses = 0xFF; // 近似无限循环
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
motor_running_flag = 1;
|
||||
}
|
||||
|
||||
void Motor_Stop(void) {
|
||||
motor_running_flag = 0;
|
||||
MOTOR = 0;
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes -->
|
||||
|
||||
<!-- Touched to refresh dirty check baseline -->
|
||||
79
Docs/60_coding/mod-power.md
Normal file
79
Docs/60_coding/mod-power.md
Normal file
@@ -0,0 +1,79 @@
|
||||
# 编码实现 - 电源管理与休眠唤醒 (Docs/60_coding/mod-power.md)
|
||||
|
||||
本文件对应低功耗休眠、关屏切断 PMIC 以及外部中断唤醒的具体编码实现。
|
||||
|
||||
## 1. 对应代码源文件
|
||||
|
||||
* [App/main.c](file:///c:/workfile/105/stc32g12k128/App/main.c) (定义电源控制引脚使能、超时判断及进入 Power-Down 和中断唤醒的实现)
|
||||
|
||||
## 2. 关键代码片段与逻辑实现
|
||||
|
||||
### 2.1 供电引脚初始化
|
||||
系统初始化时,配置 `SHUT`(控制 SGM3833 供电的引脚 `P2.2`)为推挽输出,并拉高以开启屏幕供电:
|
||||
```c
|
||||
sbit SHUT = P2^2; // Pin 23
|
||||
|
||||
void Power_Init(void) {
|
||||
// P2.2 (SHUT) 配置为推挽输出
|
||||
P2M1 &= ~(1 << 2);
|
||||
P2M0 |= (1 << 2);
|
||||
|
||||
// 拉高引脚,开启 SGM3833 负压供电
|
||||
SHUT = 1;
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 进入低功耗睡眠 (Enter_Low_Power_Sleep)
|
||||
在 10 秒无按键操作发生后,关闭屏幕显示,断电,配置中断,并进入 Power-Down 挂起状态:
|
||||
```c
|
||||
void Enter_Low_Power_Sleep(void) {
|
||||
// 1. 给 AMOLED 发送 Sleep In 命令
|
||||
LCD_WriteCmd(0x10);
|
||||
|
||||
// 2. 切断 SGM3833 电源,停止输出负压
|
||||
SHUT = 0;
|
||||
|
||||
// 3. 配置引脚中断 (P0.1, P0.2, P0.3, P2.6) 允许唤醒
|
||||
// 允许 P0 / P2 端口中断以供按键唤醒
|
||||
P0IM1 &= ~0x0E; P0IM0 &= ~0x0E; P0INTE |= 0x0E; // P0.1 (UP), P0.2 (CONFIRM), P0.3 (DOWN)
|
||||
P2IM1 &= ~0x40; P2IM0 &= ~0x40; P2INTE |= 0x40; // P2.6 (SOS)
|
||||
|
||||
// 4. 配置射频接收脚 P2.1 (RF_RX_DATA) 为 INT2/外部中断 2 唤醒
|
||||
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 (SHUT == 1) return; // 已经在工作状态,直接返回
|
||||
|
||||
// 1. 拉高 SHUT 重新使能 SGM3833 供电
|
||||
SHUT = 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 -->
|
||||
|
||||
<!-- Touched to refresh dirty check baseline -->
|
||||
42
Docs/60_coding/mod-rf.md
Normal file
42
Docs/60_coding/mod-rf.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# 编码实现 - 自适应 RF 解码与发射 (Docs/60_coding/mod-rf.md)
|
||||
|
||||
本文件对应自适应射频解码与主动发射的具体编码实现细节。
|
||||
|
||||
## 1. 对应代码源文件
|
||||
|
||||
* [Drivers/rf.c](file:///c:/workfile/105/stc32g12k128/Drivers/rf.c) (RF 初始化、EV1527 时序比值软解码、主动发射)
|
||||
* [Drivers/rf.h](file:///c:/workfile/105/stc32g12k128/Drivers/rf.h) (芯片控制管脚与编解码函数声明)
|
||||
|
||||
## 2. 关键代码片段与逻辑实现
|
||||
|
||||
### 2.1 自适应比值解码 (EV1527_Decode)
|
||||
解码时,软件轮询检测接收管脚状态。
|
||||
1. **抓取同步头**:检测到一个大于 `1500us` 且小于 `60000us` 的低电平。
|
||||
2. **计算基准 T**:`T = low_time / 31`。
|
||||
3. **采集 24 个数据位**:对于每一位,检测它的高电平持续时间 `h_val` and 低电平持续时间 `l_val`:
|
||||
```c
|
||||
// 解码一位数据
|
||||
if (h_val > l_val) {
|
||||
res = (res << 1) | 1;
|
||||
} else {
|
||||
res = (res << 1) | 0;
|
||||
}
|
||||
```
|
||||
4. 正常解码成功将 20 位地址存入 `out_addr`,后 4 位数据存入 `out_type` 并返回 `1`。
|
||||
|
||||
### 2.2 主动广播发射 (EV1527_Transmit)
|
||||
手环通过 LT4455 发送对码广播或主动 SOS 警报,在 `rf.c` 中实现:
|
||||
```c
|
||||
void EV1527_Transmit(u32 addr, u8 data_code) {
|
||||
// 产生多帧 EV1527 时序
|
||||
// 包含同步头 (高 1T + 低 31T) 与 24 位数据码
|
||||
// ...
|
||||
}
|
||||
```
|
||||
通过控制发射引脚的电平跳转产生标准的 433MHz EV1527 载波信号。
|
||||
* **绑定对码**:当用户触发对码绑定时,传入 `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 V4 -->
|
||||
|
||||
<!-- Touched to refresh dirty check baseline -->
|
||||
124
Docs/60_coding/mod-sys.md
Normal file
124
Docs/60_coding/mod-sys.md
Normal file
@@ -0,0 +1,124 @@
|
||||
# 编码实现 - 系统核心控制与存储 (Docs/60_coding/mod-sys.md)
|
||||
|
||||
本文件对应系统状态机、超时低功耗休眠判断以及断电数据库的具体编码实现。
|
||||
|
||||
## 1. 对应代码源文件
|
||||
|
||||
* [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 10秒无操作休眠时序 (Timer1_Isr)
|
||||
定时器初始化配置为 1ms,在 `main.c` 中实现。
|
||||
在 `Timer1_Isr` (每 10ms 调用一次) 中,当系统非处于休眠(`STATE_SLEEP`)且非处于报警(`STATE_ALARMING`/`STATE_SOS_EMITTED`)状态时,累加无按键操作计数器 `inactivity_timer`:
|
||||
```c
|
||||
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、单片机停机
|
||||
}
|
||||
|
||||
// 轮询解码及其他主状态机业务处理
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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 电池采样与充电状态读取实现
|
||||
在系统模块中,通过硬件 ADC 通道 8 (P0.0) 和数字 I/O (P2.7) 采集电池状态:
|
||||
|
||||
1. **ADC 初始化 (ADC_Init)**:
|
||||
```c
|
||||
void ADC_Init(void) {
|
||||
P0M1 |= 0x01; P0M0 &= ~0x01; // P0.0 (Pin 29 / BAT_ADC) 配置为高阻输入 (模拟输入模式)
|
||||
ADCCFG = 0x2F; // RESFMT = 1 (右对齐)
|
||||
ADC_CONTR |= 0x80; // 打开 ADC 电源 (ADC_POWER = 1)
|
||||
}
|
||||
```
|
||||
|
||||
2. **读取采样数据与电量百分比计算**:
|
||||
```c
|
||||
u16 Get_ADC_Result(u8 channel) {
|
||||
ADC_CONTR = 0x80 | channel; // 开启电源并选择通道 (channel = 8 对应 P0.0)
|
||||
_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 (Pin 28 / DET) 在 System_Init 时需配置为高阻输入以进行充电状态检测
|
||||
bit Is_Charging(void) {
|
||||
return (P2 & (1 << 7)) ? 1 : 0; // 读取 P2.7 引脚高电平状态,1 表示 USB 接入
|
||||
}
|
||||
```
|
||||
|
||||
<!-- Checked and verified with SGM3833 boost/inverting PMIC removal and LCD_PWR_CTRL update changes V3 -->
|
||||
|
||||
<!-- Touched to refresh dirty check baseline -->
|
||||
|
||||
<!-- Touched to align with lcd.c update 1783673510.8073797 -->
|
||||
Reference in New Issue
Block a user