docs: add design specification (architecture/protocol/conventions/extension guide)

This commit is contained in:
evan.liu 2026-09-05 08:16:50 +08:00
parent 3e87d333cb
commit 30b5dd908b

238
docs/设计说明书.md Normal file
View File

@ -0,0 +1,238 @@
# mothercup(母乳杯)设计说明书
V1.0 / 2026-09-05
本文整合当前系统的架构、协议、约定与扩展指南,供后续功能扩展时查阅。
细则以 `docs/ble_protocol.md`(协议权威)、`docs/开发日志.md`(踩坑根因)、
`AGENTS.md`(工程约定)为准;本文是三者 + 真机联调经验的汇总索引。
---
## 1. 系统组成
```
┌─────────────┐ BLE (GATT) ┌──────────────────────┐
│ 手机 App │ ◄─────────────► │ 设备 N32WB031 │
│ SmartAssiter│ │ Boot + 双bank APP │
└─────────────┘ │ USART1 (PB6/PB7) │
┌─────────────┐ BLE / UART │ ↑ │
│ PC 工具 │ ◄─────────────► │ └─ SWD (PA4/PA5) │
│ tools/ │ └──────────────────────┘
└─────────────┘
```
| 端 | 位置 | 技术栈 | 职责 |
|---|---|---|---|
| Bootloader | `Boot/` | C / ARMCC / 16KB @0x01000000 | 校验 bootsetting 记录 → 按 active bank 跳转 |
| 应用固件 | `mcm-ddc-ble/` | C / FreeRTOS V9.0.0 原生 API | BLE GATT 服务、CLI、参数、OTA |
| 手机 App | `SmartAssiter/` | uni-app Vue3 + TS(HBuilderX 工程) | 参数展示、CLI 终端、参数读写、OTA |
| PC 工具 | `tools/` | Python(无三方依赖的出包脚本 + bleak/pyserial 测试客户端) | 出包、烧录、OTA/CLI 参考实现 |
厂商 SDK/PDF 在 `nations-tec/`(**只读**)。SDK 源码经相对路径被工程引用,
各工程目录不能脱离仓库根单独移动。
## 2. 硬件平台与资源约束
- SoC:国民技术 N32WB031(板载硅片实测 **256KB Flash**,Cortex-M0 64MHz,
RAM 48KB+16KB)
- **RAM 铁律**:低 16KB(0x20000000~0x20003FFF)被芯片 ROM/BLE 子系统占用,
任何工程 IRAM 执行区必须从 **0x20004000** 起(否则 BLE init 必 HardFault)
- **SWD 引脚 SWCLK=PA4 / SWDIO=PA5,应用程序绝对禁止占用**
- 板载 LED:LED1=PB0、LED2=PA6;USART1:TX=PB6 / RX=PB7(AF4,115200 8N1)
- 新增 GPIO 前核对开发日志 §4 引脚分配表
- **不要直接写寄存器**;勘误修复必须先在 SDK 源码交叉验证地址用途
(曾按勘误表写 AFEC 寄存器导致 BLE 射频挂死,开发日志 §28)
## 3. Flash 布局与启动
| 区域 | 地址 | 大小 | 说明 |
|---|---|---|---|
| Bootloader | 0x01000000 | 16KB | 只校验 bootsetting 记录自身(magic+CRC32)后跳转 |
| bootsetting | 0x01004000 | 8KB | active bank、双 bank 的 addr/size/crc32/version |
| APP_DATA | 0x01006000 | 8KB | 区头 `caiic_params_t` 参数记录(magic `0xCA12DA7A`+CRC32) |
| APP1 | 0x01008000 | 112KB | bank1 |
| APP2 | 0x01024000 | 112KB | bank2 |
- Boot **不校验镜像 CRC**(CRC 在 OTA 过程中完成);记录无效/地址越界回退 APP1
- **VTOR**:Cortex-M0 无 SCB->VTOR,用 `PWR->VTOR_REG`。BLE init 前指向 APP 自身
向量表(`0x80000000|0x01008000`);`ns_ble_stack_init()` 之后必须为 0,
**绝对不要在 BLE init 后重设 VTOR**(开发日志 §18)
- 固件不接 ns_sleep 低功耗(与 FreeRTOS tick 冲突),main 里永久锁睡眠保 SWD
## 4. 版本号与出包约定
- **每改固件必递增** `mcm-ddc-ble/inc/app_version.h` 的 `APP_FW_VERSION` /
`APP_FW_VERSION_NUM`(`0x00MMmmpp`,补丁位 +1)
- 构建:Keil µVision5(ARMCC V5.06u6),要求 **0 Error 0 Warning**
- **出包必须 `UV4 -r` 全量重建**(增量构建产生过砖包,开发日志 §24),
一键 `tools\make_package.bat`,产物在 `tools/out/`:
- `mothercup_ble_prod.hex/.bin` — 生产整片包(Boot + 缺省 bootsetting + APP1)
- `mothercup_ble_ota.bin` — OTA 发布单文件(52B 头 + 双 bank 载荷,格式见
ble_protocol.md §7)
- 烧录:`tools\flash_package.bat`(NSpyocd 整片);Keil 调试下载必须
**Erase Sectors**(整片擦除会杀掉 Boot)
- `merge_image.py` 手动调用必须显式传参(默认路径指向已删除的旧工程)
## 5. BLE 接口(详见 ble_protocol.md §1/§2/§5)
广播名 `CAIIC-MCM-20260902`;服务 `00002760-08C2-11E1-9073-0E8AC72E1001`:
| 特征尾段 | 属性 | 通道定位 |
|---|---|---|
| e0001 | Write NoRsp | 帧协议下行(0xCA 字节流) |
| e0002 | Notify | 帧协议上行(V1.00.19 修复后可用) |
| e0003 | Read+Write | **CLI 通道**:写命令行(≤63B) → 分包读回文本(0 长度包=结束,上限 1024B) |
| e0004 | Read | **参数通道**:全信息项 TLV(同 INFO_RSP all) |
| e0005 | Read+Write | **OTA 通道**:写一传输帧 → 轮询读回 OTA_RSP(至非空) |
- MTU:连接后设备主动发起 247 交换;单次写入上限 = att_mtu − 3
- 帧格式:`CA | TYPE | SEQ | LEN(LE16) | PAYLOAD | CRC16-CCITT(LE)`,
**CRC 覆盖 TYPE..PAYLOAD 共 4+payLen 字节**(新旧实现必须统一)
- 信息项:0x01 版本 / 0x02 温度(0.1℃) / 0x03 风扇 / 0x04 电压(mV) /
0x05 运行时长(s) / 0x06 堆剩余(B) / 0x07 当前 bank(OTA 选包依据)
## 6. CLI 命令集(UART 与 BLE e0003 完全一致)
`help / version / sysinfo / devinfo / blelog / led / appget [json] /
appset / bsdump / bsset / appsw [1|2] / ota / reset / factory / uartrst / uartinfo`
- 维护区(bootsetting / APP_DATA)**只暴露结构化命令,无裸读写**
- `appsw` 切 bank 前校验目标镜像 CRC 与记录一致,不匹配拒绝
- `ota` 命令使 UART 进入二进制 OTA 模式(握手标记行 `[ota] binary mode ON`)
- `reset`/`factory`/`appsw` 均先应答、延时后复位(保证应答送达)
## 7. OTA 设计(详见 ble_protocol.md §6/§6.6/§7)
- **双 bank 直写**,无中转区;惰性擦扇区;仅 4B 对齐暂存
- **传输层通道无关**:同一 0xCA 帧会话(BEGIN/DATA/END/ABORT → OTA_RSP)
可跑在 BLE e0005 / UART 二进制模式 / 旧帧协议通道;设备侧 OTA 引擎经
通道注册的 sink 出口应答,会话全程只允许一个通道占用
- **逐帧锁步**:每帧等 ack(ack.offset = 设备期望的下一字节);ack 超时直接
重发同一帧(≤3 次);重复帧回 BAD_STATE+期望 offset,恰好等于本帧结束位置
则视为 ack(丢 ack 容错);status=2 按其 offset 重发即再同步
- **END 可靠送达**(V1.00.20):设备先发 ok 应答,等主机取走 + 300ms 宽限
才复位,2s 兜底
- **选包规则**:读 CUR_BANK → 目标 = 对侧 bank → 从头取对应载荷;三重防呆:
客户端向量表预检 + 固件 OTA_END bank_mismatch(status=6) + `appsw` CRC 校验
- 主机兜底:END 应答丢失不等于失败,复位后重连复核 CUR_BANK+FW_VERSION 定论
## 8. 手机 App 设计(SmartAssiter)
### 8.1 分层
```
pages/ 页面(home / ota / my / bleTest 终端 / 云端账号页)
src/mcm/ 设备业务层(只碰业务,不碰 BLE 细节)
McmCli.ts CLI 通道(Promise 链串行;写命令→分包读回)
McmInfo.ts 参数通道(TLV 解析,含 CUR_BANK)
McmOta.ts OTA 通道(combo 包解析 + 锁步会话 + 重发/再同步/中止)
McmCommands.ts 业务命令封装(sysinfo/devinfo/appget/appset…)
src/BluetoothManager.ts 蓝牙管理(扫描/连接/服务发现/读写/MTU)
common/ api/ token、已连设备记忆、云端 HTTP
```
### 8.2 通道隔离约定(重要)
- **每个特征值一条独立通道**:读请求按 UUID 分键排队/超时(3s)/分发;
写无跨特征阻塞;CLI 与参数轮询与 OTA 可并行,一路故障不扩散
- 安卓兼容(实测踩坑):服务发现后 300ms 稳定期;读/写被误报
10007 "property not support" 时各延时 1600ms 重试一次;连接初始化
**先 setMtu 再发起读写**(并发 GATT 操作会被安卓无声丢弃)
- home 页参数轮询由 **Auto refresh 开关**控制,**缺省关闭**(避免干扰 OTA)
- App 侧错误码:0 成功;10000 连接失败;10001 断开;10002 蓝牙未开;
10003 无权限;10004/10005 服务/特征缺失;10008 读写/MTU/超时
### 8.3 OTA 页文件读取(Android 10+ 分区存储)
- `/sdcard/Download` 直读被禁(targetSdk≥29);`uni.chooseFile` App 端不存在
- 现行方案:**SAF 选择器**(plus.android `ACTION_GET_CONTENT`,免权限)→
ContentResolver 流 → Scanner 整流读 String(ISO-8859-1 字节 1:1)→
**magic 自验**,不过则自动逐字节 `read()` 直读(二进制精确兜底)
- plus 桥注意:**byte[] 参数按值拷贝,Java 侧写入不会回传 JS**;
Java 对象方法一律用 `plus.android.invoke(obj, name, args...)` 调用;
App 端 vue3 无 `window`,plus 挂在 `globalThis.plus`
- 备用:adb push 到 `_doc/`(应用私有目录)后走手动路径
## 9. PC 工具(tools/)
| 工具 | 用途 |
|---|---|
| `make_package.bat` | 一键出包(UV4 -r 全量重建 Boot+APP → merge_image.py) |
| `merge_image.py` | 合并整片包 + 生成 OTA 发布件(自动取版本号);**手动调用必须显式传参** |
| `flash_package.bat` | NSpyocd 整片烧录(需 NS-LINK,先关 Keil) |
| `ble_ota_update.py` / `ble_ota.bat` | **OTA 参考实现**(BLE 默认,`--uart COMx` 串口);锁步/重发/回连复核的标准实现,新主机端照此移植 |
| `ble_cli_test.py` / `ble_cli.bat` | BLE CLI 测试客户端(帧协议 + `-rw` 读写特征两种模式) |
| `ble_temp_watch.py` | 温度监测 |
Python 环境:`tools/.venv-ble`(bleak/pyserial);本机 `python` 可能是商店占位
stub,脚本内已处理全路径查找。
## 10. 扩展指南
### 10.1 加一条 CLI 命令
1. `mcm-ddc-ble/src/cli_core.c` 的 `s_cmds[]` 加 `{name, help, handler}`,实现 handler
(输出经 `cli_printf`,自动同时可用 UART 与 BLE e0003)
2. 递增版本号 → 出包 → 烧录验证
3. App 侧如需业务封装,在 `McmCommands.ts` 加函数(命令名常量集中在顶部)
### 10.2 加一个信息项(温度/转速类遥测)
1. 固件 `app_info.c` 加数据源,`app_ble_proto.c` 的 TLV 组装加一项(新 id)
2. 协议文档 §5 表格登记;App `McmInfo.ts` 的 `parseTlv` 加分支 + `DeviceInfo`
加字段(旧 App 会按 len 跳过未知 id,天然兼容)
3. 版本号 + 出包 + 修改记录
### 10.3 加一个 GATT 特征
1. 固件 `app_rdtss.c` 的 `app_rdts_att_db` 加声明+值条目(注意 `RDTSS_IDX_NB`),
UUID 取 `...e0006` 起顺延;读写回调在 `app_ble_proto.c` 按 att_idx 分发
2. App `BluetoothManager.ts` 加 UUID 常量与读写封装;`scanServices` 校验清单
按需更新(**注意:校验清单变严会让旧固件连不上,考虑兼容性**)
3. 手机端 GATT 缓存注意:特征表变更后若手机行为异常,系统蓝牙关开/
忽略设备清缓存
### 10.4 加一条 OTA 传输通道
设备侧 OTA 引擎与通道解耦:新通道只需①把收到的字节流喂给帧重组器、
②注册应答 sink。参考 UART 通道(`ota` 命令模式切换)与 BLE e0005 通道
(写帧→读应答轮询)。主机端参考 `tools/ble_ota_update.py` 与
`SmartAssiter/src/mcm/McmOta.ts`。
### 10.5 加 APP_DATA 参数字段
1. `app_params.h` 的 `caiic_params_t` 加字段(保持 magic+CRC 结构;reserved
有余量,优先占用 reserved 保持记录长度 52B 不变)
2. `appget/appset` 命令加 key;JSON 模式同步
3. App `McmCommands.ts` 的 `AppParams` 类型与解析同步
### 10.6 App 加一个页面/业务
- BLE 一律走 `BluetoothManager` 的 `MyApiResult` 回调约定;设备业务封装进
`src/mcm/`;不改 `uni_modules` 官方组件;页面注意**缩进用 tab**
- HBuilderX 运行调试;云端接口走 `api/httpInstance.ts`(SERVER_LIST 三地址)
## 11. 验证与排障手段
1. Keil 命令行构建 0 Error/0 Warning;出包核对 map 的 `__initial_sp` 与 bin
向量表初始 SP 一致
2. 串口 banner/`caiic->`/`devinfo` 人工确认;`blelog on` 抓 BLE 帧(走 printf
不经 BLE,避免自激)
3. BLE 链路对照:PC `ble_cli_test.py` / `ble_ota_update.py` 与手机 App 互验
4. App 侧控制台日志体系:`[MCM]`(连接/tx/rx hex/超时/重试)、`[CLI]`、
`[INFO]`、`[OTA]`(帧)、`[OTA-PAGE]`(选包/升级流程)、`[HOME]`
5. 变砖恢复:SWD 重烧整片包
## 12. 铁律清单(变更前必读)
1. IRAM 从 0x20004000 起;BLE init 后 VTOR 必须为 0 且不再重设
2. PA4/PA5(SWD)禁止占用;不用 cmsis_os 封装;SysTick 由 FreeRTOS port 接管
3. USART1_IRQn 优先级 3(ISR 内调 FromISR API 的硬性要求);BLE_SW/FIFO IRQ
优先级 0 且禁止 FromISR API
4. ke_msg/ke_timer 只允许在 BLE 调度任务上下文运行;CLI 输出经环形缓冲由
BLE 任务发上行
5. 出包必须 UV4 -r 全量;Keil 下载只能 Erase Sectors;每改固件必升版本号
6. 帧 CRC16 覆盖 4+payLen 字节;OTA 逐帧锁步;END 应答送达后才复位
7. 安卓 BLE:初始化先 MTU 后读写;10007 重试;通道隔离;OTA 期间不开轮询
8. 固件重大改动追加 `docs/开发日志.md`;协议改动同步 `docs/ble_protocol.md`;
App 改动写 `SmartAssiter/docs/修改记录_日期_主题.md`;同步更新 `AGENTS.md`