feat: complete assembly mesh relay control path

- synchronize STM8 application and external flash layout updates\n- add assembly bootloader and assembly application test projects\n- add UART1 interrupt receive ring buffer\n- parse JDY-28M Mesh outer frames and source addresses\n- add CRC-16/MODBUS validation\n- implement relay control command 0x01 and response 0x81\n- support unicast and broadcast relay control frames\n- update host protocol, OTA documents, and serial tools
This commit is contained in:
十七
2026-11-05 16:12:51 +08:00
parent df1aad3d16
commit ebef6a8694
40 changed files with 2133 additions and 307 deletions
+248
View File
@@ -0,0 +1,248 @@
# STM8S003F3 TIM4 中断问题排查记录
## 1. 问题现象
正式主程序烧录后,PB5 程序运行指示灯没有按照约 500 ms 周期闪烁。
此前已经确认:
- 独立 PB5 测试程序可以正常闪烁;
- 测试主程序链接到 `0x8800` 后,Bootloader 可以正常跳转;
- PB5 GPIO 连接和芯片引脚基本正常;
- 正式主程序最初还存在代码超出应用区的问题,但缩减为最小主循环后,问题依然与系统节拍有关。
## 2. 逐步排查过程
### 2.1 先屏蔽 OTA
正式主程序在初始化阶段先调用 OTA 和外部 SPI Flash 初始化。为排除 W25Q80BV 初始化阻塞,临时屏蔽:
```c
OtaManager_Init();
OtaManager_Task();
```
结果:PB5 仍然没有正常闪烁,因此问题不只在 OTA 初始化。
### 2.2 再屏蔽蓝牙和 Mesh
继续屏蔽蓝牙状态机、Mesh 任务和上线广播,避免 UART 等待影响主循环。
结果:PB5 仍然没有正常闪烁,蓝牙任务不是唯一原因。
### 2.3 缩减为最小主循环
最终只保留:
```text
Board_Init()
软件延时
PB5电平翻转
```
并临时关闭:
- TIM4;
- 全局中断;
- 看门狗;
- UART;
- ADC;
- 蓝牙;
- Mesh;
- 故障管理;
- OTA。
结果:PB5 可以正常闪烁。
由此确认 Bootloader 跳转、主程序入口和 PB5 GPIO 硬件均正常。
### 2.4 逐步恢复 TIM4
恢复 TIM4 和正式 `Board_StatusLedTask()` 后,PB5 又变成常亮。
随后使用软件延时配合 TIM4、直接读取 `SystemTick_NowMs()`、轮询 `TIM4_SR` 等方法进行区分,最终发现主程序使用 Bootloader 后,应用程序的中断向量不能直接被硬件访问。
## 3. 最终原因
STM8S003F3 的硬件中断向量地址固定在程序 Flash 起始区域。
官方数据手册规定:
```text
TIM4 IRQ23 向量地址:0x8064
UART1 RX IRQ18 向量地址:0x8050
```
当前工程的存储布局为:
```text
0x8000 ~ 0x87FF:Bootloader
0x8800 ~ 0x9FFF:Application
```
主程序虽然链接到 `0x8800`,但硬件中断不会自动把向量表移动到 `0x8800`。如果直接在主程序中声明:
```c
void SystemTick_TIM4_ISR(void) __interrupt(23);
```
硬件发生 TIM4 中断时,仍然访问固定地址 `0x8064`,而不是主程序向量表中的 `0x8864`。
因此 Bootloader 跳转正常,但应用程序的 TIM4 中断处理函数没有被正确调用,系统毫秒计数不会正常驱动 PB5 状态灯、超时判断和故障计时。
## 4. 修正方案
采用 Bootloader 固定向量转发方案:
```text
TIM4硬件中断
↓
Bootloader固定向量 0x8064
↓
转发到主程序向量 0x8864
↓
主程序 SystemTick_TIM4_ISR()
↓
SystemTick_TIM4_Handler()
```
主程序中保留自己的 TIM4 向量入口:
```c
void SystemTick_TIM4_ISR(void) __interrupt(23)
{
SystemTick_TIM4_Handler();
}
```
Bootloader 在固定的 IRQ23 入口中跳转到主程序 TIM4 向量槽:
```c
void Bootloader_TIM4_Vector(void) __interrupt(23)
{
__asm__("jpf 0x8864");
}
```
这样主程序内部的实际 ISR 地址可以变化,Bootloader 只需要转发到主程序固定向量槽,不需要写死主程序 ISR 的最终代码地址。
## 5. 验证结果
修正后使用整片固件烧录:
```text
build/stm8_mesh_full.hex
```
烧录起始地址:
```text
0x8000
```
PB5 已经能够按照约 500 ms 周期正常闪烁,说明以下链路已经验证通过:
```text
TIM4 → 0x8064 → Bootloader转发 → 0x8864 → 主程序ISR → 系统毫秒计数
```
## 6. 经验和教训
### 6.1 Bootloader 分区不仅影响代码地址,也影响中断体系
把主程序从 `0x8000` 移动到 `0x8800`,不能只修改链接地址。还必须同步设计:
- 中断向量转发;
- 应用程序复位入口;
- Bootloader 跳转前的中断清理;
- Bootloader 和主程序外设状态交接。
### 6.2 独立 GPIO 测试不能证明系统节拍正常
PB5 独立测试程序只验证了:
- Bootloader 能跳转;
- GPIO 能输出;
- LED 硬件正常。
它不能证明 TIM4、UART 中断或应用程序中断向量正常。因此系统功能应分层验证。
### 6.3 必须先做最小可运行程序
本次通过逐步屏蔽模块,最终定位到:
```text
Bootloader跳转和GPIO正常
业务模块不是第一嫌疑
系统节拍/中断链路异常
```
以后遇到“主程序没有运行”的问题,应优先建立最小固件:
```text
GPIO + 软件延时
GPIO + 定时器轮询
GPIO + 定时器中断
GPIO + 看门狗
最后再逐个恢复业务模块
```
### 6.4 诊断信号的翻转频率必须适合肉眼观察
曾经使用“每 1 ms 翻转一次 PB5”的测试方法。该信号约为 500 Hz,LED 肉眼看起来会接近常亮,容易造成误判。
诊断 LED 建议使用:
- 100 ms;
- 500 ms;
- 1 s;
等肉眼可以明确区分的周期。
### 6.5 LED 有效电平必须以实物为准
程序注释、原理图标注和实际 LED 驱动极性必须保持一致。调试时应直接测量 MCU 引脚电平,确认:
```text
PB5 = 1 时 LED 是否点亮
PB5 = 0 时 LED 是否熄灭
```
不能只根据变量名或旧注释判断。
### 6.6 应用固件大小必须按地址范围判断
主程序的有效空间是:
```text
0x8800 ~ 0x9FFF
```
不能只看 `mesh_relay.bin` 的文件大小。必须检查链接 map 中的最高代码地址,确认没有超过 `0x9FFF`。
### 6.7 中断向量转发必须随新增中断同步维护
当前已验证 TIM4 转发。后续启用 UART 接收中断时,还必须增加:
```text
UART1 RX IRQ18
固定向量:0x8050
主程序向量槽:0x8850
```
后续如果 ADC 使用中断,还要增加 ADC1 IRQ22 的转发。
## 7. 后续恢复顺序
建议严格按照以下顺序继续:
1. 保持 TIM4 中断转发,验证 PB5 正式状态灯;
2. 恢复看门狗并验证不复位;
3. 恢复 UART 初始化和 UART 接收中断转发;
4. 验证 JDY-28M AT 指令通信;
5. 恢复 ADC 电流采样;
6. 恢复蓝牙 Mesh 状态机;
7. 恢复业务协议和故障管理;
8. 最后恢复 OTA 和 W25Q80BV 访问。
每恢复一个模块,都应重新编译、烧录并进行实物验证,不能一次性恢复所有模块后再统一排查。