补齐 MCU 桥 P1(管理/对时/版本),并接入硬件 RTC;网关侧发送改为等 GATT CCC 就绪,避免回连刚连上就 app_send_fail。
P1 业务: - 管理:0x16 出厂复位(清配对+时区)、0x18 模组重启、0x1A 通知 MCU 重启(预留) - 对时:网关 DEVICEINFO 下发 Unix 秒+时区,写入 RTC;MCU 0x1C/0x1D 读当前时间 - 版本:UART 0x40 拉 MCU 信息,SOC 0x41 上报网关;网关 ACK 等待 2s,最多 3 次 RTC: - 新增 usr_periph,打开板级 "rtc";开发板无 32K 用 LRC,量产模组再改 CLK_SEL_32K - 对时补偿 +1s;时区存 VM;出厂复位清除时区;硬件走时,软件只刷新 UTC 缓存 链路与 0x14: - VM paired_flag 仍表示已绑定;CONFIGURED 改为绑定+已连接+CCC,否则保持 RECONNECTING - 0x07/0x41 等 notify 未就绪不发送、不占重试,避免 MCU 过早上报
This commit is contained in:
+3
-1
@@ -1,2 +1,4 @@
|
||||
PY32F040开发框架/*
|
||||
.vscode/
|
||||
.vscode/
|
||||
模组功能分期清单.md
|
||||
.clang-format
|
||||
@@ -1,4 +1,4 @@
|
||||
# MCU方案接入协议通讯文档
|
||||
# MCU方案接入协议通讯文档
|
||||
|
||||
# 修订记录
|
||||
|
||||
|
||||
@@ -0,0 +1,335 @@
|
||||
# SOC方案接入通信协议文档
|
||||
|
||||
# 修订记录
|
||||
|
||||
|文档版本|协议版本|日期|编写人|修订类型|修订内容|
|
||||
|---|---|---|---|---|---|
|
||||
|V1\.0\.0|V1\.0|2026\-07\-08|麦田|新建|首次发布SOC方案接入协议通讯文档|
|
||||
|
||||
---
|
||||
|
||||
# 协议说明
|
||||
|
||||
- 数据采用**大端模式**(Big Endian)传输。
|
||||
|
||||
- 数据帧格式: **包头 \+ 包长度 \+ 命令 \+ 序列号 \+ 数据 \+ 校验和**。
|
||||
|
||||
- 包头固定为 `0x55AA`。
|
||||
|
||||
- 包长度表示 **命令开始至校验和结束** 的数据长度。
|
||||
|
||||
- 命令字(Command)用于区分不同功能。
|
||||
|
||||
- 序列号(Seq)由请求方递增生成,ACK需原样返回,用于匹配请求与响应。
|
||||
|
||||
- 默认所有命令采用 **一问一答机制**,发送方发送命令后,接收方必须返回对应 ACK。
|
||||
|
||||
- ACK 数据包包含 Result 字段,用于返回执行结果。
|
||||
|
||||
- 超时未收到 ACK 时,发送方可进行重试,最大重试次数建议为 3 次。
|
||||
|
||||
- 数据点标志(Bitmap)由 DP 配置文档自动生成,一个 Bit 对应一个 DP。
|
||||
|
||||
- 数据点值按照 Bitmap 中 Bit 置位顺序依次排列。
|
||||
|
||||
- 读取命令通过 Bitmap 指定需要读取的 DP,Bitmap 全 1 表示读取全部 DP。
|
||||
|
||||
- BLE 模组主要负责数据透传、蓝牙连接及状态管理,业务数据由 MCU 处理。
|
||||
|
||||
- 校验方式:累加和校验(Sum Check),计算范围为 **包长度字段至校验和前一个字节**。
|
||||
|
||||
## Result 定义:
|
||||
|
||||
|值|含义|
|
||||
|---|---|
|
||||
|0x00|成功|
|
||||
|0x01|参数错误|
|
||||
|0x02|设备忙|
|
||||
|0x03|不支持该操作|
|
||||
|0x04|校验错误|
|
||||
|0x05|执行失败|
|
||||
|
||||
### OTA 专用错误码
|
||||
|
||||
|值|含义|
|
||||
|---|---|
|
||||
|0x20|无需升级(版本一致)|
|
||||
|0x21|OTA 版本低于当前版本|
|
||||
|0x22|Flash 空间不足|
|
||||
|0x23|OTA 状态错误|
|
||||
|0x24|OTA文件检验失败|
|
||||
|0x25|升级失败|
|
||||
|
||||
# 基本通讯协议
|
||||
|
||||
## 设备上报版本信息
|
||||
|
||||
> 设备绑定上网关之后,主动上报固件版本到服务器,此命令通过网关的应答格式判断是否发送成功。。
|
||||
>
|
||||
>
|
||||
|
||||
### 设备 =\> 网关
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|0x55AA|
|
||||
|2|包长度|2|len\(命令\.\.\.校验和\)|
|
||||
|3|命令|1|0x41|
|
||||
|4|序列号|1|与请求的序列号一致|
|
||||
|5|Result|1|执行结果(见 Result 定义)|
|
||||
|6|BLE Version|3|固件版本号,格式:主版本号\(1Byte\)\+次版本号\(1Byte\)\+修订版本号\(1Byte\),显示格式为:`Vx.x.x`|
|
||||
|7|MCU Version|3|固件版本号,格式:主版本号\(1Byte\)\+次版本号\(1Byte\)\+修订版本号\(1Byte\),显示格式为:`Vx.x.x`,如MCU不存在则填0|
|
||||
|8|Hardware Version|3|硬件版本号,格式:硬件主版本号\(1Byte\)\+硬件次版本号\(1Byte\)\+硬件修订版本号\(1Byte\),显示格式:`Vx.x.x`|
|
||||
|9|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
#### Version版本格式说明
|
||||
|
||||
|字节|字段名称|内容说明|
|
||||
|---|---|---|
|
||||
|Byte0|主版本号|产品架构、通信协议等重大变化时递增|
|
||||
|Byte1|次版本号|新增功能或功能优化时递增|
|
||||
|Byte2|修订版本号|Bug修复、参数调整、性能优化时递增|
|
||||
|
||||
#### 示例
|
||||
|
||||
|MCU Version|版本号|
|
||||
|---|---|
|
||||
|0x01 0x02 0x05|V1\.2\.5|
|
||||
|0x05 0x02 0x21|V5\.2\.33|
|
||||
|
||||
### 网关 =\> 设备
|
||||
|
||||
通过网关的应答格式判断是否发送成功
|
||||
|
||||
## APP控制设备
|
||||
|
||||
### APP =\> 设备
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|0x55AA|
|
||||
|2|包长度|2|len\(命令\.\.\.校验和\)|
|
||||
|3|命令|1|0x03(下发控制)|
|
||||
|4|序列号|1|APP递增生成,ACK原样回传|
|
||||
|5|数据点标志|x(\(DP数量\+7\)/8)|一个字节可代表8个数据点|
|
||||
|6|数据点值|x|数据值|
|
||||
|7|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
### 设备 =\> APP
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|0x55AA|
|
||||
|2|包长度|2|len\(命令\.\.\.校验和\)|
|
||||
|3|命令|1|0x04(应答下发控制)|
|
||||
|4|序列号|1|与请求的序列号一致|
|
||||
|5|Result|1|执行结果(见 Result 定义)|
|
||||
|6|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
---
|
||||
|
||||
## APP读取设备
|
||||
|
||||
### APP =\> 设备
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|0x55AA|
|
||||
|2|包长度|2|len\(命令\.\.\.校验和\)|
|
||||
|3|命令|1|0x05(下发读取)|
|
||||
|4|序列号|1|APP递增生成,ACK原样回传|
|
||||
|5|数据点标志|x(\(DP数量\+7\)/8)|指定需要读取的 DP,Bitmap 全 1 表示读取全部 DP|
|
||||
|6|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
### 设备=\> APP
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|0x55AA|
|
||||
|2|包长度|2|len\(命令\.\.\.校验和\)|
|
||||
|3|命令|1|0x06(应答读取)|
|
||||
|4|序列号|1|与请求的序列号一致|
|
||||
|5|数据点标志|x(\(DP数量\+7\)/8)|返回的数据点标志|
|
||||
|6|数据点值|x|数据值|
|
||||
|7|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
---
|
||||
|
||||
## 设备主动上报数据
|
||||
|
||||
此命令直接上报到服务器,通过网关的应答格式判断是否发送成功。
|
||||
|
||||
### 设备 =\> APP/IPC
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|0x55AA|
|
||||
|2|包长度|2|len\(命令\.\.\.校验和\)|
|
||||
|3|命令|1|0x07(主动上报)|
|
||||
|4|序列号|1|设备递增生成|
|
||||
|5|数据点标志|x(\(DP数量\+7\)/8)|一个字节可代表8个数据点|
|
||||
|6|数据点值|x|数据值|
|
||||
|7|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
### APP/IPC =\> 设备
|
||||
|
||||
通过网关的应答格式判断是否发送成功
|
||||
|
||||
---
|
||||
|
||||
# 扩展通讯协议
|
||||
|
||||
## APP向设备推送OTA升级
|
||||
|
||||
> **功能说明:**
|
||||
APP 将与设备升级相关的信息发送给设备,设备根据这些信息判断是否允许OTA升级,并返回升级准备结果。
|
||||
|
||||
### APP =\> 设备
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|固定 `0x55AA`|
|
||||
|2|包长度|2|len\(命令\.\.\.校验和\)|
|
||||
|3|命令|1|0x30|
|
||||
|4|序列号|1|数据包序列号|
|
||||
|5|固件类型(Type)|1|0x01:MCU<br>0x02:BLE|
|
||||
|6|固件版本(Firmware Version)|3|OTA 固件版本号|
|
||||
|7|固件长度(Payload Length)|4|OTA Bin 数据长度|
|
||||
|8|固件 CRC32(Payload CRC32)|4|OTA Bin 数据 CRC32 校验值|
|
||||
|9|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
### 设备 =\> APP
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|固定 `0x55AA`|
|
||||
|2|包长度|2|`len(命令...校验和)`|
|
||||
|3|命令|1|0x31|
|
||||
|4|序列号|1|与请求保持一致|
|
||||
|5|Result|1|执行结果(见 Result OTA 专用错误码定义)|
|
||||
|6|Max Data Length|2|设备 单次允许接收的最大 Payload 长度(Byte)|
|
||||
|7|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
---
|
||||
|
||||
## APP向设备推送OTA数据
|
||||
|
||||
> **功能说明:**
|
||||
APP 根据 设备 返回的 **Max Data Length** 对 OTA Bin 进行分片,并按顺序发送给设备。
|
||||
设备 每接收一包数据后立即返回应答,APP 收到成功应答后继续发送下一包。
|
||||
|
||||
### APP =\> 设备
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|固定 `0x55AA`|
|
||||
|2|包长度|2|`len(命令...校验和)`|
|
||||
|3|命令|1|0x32|
|
||||
|4|序列号|1|数据包序列号|
|
||||
|5|分片序号|2|当前数据包的分片序号,分片序号从1开始计算|
|
||||
|6|总分片数|2||
|
||||
|7|Payload Data|x|OTA Bin 数据,长度 ≤ Max Data Length|
|
||||
|8|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
### 设备 =\> APP
|
||||
|
||||
> **功能说明:**
|
||||
设备 成功接收并处理当前 OTA 数据包后返回 Result。
|
||||
APP收到成功应答后继续发送下一包 OTA 数据;若返回失败,则停止本次 OTA 升级。
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|固定 `0x55AA`|
|
||||
|2|包长度|2|`len(命令...校验和)`|
|
||||
|3|命令|1|0x33|
|
||||
|4|序列号|1|与请求保持一致|
|
||||
|5|Result|1|执行结果(见 Result 定义)|
|
||||
|6|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
## 设备通知APP模组OTA升级完成
|
||||
|
||||
> **功能说明:
|
||||
** 设备完成OTA升级后,主动通知APP当前升级结果及固件版本信息。
|
||||
|
||||
### 设备 =\> APP
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|固定 `0x55AA`|
|
||||
|2|包长度|2|`len(命令...校验和)`|
|
||||
|3|命令|1|0x34|
|
||||
|4|序列号|1|数据包序列号|
|
||||
|5|类型(Type)|1|0x01:MCU<br>0x02:BLE|
|
||||
|6|OTA Result|1|OTA结果|
|
||||
|7|Firmware Version|3|当前固件版本|
|
||||
|8|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
### APP =\> 设备
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|固定 `0x55AA`|
|
||||
|2|包长度|2|`len(命令...校验和)`|
|
||||
|3|命令|1|0x35|
|
||||
|4|序列号|1|与请求保持一致|
|
||||
|5|Result|1|执行结果(见 Result 定义)|
|
||||
|6|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
---
|
||||
|
||||
## APP向设备通知OTA停止
|
||||
|
||||
> **功能说明:**
|
||||
APP 主动通知设备终止当前 OTA 升级流程。设备收到后立即停止 OTA 数据接收,释放 OTA 相关资源,并返回处理结果。
|
||||
|
||||
### APP =\> 设备
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|固定 `0x55AA`|
|
||||
|2|包长度|2|`len(命令...校验和)`|
|
||||
|3|命令|1|0x36|
|
||||
|4|序列号|1|数据包序列号|
|
||||
|5|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
### 设备 =\> APP
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|固定 `0x55AA`|
|
||||
|2|包长度|2|`len(命令...校验和)`|
|
||||
|3|命令|1|0x37|
|
||||
|4|序列号|1|与请求保持一致|
|
||||
|5|Result|1|执行结果(见 Result 定义)|
|
||||
|6|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
---
|
||||
|
||||
## 设备向APP通知OTA停止
|
||||
|
||||
> **功能说明:**
|
||||
设备在 OTA 过程中因内部异常无法继续升级时,主动通知 APP 停止当前 OTA。APP 收到通知后立即停止发送 OTA 数据,并返回应答。
|
||||
|
||||
### 设备 =\> APP
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|固定 `0x55AA`|
|
||||
|2|包长度|2|`len(命令...校验和)`|
|
||||
|3|命令|1|0x38|
|
||||
|4|序列号|1|数据包序列号|
|
||||
|5|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
### APP =\> 设备
|
||||
|
||||
|序号|字段名称|字节长度|内容说明|
|
||||
|---|---|---|---|
|
||||
|1|包头|2|固定 `0x55AA`|
|
||||
|2|包长度|2|`len(命令...校验和)`|
|
||||
|3|命令|1|0x39|
|
||||
|4|序列号|1|与通知保持一致|
|
||||
|5|Result|1|`0x00`:已收到并停止 OTA|
|
||||
|6|校验和|1|和校验(包长度\.\.\.校验和前一个字节)|
|
||||
|
||||
|
||||
|
||||
@@ -177,6 +177,7 @@
|
||||
<Add directory="apps/usr_mcu_bridge" />
|
||||
<Add directory="apps/usr_mcu_bridge/protocol" />
|
||||
<Add directory="apps/usr_mcu_bridge/business" />
|
||||
<Add directory="apps/usr_periph" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-flto" />
|
||||
@@ -759,43 +760,6 @@
|
||||
<Unit filename="apps/earphone/version.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/mcu_bridge.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/mcu_bridge.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_cmd.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_frame.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_frame.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_uart.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_uart.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_util.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_util.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_protocol.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_protocol.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_identity.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_identity.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_pairing.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_pairing.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_link_status.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_link_status.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_business.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_business.h" />
|
||||
<Unit filename="apps/usr_le_code/ble_proto.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
@@ -821,6 +785,56 @@
|
||||
<Unit filename="apps/usr_le_code/usr_le_recieve.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_business.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_business.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_identity.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_identity.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_link_status.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_link_status.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_mgmt.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_pairing.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_pairing.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_time.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_periph/usr_rtc.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_periph/usr_rtc.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/business/mcu_version.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/mcu_bridge.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/mcu_bridge.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_cmd.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_frame.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_frame.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_protocol.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_protocol.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_uart.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_uart.h" />
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_util.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
<Unit filename="apps/usr_mcu_bridge/protocol/mcu_util.h" />
|
||||
<Unit filename="cpu/br28/adc_api.c">
|
||||
<Option compilerVar="CC" />
|
||||
</Unit>
|
||||
@@ -1271,10 +1285,6 @@
|
||||
<Unit filename="include_lib/update/uart_update.h" />
|
||||
<Unit filename="include_lib/update/update.h" />
|
||||
<Unit filename="include_lib/update/update_loader_download.h" />
|
||||
<Extensions>
|
||||
<code_completion />
|
||||
<envvars />
|
||||
<debugger />
|
||||
</Extensions>
|
||||
<Extensions />
|
||||
</Project>
|
||||
</CodeBlocks_project_file>
|
||||
|
||||
@@ -251,6 +251,7 @@ INCLUDES := \
|
||||
-Iapps/usr_mcu_bridge \
|
||||
-Iapps/usr_mcu_bridge/protocol \
|
||||
-Iapps/usr_mcu_bridge/business \
|
||||
-Iapps/usr_periph \
|
||||
-I$(SYS_INC_DIR) \
|
||||
|
||||
|
||||
@@ -624,6 +625,10 @@ c_SRC_FILES := \
|
||||
apps/usr_mcu_bridge/business/mcu_pairing.c \
|
||||
apps/usr_mcu_bridge/business/mcu_link_status.c \
|
||||
apps/usr_mcu_bridge/business/mcu_business.c \
|
||||
apps/usr_mcu_bridge/business/mcu_mgmt.c \
|
||||
apps/usr_mcu_bridge/business/mcu_time.c \
|
||||
apps/usr_mcu_bridge/business/mcu_version.c \
|
||||
apps/usr_periph/usr_rtc.c \
|
||||
apps/usr_le_code/ble_proto.c \
|
||||
apps/usr_le_code/bleproto.c \
|
||||
apps/usr_le_code/bleproto_packer.c \
|
||||
|
||||
+14
-1
@@ -526,6 +526,11 @@ static int att_write_callback(hci_con_handle_t connection_handle, uint16_t att_h
|
||||
|
||||
void usr_ble_send_data(u8* data,u16 len)
|
||||
{
|
||||
/* CCC 未写时 ATT 发数会失败(常见 app_send_fail:-93),等主机使能通知再发 */
|
||||
if (get_ble_work_state() != BLE_ST_NOTIFY_IDICATE) {
|
||||
log_info("ble send skip, wait CCC");
|
||||
return;
|
||||
}
|
||||
if (app_send_user_data_check(len))
|
||||
{
|
||||
app_send_user_data(ATT_CHARACTERISTIC_15f1e601_a277_43fc_a484_dd39ef8a9100_01_VALUE_HANDLE, data, len, ATT_OP_AUTO_READ_CCC);
|
||||
@@ -538,7 +543,6 @@ void usr_ble_send_data(u8* data,u16 len)
|
||||
// printf("%c",data[i]);
|
||||
// }
|
||||
// printf("\n");
|
||||
|
||||
}
|
||||
static int app_send_user_data(u16 handle, u8 *data, u16 len, u8 handle_type)
|
||||
{
|
||||
@@ -961,6 +965,15 @@ u16 usr_get_conn_service()
|
||||
{
|
||||
return con_handle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 主机是否已写 CCC,允许 GATT notify/indicate
|
||||
* @return 1=可发数(BLE_ST_NOTIFY_IDICATE),0=还不行
|
||||
*/
|
||||
u8 usr_ble_is_notify_ready(void)
|
||||
{
|
||||
return (get_ble_work_state() == BLE_ST_NOTIFY_IDICATE) ? 1 : 0;
|
||||
}
|
||||
void ble_app_disconnect(void)
|
||||
{
|
||||
ble_disconnect(NULL);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "dev_manager/dev_manager.h"
|
||||
#include "update_loader_download.h"
|
||||
#include "mcu_bridge.h"
|
||||
#include "usr_rtc.h"
|
||||
|
||||
#ifndef CONFIG_MEDIA_NEW_ENABLE
|
||||
#ifndef CONFIG_MEDIA_DEVELOP_ENABLE
|
||||
@@ -292,6 +293,7 @@ void app_main()
|
||||
}
|
||||
|
||||
app_var_init();
|
||||
usr_rtc_init(); /* 打开硬件 RTC,后续对时/0x1D 都走这里 */
|
||||
mcu_bridge_init();
|
||||
|
||||
#if TCFG_MC_BIAS_AUTO_ADJUST
|
||||
|
||||
@@ -586,9 +586,9 @@ GSENSOR_PLATFORM_DATA_END();
|
||||
|
||||
|
||||
|
||||
#if 1
|
||||
#if TCFG_APP_RTC_EN
|
||||
|
||||
#define RTC_CLK_RES_SEL CLK_SEL_32K //rtc时钟源选择:CLK_SEL_32K/CLK_SEL_12M/CLK_SEL_24M
|
||||
#define RTC_CLK_RES_SEL CLK_SEL_LRC // 开发板无 32K,用 LRC;量产模组有晶振时改 CLK_SEL_32K
|
||||
|
||||
const struct sys_time def_sys_time = { //初始一下当前时间
|
||||
.year = 2020,
|
||||
@@ -615,7 +615,7 @@ RTC_DEV_PLATFORM_DATA_BEGIN(rtc_data)
|
||||
.cbfun = NULL, //闹钟中断的回调函数,用户自行定义
|
||||
/* .cbfun = alarm_isr_user_cbfun, */
|
||||
RTC_DEV_PLATFORM_DATA_END()
|
||||
#endif // 0
|
||||
#endif /* TCFG_APP_RTC_EN */
|
||||
|
||||
|
||||
|
||||
@@ -632,7 +632,9 @@ REGISTER_DEVICES(device_table) = {
|
||||
#if TCFG_OTG_MODE
|
||||
{ "otg", &usb_dev_ops, (void *)&otg_data},
|
||||
#endif
|
||||
#if TCFG_APP_RTC_EN
|
||||
{ "rtc", &rtc_dev_ops , (void *)&rtc_data},
|
||||
#endif
|
||||
};
|
||||
|
||||
/************************** imu sensor ****************************/
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#ifndef APP_MAIN_H
|
||||
#define APP_MAIN_H
|
||||
|
||||
#include "generic/typedef.h" /* u8/u16/u32 等杰理类型,本头必须自包含 */
|
||||
|
||||
typedef struct _USR_VAR {
|
||||
u8 usr_start_write_flash ;
|
||||
u8 usr_plan_cnt ;
|
||||
@@ -37,12 +39,10 @@ typedef struct _USR_VAR {
|
||||
u32 usr_data_index ;
|
||||
u32 usr_allow_to_flash ;
|
||||
u32 usr_ota_data_size ;
|
||||
u32 usr_cureent_utc ;
|
||||
|
||||
int usr_double_ota_timer_id ;
|
||||
int ota_timer ;
|
||||
int usr_ota_data_id ;
|
||||
int usr_timezoneOffset ;
|
||||
} USR_VAR;
|
||||
|
||||
|
||||
|
||||
@@ -381,7 +381,7 @@ typedef struct bleproto_ycmd {
|
||||
// E_BLEPROTO_SERVICE_ID_DEVICEINFO
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
typedef struct bleproto_deviceinfo_req {
|
||||
uint64_t t; ///< utcms时间戳
|
||||
uint64_t t; ///< Unix 时间戳,单位秒。网关从子设备连上起计时,每 12 小时同步一次
|
||||
int gmtoff; ///< 时区偏移,单位秒,例如东八区(8x60x60=28800秒)
|
||||
} bleproto_deviceinfo_req_t;
|
||||
|
||||
|
||||
@@ -99,7 +99,7 @@ void usr_write_pair_info()
|
||||
*/
|
||||
void usr_clear_pair_info()
|
||||
{
|
||||
printf("usr_clear_pair_info\n");
|
||||
printf("usr_clear_pair_info_reset\n");
|
||||
memset(&usr_auth_data,0x00,sizeof(usr_auth_data));
|
||||
usr_auth_data.paired_flag=0; /* 回到未配网 */
|
||||
syscfg_write(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data));
|
||||
@@ -112,7 +112,7 @@ void usr_clear_pair_info()
|
||||
*/
|
||||
void usr_clear_pair_info_noreset()
|
||||
{
|
||||
printf("usr_clear_pair_info2\n");
|
||||
printf("usr_clear_pair_info_noreset\n");
|
||||
memset(&usr_auth_data,0x00,sizeof(usr_auth_data));
|
||||
usr_auth_data.paired_flag=0; /* 回到未配网,后续由 usr_goto_pair 决定是否绑广播 */
|
||||
syscfg_write(CFG_USER_PAIR_INFO, (u8 *)&usr_auth_data, sizeof(usr_auth_data));
|
||||
@@ -138,6 +138,18 @@ void usr_goto_pair_mode()
|
||||
usr_ble_adv_init();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 断开 BLE 并停止广播(模组复位/重启前)
|
||||
*/
|
||||
void usr_ble_disconnect_and_stop_adv(void)
|
||||
{
|
||||
printf("usr_ble_disconnect_and_stop_adv\n");
|
||||
ble_multi_trans_disconnect();
|
||||
usr_var.usr_goto_pair = 0;
|
||||
usr_var.usr_adv_flag = 0;
|
||||
ble_trans_module_enable(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 按当前标志刷新广播策略(不一定开广播)
|
||||
* @note 见文件头「三个标志」说明;未拿身份 / 未配网时保持不广播
|
||||
@@ -199,14 +211,6 @@ void usr_ble_adv_init()
|
||||
if(le_timer_handle==0)le_timer_handle = sys_timer_add(NULL, usr_ble_adv_updata, 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取是否已配网
|
||||
* @return 1=已配网(paired_flag),0=未配网
|
||||
*/
|
||||
/**
|
||||
* @brief 获取是否已配网
|
||||
* @return 1=已配网(paired_flag),0=未配网
|
||||
*/
|
||||
/**
|
||||
* @brief 获取是否已配网
|
||||
* @return paired_flag:1=已配网,0=未配网
|
||||
@@ -239,8 +243,9 @@ void usr_ble_adv_updata()
|
||||
static u16 usr_goto_slow_adv=0;
|
||||
if(usr_var.usr_adv_flag)
|
||||
{
|
||||
/* usr_adv_flag=1:把 muti_adv_data 刷进协议栈并开广播,用完即清 */
|
||||
ble_trans_module_enable(0);
|
||||
usr_var.usr_adv_flag=0; /* 一次性触发,用完即清 */
|
||||
usr_var.usr_adv_flag=0;
|
||||
muti_make_set_adv_data(muti_adv_data,muti_adv_data_len);
|
||||
printf("###muti_adv_data = ");
|
||||
printf_buf(muti_adv_data,muti_adv_data_len);
|
||||
@@ -249,10 +254,12 @@ void usr_ble_adv_updata()
|
||||
|
||||
if(usr_get_conn_service()==0)
|
||||
{
|
||||
/* 未连接:回连快广播(80)持续约 60s(500ms×120)后切慢广播 */
|
||||
if(usr_get_adv_interval()==80)
|
||||
{
|
||||
if((usr_var.usr_goto_pair==0)&&(usr_auth_data.paired_flag))
|
||||
{
|
||||
/* 已配网回连,且不是绑广播、不是 OTA 成功态,才开始计时 */
|
||||
if(usr_var.usr_ota_succ_flag==0)
|
||||
{
|
||||
usr_goto_slow_adv++;
|
||||
@@ -262,23 +269,24 @@ void usr_ble_adv_updata()
|
||||
{
|
||||
usr_goto_slow_adv=0;
|
||||
usr_set_adv_interval(80*10);
|
||||
usr_var.usr_adv_flag=1;
|
||||
usr_var.usr_adv_flag=1; /* 下一拍刷新成慢间隔 */
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
usr_goto_slow_adv=0;
|
||||
usr_goto_slow_adv=0; /* 已经是慢广播,计数清掉 */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
usr_goto_slow_adv=0;
|
||||
usr_goto_slow_adv=0; /* 已连接,不需要慢广播计时 */
|
||||
}
|
||||
|
||||
|
||||
if(usr_get_ota_status()==0)
|
||||
{
|
||||
/* 不在 OTA:未连接则退出 OTA 残留(ota_succ 时不退) */
|
||||
if(usr_get_conn_service()==0)
|
||||
{
|
||||
if(usr_var.usr_ota_succ_flag==0)usr_ota_exit();
|
||||
@@ -312,7 +320,7 @@ void usr_ota_process()
|
||||
static u8 cnt = 0;
|
||||
if (cnt == 0)
|
||||
{
|
||||
ble_multi_trans_disconnect();
|
||||
ble_multi_trans_disconnect(); /* 第一次:先断开,好换 OTA 广播地址 */
|
||||
}
|
||||
|
||||
if (usr_get_conn_service() == 0)
|
||||
@@ -320,17 +328,17 @@ void usr_ota_process()
|
||||
cnt++;
|
||||
if (cnt == 2)
|
||||
{
|
||||
usr_ble_adv_updata_OTA();
|
||||
usr_ble_adv_updata_OTA(); /* 断连后第二拍:改 MAC 并发 OTA 广播 */
|
||||
}
|
||||
if (cnt >= 60)
|
||||
{
|
||||
cnt = 0;
|
||||
cpu_reset();
|
||||
cpu_reset(); /* 约 60s 仍没连上 OTA,复位退出 */
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cnt = 1;
|
||||
cnt = 1; /* 已连上:保持断开过的状态,不再累加复位计时 */
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
* @file usr_le_api.h
|
||||
* @brief usr_le_code 对外 API(广播 / 收发 / 配网 / MCU 帧转发)
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.06
|
||||
* @version V1.1.0
|
||||
* @date 2026.08.13
|
||||
* @version V1.2.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.03, cyWu, 首次发布
|
||||
* - V1.1.0, 2026.08.06, cyWu, 增加 MCU 帧 BLE 发送接口,解耦模组桥
|
||||
* - V1.2.0, 2026.08.13, cyWu, P1:复位前断连停广播;0x41 走 cmd100 上报
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __USR_LE_API_H__
|
||||
@@ -39,9 +40,10 @@ void usr_le_data_recieve(uint8_t *data, uint16_t len);
|
||||
* @brief 将 MCU 协议 55AA 帧封装为 bleproto 后经 GATT 发送
|
||||
* @param data 完整 55AA 帧
|
||||
* @param len 帧长度
|
||||
* @note 0x07/0x34/0x38 走 REPORTCMD_V2 主动上报;其余走 RUNCMD_V2 应答
|
||||
* @note 0x07/0x34/0x38/0x41 走 REPORTCMD_V2 主动上报;其余走 RUNCMD_V2 应答
|
||||
* @note 主机未写 CCC 时不会发出,返回 0
|
||||
*/
|
||||
void usr_le_send_mcu_frame(uint8_t *data, uint16_t len);
|
||||
uint8_t usr_le_send_mcu_frame(uint8_t *data, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief 进入配对/配网模式
|
||||
@@ -49,6 +51,11 @@ void usr_le_send_mcu_frame(uint8_t *data, uint16_t len);
|
||||
*/
|
||||
void usr_goto_pair_mode(void);
|
||||
|
||||
/**
|
||||
* @brief 断开 BLE 连接并停止广播(复位/重启前调用)
|
||||
*/
|
||||
void usr_ble_disconnect_and_stop_adv(void);
|
||||
|
||||
/**
|
||||
* @brief 清除配对信息但不复位(paired_flag→0)
|
||||
*/
|
||||
@@ -65,6 +72,12 @@ void usr_clear_pair_info(void);
|
||||
*/
|
||||
uint8_t usr_get_pair_flag(void);
|
||||
|
||||
/**
|
||||
* @brief GATT 是否已允许发数(主机已写 CCC)
|
||||
* @return 1=可 notify/indicate,0=刚连上还不能发
|
||||
*/
|
||||
uint8_t usr_ble_is_notify_ready(void);
|
||||
|
||||
/**
|
||||
* @brief 启动应用侧 OTA 流程
|
||||
*/
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "bleproto_api.h"
|
||||
#include "ble_proto.h"
|
||||
#include "mcu_frame.h"
|
||||
#include "usr_le_api.h"
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[APP]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
@@ -50,6 +51,7 @@ extern void mcu_bridge_on_app_frame(const uint8_t *data, uint16_t len);
|
||||
extern void mcu_bridge_on_gateway_report_ack(uint16_t cmd,
|
||||
uint8_t gateway_int_cnt,
|
||||
int32_t gateway_int0);
|
||||
extern void mcu_bridge_on_network_time(uint64_t unix_s, int32_t gmtoff_s);
|
||||
|
||||
void usr_ota_deal_task();
|
||||
void usr_data_ana(bleproto_appdata_desc_t *desc);
|
||||
@@ -113,6 +115,12 @@ void usr_data_ana(bleproto_appdata_desc_t *desc)
|
||||
|
||||
case E_BLEPROTO_SERVICE_ID_DEVICEINFO:
|
||||
|
||||
{
|
||||
bleproto_deviceinfo_req_t *req = &desc->datast.deviceinfo_req;
|
||||
/* 网关从子设备连上开始计时,每 12 小时经 DEVICEINFO 同步一次时间戳+时区 */
|
||||
mcu_bridge_on_network_time(req->t, req->gmtoff);
|
||||
}
|
||||
|
||||
s_encode_desc.header.version = 0;
|
||||
s_encode_desc.header.datafmt = BLEPROTO_APPDATA_DATAFMT_JSON;
|
||||
s_encode_desc.header.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP;
|
||||
@@ -479,11 +487,12 @@ void usr_run_cmd_code_v2(ble_proto_packet_t *runcmd_par, u8 usr_msg_id)
|
||||
* @brief 将 MCU 协议帧封装为 bleproto V2 后通过 BLE 发送
|
||||
* @param data 55AA 完整帧
|
||||
* @param len 帧长度
|
||||
* @note 主动上报(0x07/0x34/0x38) → REPORTCMD_V2;其余 → RUNCMD_V2 应答
|
||||
* @note 主动上报(0x07/0x34/0x38/0x41) → REPORTCMD_V2;其余 → RUNCMD_V2 应答
|
||||
* @note 轻量路径:栈上仅 ble_proto_packet_t + 小 v2 缓冲,
|
||||
* 不走 bleproto_appdata_desc_t / bleproto_appdata_encode(约 2.3KB×2)
|
||||
* @return 1=已交给协议栈,0=参数非法/超长/CCC 未就绪/组包失败
|
||||
*/
|
||||
void usr_le_send_mcu_frame(u8 *data, u16 len)
|
||||
uint8_t usr_le_send_mcu_frame(u8 *data, u16 len)
|
||||
{
|
||||
ble_proto_packet_t pkt;
|
||||
bleproto_appdata_header_t hdr;
|
||||
@@ -497,16 +506,26 @@ void usr_le_send_mcu_frame(u8 *data, u16 len)
|
||||
if (!data || len < 5)
|
||||
{
|
||||
printf("usr_le_send_mcu_frame invalid param, len=%d\n", len);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
if (len > MCU_FRAME_MAX_LEN)
|
||||
{
|
||||
printf("usr_le_send_mcu_frame frame too long %u\n", len);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
/* 已配网/已连接不等于能发:主机写完 CCC 才允许 notify */
|
||||
if (!usr_ble_is_notify_ready())
|
||||
{
|
||||
printf("usr_le_send_mcu_frame wait CCC cmd=0x%02X\n", data[4]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
mcu_cmd = data[4];
|
||||
is_active_send = (mcu_cmd == 0x07) || (mcu_cmd == 0x34) || (mcu_cmd == 0x38);
|
||||
/* 主动上报走 REPORTCMD_V2(cmd100),应答走 RUNCMD_V2(cmd110) */
|
||||
is_active_send = (mcu_cmd == MCU_CMD_REPORT) ||
|
||||
(mcu_cmd == MCU_CMD_OTA_COMPLETE) ||
|
||||
(mcu_cmd == MCU_CMD_OTA_STOP_MCU) ||
|
||||
(mcu_cmd == MCU_CMD_GET_MCU_INFO_ACK);
|
||||
|
||||
memset(&pkt, 0, sizeof(pkt));
|
||||
memset(&hdr, 0, sizeof(hdr));
|
||||
@@ -517,6 +536,7 @@ void usr_le_send_mcu_frame(u8 *data, u16 len)
|
||||
|
||||
if (is_active_send)
|
||||
{
|
||||
/* 设备→网关主动上报,等网关 ACK,不带 runcmd msgid */
|
||||
hdr.msgtype = BLEPROTO_APPDATA_MSGTYPE_REQ;
|
||||
hdr.msgid = 0;
|
||||
hdr.serviceid = E_BLEPROTO_SERVICE_ID_REPORTCMD_V2;
|
||||
@@ -526,6 +546,7 @@ void usr_le_send_mcu_frame(u8 *data, u16 len)
|
||||
}
|
||||
else
|
||||
{
|
||||
/* APP 下发的应答:msgid 必须对上最近一次 RUNCMD,网关才认 */
|
||||
hdr.msgtype = BLEPROTO_APPDATA_MSGTYPE_RSP;
|
||||
hdr.msgid = s_last_runcmd_msgid;
|
||||
hdr.serviceid = E_BLEPROTO_SERVICE_ID_RUNCMD_V2;
|
||||
@@ -543,16 +564,18 @@ void usr_le_send_mcu_frame(u8 *data, u16 len)
|
||||
if (plen <= 0)
|
||||
{
|
||||
printf("usr_le_send_mcu_frame pack fail %d\n", plen);
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
elen = bleproto_appdata_wrap_raw(txbuf, sizeof(txbuf), &hdr, v2_buf, (uint16_t)plen);
|
||||
printf("usr_le_send_mcu_frame cmd=0x%02X active=%d encode_len=%d msgid=%d\n",
|
||||
mcu_cmd, is_active_send, elen, hdr.msgid);
|
||||
if (elen > 0)
|
||||
if (elen <= 0)
|
||||
{
|
||||
usr_ble_send_data(txbuf, elen);
|
||||
printf("send data:");
|
||||
printf_buf(txbuf, elen);
|
||||
return 0;
|
||||
}
|
||||
usr_ble_send_data(txbuf, elen);
|
||||
printf("send data:");
|
||||
printf_buf(txbuf, elen);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -2,22 +2,26 @@
|
||||
* @file mcu_business.c
|
||||
* @brief 业务门面实现
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V1.0.0
|
||||
* @date 2026.08.13
|
||||
* @version V1.1.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.12, cyWu, 取代原 mcu_link,编排三个业务子模块
|
||||
* - V1.1.0, 2026.08.13, cyWu, 编排 P1 管理/对时/版本
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "app_main.h"
|
||||
#include "mcu_business.h"
|
||||
#include "mcu_identity.h"
|
||||
#include "mcu_pairing.h"
|
||||
#include "mcu_link_status.h"
|
||||
#include "mcu_cmd.h"
|
||||
#include "mcu_identity.h"
|
||||
#include "mcu_link_status.h"
|
||||
#include "mcu_mgmt.h"
|
||||
#include "mcu_pairing.h"
|
||||
#include "mcu_time.h"
|
||||
#include "mcu_version.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_BIZ]"
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_BIZ]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
@@ -32,6 +36,9 @@ void mcu_business_init(void)
|
||||
mcu_identity_init();
|
||||
mcu_pairing_init();
|
||||
mcu_link_status_init();
|
||||
mcu_mgmt_init();
|
||||
mcu_time_init();
|
||||
mcu_version_init();
|
||||
|
||||
usr_var.usr_goto_pair = 0;
|
||||
usr_var.usr_adv_flag = 0;
|
||||
@@ -45,6 +52,9 @@ void mcu_business_periodic(void)
|
||||
mcu_identity_periodic();
|
||||
mcu_link_status_periodic();
|
||||
mcu_pairing_periodic();
|
||||
mcu_mgmt_periodic();
|
||||
mcu_time_periodic();
|
||||
mcu_version_periodic();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -92,8 +102,10 @@ void mcu_business_on_paired(void)
|
||||
{
|
||||
usr_var.usr_goto_pair = 0;
|
||||
mcu_pairing_stop();
|
||||
mcu_link_status_set_phase(MCU_WS_CONFIGURED);
|
||||
log_info("paired -> CONFIGURED");
|
||||
/* 三元组已写入 VM,但 GATT 未必可发。退出 PAIRING,
|
||||
* CONFIGURED 交给 mcu_link_status_periodic:连接+CCC 才置位。 */
|
||||
mcu_link_status_set_phase(MCU_WS_RECONNECTING);
|
||||
log_info("paired, wait link up");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -103,7 +115,76 @@ void mcu_business_on_paired(void)
|
||||
*/
|
||||
void mcu_business_on_pair_cleared(void)
|
||||
{
|
||||
if (mcu_link_status_get_phase() != MCU_WS_PAIRING) {
|
||||
/* 正在走 0x10 配网:清配对表是配网流程的一部分,过程态保持 PAIRING。
|
||||
* 其它情况(单纯清空配对)才回到未配网空闲。 */
|
||||
if (mcu_link_status_get_phase() != MCU_WS_PAIRING)
|
||||
{
|
||||
mcu_link_status_set_phase(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 恢复出厂
|
||||
*/
|
||||
void mcu_business_on_factory_reset(void)
|
||||
{
|
||||
mcu_mgmt_on_factory_reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 模组重启
|
||||
*/
|
||||
void mcu_business_on_mod_reboot(void)
|
||||
{
|
||||
mcu_mgmt_on_mod_reboot();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 0x1B
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
*/
|
||||
void mcu_business_on_mcu_reboot_ack(const uint8_t *frame, uint16_t len)
|
||||
{
|
||||
mcu_mgmt_on_mcu_reboot_ack(frame, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 组 0x1D
|
||||
* @param payload 输出
|
||||
* @param plen 长度
|
||||
*/
|
||||
void mcu_business_build_time_ack(uint8_t *payload, uint16_t *plen)
|
||||
{
|
||||
mcu_time_build_ack(payload, plen);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 网络时间
|
||||
* @param unix_s UTC Unix 时间戳(秒)
|
||||
* @param gmtoff_s 时区偏移
|
||||
*/
|
||||
void mcu_business_on_network_time(uint64_t unix_s, int32_t gmtoff_s)
|
||||
{
|
||||
mcu_time_set_from_gateway(unix_s, gmtoff_s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UART 0x41
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
*/
|
||||
void mcu_business_on_mcu_info_ack(const uint8_t *frame, uint16_t len)
|
||||
{
|
||||
mcu_version_on_mcu_info_ack(frame, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 网关 ACK
|
||||
* @param success 成功
|
||||
* @param mcu_report_consumed MCU 0x07 已消费
|
||||
*/
|
||||
void mcu_business_on_gateway_ack(uint8_t success, uint8_t mcu_report_consumed)
|
||||
{
|
||||
mcu_version_on_gateway_ack(success, mcu_report_consumed);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_business.h
|
||||
* @brief 业务门面:协调身份 / 配网 / 连接状态三个业务子模块
|
||||
* @brief 业务门面:协调身份 / 配网 / 连接 / 管理 / 对时 / 版本
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V1.0.0
|
||||
* @date 2026.08.13
|
||||
* @version V1.1.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.12, cyWu, 取代原 mcu_link,供协议层(mcu_protocol)
|
||||
* 单点调用;协议层不直接依赖 identity/pairing/link_status 三个子模块
|
||||
* - V1.1.0, 2026.08.13, cyWu, 接入 P1 管理/对时/版本子模块
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MCU_BUSINESS_H__
|
||||
@@ -20,16 +21,19 @@
|
||||
/* mcu_identity.c —— 身份获取 */
|
||||
/* mcu_pairing.c —— 配网请求 / 超时 */
|
||||
/* mcu_link_status.c —— 连接状态位图(Bit0/1/2) */
|
||||
/* mcu_mgmt.c —— 出厂复位 / 重启 / 通知 MCU 重启 */
|
||||
/* mcu_time.c —— 对时(写 RTC / 0x1D 读回) */
|
||||
/* mcu_version.c —— UART 0x40/0x41 与 SOC 0x41 上报 */
|
||||
/* 须在协议任务(mcu_protocol_task)单一上下文中调用。 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 初始化业务层(身份 + 配网 + 连接状态)
|
||||
* @brief 初始化业务层
|
||||
*/
|
||||
void mcu_business_init(void);
|
||||
|
||||
/**
|
||||
* @brief 周期处理:依次驱动身份 / 连接状态 / 配网三个子业务
|
||||
* @brief 周期处理:依次驱动各子业务
|
||||
*/
|
||||
void mcu_business_periodic(void);
|
||||
|
||||
@@ -69,4 +73,50 @@ void mcu_business_on_paired(void);
|
||||
*/
|
||||
void mcu_business_on_pair_cleared(void);
|
||||
|
||||
/**
|
||||
* @brief MCU 请求恢复出厂(协议层已 ACK)
|
||||
*/
|
||||
void mcu_business_on_factory_reset(void);
|
||||
|
||||
/**
|
||||
* @brief MCU 请求模组重启(协议层已 ACK)
|
||||
*/
|
||||
void mcu_business_on_mod_reboot(void);
|
||||
|
||||
/**
|
||||
* @brief 处理 UART 0x1B
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
*/
|
||||
void mcu_business_on_mcu_reboot_ack(const uint8_t *frame, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief 组 0x1D 载荷
|
||||
* @param payload 输出缓冲
|
||||
* @param plen 输出长度
|
||||
*/
|
||||
void mcu_business_build_time_ack(uint8_t *payload, uint16_t *plen);
|
||||
|
||||
/**
|
||||
* @brief 写入网关网络时间
|
||||
* @param unix_s UTC Unix 时间戳(秒)
|
||||
* @param gmtoff_s 时区偏移秒
|
||||
* @note 网关从子设备连上开始计时,每 12 小时同步一次
|
||||
*/
|
||||
void mcu_business_on_network_time(uint64_t unix_s, int32_t gmtoff_s);
|
||||
|
||||
/**
|
||||
* @brief 处理 UART 0x41(仅本地解析)
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
*/
|
||||
void mcu_business_on_mcu_info_ack(const uint8_t *frame, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief 网关 REPORTCMD_V2 应答
|
||||
* @param success 1=成功
|
||||
* @param mcu_report_consumed 1=已按 MCU 0x07 回了 0x08
|
||||
*/
|
||||
void mcu_business_on_gateway_ack(uint8_t success, uint8_t mcu_report_consumed);
|
||||
|
||||
#endif /* __MCU_BUSINESS_H__ */
|
||||
|
||||
@@ -10,14 +10,14 @@
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "mcu_identity.h"
|
||||
#include "mcu_protocol.h"
|
||||
#include "mcu_cmd.h"
|
||||
#include "mcu_protocol.h"
|
||||
#include "mcu_util.h"
|
||||
#include "usr_le_api.h"
|
||||
#include "usr_le_product.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_ID]"
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_ID]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
@@ -27,8 +27,8 @@
|
||||
/* 私有宏 */
|
||||
/*============================================================================*/
|
||||
|
||||
#define MCU_IDENTITY_RETRY_MS (1000u)
|
||||
#define MCU_IDENTITY_PAYLOAD_MIN (2 + 8 + 8 + 16 + 8) /* Ver+Cat+Prod+SN+Cap */
|
||||
#define MCU_IDENTITY_RETRY_MS (1000u)
|
||||
#define MCU_IDENTITY_PAYLOAD_MIN (2 + 8 + 8 + 16 + 8) /* Ver+Cat+Prod+SN+Cap */
|
||||
|
||||
/*============================================================================*/
|
||||
/* 公有变量:运行时可改写的产品身份(供 BLE 广播使用) */
|
||||
@@ -42,8 +42,8 @@ uint8_t g_usr_le_pcode[USR_LE_PCODE_LEN];
|
||||
/*============================================================================*/
|
||||
|
||||
typedef struct {
|
||||
uint8_t ready;
|
||||
uint32_t last_tx_ms;
|
||||
uint8_t ready; /* 1=已解析 0x02,广播可用产品码 */
|
||||
uint32_t last_tx_ms; /* 最近一次发 0x01 的时刻 */
|
||||
} McuIdentity_t;
|
||||
|
||||
static McuIdentity_t s_identity;
|
||||
@@ -69,17 +69,22 @@ void mcu_identity_periodic(void)
|
||||
{
|
||||
uint32_t now;
|
||||
|
||||
if (s_identity.ready) {
|
||||
/* 已经拿到身份,不再发 0x01 */
|
||||
if (s_identity.ready)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
now = mcu_util_get_ms();
|
||||
if ((now - s_identity.last_tx_ms) < MCU_IDENTITY_RETRY_MS) {
|
||||
/* 未就绪则每 1s 问一次 MCU,无限轮询,没有次数上限 */
|
||||
if ((now - s_identity.last_tx_ms) < MCU_IDENTITY_RETRY_MS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
s_identity.last_tx_ms = now;
|
||||
|
||||
if (mcu_protocol_send_cmd(MCU_CMD_GET_IDENTITY, NULL, 0) > 0) {
|
||||
if (mcu_protocol_send_cmd(MCU_CMD_GET_IDENTITY, NULL, 0) > 0)
|
||||
{
|
||||
log_debug("identity req");
|
||||
}
|
||||
}
|
||||
@@ -103,14 +108,19 @@ void mcu_identity_on_ack(const uint8_t *frame, uint16_t len)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!frame || len < 7) {
|
||||
if (!frame || len < 7)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (frame[6] != MCU_RESULT_OK) {
|
||||
/* frame[6]=Result,MCU 拒绝则继续 periodic 里每秒重问 */
|
||||
if (frame[6] != MCU_RESULT_OK)
|
||||
{
|
||||
log_error("identity ack result=0x%02X", frame[6]);
|
||||
return;
|
||||
}
|
||||
if (len < (7 + MCU_IDENTITY_PAYLOAD_MIN + 1)) {
|
||||
/* 7 字节头 + 身份载荷 + 1 字节校验 */
|
||||
if (len < (7 + MCU_IDENTITY_PAYLOAD_MIN + 1))
|
||||
{
|
||||
log_error("identity ack too short %u", len);
|
||||
return;
|
||||
}
|
||||
@@ -123,12 +133,14 @@ void mcu_identity_on_ack(const uint8_t *frame, uint16_t len)
|
||||
s_identity.ready = 1;
|
||||
|
||||
printf("identity ok prodcode:");
|
||||
for (i = 0; i < USR_LE_PRODCODE_LEN; i++) {
|
||||
for (i = 0; i < USR_LE_PRODCODE_LEN; i++)
|
||||
{
|
||||
printf("%c", g_usr_le_prodcode[i]);
|
||||
}
|
||||
printf("\n");
|
||||
printf("identity ok pcode:");
|
||||
for (i = 0; i < USR_LE_PCODE_LEN; i++) {
|
||||
for (i = 0; i < USR_LE_PCODE_LEN; i++)
|
||||
{
|
||||
printf("%c", g_usr_le_pcode[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
@@ -11,12 +11,12 @@
|
||||
#include "system/includes.h"
|
||||
#include "app_main.h"
|
||||
#include "mcu_link_status.h"
|
||||
#include "mcu_identity.h"
|
||||
#include "mcu_cmd.h"
|
||||
#include "mcu_identity.h"
|
||||
#include "usr_le_api.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_LNK]"
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_LNK]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
@@ -33,8 +33,8 @@ extern u16 usr_get_conn_service(void);
|
||||
/*============================================================================*/
|
||||
|
||||
typedef struct {
|
||||
uint8_t work_status;
|
||||
uint8_t last_ble_conn;
|
||||
uint8_t work_status; /* 0x14 用的整字节:Bit0/1/2 过程态 + Bit6 OTA */
|
||||
uint8_t last_ble_conn; /* 上一拍 BLE 是否已连接,用来检测边沿 */
|
||||
} McuLinkStatus_t;
|
||||
|
||||
static McuLinkStatus_t s_status;
|
||||
@@ -76,7 +76,8 @@ uint8_t mcu_link_status_get_phase(void)
|
||||
void mcu_link_status_set_phase(uint8_t phase)
|
||||
{
|
||||
s_status.work_status &= (uint8_t)~MCU_WS_LINK_MASK;
|
||||
if (phase & MCU_WS_LINK_MASK) {
|
||||
if (phase & MCU_WS_LINK_MASK)
|
||||
{
|
||||
s_status.work_status |= (uint8_t)(phase & MCU_WS_LINK_MASK);
|
||||
}
|
||||
}
|
||||
@@ -87,9 +88,12 @@ void mcu_link_status_set_phase(uint8_t phase)
|
||||
*/
|
||||
void mcu_link_status_set_ota(uint8_t on)
|
||||
{
|
||||
if (on) {
|
||||
if (on)
|
||||
{
|
||||
s_status.work_status |= MCU_WS_OTA_UPGRADING;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
s_status.work_status &= (uint8_t)~MCU_WS_OTA_UPGRADING;
|
||||
}
|
||||
}
|
||||
@@ -97,33 +101,46 @@ void mcu_link_status_set_ota(uint8_t on)
|
||||
/**
|
||||
* @brief 周期处理:轮询 BLE 连接,维护 Bit0/1/2,已配网断连时开回连广播
|
||||
* @note - 配网中(phase==PAIRING)由配网业务维护,此处不覆盖
|
||||
* - 已配网:已连接→CONFIGURED,未连接→RECONNECTING
|
||||
* - VM 已绑定:链路可发数(连接+CCC)→CONFIGURED,否则→RECONNECTING
|
||||
* - Bit3~5 预留固定 0,不在此处更新
|
||||
*/
|
||||
void mcu_link_status_periodic(void)
|
||||
{
|
||||
uint8_t conn = (usr_get_conn_service() != 0) ? 1 : 0;
|
||||
uint8_t paired = usr_get_pair_flag();
|
||||
uint8_t conn = (usr_get_conn_service() != 0) ? 1 : 0; /* 1=BLE 已连接 */
|
||||
uint8_t paired = usr_get_pair_flag(); /* 1=VM 已绑定三元组 */
|
||||
uint8_t link_up;
|
||||
|
||||
if (conn != s_status.last_ble_conn) {
|
||||
/* 只在连接状态发生变化时处理广播,避免每拍都重开广播 */
|
||||
if (conn != s_status.last_ble_conn)
|
||||
{
|
||||
s_status.last_ble_conn = conn;
|
||||
|
||||
if (!conn && paired) {
|
||||
/* 已配网断连:发回连广播(非绑定广播) */
|
||||
if (!conn && paired)
|
||||
{
|
||||
/* 已绑定后断连:发回连广播(usr_goto_pair=0 → 非绑定广播) */
|
||||
usr_var.usr_goto_pair = 0;
|
||||
if (mcu_identity_is_ready()) {
|
||||
if (mcu_identity_is_ready())
|
||||
{
|
||||
usr_ble_adv_init();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (mcu_link_status_get_phase() == MCU_WS_PAIRING) {
|
||||
return; /* 配网流程中不切换到回连/已配网 */
|
||||
/* 配网中由 pairing 模块维护过程态,这里不要改成回连/已配网 */
|
||||
if (mcu_link_status_get_phase() == MCU_WS_PAIRING)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (paired) {
|
||||
mcu_link_status_set_phase(conn ? MCU_WS_CONFIGURED : MCU_WS_RECONNECTING);
|
||||
} else {
|
||||
mcu_link_status_set_phase(0); /* 未配网空闲 */
|
||||
/* VM 已绑定 ≠ 已配网可用。MCU 看到 CONFIGURED 才会上报 0x07,
|
||||
* 必须等 GATT CCC 写完,整条链路才能发到网关。 */
|
||||
link_up = (uint8_t)(paired && conn && usr_ble_is_notify_ready());
|
||||
if (paired)
|
||||
{
|
||||
mcu_link_status_set_phase(link_up ? MCU_WS_CONFIGURED : MCU_WS_RECONNECTING);
|
||||
}
|
||||
else
|
||||
{
|
||||
mcu_link_status_set_phase(0); /* 未绑定空闲,Bit0/1/2 全 0 */
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,8 +19,9 @@
|
||||
void mcu_link_status_init(void);
|
||||
|
||||
/**
|
||||
* @brief 周期处理:轮询 BLE 连接,维护 Bit0/1/2,已配网断连时触发回连广播
|
||||
* @note 配网中(phase==PAIRING)时不会被本函数覆盖
|
||||
* @brief 周期处理:轮询 BLE 连接/CCC,维护 Bit0/1/2,已绑定断连时触发回连广播
|
||||
* @note 配网中(phase==PAIRING)时不会被本函数覆盖;
|
||||
* CONFIGURED 要求 VM 已绑定 + BLE 已连接 + CCC 已写
|
||||
*/
|
||||
void mcu_link_status_periodic(void);
|
||||
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_mgmt.c
|
||||
* @brief 模组管理业务实现
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.13
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.13, cyWu, 首次发布
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "asm/cpu.h"
|
||||
#include "mcu_mgmt.h"
|
||||
#include "mcu_cmd.h"
|
||||
#include "mcu_protocol.h"
|
||||
#include "mcu_util.h"
|
||||
#include "usr_le_api.h"
|
||||
#include "usr_rtc.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_MGMT]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有宏 */
|
||||
/*============================================================================*/
|
||||
|
||||
#define MCU_MGMT_RESET_DELAY_MS (600u)
|
||||
#define MCU_MGMT_ACK_WAIT_MS (500u)
|
||||
#define MCU_MGMT_MAX_RETRY (3u)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MCU_MGMT_RESET_NONE = 0, /* 无待执行复位 */
|
||||
MCU_MGMT_RESET_FACTORY, /* 恢复出厂后复位 */
|
||||
MCU_MGMT_RESET_REBOOT /* 普通重启 */
|
||||
} McuMgmtResetKind_t;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有类型/变量 */
|
||||
/*============================================================================*/
|
||||
|
||||
typedef struct {
|
||||
McuMgmtResetKind_t reset_kind; /* 延迟复位类型,NONE 表示没有 */
|
||||
uint32_t reset_due_ms; /* 到点执行 cpu_reset */
|
||||
uint8_t notify_waiting; /* 1=已发 0x1A,等 MCU 0x1B */
|
||||
uint8_t notify_retry; /* 0x1A 已发送次数(含首次) */
|
||||
uint32_t notify_tx_ms; /* 最近一次 0x1A 发出时刻 */
|
||||
} McuMgmt_t;
|
||||
|
||||
static McuMgmt_t s_mgmt;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有函数 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 启动延迟复位
|
||||
* @param kind 复位类型
|
||||
*/
|
||||
static void mcu_mgmt_arm_reset(McuMgmtResetKind_t kind)
|
||||
{
|
||||
s_mgmt.reset_kind = kind;
|
||||
s_mgmt.reset_due_ms = mcu_util_get_ms() + MCU_MGMT_RESET_DELAY_MS;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* 公有函数 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 初始化模组管理业务
|
||||
*/
|
||||
void mcu_mgmt_init(void)
|
||||
{
|
||||
memset(&s_mgmt, 0, sizeof(s_mgmt));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 周期处理:0x1A 重试、延迟复位
|
||||
*/
|
||||
void mcu_mgmt_periodic(void)
|
||||
{
|
||||
uint32_t now = mcu_util_get_ms();
|
||||
|
||||
/* 已安排延迟复位:到点 cpu_reset,期间不再发 0x1A */
|
||||
if (s_mgmt.reset_kind != MCU_MGMT_RESET_NONE)
|
||||
{
|
||||
if ((int32_t)(now - s_mgmt.reset_due_ms) >= 0)
|
||||
{
|
||||
log_info("cpu_reset kind=%u", (unsigned)s_mgmt.reset_kind);
|
||||
cpu_reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* 没在等 0x1B,无事可做 */
|
||||
if (!s_mgmt.notify_waiting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* 500ms 窗口内继续等 MCU ACK */
|
||||
if ((now - s_mgmt.notify_tx_ms) <= MCU_MGMT_ACK_WAIT_MS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (s_mgmt.notify_retry >= MCU_MGMT_MAX_RETRY)
|
||||
{
|
||||
log_error("notify MCU reboot give up");
|
||||
s_mgmt.notify_waiting = 0;
|
||||
return;
|
||||
}
|
||||
/* UART 被心跳/0x14 占用则下周期再试 */
|
||||
if (mcu_protocol_is_ble_pending())
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (mcu_protocol_send_cmd_wait_ack(MCU_CMD_MCU_REBOOT, NULL, 0,
|
||||
MCU_CMD_MCU_REBOOT_ACK) > 0)
|
||||
{
|
||||
s_mgmt.notify_retry++;
|
||||
s_mgmt.notify_tx_ms = now;
|
||||
log_debug("notify MCU reboot retry %u", s_mgmt.notify_retry);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 恢复出厂:清配对后延迟复位
|
||||
*/
|
||||
void mcu_mgmt_on_factory_reset(void)
|
||||
{
|
||||
log_info("factory reset");
|
||||
usr_ble_disconnect_and_stop_adv();
|
||||
usr_clear_pair_info_noreset();
|
||||
usr_rtc_clear_tz(); /* 时区是上次配网对时留下的,出厂一并清掉 */
|
||||
mcu_mgmt_arm_reset(MCU_MGMT_RESET_FACTORY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 模组重启:保留配对后延迟复位
|
||||
*/
|
||||
void mcu_mgmt_on_mod_reboot(void)
|
||||
{
|
||||
log_info("mod reboot");
|
||||
usr_ble_disconnect_and_stop_adv();
|
||||
mcu_mgmt_arm_reset(MCU_MGMT_RESET_REBOOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送 0x1A 通知 MCU 重启
|
||||
*/
|
||||
void mcu_mgmt_notify_mcu_reboot(void)
|
||||
{
|
||||
/* 已经在等 0x1B,或马上要复位,不要重复发 0x1A */
|
||||
if (s_mgmt.notify_waiting || s_mgmt.reset_kind != MCU_MGMT_RESET_NONE)
|
||||
{
|
||||
log_error("notify MCU reboot busy");
|
||||
return;
|
||||
}
|
||||
if (mcu_protocol_is_ble_pending())
|
||||
{
|
||||
log_error("notify MCU reboot uart busy");
|
||||
return;
|
||||
}
|
||||
if (mcu_protocol_send_cmd_wait_ack(MCU_CMD_MCU_REBOOT, NULL, 0,
|
||||
MCU_CMD_MCU_REBOOT_ACK) <= 0)
|
||||
{
|
||||
log_error("notify MCU reboot tx fail");
|
||||
return;
|
||||
}
|
||||
s_mgmt.notify_waiting = 1;
|
||||
s_mgmt.notify_retry = 1;
|
||||
s_mgmt.notify_tx_ms = mcu_util_get_ms();
|
||||
log_info("notify MCU reboot tx");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理 0x1B
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
*/
|
||||
void mcu_mgmt_on_mcu_reboot_ack(const uint8_t *frame, uint16_t len)
|
||||
{
|
||||
/* 不是自己发出去的 0x1A 对应的应答,忽略 */
|
||||
if (!s_mgmt.notify_waiting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
s_mgmt.notify_waiting = 0;
|
||||
if (!frame || len < 7)
|
||||
{
|
||||
return;
|
||||
}
|
||||
log_info("MCU reboot ack result=0x%02X", frame[6]);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_mgmt.h
|
||||
* @brief 模组管理业务:出厂复位 / 模组重启 / 通知 MCU 重启
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.13
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.13, cyWu, 首次发布(P1:0x16/0x18/0x1A)
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MCU_MGMT_H__
|
||||
#define __MCU_MGMT_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**
|
||||
* @brief 初始化模组管理业务
|
||||
*/
|
||||
void mcu_mgmt_init(void);
|
||||
|
||||
/**
|
||||
* @brief 周期处理:0x1A 重试、延迟复位倒计时
|
||||
* @note 仅在协议任务上下文调用
|
||||
*/
|
||||
void mcu_mgmt_periodic(void);
|
||||
|
||||
/**
|
||||
* @brief MCU 请求恢复出厂(协议层已回 0x17 OK)
|
||||
* @note 清配对、清时区 → 断 BLE/停广播 → 600ms 后复位;起来后不广播
|
||||
*/
|
||||
void mcu_mgmt_on_factory_reset(void);
|
||||
|
||||
/**
|
||||
* @brief MCU 请求模组重启(协议层已回 0x19 OK)
|
||||
* @note 保留配对 → 断 BLE/停广播 → 600ms 后复位
|
||||
*/
|
||||
void mcu_mgmt_on_mod_reboot(void);
|
||||
|
||||
/**
|
||||
* @brief 主动通知 MCU 重启(发 0x1A,等 0x1B)
|
||||
* @note P1 无自动触发,供后续 OTA 等调用;busy 时忽略
|
||||
*/
|
||||
void mcu_mgmt_notify_mcu_reboot(void);
|
||||
|
||||
/**
|
||||
* @brief 处理 MCU 对 0x1A 的应答 0x1B
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
*/
|
||||
void mcu_mgmt_on_mcu_reboot_ack(const uint8_t *frame, uint16_t len);
|
||||
|
||||
#endif /* __MCU_MGMT_H__ */
|
||||
@@ -83,14 +83,17 @@ void mcu_pairing_periodic(void)
|
||||
uint32_t now;
|
||||
uint32_t timeout_ms;
|
||||
|
||||
/* 当前不是配网过程态,不管超时 */
|
||||
if (mcu_link_status_get_phase() != MCU_WS_PAIRING) {
|
||||
return;
|
||||
}
|
||||
/* timeout_s=0 表示 MCU 没给超时,一直等到配上或被 stop */
|
||||
if (s_pairing.timeout_s == 0) {
|
||||
return;
|
||||
}
|
||||
/* 已经配上:切态由 mcu_business_on_paired 做,这里别抢 */
|
||||
if (usr_get_pair_flag()) {
|
||||
return; /* 已配网成功,交由 mcu_business_on_paired 处理切态 */
|
||||
return;
|
||||
}
|
||||
|
||||
now = mcu_util_get_ms();
|
||||
@@ -99,6 +102,7 @@ void mcu_pairing_periodic(void)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 超时仍未配上:停绑定广播,过程态回到未配网空闲 */
|
||||
log_info("pairing timeout, stop adv");
|
||||
usr_var.usr_goto_pair = 0;
|
||||
s_pairing.timeout_s = 0;
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_time.c
|
||||
* @brief 对时业务:网关时间写入 RTC,0x1D 从 RTC 读回
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.14
|
||||
* @version V1.1.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.13, cyWu, 首次发布(RAM 缓存)
|
||||
* - V1.1.0, 2026.08.14, cyWu, 改为写硬件 RTC,周期读回 UTC
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "mcu_time.h"
|
||||
#include "mcu_cmd.h"
|
||||
#include "mcu_util.h"
|
||||
#include "usr_rtc.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_TIME]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* 公有函数 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 初始化对时业务(RTC 已由 app_main 打开则直接返回 OK)
|
||||
*/
|
||||
void mcu_time_init(void)
|
||||
{
|
||||
if (usr_rtc_init() != USR_RTC_OK)
|
||||
{
|
||||
log_error("rtc not ready");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 周期读 RTC,刷新 UTC 给业务
|
||||
*/
|
||||
void mcu_time_periodic(void)
|
||||
{
|
||||
usr_rtc_run();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 网关 DEVICEINFO 对时:UTC + 时区写入 RTC
|
||||
* @param unix_s UTC Unix 秒
|
||||
* @param gmtoff_s 时区偏移秒
|
||||
* @note 网关从子设备连上开始计时,每 12 小时同步一次
|
||||
*/
|
||||
void mcu_time_set_from_gateway(uint64_t unix_s, int32_t gmtoff_s)
|
||||
{
|
||||
UsrRtc_Ret_t ret;
|
||||
|
||||
ret = usr_rtc_set_from_unix(unix_s, gmtoff_s);
|
||||
if (ret != USR_RTC_OK)
|
||||
{
|
||||
log_debug("set rtc fail ret=%d", (int)ret);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 组 0x1D 载荷:本地日历 + UTC
|
||||
* @param payload 输出
|
||||
* @param plen 输出长度
|
||||
*/
|
||||
void mcu_time_build_ack(uint8_t *payload, uint16_t *plen)
|
||||
{
|
||||
struct sys_time t;
|
||||
uint32_t unix_utc;
|
||||
|
||||
if (!payload || !plen)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
payload[0] = MCU_RESULT_OK;
|
||||
/* 年份不在 2025~2098 或还没有时区:载荷后面全 0 */
|
||||
if (!usr_rtc_is_valid() || (usr_rtc_read(&t) != USR_RTC_OK))
|
||||
{
|
||||
memset(&payload[1], 0, 12);
|
||||
*plen = MCU_TIME_ACK_LEN;
|
||||
return;
|
||||
}
|
||||
|
||||
unix_utc = usr_rtc_get_unix_utc();
|
||||
mcu_util_write_u16_be(&payload[1], t.year);
|
||||
payload[3] = t.month;
|
||||
payload[4] = t.day;
|
||||
payload[5] = t.hour;
|
||||
payload[6] = t.min;
|
||||
payload[7] = t.sec;
|
||||
mcu_util_write_u32_be(&payload[8], unix_utc);
|
||||
*plen = MCU_TIME_ACK_LEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 是否已有可用 RTC 时间
|
||||
* @return 1/0
|
||||
*/
|
||||
uint8_t mcu_time_is_valid(void)
|
||||
{
|
||||
return usr_rtc_is_valid();
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_time.h
|
||||
* @brief 对时业务:网关时间写入 RTC,应答 MCU 0x1C/0x1D
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.14
|
||||
* @version V1.1.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.13, cyWu, 首次发布(P1:0x1C/0x1D)
|
||||
* - V1.1.0, 2026.08.14, cyWu, 对接 usr_rtc 硬件 RTC
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MCU_TIME_H__
|
||||
#define __MCU_TIME_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** 0x1D 载荷长度:Result(1)+年(2)+月日时分秒(5)+Unix(4) */
|
||||
#define MCU_TIME_ACK_LEN (13u)
|
||||
|
||||
/**
|
||||
* @brief 初始化对时业务(确保 RTC 已打开)
|
||||
*/
|
||||
void mcu_time_init(void);
|
||||
|
||||
/**
|
||||
* @brief 周期读 RTC,刷新 UTC
|
||||
*/
|
||||
void mcu_time_periodic(void);
|
||||
|
||||
/**
|
||||
* @brief 写入网关 DEVICEINFO 下发的网络时间
|
||||
* @param unix_s UTC Unix 时间戳(秒);0 视为无效
|
||||
* @param gmtoff_s 时区偏移(秒),如东八区 28800
|
||||
* @note 网关从子设备连上开始计时,每 12 小时同步一次;
|
||||
* 模组收到后转为本地日历 write_sys_time,时区存 VM
|
||||
*/
|
||||
void mcu_time_set_from_gateway(uint64_t unix_s, int32_t gmtoff_s);
|
||||
|
||||
/**
|
||||
* @brief 组 0x1D 载荷(Result 起,不含帧头/校验)
|
||||
* @param payload 输出缓冲,至少 MCU_TIME_ACK_LEN
|
||||
* @param plen 输出载荷长度
|
||||
* @note 无有效时间时 Result=OK,日历与 Unix 全 0
|
||||
*/
|
||||
void mcu_time_build_ack(uint8_t *payload, uint16_t *plen);
|
||||
|
||||
/**
|
||||
* @brief RTC 时间是否可用(年份合法且已有时区)
|
||||
* @return 1=有效,0=无
|
||||
*/
|
||||
uint8_t mcu_time_is_valid(void);
|
||||
|
||||
#endif /* __MCU_TIME_H__ */
|
||||
@@ -0,0 +1,331 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_version.c
|
||||
* @brief 版本业务实现:拉 MCU 信息并按 SOC 0x41 上报网关
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.13
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.13, cyWu, 首次发布
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "mcu_version.h"
|
||||
#include "mcu_cmd.h"
|
||||
#include "mcu_frame.h"
|
||||
#include "mcu_protocol.h"
|
||||
#include "mcu_util.h"
|
||||
#include "usr_le_api.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_VER]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* 外部符号 */
|
||||
/*============================================================================*/
|
||||
|
||||
extern u16 usr_get_conn_service(void);
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有宏 */
|
||||
/*============================================================================*/
|
||||
|
||||
#define MCU_VER_ACK_WAIT_MS (500u) /* UART 0x40 等 MCU 应答 */
|
||||
#define MCU_VER_REPORT_WAIT_MS (2000u) /* SOC 0x41 走网关,等 ACK 需更长 */
|
||||
#define MCU_VER_MAX_RETRY (3u)
|
||||
#define MCU_VER_UART_ACK_MIN_LEN (14u) /* 55AA+Len+Cmd+Sn+Result+MCU3+HW3+Sum */
|
||||
#define MCU_VER_SOC_PAYLOAD_LEN (10u) /* Result+BLE3+MCU3+HW3 */
|
||||
|
||||
typedef enum
|
||||
{
|
||||
MCU_VER_STATE_IDLE = 0, /* 空闲:无进行中的拉取/上报 */
|
||||
MCU_VER_STATE_FETCH, /* 正在 UART 发 0x40,等 MCU 回 0x41 */
|
||||
MCU_VER_STATE_REPORT /* 正在 BLE 发 SOC 0x41,等网关 ACK */
|
||||
} McuVerState_t;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有类型/变量 */
|
||||
/*============================================================================*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
McuVerState_t state; /* 当前阶段 */
|
||||
uint8_t cached; /* 1=已从 MCU 拿到版本,RAM 里有 mcu_ver/hw_ver */
|
||||
uint8_t reported; /* 1=SOC 0x41 已被网关成功 ACK,本连接不再报 */
|
||||
uint8_t last_conn; /* 上一拍 BLE 是否已连接,仅作记录 */
|
||||
uint8_t give_up; /* 1=本轮 3 次已用尽,等断连后再试,避免连着打 */
|
||||
uint8_t fetch_retry; /* 0x40 已发送次数(含首次) */
|
||||
uint8_t report_retry; /* SOC 0x41 已发送次数(含首次) */
|
||||
uint8_t report_sn; /* SOC 0x41 序列号,每次上报递增 */
|
||||
uint32_t last_tx_ms; /* 最近一次 0x40 或 SOC 0x41 发出时刻 */
|
||||
uint8_t mcu_ver[3]; /* MCU 软件版本 Vx.x.x */
|
||||
uint8_t hw_ver[3]; /* 硬件版本 Vx.x.x */
|
||||
} McuVersion_t;
|
||||
|
||||
static McuVersion_t s_ver;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有函数 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 发送 UART 0x40
|
||||
* @return 1=已发出,0=忙或失败
|
||||
*/
|
||||
static uint8_t mcu_version_tx_fetch(void)
|
||||
{
|
||||
/* UART 正在等别的命令 ACK(心跳/0x14 等),先不抢 */
|
||||
if (mcu_protocol_is_ble_pending())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/* 发 0x40,期望 MCU 回 0x41 */
|
||||
if (mcu_protocol_send_cmd_wait_ack(MCU_CMD_GET_MCU_INFO, NULL, 0,
|
||||
MCU_CMD_GET_MCU_INFO_ACK) <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
s_ver.last_tx_ms = mcu_util_get_ms();
|
||||
s_ver.fetch_retry++;
|
||||
log_debug("0x40 fetch retry=%u", s_ver.fetch_retry);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 组 SOC 0x41 并走 cmd 100 上报
|
||||
* @return 1=已发出,0=失败
|
||||
*/
|
||||
static uint8_t mcu_version_tx_report(void)
|
||||
{
|
||||
uint8_t payload[MCU_VER_SOC_PAYLOAD_LEN];
|
||||
uint8_t frame[MCU_FRAME_MAX_LEN];
|
||||
int32_t total;
|
||||
|
||||
/* MCU 0x07 正在等网关 ACK,通道被占用,版本上报让路 */
|
||||
if (mcu_protocol_is_mcu_report_waiting())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
/* 刚连上、主机还没写 CCC:发了也会 app_send_fail,不占重试次数 */
|
||||
if (!usr_ble_is_notify_ready())
|
||||
{
|
||||
log_debug("SOC 0x41 wait CCC");
|
||||
return 0;
|
||||
}
|
||||
|
||||
payload[0] = MCU_RESULT_OK;
|
||||
payload[1] = MCU_BLE_FW_VER_MAJOR;
|
||||
payload[2] = MCU_BLE_FW_VER_MINOR;
|
||||
payload[3] = MCU_BLE_FW_VER_PATCH;
|
||||
memcpy(&payload[4], s_ver.mcu_ver, 3);
|
||||
memcpy(&payload[7], s_ver.hw_ver, 3);
|
||||
|
||||
s_ver.report_sn++;
|
||||
total = mcu_frame_pack(frame, sizeof(frame), MCU_CMD_GET_MCU_INFO_ACK,
|
||||
s_ver.report_sn, payload, MCU_VER_SOC_PAYLOAD_LEN);
|
||||
if (total <= 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (usr_le_send_mcu_frame(frame, (uint16_t)total) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
s_ver.last_tx_ms = mcu_util_get_ms();
|
||||
s_ver.report_retry++;
|
||||
log_info("SOC 0x41 report sn=%u retry=%u ble=%u.%u.%u mcu=%u.%u.%u hw=%u.%u.%u",
|
||||
s_ver.report_sn, s_ver.report_retry,
|
||||
payload[1], payload[2], payload[3],
|
||||
s_ver.mcu_ver[0], s_ver.mcu_ver[1], s_ver.mcu_ver[2],
|
||||
s_ver.hw_ver[0], s_ver.hw_ver[1], s_ver.hw_ver[2]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 进入拉取或上报(缓存空则拉,有缓存未上报则报)
|
||||
*/
|
||||
static void mcu_version_start_session(void)
|
||||
{
|
||||
s_ver.fetch_retry = 0;
|
||||
s_ver.report_retry = 0;
|
||||
if (!s_ver.cached)
|
||||
{
|
||||
/* 还没有 MCU 版本:先 UART 拉 0x40 */
|
||||
s_ver.state = MCU_VER_STATE_FETCH;
|
||||
(void)mcu_version_tx_fetch();
|
||||
return;
|
||||
}
|
||||
if (!s_ver.reported)
|
||||
{
|
||||
/* 已有缓存但网关还没 ACK:直接报 SOC 0x41 */
|
||||
s_ver.state = MCU_VER_STATE_REPORT;
|
||||
(void)mcu_version_tx_report();
|
||||
return;
|
||||
}
|
||||
/* 已拉过也已报过,保持空闲 */
|
||||
s_ver.state = MCU_VER_STATE_IDLE;
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* 公有函数 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 初始化版本业务
|
||||
*/
|
||||
void mcu_version_init(void)
|
||||
{
|
||||
memset(&s_ver, 0, sizeof(s_ver));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 周期:已配网且已连接时拉版本/上报;超时重试
|
||||
* @note 流程:BLE 已连 + 已配网 → 拉 MCU 0x40 → 报网关 SOC 0x41 → 等 ACK
|
||||
*/
|
||||
void mcu_version_periodic(void)
|
||||
{
|
||||
uint8_t conn = (usr_get_conn_service() != 0) ? 1 : 0; /* 1=BLE 已连接 */
|
||||
uint8_t paired = usr_get_pair_flag(); /* 1=已配网/已绑定 */
|
||||
uint32_t now = mcu_util_get_ms();
|
||||
|
||||
s_ver.last_conn = conn;
|
||||
|
||||
/* BLE 断了:清 give_up,下次连上允许再试;进行中的拉取/上报也停掉 */
|
||||
if (!conn)
|
||||
{
|
||||
s_ver.give_up = 0;
|
||||
if (s_ver.state != MCU_VER_STATE_IDLE)
|
||||
{
|
||||
s_ver.state = MCU_VER_STATE_IDLE;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* 已连接但未配网:版本要报给网关,没绑定就不上报。
|
||||
* give_up=1:本轮 3 次已失败,连着重打没用,等下次断连再清标志。
|
||||
* CCC 未就绪:UART 可以先拉 0x40,但 SOC 0x41 等主机使能通知后再发。 */
|
||||
if (!paired || s_ver.give_up)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 空闲且还有事没做完:没缓存就去拉,有缓存没上报就去报 */
|
||||
if (s_ver.state == MCU_VER_STATE_IDLE)
|
||||
{
|
||||
if (!s_ver.cached || !s_ver.reported)
|
||||
{
|
||||
mcu_version_start_session();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* 等 MCU 回 UART 0x41:500ms 窗口内不重发;满 3 次则本轮放弃 */
|
||||
if (s_ver.state == MCU_VER_STATE_FETCH)
|
||||
{
|
||||
if ((s_ver.fetch_retry > 0) &&
|
||||
((now - s_ver.last_tx_ms) <= MCU_VER_ACK_WAIT_MS))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (s_ver.fetch_retry >= MCU_VER_MAX_RETRY)
|
||||
{
|
||||
log_error("0x40 give up, wait next connect");
|
||||
s_ver.state = MCU_VER_STATE_IDLE;
|
||||
s_ver.give_up = 1;
|
||||
return;
|
||||
}
|
||||
(void)mcu_version_tx_fetch();
|
||||
return;
|
||||
}
|
||||
|
||||
/* 等网关 ACK:2s 窗口内不重发(走网关比 UART 慢);满 3 次则本轮放弃 */
|
||||
if (s_ver.state == MCU_VER_STATE_REPORT)
|
||||
{
|
||||
if ((s_ver.report_retry > 0) &&
|
||||
((now - s_ver.last_tx_ms) <= MCU_VER_REPORT_WAIT_MS))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (s_ver.report_retry >= MCU_VER_MAX_RETRY)
|
||||
{
|
||||
log_error("SOC 0x41 give up, wait next connect");
|
||||
s_ver.state = MCU_VER_STATE_IDLE;
|
||||
s_ver.give_up = 1;
|
||||
return;
|
||||
}
|
||||
(void)mcu_version_tx_report();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理 UART 0x41
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
*/
|
||||
void mcu_version_on_mcu_info_ack(const uint8_t *frame, uint16_t len)
|
||||
{
|
||||
if (!frame || len < MCU_VER_UART_ACK_MIN_LEN)
|
||||
{
|
||||
log_error("0x41 too short %u", len);
|
||||
return;
|
||||
}
|
||||
/* frame[6] 是 Result,非 0 表示 MCU 拒绝,不缓存、不上报 */
|
||||
if (frame[6] != MCU_RESULT_OK)
|
||||
{
|
||||
log_error("0x41 result=0x%02X", frame[6]);
|
||||
return;
|
||||
}
|
||||
|
||||
memcpy(s_ver.mcu_ver, &frame[7], 3);
|
||||
memcpy(s_ver.hw_ver, &frame[10], 3);
|
||||
s_ver.cached = 1;
|
||||
s_ver.reported = 0; /* 新版本尚未报给网关 */
|
||||
s_ver.report_retry = 0;
|
||||
s_ver.state = MCU_VER_STATE_REPORT;
|
||||
log_info("MCU ver %u.%u.%u hw %u.%u.%u",
|
||||
s_ver.mcu_ver[0], s_ver.mcu_ver[1], s_ver.mcu_ver[2],
|
||||
s_ver.hw_ver[0], s_ver.hw_ver[1], s_ver.hw_ver[2]);
|
||||
(void)mcu_version_tx_report();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 网关 ACK:MCU 0x07 优先消费,版本上报绝不回 MCU 0x08
|
||||
* @param success 成功
|
||||
* @param mcu_report_consumed 已被 MCU 上报占用
|
||||
*/
|
||||
void mcu_version_on_gateway_ack(uint8_t success, uint8_t mcu_report_consumed)
|
||||
{
|
||||
/* 当前不是在等版本上报的 ACK,忽略 */
|
||||
if (s_ver.state != MCU_VER_STATE_REPORT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* 这条 ACK 已经给 MCU 0x07 回过 0x08 了,不是给版本的,版本继续等 */
|
||||
if (mcu_report_consumed)
|
||||
{
|
||||
log_debug("gateway ack used by MCU 0x07, version waits");
|
||||
return;
|
||||
}
|
||||
/* 网关明确失败:不置 reported,周期函数超时后会重发 SOC 0x41 */
|
||||
if (!success)
|
||||
{
|
||||
log_error("SOC 0x41 gateway ack fail");
|
||||
return;
|
||||
}
|
||||
s_ver.reported = 1;
|
||||
s_ver.state = MCU_VER_STATE_IDLE;
|
||||
log_info("SOC 0x41 report ok");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 是否已缓存 MCU 版本
|
||||
* @return 1/0
|
||||
*/
|
||||
uint8_t mcu_version_is_cached(void)
|
||||
{
|
||||
return s_ver.cached;
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/******************************************************************************
|
||||
* @file mcu_version.h
|
||||
* @brief 版本业务:UART 0x40/0x41 拉 MCU 信息,SOC 0x41 上报网关
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.13
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.13, cyWu, 首次发布(P1:0x40/0x41 + SOC 0x41)
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MCU_VERSION_H__
|
||||
#define __MCU_VERSION_H__
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/** BLE 模组软件版本 V1.0.0(三字节,对应显示 Vx.x.x) */
|
||||
#define MCU_BLE_FW_VER_MAJOR (1u)
|
||||
#define MCU_BLE_FW_VER_MINOR (0u)
|
||||
#define MCU_BLE_FW_VER_PATCH (0u)
|
||||
|
||||
/**
|
||||
* @brief 初始化版本业务(清空 RAM 缓存)
|
||||
*/
|
||||
void mcu_version_init(void);
|
||||
|
||||
/**
|
||||
* @brief 周期处理:已配网且已连接时拉 MCU 版本,成功后上报网关
|
||||
* @note 仅在协议任务上下文调用
|
||||
*/
|
||||
void mcu_version_periodic(void);
|
||||
|
||||
/**
|
||||
* @brief 处理 UART 0x41(MCU 信息应答),只本地解析,不转发网关
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
*/
|
||||
void mcu_version_on_mcu_info_ack(const uint8_t *frame, uint16_t len);
|
||||
|
||||
/**
|
||||
* @brief 网关对 REPORTCMD_V2 的应答
|
||||
* @param success 1=网关成功 ACK
|
||||
* @param mcu_report_consumed 1=该 ACK 已按 MCU 0x07 消费(回了 0x08)
|
||||
*/
|
||||
void mcu_version_on_gateway_ack(uint8_t success, uint8_t mcu_report_consumed);
|
||||
|
||||
/**
|
||||
* @brief MCU 版本缓存是否就绪
|
||||
* @return 1=已缓存,0=无
|
||||
*/
|
||||
uint8_t mcu_version_is_cached(void);
|
||||
|
||||
#endif /* __MCU_VERSION_H__ */
|
||||
@@ -2,8 +2,8 @@
|
||||
* @file mcu_bridge.c
|
||||
* @brief BLE+MCU 接入模块对外总入口实现(组合门面,仅转调)
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V2.0.0
|
||||
* @date 2026.08.13
|
||||
* @version V2.1.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.06, cyWu, 首次发布
|
||||
* - V1.1.0, 2026.08.06, cyWu, 改为事件队列 + 独立任务驱动
|
||||
@@ -12,6 +12,7 @@
|
||||
* - V1.4.0, 2026.08.12, cyWu, 工作状态 Bit0/1/2 互斥
|
||||
* - V2.0.0, 2026.08.12, cyWu, 拆分为 protocol/ + business/ 分层,
|
||||
* 本文件收敛为纯组合门面,不再持有任何状态
|
||||
* - V2.1.0, 2026.08.13, cyWu, P1 增加网络时间入口
|
||||
******************************************************************************/
|
||||
|
||||
#include "mcu_bridge.h"
|
||||
@@ -84,3 +85,11 @@ void mcu_bridge_on_gateway_report_ack(uint16_t cmd,
|
||||
{
|
||||
mcu_protocol_notify_gateway_ack(cmd, gateway_int_cnt, gateway_int0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 网关网络时间:经协议层事件队列串行化
|
||||
*/
|
||||
void mcu_bridge_on_network_time(uint64_t unix_s, int32_t gmtoff_s)
|
||||
{
|
||||
mcu_protocol_notify_network_time(unix_s, gmtoff_s);
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
* @file mcu_bridge.h
|
||||
* @brief BLE+MCU 接入模块对外总入口(组合门面)
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V2.0.0
|
||||
* @date 2026.08.13
|
||||
* @version V2.1.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.06, cyWu, 首次发布
|
||||
* - V1.1.0, 2026.08.06, cyWu, 内部改为事件队列 + 独立任务驱动
|
||||
@@ -11,6 +11,7 @@
|
||||
* - V2.0.0, 2026.08.12, cyWu, 整体重构为 protocol/ + business/ 分层:
|
||||
* 本文件不再包含任何实现逻辑,只做“组合根”,对外 API 保持不变,
|
||||
* 外部调用方(app_main.c / usr_le_adv.c / usr_le_recieve.c 等)无需改动
|
||||
* - V2.1.0, 2026.08.13, cyWu, P1 增加网络时间入口;业务子模块扩到管理/对时/版本
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MCU_BRIDGE_H__
|
||||
@@ -34,7 +35,10 @@
|
||||
/* ├── mcu_identity.c/h 身份业务(0x01/0x02) */
|
||||
/* ├── mcu_pairing.c/h 配网业务(0x10、超时) */
|
||||
/* ├── mcu_link_status.c/h 连接状态业务(Bit0/1/2/6 位图) */
|
||||
/* └── mcu_business.c/h 业务门面:协调以上三者,供协议层调用 */
|
||||
/* ├── mcu_mgmt.c/h 模组管理(0x16/0x18/0x1A) */
|
||||
/* ├── mcu_time.c/h 对时(0x1C/0x1D,写入 usr_rtc) */
|
||||
/* ├── mcu_version.c/h MCU 信息 + SOC 0x41 上报 */
|
||||
/* └── mcu_business.c/h 业务门面:协调子模块,供协议层调用 */
|
||||
/* */
|
||||
/* 依赖方向(单向,禁止反向 include): */
|
||||
/* mcu_bridge → protocol → business(子模块) → business(门面) */
|
||||
@@ -110,4 +114,12 @@ void mcu_bridge_on_gateway_report_ack(uint16_t cmd,
|
||||
uint8_t gateway_int_cnt,
|
||||
int32_t gateway_int0);
|
||||
|
||||
/**
|
||||
* @brief 网关 DEVICEINFO 中的网络时间(BLE 上下文可调,内部入队)
|
||||
* @param unix_s UTC Unix 时间戳(秒)
|
||||
* @param gmtoff_s 时区偏移(秒)
|
||||
* @note 网关从子设备连上开始计时,每 12 小时同步一次
|
||||
*/
|
||||
void mcu_bridge_on_network_time(uint64_t unix_s, int32_t gmtoff_s);
|
||||
|
||||
#endif /* __MCU_BRIDGE_H__ */
|
||||
|
||||
@@ -24,8 +24,8 @@
|
||||
/*============================================================================*/
|
||||
|
||||
#define MCU_WS_PAIRING (1u << 0) /**< 配网中(绑定广播) */
|
||||
#define MCU_WS_CONFIGURED (1u << 1) /**< 已配网正常态(已连接/稳定工作) */
|
||||
#define MCU_WS_RECONNECTING (1u << 2) /**< 断线重连中(回连广播) */
|
||||
#define MCU_WS_CONFIGURED (1u << 1) /**< 已配网且链路可发数(已连接+CCC) */
|
||||
#define MCU_WS_RECONNECTING (1u << 2) /**< 已绑定但链路未通(断连,或已连未写 CCC) */
|
||||
#define MCU_WS_CLOUD_ONLINE (1u << 3) /**< 已连接到云,预留:暂固定 0 */
|
||||
#define MCU_WS_APP_ONLINE (1u << 4) /**< APP 在线,预留:暂固定 0 */
|
||||
#define MCU_WS_APP_DIRECT (1u << 5) /**< APP 直连,预留:暂固定 0 */
|
||||
@@ -60,7 +60,7 @@ typedef enum {
|
||||
MCU_CMD_WORK_STATUS = 0x14,
|
||||
MCU_CMD_WORK_STATUS_ACK = 0x15,
|
||||
|
||||
/* 模组管理(后续扩展) */
|
||||
/* 模组管理 */
|
||||
MCU_CMD_RESET = 0x16,
|
||||
MCU_CMD_RESET_ACK = 0x17,
|
||||
MCU_CMD_REBOOT = 0x18,
|
||||
|
||||
@@ -2,27 +2,28 @@
|
||||
* @file mcu_protocol.c
|
||||
* @brief MCU 接入协议引擎实现
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V1.1.0
|
||||
* @date 2026.08.13
|
||||
* @version V1.2.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_bridge.c 拆出(历史版本见原文件):
|
||||
* 事件队列 + 独立任务驱动、LP target、心跳/透传重传均原样保留
|
||||
* - V1.1.0, 2026.08.12, cyWu, 0x07 上报改为不缓存、不超时重发:
|
||||
* MCU 自身已有超时重发机制,模组侧只记 SN 转发+等 ACK 回 0x08,
|
||||
* 去掉 report_pending 整包缓存与 mcu_protocol_check_report_pending()
|
||||
* - V1.2.0, 2026.08.13, cyWu, P1 管理命令分发、pending 发送、网络时间入队
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "asm/power/power_api.h"
|
||||
#include "mcu_protocol.h"
|
||||
#include "mcu_uart.h"
|
||||
#include "mcu_business.h"
|
||||
#include "mcu_frame.h"
|
||||
#include "mcu_uart.h"
|
||||
#include "mcu_util.h"
|
||||
#include "usr_le_api.h"
|
||||
#include "mcu_business.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[MCU_PROTO]"
|
||||
#define LOG_TAG "[MCU_PROTO]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
@@ -34,16 +35,16 @@
|
||||
/* 私有宏 */
|
||||
/*============================================================================*/
|
||||
|
||||
#define MCU_HEARTBEAT_IDLE_MS (55000u)
|
||||
#define MCU_HEARTBEAT_IDLE_MS (55000u)
|
||||
#define MCU_HEARTBEAT_MAX_RETRY (3u)
|
||||
#define MCU_UART_ACK_WAIT_MS (500u) /* 等 MCU UART ACK 超时(心跳/透传/0x14 等) */
|
||||
#define MCU_APP_ACK_MAX_RETRY (3u)
|
||||
#define MCU_UART_ACK_WAIT_MS (500u) /* 等 MCU UART ACK 超时(心跳/透传/0x14 等) */
|
||||
#define MCU_APP_ACK_MAX_RETRY (3u)
|
||||
|
||||
#define MCU_PROTOCOL_TICK_MS (200u)
|
||||
/* 杰理 os_sem_pend 超时单位是 OS tick,OS_TICKS_PER_SEC=100 → 1 tick = 10ms */
|
||||
#define MCU_PROTOCOL_TICK_OS ((MCU_PROTOCOL_TICK_MS + 9u) / 10u)
|
||||
#define MCU_EVT_QUEUE_DEPTH (8u)
|
||||
#define MCU_PROTOCOL_TASK_PRIO (31u)
|
||||
#define MCU_PROTOCOL_TICK_OS ((MCU_PROTOCOL_TICK_MS + 9u) / 10u)
|
||||
#define MCU_EVT_QUEUE_DEPTH (8u)
|
||||
#define MCU_PROTOCOL_TASK_PRIO (31u)
|
||||
#define MCU_PROTOCOL_TASK_STACK (2048u)
|
||||
|
||||
/*============================================================================*/
|
||||
@@ -52,12 +53,12 @@
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t active;
|
||||
uint8_t sn;
|
||||
uint8_t expect_ack;
|
||||
uint8_t retry;
|
||||
uint32_t send_time;
|
||||
uint8_t buf[MCU_FRAME_MAX_LEN];
|
||||
uint8_t active; /* 1=正在等 MCU UART ACK */
|
||||
uint8_t sn; /* 发出去的 SN,用来对 ACK */
|
||||
uint8_t expect_ack; /* 期望的 ACK 命令字,如 0x04/0x13 */
|
||||
uint8_t retry; /* 已重发次数(不含首次) */
|
||||
uint32_t send_time; /* 最近一次发出时刻 */
|
||||
uint8_t buf[MCU_FRAME_MAX_LEN]; /* 仅 app_pending 缓存整包,便于重发 */
|
||||
uint16_t len;
|
||||
} McuPending_t;
|
||||
|
||||
@@ -67,7 +68,8 @@ typedef enum
|
||||
MCU_EVT_APP_FRAME,
|
||||
MCU_EVT_GATEWAY_ACK,
|
||||
MCU_EVT_BLE_PAIRED,
|
||||
MCU_EVT_BLE_PAIR_CLEARED
|
||||
MCU_EVT_BLE_PAIR_CLEARED,
|
||||
MCU_EVT_NET_TIME
|
||||
} McuEvtType_t;
|
||||
|
||||
typedef struct
|
||||
@@ -78,22 +80,24 @@ typedef struct
|
||||
uint16_t gw_cmd;
|
||||
uint8_t gw_int_cnt;
|
||||
int32_t gw_int0;
|
||||
uint64_t unix_s;
|
||||
int32_t gmtoff_s;
|
||||
} McuEvt_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t initialized;
|
||||
uint8_t sn;
|
||||
uint8_t last_pushed_status;
|
||||
uint8_t hb_fail_cnt;
|
||||
uint8_t hb_waiting;
|
||||
uint8_t mcu_comm_alarm;
|
||||
uint32_t last_mcu_rx_ms;
|
||||
uint32_t last_hb_tx_ms;
|
||||
McuPending_t app_pending; /* APP→MCU 透传等待 */
|
||||
McuPending_t ble_pending; /* BLE 主动请求等待(心跳/状态等) */
|
||||
uint8_t report_waiting; /* 1=已转网关,等 ACK 后再回 MCU 0x08 */
|
||||
uint8_t report_sn; /* 等待回 0x08 的 SN */
|
||||
uint8_t sn; /* 模组侧发出帧的 SN 发生器 */
|
||||
uint8_t last_pushed_status; /* 上次已推给 MCU 的 0x14 状态,0xFF=强制再推 */
|
||||
uint8_t hb_fail_cnt; /* 心跳连续失败次数 */
|
||||
uint8_t hb_waiting; /* 1=已发 0x12,正在等 0x13 */
|
||||
uint8_t mcu_comm_alarm; /* 1=心跳连失败 3 次,MCU 通信告警 */
|
||||
uint32_t last_mcu_rx_ms; /* 最近一次收到 MCU 任意合法帧 */
|
||||
uint32_t last_hb_tx_ms; /* 最近一次发出心跳 */
|
||||
McuPending_t app_pending; /* APP 0x03/0x05 透传,等 MCU 0x04/0x06 */
|
||||
McuPending_t ble_pending; /* 模组主动 UART(心跳/0x14/0x40/0x1A),一次只能占一个 */
|
||||
uint8_t report_waiting; /* 1=MCU 0x07 已转网关,等 ACK 后再回 0x08 */
|
||||
uint8_t report_sn; /* 等待回 0x08 的那条 0x07 的 SN */
|
||||
} McuProtocol_t;
|
||||
|
||||
typedef struct
|
||||
@@ -125,6 +129,7 @@ static int32_t mcu_protocol_send_frame(uint8_t cmd, uint8_t sn,
|
||||
static void mcu_protocol_send_ack(uint8_t cmd, uint8_t sn, uint8_t result);
|
||||
static void mcu_protocol_handle_frame(const uint8_t *frame, uint16_t len);
|
||||
static void mcu_protocol_handle_pair_cfg(const uint8_t *frame, uint16_t len);
|
||||
static void mcu_protocol_handle_get_time(const uint8_t *frame, uint16_t len);
|
||||
static void mcu_protocol_handle_report(const uint8_t *frame, uint16_t len);
|
||||
static void mcu_protocol_handle_app_frame(const uint8_t *data, uint16_t len);
|
||||
static void mcu_protocol_handle_gateway_ack(uint16_t cmd, uint8_t gateway_int_cnt,
|
||||
@@ -218,6 +223,9 @@ static void mcu_protocol_dispatch_event(const McuEvt_t *evt)
|
||||
case MCU_EVT_BLE_PAIR_CLEARED:
|
||||
mcu_business_on_pair_cleared();
|
||||
break;
|
||||
case MCU_EVT_NET_TIME:
|
||||
mcu_business_on_network_time(evt->unix_s, evt->gmtoff_s);
|
||||
break;
|
||||
default:
|
||||
log_error("unknown evt type=%d", evt->type);
|
||||
break;
|
||||
@@ -315,6 +323,59 @@ int32_t mcu_protocol_send_cmd(uint8_t cmd, const uint8_t *payload, uint16_t plen
|
||||
return mcu_protocol_send_frame(cmd, mcu_protocol_next_sn(), payload, plen);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送并占用 ble_pending
|
||||
* @param cmd 命令
|
||||
* @param payload 载荷
|
||||
* @param plen 长度
|
||||
* @param expect_ack 期望 ACK
|
||||
* @return 帧长;<0 忙或失败
|
||||
*/
|
||||
int32_t mcu_protocol_send_cmd_wait_ack(uint8_t cmd, const uint8_t *payload,
|
||||
uint16_t plen, uint8_t expect_ack)
|
||||
{
|
||||
uint8_t sn;
|
||||
int32_t total;
|
||||
|
||||
/* UART 一次只等一条 ACK,已被心跳/0x14/0x40 等占用则拒绝 */
|
||||
if (s_proto.ble_pending.active)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
sn = mcu_protocol_next_sn();
|
||||
total = mcu_protocol_send_frame(cmd, sn, payload, plen);
|
||||
if (total <= 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
s_proto.ble_pending.active = 1;
|
||||
s_proto.ble_pending.sn = sn;
|
||||
s_proto.ble_pending.expect_ack = expect_ack;
|
||||
s_proto.ble_pending.send_time = mcu_util_get_ms();
|
||||
s_proto.ble_pending.retry = 0;
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief ble_pending 是否占用
|
||||
* @return 1/0
|
||||
*/
|
||||
uint8_t mcu_protocol_is_ble_pending(void)
|
||||
{
|
||||
return s_proto.ble_pending.active ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MCU 0x07 是否在等网关 ACK
|
||||
* @return 1/0
|
||||
*/
|
||||
uint8_t mcu_protocol_is_mcu_report_waiting(void)
|
||||
{
|
||||
return s_proto.report_waiting ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 发送仅含 Result 的 ACK 帧
|
||||
* @param cmd ACK 命令
|
||||
@@ -357,11 +418,13 @@ static void mcu_protocol_handle_pair_cfg(const uint8_t *frame, uint16_t len)
|
||||
mode = frame[6];
|
||||
timeout_s = mcu_util_read_u16_be(&frame[7]);
|
||||
|
||||
/* 协议只认 mode=1/2,其它直接回参数错误 */
|
||||
if (mode != 1 && mode != 2)
|
||||
{
|
||||
mcu_protocol_send_ack(MCU_CMD_PAIR_CFG_ACK, sn, MCU_RESULT_PARAM_ERR);
|
||||
return;
|
||||
}
|
||||
/* 还没拿到 MCU 身份,广播都发不了,回忙让 MCU 稍后再试 */
|
||||
if (!mcu_business_is_identity_ready())
|
||||
{
|
||||
mcu_protocol_send_ack(MCU_CMD_PAIR_CFG_ACK, sn, MCU_RESULT_BUSY);
|
||||
@@ -372,6 +435,32 @@ static void mcu_protocol_handle_pair_cfg(const uint8_t *frame, uint16_t len)
|
||||
mcu_business_on_pair_cfg(mode, timeout_s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理 0x1C:组 0x1D(时间由 business 填充)
|
||||
* @param frame 完整帧
|
||||
* @param len 长度
|
||||
*/
|
||||
static void mcu_protocol_handle_get_time(const uint8_t *frame, uint16_t len)
|
||||
{
|
||||
uint8_t sn;
|
||||
uint8_t payload[16];
|
||||
uint16_t plen = 0;
|
||||
|
||||
(void)len;
|
||||
if (!frame)
|
||||
{
|
||||
return;
|
||||
}
|
||||
sn = frame[5];
|
||||
mcu_business_build_time_ack(payload, &plen);
|
||||
if (plen == 0)
|
||||
{
|
||||
plen = 1;
|
||||
payload[0] = MCU_RESULT_OK;
|
||||
}
|
||||
mcu_protocol_send_frame(MCU_CMD_GET_TIME_ACK, sn, payload, plen);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 处理 MCU 上报 0x07:转网关,等网关 ACK 后再回 0x08
|
||||
* @param frame 完整帧
|
||||
@@ -383,9 +472,20 @@ static void mcu_protocol_handle_report(const uint8_t *frame, uint16_t len)
|
||||
{
|
||||
uint8_t sn = frame[5];
|
||||
|
||||
/* 已配网也会在 CCC 之前连上;这时发出去会失败,不置 waiting,让 MCU 自己重发 0x07 */
|
||||
if (!usr_ble_is_notify_ready())
|
||||
{
|
||||
log_debug("MCU 0x07 wait CCC sn=%u, MCU will retry", sn);
|
||||
return;
|
||||
}
|
||||
|
||||
if (usr_le_send_mcu_frame((u8 *)frame, len) == 0)
|
||||
{
|
||||
log_error("MCU 0x07 send fail sn=%u", sn);
|
||||
return;
|
||||
}
|
||||
s_proto.report_sn = sn;
|
||||
s_proto.report_waiting = 1;
|
||||
usr_le_send_mcu_frame((u8 *)frame, len);
|
||||
log_debug("MCU report sn=%u -> gateway, wait ACK", sn);
|
||||
}
|
||||
|
||||
@@ -404,6 +504,7 @@ static void mcu_protocol_handle_frame(const uint8_t *frame, uint16_t len)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 收到任意合法 MCU 帧:MCU 还活着,清心跳失败与告警 */
|
||||
s_proto.last_mcu_rx_ms = mcu_util_get_ms();
|
||||
s_proto.hb_fail_cnt = 0;
|
||||
s_proto.hb_waiting = 0;
|
||||
@@ -413,6 +514,7 @@ static void mcu_protocol_handle_frame(const uint8_t *frame, uint16_t len)
|
||||
sn = frame[5];
|
||||
log_debug("MCU RX cmd=0x%02X sn=%u len=%u", cmd, sn, len);
|
||||
|
||||
/* APP 透传在等这条 ACK(命令+SN 都对上):转给 APP,不再往下分发 */
|
||||
if (s_proto.app_pending.active &&
|
||||
(cmd == s_proto.app_pending.expect_ack) &&
|
||||
(sn == s_proto.app_pending.sn))
|
||||
@@ -422,6 +524,7 @@ static void mcu_protocol_handle_frame(const uint8_t *frame, uint16_t len)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 模组主动 UART 在等这条 ACK:先释放 pending,再落到下面 switch */
|
||||
if (s_proto.ble_pending.active &&
|
||||
(cmd == s_proto.ble_pending.expect_ack) &&
|
||||
(sn == s_proto.ble_pending.sn))
|
||||
@@ -432,7 +535,7 @@ static void mcu_protocol_handle_frame(const uint8_t *frame, uint16_t len)
|
||||
s_proto.hb_waiting = 0;
|
||||
s_proto.hb_fail_cnt = 0;
|
||||
}
|
||||
return;
|
||||
/* 0x41/0x1B 等其余 ACK 继续走 switch,交给 business */
|
||||
}
|
||||
|
||||
switch (cmd)
|
||||
@@ -444,20 +547,37 @@ static void mcu_protocol_handle_frame(const uint8_t *frame, uint16_t len)
|
||||
mcu_protocol_handle_pair_cfg(frame, len);
|
||||
break;
|
||||
case MCU_CMD_REPORT:
|
||||
mcu_protocol_handle_report(frame, len);
|
||||
mcu_protocol_handle_report(frame, len); /* 转网关,等 ACK 再回 0x08 */
|
||||
break;
|
||||
case MCU_CMD_CONTROL_ACK:
|
||||
case MCU_CMD_READ_ACK:
|
||||
/* 没匹配上 app_pending(SN 不对或没在等)仍转给 APP,避免 MCU 应答丢掉 */
|
||||
usr_le_send_mcu_frame((u8 *)frame, len);
|
||||
break;
|
||||
case MCU_CMD_RESET:
|
||||
mcu_protocol_send_ack(MCU_CMD_RESET_ACK, sn, MCU_RESULT_OK);
|
||||
mcu_business_on_factory_reset();
|
||||
break;
|
||||
case MCU_CMD_REBOOT:
|
||||
mcu_protocol_send_ack(MCU_CMD_REBOOT_ACK, sn, MCU_RESULT_OK);
|
||||
mcu_business_on_mod_reboot();
|
||||
break;
|
||||
case MCU_CMD_GET_TIME:
|
||||
mcu_protocol_handle_get_time(frame, len);
|
||||
break;
|
||||
case MCU_CMD_GET_MCU_INFO_ACK:
|
||||
mcu_business_on_mcu_info_ack(frame, len); /* 只本地解析,不原样转发网关 */
|
||||
break;
|
||||
case MCU_CMD_MCU_REBOOT_ACK:
|
||||
mcu_business_on_mcu_reboot_ack(frame, len);
|
||||
break;
|
||||
case MCU_CMD_HEARTBEAT_ACK:
|
||||
case MCU_CMD_WORK_STATUS_ACK:
|
||||
break;
|
||||
break; /* pending 已在上面释放,无需再处理载荷 */
|
||||
default:
|
||||
if (cmd == MCU_CMD_RESET || cmd == MCU_CMD_REBOOT ||
|
||||
cmd == MCU_CMD_GET_TIME || cmd == MCU_CMD_FACTORY ||
|
||||
cmd == MCU_CMD_PHOTO || cmd == MCU_CMD_RECORD ||
|
||||
cmd == MCU_CMD_AUDIO)
|
||||
/* 工厂/拍照/录音/音频:协议有命令字但模组未实现,回不支持 */
|
||||
if (cmd == MCU_CMD_FACTORY || cmd == MCU_CMD_PHOTO ||
|
||||
cmd == MCU_CMD_RECORD || cmd == MCU_CMD_AUDIO)
|
||||
{
|
||||
mcu_protocol_send_ack((uint8_t)(cmd + 1), sn, MCU_RESULT_NOT_SUPPORT);
|
||||
}
|
||||
@@ -479,6 +599,8 @@ static void mcu_protocol_handle_app_frame(const uint8_t *data, uint16_t len)
|
||||
cmd = data[4];
|
||||
sn = data[5];
|
||||
|
||||
/* 0x03/0x05 必须等 MCU ACK 再回 APP,所以进 pending 可超时重发。
|
||||
* 其它 APP 帧直接透传到 UART,模组不管应答。 */
|
||||
if (cmd == MCU_CMD_CONTROL)
|
||||
{
|
||||
expect_ack = MCU_CMD_CONTROL_ACK;
|
||||
@@ -493,6 +615,7 @@ static void mcu_protocol_handle_app_frame(const uint8_t *data, uint16_t len)
|
||||
return;
|
||||
}
|
||||
|
||||
/* 上一条 0x03/0x05 还没回,丢掉新的,避免 SN 对乱 */
|
||||
if (s_proto.app_pending.active)
|
||||
{
|
||||
log_error("app frame busy, drop cmd=0x%02X", cmd);
|
||||
@@ -523,23 +646,29 @@ static void mcu_protocol_handle_gateway_ack(uint16_t cmd,
|
||||
int32_t gateway_int0)
|
||||
{
|
||||
uint8_t sn;
|
||||
uint8_t success;
|
||||
uint8_t mcu_consumed = 0;
|
||||
|
||||
if (!s_proto.report_waiting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* 网关成功 ACK 约定:cmd=0、只有 1 个 int、且 int0=0 */
|
||||
success = (cmd == 0 && gateway_int_cnt == 1 && gateway_int0 == 0) ? 1 : 0;
|
||||
|
||||
if (cmd == 0 && gateway_int_cnt == 1 && gateway_int0 == 0)
|
||||
/* MCU 0x07 优先:成功才回 0x08;失败不回,让 MCU 自己超时重发 0x07 */
|
||||
if (s_proto.report_waiting && success)
|
||||
{
|
||||
sn = s_proto.report_sn;
|
||||
mcu_protocol_send_ack(MCU_CMD_REPORT_ACK, sn, MCU_RESULT_OK);
|
||||
s_proto.report_waiting = 0;
|
||||
mcu_consumed = 1;
|
||||
log_info("gateway report ack ok, MCU 0x08 sn=%u", sn);
|
||||
return;
|
||||
}
|
||||
else if (s_proto.report_waiting)
|
||||
{
|
||||
log_error("gateway report ack fail cmd=%u cnt=%u int0=%ld",
|
||||
(unsigned)cmd, (unsigned)gateway_int_cnt, (long)gateway_int0);
|
||||
}
|
||||
|
||||
log_error("gateway report ack fail cmd=%u cnt=%u int0=%ld",
|
||||
(unsigned)cmd, (unsigned)gateway_int_cnt, (long)gateway_int0);
|
||||
/* 再交给版本上报:若已被 0x07 占用,版本侧会继续等,不会误当成自己的 ACK */
|
||||
mcu_business_on_gateway_ack(success, mcu_consumed);
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
@@ -556,13 +685,21 @@ static void mcu_protocol_try_heartbeat(void)
|
||||
uint8_t buf[MCU_FRAME_MAX_LEN];
|
||||
int32_t total;
|
||||
|
||||
/* 身份都还没通,先别发心跳 */
|
||||
if (!mcu_business_is_identity_ready())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* ble_pending 被 0x14/0x40 等占用时不插心跳;自己等 0x13 时除外 */
|
||||
if (s_proto.ble_pending.active && !s_proto.hb_waiting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (s_proto.hb_waiting)
|
||||
{
|
||||
/* 500ms 内还在等 0x13,不重发 */
|
||||
if ((now - s_proto.last_hb_tx_ms) <= MCU_UART_ACK_WAIT_MS)
|
||||
{
|
||||
return;
|
||||
@@ -571,6 +708,7 @@ static void mcu_protocol_try_heartbeat(void)
|
||||
s_proto.ble_pending.active = 0;
|
||||
s_proto.hb_fail_cnt++;
|
||||
log_error("heartbeat timeout fail=%u", s_proto.hb_fail_cnt);
|
||||
/* 连续 3 次没回:置告警,并刷新 last_rx 避免立刻再打心跳 */
|
||||
if (s_proto.hb_fail_cnt >= MCU_HEARTBEAT_MAX_RETRY)
|
||||
{
|
||||
s_proto.mcu_comm_alarm = 1;
|
||||
@@ -581,6 +719,7 @@ static void mcu_protocol_try_heartbeat(void)
|
||||
}
|
||||
else if ((now - s_proto.last_mcu_rx_ms) < MCU_HEARTBEAT_IDLE_MS)
|
||||
{
|
||||
/* 55s 内 MCU 有过任何合法帧,视为还活着,不发心跳 */
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -623,10 +762,12 @@ static void mcu_protocol_try_work_status(void)
|
||||
}
|
||||
|
||||
ws = mcu_business_get_work_status();
|
||||
/* 状态没变就不再推 0x14;0xFF 表示强制重推 */
|
||||
if (ws == s_proto.last_pushed_status)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* UART 正在等别的 ACK,让下个周期再推 */
|
||||
if (s_proto.ble_pending.active)
|
||||
{
|
||||
return;
|
||||
@@ -671,11 +812,13 @@ static void mcu_protocol_check_app_pending(void)
|
||||
}
|
||||
|
||||
now = mcu_util_get_ms();
|
||||
/* 500ms 窗口内继续等 MCU ACK */
|
||||
if ((now - s_proto.app_pending.send_time) <= MCU_UART_ACK_WAIT_MS)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* 重发 3 次仍无 ACK:主动给 APP 回 FAIL,避免 APP 一直干等 */
|
||||
if (s_proto.app_pending.retry >= MCU_APP_ACK_MAX_RETRY)
|
||||
{
|
||||
log_error("app pending give up cmd expect=0x%02X, notify APP FAIL",
|
||||
@@ -709,6 +852,7 @@ static void mcu_protocol_check_ble_pending(void)
|
||||
{
|
||||
return;
|
||||
}
|
||||
/* 心跳超时由 try_heartbeat 自己处理,这里不管 */
|
||||
if (s_proto.ble_pending.expect_ack == MCU_CMD_HEARTBEAT_ACK)
|
||||
{
|
||||
return;
|
||||
@@ -717,6 +861,7 @@ static void mcu_protocol_check_ble_pending(void)
|
||||
now = mcu_util_get_ms();
|
||||
if ((now - s_proto.ble_pending.send_time) > MCU_UART_ACK_WAIT_MS)
|
||||
{
|
||||
/* 0x14 超时:清 pending,并把 last 置 0xFF,下周期会再推一次 */
|
||||
if (s_proto.ble_pending.expect_ack == MCU_CMD_WORK_STATUS_ACK)
|
||||
{
|
||||
log_error("work_status ack timeout, will retry");
|
||||
@@ -879,3 +1024,19 @@ void mcu_protocol_notify_gateway_ack(uint16_t cmd,
|
||||
evt.gw_int0 = gateway_int0;
|
||||
mcu_protocol_post_event(&evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 网络时间入队
|
||||
* @param unix_s UTC Unix 时间戳(秒)
|
||||
* @param gmtoff_s 时区偏移秒
|
||||
*/
|
||||
void mcu_protocol_notify_network_time(uint64_t unix_s, int32_t gmtoff_s)
|
||||
{
|
||||
McuEvt_t evt;
|
||||
|
||||
memset(&evt, 0, sizeof(evt));
|
||||
evt.type = MCU_EVT_NET_TIME;
|
||||
evt.unix_s = unix_s;
|
||||
evt.gmtoff_s = gmtoff_s;
|
||||
mcu_protocol_post_event(&evt);
|
||||
}
|
||||
|
||||
@@ -2,11 +2,12 @@
|
||||
* @file mcu_protocol.h
|
||||
* @brief MCU 接入协议引擎:事件队列 / 任务 / 命令分发 / ACK / 透传重传 / 心跳
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.12
|
||||
* @version V1.0.0
|
||||
* @date 2026.08.13
|
||||
* @version V1.1.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.12, cyWu, 从 mcu_bridge.c 拆出,仅保留协议机制,
|
||||
* 不含任何业务判断(身份/配网/连接状态见 ../business)
|
||||
* - V1.1.0, 2026.08.13, cyWu, P1:管理命令分发、pending 发送、网关时间入队
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __MCU_PROTOCOL_H__
|
||||
@@ -62,6 +63,13 @@ void mcu_protocol_notify_gateway_ack(uint16_t cmd,
|
||||
uint8_t gateway_int_cnt,
|
||||
int32_t gateway_int0);
|
||||
|
||||
/**
|
||||
* @brief 网关 DEVICEINFO 网络时间(入队,BLE 上下文可调)
|
||||
* @param unix_s UTC Unix 时间戳(秒)
|
||||
* @param gmtoff_s 时区偏移(秒)
|
||||
*/
|
||||
void mcu_protocol_notify_network_time(uint64_t unix_s, int32_t gmtoff_s);
|
||||
|
||||
/*============================================================================*/
|
||||
/* 供 business 层调用的协议发送接口 */
|
||||
/*============================================================================*/
|
||||
@@ -75,6 +83,29 @@ void mcu_protocol_notify_gateway_ack(uint16_t cmd,
|
||||
*/
|
||||
int32_t mcu_protocol_send_cmd(uint8_t cmd, const uint8_t *payload, uint16_t plen);
|
||||
|
||||
/**
|
||||
* @brief 发送并占用 ble_pending,等待 expect_ack
|
||||
* @param cmd 命令字
|
||||
* @param payload 载荷,可为 NULL
|
||||
* @param plen 载荷长度
|
||||
* @param expect_ack 期望的 MCU ACK 命令
|
||||
* @return 帧长;<0 忙或失败
|
||||
*/
|
||||
int32_t mcu_protocol_send_cmd_wait_ack(uint8_t cmd, const uint8_t *payload,
|
||||
uint16_t plen, uint8_t expect_ack);
|
||||
|
||||
/**
|
||||
* @brief BLE 主动 UART 请求是否正在等 ACK(心跳/0x14/0x40 等)
|
||||
* @return 1=忙,0=空闲
|
||||
*/
|
||||
uint8_t mcu_protocol_is_ble_pending(void);
|
||||
|
||||
/**
|
||||
* @brief MCU 0x07 是否已转发、正在等网关 ACK
|
||||
* @return 1=是,0=否
|
||||
*/
|
||||
uint8_t mcu_protocol_is_mcu_report_waiting(void);
|
||||
|
||||
/**
|
||||
* @brief 强制下一轮重新推送 0x14(业务状态变化后调用)
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,540 @@
|
||||
/******************************************************************************
|
||||
* @file usr_rtc.c
|
||||
* @brief 板级 RTC 外设实现(BR28 硬件 rtc_dev_ops + 32K)
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.14
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.14, cyWu, 首次发布
|
||||
******************************************************************************/
|
||||
|
||||
#include "system/includes.h"
|
||||
#include "app_config.h"
|
||||
#include "usr_rtc.h"
|
||||
#include "asm/rtc.h"
|
||||
|
||||
#define LOG_TAG_CONST APP
|
||||
#define LOG_TAG "[USR_RTC]"
|
||||
#define LOG_ERROR_ENABLE
|
||||
#define LOG_DEBUG_ENABLE
|
||||
#define LOG_INFO_ENABLE
|
||||
#include "debug.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有宏 */
|
||||
/*============================================================================*/
|
||||
|
||||
#define USR_RTC_TZ_MAGIC (0x52544331u) /**< 'RTC1',校验 VM 时区 */
|
||||
#define USR_RTC_LOG_PERIOD_MS (60000u) /**< 周期日志间隔 */
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有类型 */
|
||||
/*============================================================================*/
|
||||
|
||||
typedef struct {
|
||||
uint32_t magic;
|
||||
int32_t tz_s;
|
||||
} UsrRtcTzVm_t;
|
||||
|
||||
typedef struct {
|
||||
uint8_t inited;
|
||||
uint8_t tz_valid;
|
||||
uint8_t time_valid;
|
||||
int32_t tz_s;
|
||||
uint32_t unix_utc;
|
||||
uint32_t last_log_ms;
|
||||
void *dev;
|
||||
} UsrRtc_t;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有变量 */
|
||||
/*============================================================================*/
|
||||
|
||||
static UsrRtc_t s_rtc;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有函数声明 */
|
||||
/*============================================================================*/
|
||||
|
||||
static uint8_t usr_rtc_is_leap(uint16_t year);
|
||||
static uint8_t usr_rtc_days_in_month(uint16_t year, uint8_t month);
|
||||
static uint8_t usr_rtc_year_ok(uint16_t year);
|
||||
static void usr_rtc_unix_to_calendar(uint32_t unix_utc, int32_t tz_s,
|
||||
struct sys_time *t);
|
||||
static uint32_t usr_rtc_calendar_to_utc(const struct sys_time *t, int32_t tz_s);
|
||||
static void usr_rtc_load_tz(void);
|
||||
static void usr_rtc_save_tz(int32_t tz_s);
|
||||
static void usr_rtc_refresh_cache(void);
|
||||
|
||||
/*============================================================================*/
|
||||
/* 公有函数 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 打开 rtc 设备并加载时区
|
||||
* @return UsrRtc_Ret_t
|
||||
*/
|
||||
UsrRtc_Ret_t usr_rtc_init(void)
|
||||
{
|
||||
struct sys_time t;
|
||||
|
||||
if (s_rtc.inited)
|
||||
{
|
||||
return USR_RTC_OK;
|
||||
}
|
||||
|
||||
#if TCFG_APP_RTC_EN
|
||||
s_rtc.dev = dev_open("rtc", NULL);
|
||||
if (!s_rtc.dev)
|
||||
{
|
||||
log_error("open rtc fail");
|
||||
return USR_RTC_ERR_NOT_INIT;
|
||||
}
|
||||
#else
|
||||
log_error("TCFG_APP_RTC_EN=0");
|
||||
return USR_RTC_ERR_NOT_INIT;
|
||||
#endif
|
||||
|
||||
usr_rtc_load_tz();
|
||||
s_rtc.inited = 1;
|
||||
|
||||
memset(&t, 0, sizeof(t));
|
||||
read_sys_time(&t);
|
||||
log_info("rtc open %u-%02u-%02u %02u:%02u:%02u tz=%ld tz_ok=%u",
|
||||
t.year, t.month, t.day, t.hour, t.min, t.sec,
|
||||
(long)s_rtc.tz_s, s_rtc.tz_valid);
|
||||
|
||||
usr_rtc_refresh_cache();
|
||||
return USR_RTC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 周期读 RTC,刷新 UTC
|
||||
*/
|
||||
void usr_rtc_run(void)
|
||||
{
|
||||
uint32_t now;
|
||||
|
||||
if (!s_rtc.inited)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
usr_rtc_refresh_cache();
|
||||
|
||||
now = timer_get_ms();
|
||||
if ((now - s_rtc.last_log_ms) >= USR_RTC_LOG_PERIOD_MS)
|
||||
{
|
||||
struct sys_time t;
|
||||
s_rtc.last_log_ms = now;
|
||||
if (usr_rtc_read(&t) == USR_RTC_OK)
|
||||
{
|
||||
log_info("rtc %u-%02u-%02u %02u:%02u:%02u utc=%u valid=%u",
|
||||
t.year, t.month, t.day, t.hour, t.min, t.sec,
|
||||
s_rtc.unix_utc, s_rtc.time_valid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 获取对时状态
|
||||
* @return UsrRtc_Ret_t
|
||||
*/
|
||||
UsrRtc_Ret_t usr_rtc_get_status(void)
|
||||
{
|
||||
if (!s_rtc.inited)
|
||||
{
|
||||
return USR_RTC_ERR_NOT_INIT;
|
||||
}
|
||||
return s_rtc.time_valid ? USR_RTC_OK : USR_RTC_ERR_INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读硬件日历
|
||||
* @param t 输出
|
||||
* @return UsrRtc_Ret_t
|
||||
*/
|
||||
UsrRtc_Ret_t usr_rtc_read(struct sys_time *t)
|
||||
{
|
||||
if (!t)
|
||||
{
|
||||
return USR_RTC_ERR_PARAM;
|
||||
}
|
||||
if (!s_rtc.inited)
|
||||
{
|
||||
return USR_RTC_ERR_NOT_INIT;
|
||||
}
|
||||
read_sys_time(t);
|
||||
return USR_RTC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 写硬件日历
|
||||
* @param t 输入
|
||||
* @return UsrRtc_Ret_t
|
||||
*/
|
||||
UsrRtc_Ret_t usr_rtc_write(const struct sys_time *t)
|
||||
{
|
||||
struct sys_time tmp;
|
||||
|
||||
if (!t)
|
||||
{
|
||||
return USR_RTC_ERR_PARAM;
|
||||
}
|
||||
if (!s_rtc.inited)
|
||||
{
|
||||
return USR_RTC_ERR_NOT_INIT;
|
||||
}
|
||||
if (!usr_rtc_year_ok(t->year) || (t->month < 1) || (t->month > 12) ||
|
||||
(t->day < 1) || (t->hour > 23) || (t->min > 59) || (t->sec > 59))
|
||||
{
|
||||
return USR_RTC_ERR_PARAM;
|
||||
}
|
||||
if (t->day > usr_rtc_days_in_month(t->year, t->month))
|
||||
{
|
||||
return USR_RTC_ERR_PARAM;
|
||||
}
|
||||
|
||||
tmp = *t;
|
||||
write_sys_time(&tmp);
|
||||
usr_rtc_refresh_cache();
|
||||
return USR_RTC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UTC 秒 + 时区 → 本地日历写入 RTC
|
||||
* @param unix_utc UTC 秒
|
||||
* @param tz_s 时区偏移秒
|
||||
* @return UsrRtc_Ret_t
|
||||
*/
|
||||
UsrRtc_Ret_t usr_rtc_set_from_unix(uint64_t unix_utc, int32_t tz_s)
|
||||
{
|
||||
struct sys_time t;
|
||||
UsrRtc_Ret_t ret;
|
||||
|
||||
if (!s_rtc.inited)
|
||||
{
|
||||
return USR_RTC_ERR_NOT_INIT;
|
||||
}
|
||||
if (unix_utc == 0)
|
||||
{
|
||||
log_debug("unix=0, ignore");
|
||||
return USR_RTC_ERR_PARAM;
|
||||
}
|
||||
/* 网关采样到模组写入大约慢 1s,在 UTC 上补偿,跨分跨时由日历换算进位 */
|
||||
unix_utc += USR_RTC_SYNC_COMPENSATE_S;
|
||||
if ((tz_s > USR_RTC_TZ_ABS_MAX_S) || (tz_s < -USR_RTC_TZ_ABS_MAX_S))
|
||||
{
|
||||
log_error("tz out of range %ld", (long)tz_s);
|
||||
return USR_RTC_ERR_PARAM;
|
||||
}
|
||||
if (unix_utc > 0xFFFFFFFFull)
|
||||
{
|
||||
log_error("unix too large");
|
||||
return USR_RTC_ERR_PARAM;
|
||||
}
|
||||
|
||||
usr_rtc_unix_to_calendar((uint32_t)unix_utc, tz_s, &t);
|
||||
if (!usr_rtc_year_ok(t.year))
|
||||
{
|
||||
log_error("year %u not in %u~%u, wait sync",
|
||||
t.year, USR_RTC_YEAR_MIN, USR_RTC_YEAR_MAX);
|
||||
return USR_RTC_ERR_INVALID;
|
||||
}
|
||||
|
||||
ret = usr_rtc_write(&t);
|
||||
if (ret != USR_RTC_OK)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
|
||||
usr_rtc_save_tz(tz_s);
|
||||
usr_rtc_refresh_cache();
|
||||
log_info("set rtc %u-%02u-%02u %02u:%02u:%02u utc=%u tz=%ld",
|
||||
t.year, t.month, t.day, t.hour, t.min, t.sec,
|
||||
s_rtc.unix_utc, (long)tz_s);
|
||||
return USR_RTC_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 时间是否可用
|
||||
* @return 1/0
|
||||
*/
|
||||
uint8_t usr_rtc_is_valid(void)
|
||||
{
|
||||
return (s_rtc.inited && s_rtc.time_valid) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 当前 UTC 秒
|
||||
* @return Unix 秒,无效为 0
|
||||
*/
|
||||
uint32_t usr_rtc_get_unix_utc(void)
|
||||
{
|
||||
return s_rtc.time_valid ? s_rtc.unix_utc : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 当前时区偏移
|
||||
* @return 秒
|
||||
*/
|
||||
int32_t usr_rtc_get_timezone(void)
|
||||
{
|
||||
return s_rtc.tz_valid ? s_rtc.tz_s : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 恢复出厂:清 VM 时区和 RAM 对时状态
|
||||
*/
|
||||
void usr_rtc_clear_tz(void)
|
||||
{
|
||||
UsrRtcTzVm_t vm;
|
||||
|
||||
s_rtc.tz_valid = 0;
|
||||
s_rtc.tz_s = 0;
|
||||
s_rtc.time_valid = 0;
|
||||
s_rtc.unix_utc = 0;
|
||||
|
||||
memset(&vm, 0, sizeof(vm));
|
||||
syscfg_write(CFG_USER_TIM_FLAG, (u8 *)&vm, sizeof(vm));
|
||||
log_info("tz cleared");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 计算星期
|
||||
* @param t 日历
|
||||
* @return 0=周日 … 6=周六
|
||||
*/
|
||||
uint8_t usr_rtc_get_weekday(const struct sys_time *t)
|
||||
{
|
||||
static const uint8_t sakamoto[12] = {
|
||||
0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
|
||||
uint16_t y;
|
||||
|
||||
if (!t || (t->month < 1) || (t->month > 12) || (t->day < 1))
|
||||
{
|
||||
return 0xFF;
|
||||
}
|
||||
|
||||
y = t->year;
|
||||
if (t->month < 3)
|
||||
{
|
||||
y--;
|
||||
}
|
||||
return (uint8_t)((y + y / 4u - y / 100u + y / 400u +
|
||||
sakamoto[t->month - 1] + t->day) % 7u);
|
||||
}
|
||||
|
||||
/*============================================================================*/
|
||||
/* 私有函数 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 是否闰年
|
||||
* @param year 公历年
|
||||
* @return 1/0
|
||||
*/
|
||||
static uint8_t usr_rtc_is_leap(uint16_t year)
|
||||
{
|
||||
if ((year % 400u) == 0)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if ((year % 100u) == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
return ((year % 4u) == 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 某月天数
|
||||
* @param year 年
|
||||
* @param month 月 1~12
|
||||
* @return 天数,非法月返回 0
|
||||
*/
|
||||
static uint8_t usr_rtc_days_in_month(uint16_t year, uint8_t month)
|
||||
{
|
||||
static const uint8_t days[12] = {
|
||||
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
|
||||
if ((month < 1) || (month > 12))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if ((month == 2) && usr_rtc_is_leap(year))
|
||||
{
|
||||
return 29;
|
||||
}
|
||||
return days[month - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 年份是否在合法窗口
|
||||
* @param year 年
|
||||
* @return 1/0
|
||||
*/
|
||||
static uint8_t usr_rtc_year_ok(uint16_t year)
|
||||
{
|
||||
return ((year >= USR_RTC_YEAR_MIN) && (year <= USR_RTC_YEAR_MAX)) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UTC 秒 + 时区 → 本地日历
|
||||
* @param unix_utc UTC 秒
|
||||
* @param tz_s 时区偏移
|
||||
* @param t 输出
|
||||
*/
|
||||
static void usr_rtc_unix_to_calendar(uint32_t unix_utc, int32_t tz_s,
|
||||
struct sys_time *t)
|
||||
{
|
||||
int64_t local;
|
||||
uint32_t days;
|
||||
uint32_t tod;
|
||||
uint16_t y;
|
||||
uint8_t m;
|
||||
uint8_t dim;
|
||||
|
||||
memset(t, 0, sizeof(*t));
|
||||
local = (int64_t)unix_utc + (int64_t)tz_s;
|
||||
if (local < 0)
|
||||
{
|
||||
local = 0;
|
||||
}
|
||||
|
||||
tod = (uint32_t)(local % 86400);
|
||||
days = (uint32_t)(local / 86400);
|
||||
t->sec = (uint8_t)(tod % 60u);
|
||||
t->min = (uint8_t)((tod / 60u) % 60u);
|
||||
t->hour = (uint8_t)(tod / 3600u);
|
||||
|
||||
y = 1970;
|
||||
while (1)
|
||||
{
|
||||
uint32_t diy = usr_rtc_is_leap(y) ? 366u : 365u;
|
||||
if (days < diy)
|
||||
{
|
||||
break;
|
||||
}
|
||||
days -= diy;
|
||||
y++;
|
||||
if (y > 4095u)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
t->year = y;
|
||||
|
||||
for (m = 1; m <= 12u; m++)
|
||||
{
|
||||
dim = usr_rtc_days_in_month(y, m);
|
||||
if (days < dim)
|
||||
{
|
||||
break;
|
||||
}
|
||||
days -= dim;
|
||||
}
|
||||
t->month = m;
|
||||
t->day = (uint8_t)(days + 1u);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 本地日历 − 时区 → UTC 秒
|
||||
* @param t 本地日历
|
||||
* @param tz_s 时区偏移
|
||||
* @return UTC Unix 秒
|
||||
*/
|
||||
static uint32_t usr_rtc_calendar_to_utc(const struct sys_time *t, int32_t tz_s)
|
||||
{
|
||||
uint32_t days = 0;
|
||||
uint16_t y;
|
||||
uint8_t m;
|
||||
int64_t local_s;
|
||||
int64_t utc;
|
||||
|
||||
for (y = 1970; y < t->year; y++)
|
||||
{
|
||||
days += usr_rtc_is_leap(y) ? 366u : 365u;
|
||||
}
|
||||
for (m = 1; m < t->month; m++)
|
||||
{
|
||||
days += usr_rtc_days_in_month(t->year, m);
|
||||
}
|
||||
days += (uint32_t)(t->day - 1u);
|
||||
|
||||
local_s = (int64_t)days * 86400 +
|
||||
(int64_t)t->hour * 3600 +
|
||||
(int64_t)t->min * 60 +
|
||||
(int64_t)t->sec;
|
||||
utc = local_s - (int64_t)tz_s;
|
||||
if (utc < 0)
|
||||
{
|
||||
utc = 0;
|
||||
}
|
||||
return (uint32_t)utc;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 从 VM 加载时区(CFG_USER_TIM_FLAG)
|
||||
*/
|
||||
static void usr_rtc_load_tz(void)
|
||||
{
|
||||
UsrRtcTzVm_t vm;
|
||||
int ret;
|
||||
|
||||
s_rtc.tz_valid = 0;
|
||||
s_rtc.tz_s = 0;
|
||||
memset(&vm, 0, sizeof(vm));
|
||||
ret = syscfg_read(CFG_USER_TIM_FLAG, (u8 *)&vm, sizeof(vm));
|
||||
if ((ret == (int)sizeof(vm)) && (vm.magic == USR_RTC_TZ_MAGIC))
|
||||
{
|
||||
if ((vm.tz_s <= USR_RTC_TZ_ABS_MAX_S) && (vm.tz_s >= -USR_RTC_TZ_ABS_MAX_S))
|
||||
{
|
||||
s_rtc.tz_s = vm.tz_s;
|
||||
s_rtc.tz_valid = 1;
|
||||
log_info("tz from vm %ld", (long)s_rtc.tz_s);
|
||||
return;
|
||||
}
|
||||
}
|
||||
log_debug("tz vm empty");
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 时区写入 VM
|
||||
* @param tz_s 时区偏移秒
|
||||
*/
|
||||
static void usr_rtc_save_tz(int32_t tz_s)
|
||||
{
|
||||
UsrRtcTzVm_t vm;
|
||||
|
||||
s_rtc.tz_s = tz_s;
|
||||
s_rtc.tz_valid = 1;
|
||||
vm.magic = USR_RTC_TZ_MAGIC;
|
||||
vm.tz_s = tz_s;
|
||||
syscfg_write(CFG_USER_TIM_FLAG, (u8 *)&vm, sizeof(vm));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 读 RTC,年份+时区都合法才刷新 UTC
|
||||
*/
|
||||
static void usr_rtc_refresh_cache(void)
|
||||
{
|
||||
struct sys_time t;
|
||||
|
||||
s_rtc.time_valid = 0;
|
||||
s_rtc.unix_utc = 0;
|
||||
if (!s_rtc.inited || !s_rtc.tz_valid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&t, 0, sizeof(t));
|
||||
read_sys_time(&t);
|
||||
if (!usr_rtc_year_ok(t.year))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
s_rtc.unix_utc = usr_rtc_calendar_to_utc(&t, s_rtc.tz_s);
|
||||
s_rtc.time_valid = 1;
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
/******************************************************************************
|
||||
* @file usr_rtc.h
|
||||
* @brief 板级 RTC 外设:打开 "rtc" 设备,读写系统时间,UTC↔本地日历
|
||||
* @author cyWu <1917507415@qq.com>
|
||||
* @date 2026.08.14
|
||||
* @version V1.0.0
|
||||
* @history
|
||||
* - V1.0.0, 2026.08.14, cyWu, 首次发布(硬件 RTC,不对闹钟)
|
||||
******************************************************************************/
|
||||
|
||||
#ifndef __USR_RTC_H__
|
||||
#define __USR_RTC_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include "system/sys_time.h"
|
||||
|
||||
/*============================================================================*/
|
||||
/* 宏定义 */
|
||||
/*============================================================================*/
|
||||
|
||||
#define USR_RTC_YEAR_MIN (2026u) /**< 合法年份下限 */
|
||||
#define USR_RTC_YEAR_MAX (2098u) /**< 合法年份上限 */
|
||||
#define USR_RTC_TZ_ABS_MAX_S (57600) /**< 时区偏移绝对值上限(±16h) */
|
||||
#define USR_RTC_SYNC_COMPENSATE_S (1u) /**< 网关对时延迟补偿,写入 RTC 前加的秒数 */
|
||||
|
||||
/*============================================================================*/
|
||||
/* 错误码 */
|
||||
/*============================================================================*/
|
||||
|
||||
typedef enum {
|
||||
USR_RTC_OK = 0,
|
||||
USR_RTC_ERR_PARAM,
|
||||
USR_RTC_ERR_BUSY,
|
||||
USR_RTC_ERR_TIMEOUT,
|
||||
USR_RTC_ERR_NOT_INIT,
|
||||
USR_RTC_ERR_INVALID, /**< 年份非法,等待 App 对时 */
|
||||
USR_RTC_ERR_UNKNOWN
|
||||
} UsrRtc_Ret_t;
|
||||
|
||||
/*============================================================================*/
|
||||
/* 外部接口 */
|
||||
/*============================================================================*/
|
||||
|
||||
/**
|
||||
* @brief 打开 "rtc" 设备,加载时区,读一次硬件时间
|
||||
* @return UsrRtc_Ret_t
|
||||
* @note 可重复调用;板级需已 REGISTER_DEVICES 注册 rtc
|
||||
*/
|
||||
UsrRtc_Ret_t usr_rtc_init(void);
|
||||
|
||||
/**
|
||||
* @brief 周期读 RTC,刷新 UTC 缓存(给业务用)
|
||||
*/
|
||||
void usr_rtc_run(void);
|
||||
|
||||
/**
|
||||
* @brief 获取对时状态
|
||||
* @return USR_RTC_OK=时间合法,ERR_INVALID=等待对时,ERR_NOT_INIT=未打开
|
||||
*/
|
||||
UsrRtc_Ret_t usr_rtc_get_status(void);
|
||||
|
||||
/**
|
||||
* @brief 读硬件日历(本地时区)
|
||||
* @param t 输出
|
||||
* @return UsrRtc_Ret_t
|
||||
*/
|
||||
UsrRtc_Ret_t usr_rtc_read(struct sys_time *t);
|
||||
|
||||
/**
|
||||
* @brief 写硬件日历(本地时区)
|
||||
* @param t 输入
|
||||
* @return UsrRtc_Ret_t
|
||||
*/
|
||||
UsrRtc_Ret_t usr_rtc_write(const struct sys_time *t);
|
||||
|
||||
/**
|
||||
* @brief App/网关下发 UTC 秒 + 时区偏移,写入 RTC
|
||||
* @param unix_utc UTC Unix 秒;0 视为无效
|
||||
* @param tz_s 时区偏移秒(东八区 28800)
|
||||
* @return UsrRtc_Ret_t
|
||||
* @note 网关从子设备连上开始计时,每 12 小时经 DEVICEINFO 同步一次;
|
||||
* timestamp + tz → 本地年月日时分秒 → write_sys_time;tz 写入 VM
|
||||
*/
|
||||
UsrRtc_Ret_t usr_rtc_set_from_unix(uint64_t unix_utc, int32_t tz_s);
|
||||
|
||||
/**
|
||||
* @brief 年份合法且已有时区
|
||||
* @return 1/0
|
||||
*/
|
||||
uint8_t usr_rtc_is_valid(void);
|
||||
|
||||
/**
|
||||
* @brief 当前 UTC Unix 秒(日历换算后再减时区)
|
||||
* @return 无效时返回 0
|
||||
*/
|
||||
uint32_t usr_rtc_get_unix_utc(void);
|
||||
|
||||
/**
|
||||
* @brief 当前时区偏移秒
|
||||
* @return 未保存过时返回 0
|
||||
*/
|
||||
int32_t usr_rtc_get_timezone(void);
|
||||
|
||||
/**
|
||||
* @brief 恢复出厂:清 VM 时区和 RAM 对时状态
|
||||
* @note 硬件日历不改;下次需网关重新对时。0x1D 在此之后回全 0
|
||||
*/
|
||||
void usr_rtc_clear_tz(void);
|
||||
|
||||
/**
|
||||
* @brief 由日历计算星期
|
||||
* @param t 本地日历
|
||||
* @return 0=周日 … 6=周六;参数非法返回 0xFF
|
||||
*/
|
||||
uint8_t usr_rtc_get_weekday(const struct sys_time *t);
|
||||
|
||||
#endif /* __USR_RTC_H__ */
|
||||
Reference in New Issue
Block a user