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:
+67
-13
@@ -36,12 +36,13 @@ DST_L = 0C
|
||||
### 1.2 接收端串口收到的 Mesh 数据格式
|
||||
|
||||
```text
|
||||
F1 DD 09 00 0A | SRC_H SRC_L | BUSINESS | 0D 0A
|
||||
F1 DD 09 | SRC_H SRC_L | DST_H DST_L | BUSINESS | 0A
|
||||
```
|
||||
|
||||
这是接收方 UART 侧看到的接收格式,不是设备或主机发送时使用的格式。
|
||||
其中两字节是 JDY-28M 接收帧中携带的地址字段;业务发送方短地址仍然放在
|
||||
业务帧 `BUSINESS` 的 `SRC_H SRC_L` 字段中。
|
||||
其中前两字节是发送方短地址,后两字节是接收方短地址;业务帧中不再重复
|
||||
嵌入发送方或接收方地址。部分模块/串口工具可能显示为`0D 0A`,设备解析器
|
||||
兼容`0A`和`0D 0A`两种接收表现。
|
||||
|
||||
无论发送方是主机还是设备,发送时都统一使用第1.1节的格式:
|
||||
|
||||
@@ -54,15 +55,16 @@ F1 DD 09 00 0A | SRC_H SRC_L | BUSINESS | 0D 0A
|
||||
JDY-28M完整Mesh帧最大20字节。当前业务帧最大DATA长度为4字节:
|
||||
|
||||
```text
|
||||
BUSINESS = SRC_H SRC_L CMD LEN DATA CRC16_H CRC16_L
|
||||
BUSINESS = CMD LEN DATA CRC16_H CRC16_L
|
||||
```
|
||||
|
||||
业务帧长度为`6 + LEN`,完整外层发送帧长度为`16 + LEN`,因此`LEN`最大为4。
|
||||
业务帧长度为`4 + LEN`,完整外层发送帧长度为`14 + LEN`,按20字节限制计算,
|
||||
`LEN`最大为6;当前继电器控制命令使用`LEN=1`。
|
||||
|
||||
## 2. 业务帧格式
|
||||
|
||||
```text
|
||||
SRC_H SRC_L | CMD | LEN | DATA[0..LEN-1] | CRC16_H CRC16_L
|
||||
CMD | LEN | DATA[0..LEN-1] | CRC16_H CRC16_L
|
||||
```
|
||||
|
||||
### 2.1 CRC16规则
|
||||
@@ -73,7 +75,7 @@ SRC_H SRC_L | CMD | LEN | DATA[0..LEN-1] | CRC16_H CRC16_L
|
||||
初始值:0xFFFF
|
||||
输入反射:是
|
||||
输出反射:是
|
||||
计算范围:SRC_H、SRC_L、CMD、LEN和DATA
|
||||
计算范围:CMD、LEN和DATA
|
||||
发送顺序:CRC高字节在前,低字节在后
|
||||
```
|
||||
|
||||
@@ -88,8 +90,8 @@ CRC错误时设备直接丢弃整帧,不执行命令,也不返回错误应
|
||||
```
|
||||
|
||||
当请求通过 Mesh 广播地址 `FFFF` 发送时,设备不会立即同时回复。设备会
|
||||
根据本机短地址和系统时间生成约20~170ms、按10ms分档的随机延时,然后向业务帧中的
|
||||
发送方 `SRC_H SRC_L` 单播回复,以降低多个设备同时应答造成的网络拥塞。
|
||||
根据本机短地址和系统时间生成约20~170ms、按10ms分档的随机延时,然后向外层
|
||||
接收帧中的发送方地址单播回复,以降低多个设备同时应答造成的网络拥塞。
|
||||
单播请求仍然直接回复。
|
||||
|
||||
错误应答:
|
||||
@@ -118,7 +120,7 @@ CMD = 07
|
||||
LEN = 00
|
||||
```
|
||||
|
||||
发送方短地址位于业务帧的 `SRC_H SRC_L` 字段中,因此该命令不需要 DATA,
|
||||
发送方短地址由JDY-28M外层提供,因此该命令不需要 DATA,
|
||||
其他设备收到后不回复。主机或其他设备可据此更新在线状态;如果后续长期
|
||||
未收到该设备的上线或业务消息,可以结合应用层超时判断设备是否离线。
|
||||
|
||||
@@ -154,7 +156,7 @@ DATA[2] = 需要升级的版本高字节
|
||||
DATA[3] = 需要升级的版本低字节
|
||||
```
|
||||
|
||||
设备短地址位于响应业务帧的`SRC_H SRC_L`字段中。主机收到多个`0x90`后,
|
||||
设备短地址由JDY-28M外层提供。主机收到多个`0x90`后,
|
||||
生成待升级设备列表,由人工选择设备,再对选中的短地址执行后续OTA流程。
|
||||
|
||||
## 4. 主机下发的普通命令
|
||||
@@ -173,7 +175,7 @@ DATA[0] = 01:继电器吸合
|
||||
业务帧结构示例:
|
||||
|
||||
```text
|
||||
SRC_H SRC_L 01 01 01 CRC16_H CRC16_L
|
||||
01 01 01 CRC16_H CRC16_L
|
||||
```
|
||||
|
||||
设备执行后返回:
|
||||
@@ -191,6 +193,33 @@ DATA[0] = 当前继电器控制状态
|
||||
|
||||
注意:当前没有继电器触点反馈,设备只能确认PA1控制输出已经设置,不能确认触点机械动作是否真实成功。电流异常由故障检测功能另行上报。
|
||||
|
||||
以主机地址`34FA`向设备地址`35FA`发送吸合命令为例,主机串口发送:
|
||||
|
||||
```text
|
||||
41 54 2B AA A1 35 FA A0 01 01 01 50 E0 0D 0A
|
||||
```
|
||||
|
||||
设备接收侧看到:
|
||||
|
||||
```text
|
||||
F1 DD 09 34 FA 35 FA 01 01 01 50 E0 0A
|
||||
```
|
||||
|
||||
设备向主机地址`34FA`回复:
|
||||
|
||||
```text
|
||||
41 54 2B AA A1 34 FA A0 81 01 01 B8 E1 0D 0A
|
||||
```
|
||||
|
||||
广播吸合时,将目标地址改为`FFFF`:
|
||||
|
||||
```text
|
||||
41 54 2B AA A1 FF FF A0 01 01 01 50 E0 0D 0A
|
||||
```
|
||||
|
||||
设备收到广播后执行控制,并向发送方地址回复`0x81`。正式多设备版本应对广播
|
||||
应答增加随机错峰延时,避免多个设备同时发送。
|
||||
|
||||
### 4.2 `0x02` 查询继电器状态
|
||||
|
||||
主机发送:
|
||||
@@ -462,7 +491,7 @@ DATA[0] = 01:校验失败
|
||||
|
||||
当`DATA[0]=00`时,设备已经完成外部Flash固件校验并写入升级元数据。
|
||||
设备会先发送`0x8B`应答,随后自动触发MCU复位,由Bootloader把OTA固件
|
||||
搬运到内部Flash的`0x8800`区域。`0x8B`不代表Bootloader已经搬运完成,
|
||||
搬运到内部Flash的`0x8500`区域。`0x8B`不代表Bootloader已经搬运完成,
|
||||
主机必须继续等待设备重新启动后主动上报的`0x0D`。
|
||||
|
||||
### 7.5 `0x0C` 取消OTA
|
||||
@@ -541,3 +570,28 @@ DATA[0] = 对应的升级结果码
|
||||
设备收到确认后清除`0x005010`。
|
||||
如果主机没有发送`0x8D`,设备不会清除结果记录,后续启动时可能再次上报,
|
||||
便于主机发现升级结果确认丢失。
|
||||
|
||||
## 9. 整片初次烧录
|
||||
|
||||
整片初次烧录使用合并固件,不用于OTA:
|
||||
|
||||
```bash
|
||||
make full
|
||||
```
|
||||
|
||||
生成文件:
|
||||
|
||||
```text
|
||||
build/stm8_mesh_full.hex:带绝对地址的完整HEX
|
||||
build/stm8_mesh_full.bin:从0x8000开始的连续BIN
|
||||
```
|
||||
|
||||
合并地址范围为:
|
||||
|
||||
```text
|
||||
0x8000~0x84FF:Bootloader
|
||||
0x8500~0x9FFF:主程序
|
||||
```
|
||||
|
||||
使用ST-LINK V2/SWIM烧录时优先使用HEX文件;OTA升级仍只发送
|
||||
`build/mesh_relay.bin`,不能发送合并固件。
|
||||
|
||||
@@ -12,10 +12,10 @@ STM8S003F3P6TR 内部程序 Flash 总容量为 8KiB,地址范围为 `0x8000~0x
|
||||
|
||||
| 地址范围 | 容量 | 用途 |
|
||||
|---|---:|---|
|
||||
| `0x8000~0x87FF` | 2KiB | Bootloader |
|
||||
| `0x8800~0x9FFF` | 6KiB | Application 主程序 |
|
||||
| `0x8000~0x84FF` | 1.25KiB | Bootloader |
|
||||
| `0x8500~0x9FFF` | 6.75KiB | Application 主程序 |
|
||||
|
||||
应用程序必须按照 `0x8800` 重新链接,不能使用默认的 `0x8000` 起始地址。
|
||||
应用程序必须按照 `0x8500` 重新链接,不能使用默认的 `0x8000` 起始地址。
|
||||
|
||||
### 1.2 W25Q80BV 外部 Flash
|
||||
|
||||
@@ -51,9 +51,9 @@ PA3 = 0:按键按下
|
||||
|
||||
JDY-28M 控制脚当前分配为:`PA2=PWRC`、`PD2=RESET`。`PA2` 不再作为Flash片选。
|
||||
|
||||
蓝牙状态/故障指示灯连接到 `PB5`,低电平点亮;继电器状态指示灯连接到 `PD3`,高电平点亮。
|
||||
蓝牙状态/故障指示灯连接到 `PB4`,低电平点亮;继电器状态指示灯连接到 `PD3`,高电平点亮。
|
||||
|
||||
程序运行/OTA状态灯连接到 `PB4`,低电平点亮:正常运行时按500ms节奏闪烁,OTA接收或校验时按约100ms节奏快速闪烁。
|
||||
程序运行/OTA状态灯连接到 `PB5`,低电平点亮:正常运行时按500ms节奏闪烁,OTA接收或校验时按约100ms节奏快速闪烁。
|
||||
|
||||
## 2. 元数据格式
|
||||
|
||||
@@ -134,7 +134,7 @@ Bootloader负责:
|
||||
- 清除升级请求;
|
||||
- 检查内部应用程序是否有效;
|
||||
- 应用损坏时恢复出厂固件;
|
||||
- 跳转到 `0x8800` 启动主程序。
|
||||
- 跳转到 `0x8500` 启动主程序。
|
||||
|
||||
升级安全顺序必须是:
|
||||
|
||||
@@ -218,12 +218,12 @@ SRC_H | SRC_L | CMD | LEN | DATA | CRC16_L | CRC16_H
|
||||
|
||||
说明:
|
||||
|
||||
- `SRC_H SRC_L`:发送方 Mesh 短地址,高字节在前;设备应答时将该地址作为
|
||||
JDY-28M 外层目标地址,业务帧中的 SRC 改为设备自身短地址;
|
||||
- JDY-28M 外层地址:发送方和接收方 Mesh 短地址均由外层提供,高字节在前;
|
||||
设备应答时将外层发送方地址作为目标地址;
|
||||
- `CMD`:命令;
|
||||
- `LEN`:DATA长度;
|
||||
- `DATA`:命令数据;
|
||||
- `CRC16`:CRC-16/MODBUS,计算范围为 SRC、CMD、LEN、DATA,低字节在前;
|
||||
- `CRC16`:CRC-16/MODBUS,计算范围为 CMD、LEN、DATA,高字节在前;
|
||||
- CRC错误时静默丢弃,不执行继电器动作,不返回错误帧。
|
||||
|
||||
JDY-28M的Mesh外层帧负责目标地址、源地址、Mesh头和帧尾,业务帧本身不重复添加地址。
|
||||
|
||||
@@ -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 访问。
|
||||
|
||||
每恢复一个模块,都应重新编译、烧录并进行实物验证,不能一次性恢复所有模块后再统一排查。
|
||||
Reference in New Issue
Block a user