mothercup/docs/开发日志.md

1329 lines
82 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# N32WB031 基础工程开发日志
日期:2025-06-09
工程:`H:\caiic_workspace\mothercup\embeddedSrc`
目标芯片:N32WB031(国民技术 BLE SoC,Cortex-M0,64MHz,Flash 256KB,RAM 48KB)
## 1. 任务目标
基于 N32WB03x SDK V2.0.0 新建裸工程,实现:
- UART 串口打印(printf 重定向)
- 集成 FreeRTOS,构建实时多任务架构
- GPIO 点灯(LED 周期翻转)
- 引脚分配不得与 SWD 调试管脚冲突
## 2. 参考资料
| 资料 | 路径 |
|---|---|
| SDK V2.0.0 | `nations-tec/N32WB03x_SDK_V2.0.0` |
| 器件包 | `nations-tec/N32WB03x_DFP.1.4.0.pack` |
| 用户手册 | `nations-tec/CN_UM_N32WB03X_Series_User_Manual_V1.5.pdf` |
| 硬件设计指南 | `nations-tec/CN_DG_N32WB03x_Series_Chips_Hardware_Design_Guide_V1.4.pdf` |
| STB 开发板资料 | `nations-tec/N32WB031_STB_V1.3` |
参考例程:
- 串口:`projects/n32wb03x_EVAL/peripheral/USART/Printf`
- 点灯:`projects/n32wb03x_EVAL/peripheral/GPIO/LedBlink`
- FreeRTOS:`projects/n32wb03x_EVAL/application/FreeRTOS/FreeRTOS_ThreadCreation`
## 3. 关键硬件结论
- **SWD 调试管脚:SWCLK=PA4,SWDIO=PA5**(复位后默认 AF0)——本工程所有 GPIO 分配均避开这两个脚
- STB 开发板板载 LED:LED1=PB0(跳线 J21)、LED2=PA6(跳线 J22),蓝色 LED,4.7K 限流
- 串口排针 J3 已引出 PB6/PB7
- 注意:PB8/PB9 默认接 32.768K 晶振;PB3 有下拉注意事项(手册 5.2.4)
## 4. 引脚分配表
| 功能 | 引脚 | 复用 | 说明 |
|---|---|---|---|
| USART1_TX | PB6 | AF4 | 115200 8N1 |
| USART1_RX | PB7 | AF4 | |
| LED1 | PB0 | GPIO 推挽输出 | 板载 D1 |
| LED2 | PA6 | GPIO 推挽输出 | 板载 D2 |
| SWDCLK | PA4 | AF0 | 调试占用,应用程序禁用 |
| SWDIO | PA5 | AF0 | 调试占用,应用程序禁用 |
## 5. 工程结构
```
embeddedSrc/
├── app/
│ ├── main.c # 入口:时钟更新 → USART 初始化+banner → LED 初始化 → 建队列/任务 → vTaskStartScheduler
│ ├── bsp_usart.c/.h # USART1 初始化 + fputc 重定向('\n' 前自动补 '\r')
│ ├── bsp_led.c/.h # LED1/LED2 初始化、on/off/toggle
│ ├── app_tasks.c/.h # 3 个 FreeRTOS 任务 + 日志队列
│ ├── n32wb03x_it.c/.h # NMI / HardFault 异常处理
│ └── FreeRTOSConfig.h # heap 8KB,SVC/PendSV/SysTick 映射给 FreeRTOS port
├── MDK-ARM/
│ ├── embeddedSrc.uvprojx
│ └── embeddedSrc.uvoptx
└── docs/
└── 开发日志.md(本文件)
```
SDK 源码(固件库、CMSIS、FreeRTOS V9.0.0、启动文件)以相对路径 `..\..\nations-tec\N32WB03x_SDK_V2.0.0` 引用,不复制到工程内。
工程配置:器件 N32WB031,IROM 0x01000000 / 0x40000,IRAM 0x20000000 / 0xC000,MicroLIB,宏定义 `N32WB03X, USE_STDPERIPH_DRIVER`。
## 6. 实时多任务架构
使用 FreeRTOS 原生 API(xTaskCreate / vTaskDelay / xQueue),不用 cmsis_os 封装。
| 任务 | 功能 | 周期/触发 | 栈(字) | 优先级 |
|---|---|---|---|---|
| LedTask | 翻转 LED1,向日志队列发送 LogMsg_t | 500ms | 128 | 2 |
| LedTask2 | 翻转 LED2,向日志队列发送 LogMsg_t | 1000ms | 128 | 2 |
| PrintTask | xQueueReceive 阻塞取消息并 printf | 队列驱动 | 256 | 3 |
生产者-消费者模型:LED 任务只负责投递消息,打印任务独占串口输出,避免多任务直接 printf 造成的输出交错。
调度相关:
- SysTick 由 FreeRTOS port 接管(FreeRTOSConfig.h 中 `xPortSysTickHandler → SysTick_Handler` 映射),main 中不再手动 `SysTick_Config`
- `configTOTAL_HEAP_SIZE = 8*1024`(48KB RAM 余量充足)
## 7. 编译验证
- 工具链:Keil µVision5(`D:\Keil_v5\UV4\UV4.exe`,ARMCC V5.06 update 6)
- 命令行:`UV4.exe -b embeddedSrc.uvprojx -j0 -o build.log`(工作目录 `embeddedSrc/MDK-ARM`)
- 结果:**0 Error(s), 0 Warning(s)**
- 体积:`Code=8248 RO-data=724 RW-data=100 ZI-data=10532`
- 产物:`Objects/embeddedSrc.axf`、`Objects/embeddedSrc.hex`、`bin/embeddedSrc.bin`
- 自查:全工程无 PA4/PA5 占用;SVC/PendSV/SysTick_Handler 无重复定义
## 8. 烧录与运行
1. Keil 打开 `embeddedSrc/MDK-ARM/embeddedSrc.uvprojx`,F7 编译
2. NS-LINK 接 SWD(PA4/PA5),F8 下载
3. 串口助手接 PB6(TX)/PB7(RX),115200 8N1
4. 复位后预期输出:启动 banner + `LED1 toggled, count = N` / `LED2 toggled, count = N` 滚动日志
注意:未在真实硬件上运行验证。若 LED 不亮,检查 STB 板 J21/J22 跳线;若串口无输出,检查 J3 排针接线。
## 9. 后续计划
- 板卡实测验证 LED 与串口日志
- 按需接入 BLE 协议栈(SDK ble 例程为裸机 rwip_schedule 调度,与 FreeRTOS 架构需做整合设计)
---
日期:2026-09-01
## 10. UART DMA 发送 + 日志开关 + CLI 命令框架
### 10.1 DMA 可行性结论
N32WB031 具备 DMA 外设(5 通道),固件库支持 `USART_EnableDMA` 与通道重映射
(`DMA_RequestRemap`),USART1_TX 可映射到 DMA_CH1(参考官方例程
`projects/n32wb03x_EVAL/peripheral/USART/DMA_Interrupt`)。**硬件支持,已实现 DMA 发送。**
### 10.2 串口驱动扩展(bsp_usart)
- TX 双通道并存:`fputc` 轮询发送保留给调度器启动前的 banner 与 PrintTask;
新增 `bsp_usart_write_dma(buf, len)` 走 DMA_CH1(每次发送重新 `DMA_Init`,轮询
`DMA_FLAG_TC1` 完成标志 + 等 `USART_FLAG_TXC` 移位寄存器排空),供 CLI 输出使用。
- RX 新增 RXDNE 中断:`USART1_IRQHandler`(n32wb03x_it.c)→ `bsp_usart_rx_isr_handler()`,
字节经 `xQueueSendFromISR` 投递到 64 字节接收队列;应用侧用
`bsp_usart_read_byte(ch, timeout_ms)` 阻塞取字节。NVIC 优先级 = 3(最低,满足
`configMAX_SYSCALL_INTERRUPT_PRIORITY` 约束)。
- 新增 TX 互斥锁 `bsp_usart_tx_lock/unlock`:调度器未运行时为空操作;
PrintTask 的整条 printf 与 CLI 的整段应答各持锁一次,避免 log 开启后两类输出字节级交错。
### 10.3 主动打印开关
`app_tasks` 新增 `AppTasks_SetLogEnabled()/AppTasks_GetLogEnabled()`,**默认关闭**。
LED 任务照常翻转,仅在开关打开时才向日志队列投递消息。运行时通过 CLI
`log on|off` 切换,`log` 无参数查询当前状态。
### 10.4 CLI 框架(app_cli)
- 独立任务 `CliTask`(栈 256 字,优先级 3),独占完成:行接收 → 解析 → 执行 → 应答,
应答全部走 DMA TX(`cli_write`/`cli_printf`,需显式带 `\r\n`)。
- 命令表 `CliCmd_t s_cmds[]`(name/help/handler),新增命令只需在表中加一项。
- 行结束符兼容 **`\r\n`、`\n\r`、`\r`、`\n`** 四种:首个 `\r` 或 `\n` 触发分发,
紧跟的互补字符被吞掉一次。支持退格(0x08/0x7F)编辑,行缓冲 64 字节,超长响铃。
- 内置命令:`help`、`version`、`log [on|off]`、`led <1|2> <on|off|toggle>`。
- 提示符 `n32> `,空行仅重显提示符。
### 10.5 编译验证
- `UV4.exe -b embeddedSrc.uvprojx -j0 -o build.log`:**0 Error(s), 0 Warning(s)**
- 体积:`Code=14712 RO-data=944 RW-data=112 ZI-data=10696`(Flash/RAM 余量仍充足)
- 新增源文件:`app/app_cli.c/.h`;uvprojx 的 FWLB 组加入 `n32wb03x_dma.c`
- 烧录后预期:banner 之后出现 `CLI ready. Type 'help' for commands.` 与 `n32> ` 提示符;
默认无 LED 滚动日志,`log on` 后恢复。
- 注意:尚未上板实测,DMA 与中断路径需硬件验证。
---
日期:2026-09-01
## 11. 任务栈统一调整为 1024 字
- 上板实测 DMA/CLI/日志开关运行正常后,4 个任务栈统一调整为 1024 字(4KB/任务):
`LED_TASK_STACK` / `PRINT_TASK_STACK`(app_tasks.c)、`CLI_TASK_STACK`(app_cli.c)。
- 4 任务共 16KB 栈,超出原 8KB heap,故 `configTOTAL_HEAP_SIZE` 由 8KB 调至 20KB
(含队列、TCB、空闲任务栈 128 字的开销)。
- 编译:0 Error / 0 Warning;体积 `Code=14712 RO-data=944 RW-data=112 ZI-data=22984`,
ZI 约占 48KB RAM 的 47%,余量充足。
---
日期:2026-09-01
## 12. CLI 增强:Ctrl+Z 关日志、Tab 补全、命令历史
均在 `app_cli.c` 的 CliTask 中实现:
- **Ctrl+Z(0x1A)**:立即 `AppTasks_SetLogEnabled(0)` 关闭主动打印,打印 `[log off]`
并重绘当前输入行。用于 log 滚动刷屏时快速停止。
- **Tab(0x09)自动补全**:仅补全行首命令名(已含空格则不动作)。唯一匹配则补全
并追加空格;多个匹配则先扩展公共前缀,无法扩展时列出候选并重绘输入行。
- **命令历史**:环形缓冲 8 条 × 64 字节(`s_history`),提交时去重(空行、与上一条
相同不入队);上/下方向键(`ESC [ A` / `ESC [ B` 转义序列状态机解析)回翻历史,
越过最新一条回到空行。回翻采用"清行重绘"(`\r` + 提示符 + 空格擦除 + 重显)。
- `help` 输出末尾附按键说明。
- 编译:0 Error / 0 Warning;体积 `Code=15688 RO-data=944 RW-data=112 ZI-data=23496`。
---
日期:2026-09-01
## 13. CLI 提示符修改
- 命令行提示符由 `n32> ` 改为 `caiic->`(`app_cli.c` 中 `CLI_PROMPT` 宏)。
- 编译:0 Error / 0 Warning;体积 `Code=15692 RO-data=944 RW-data=112 ZI-data=23496`。
---
日期:2026-09-01
## 14. 软件版本号 + sysinfo 系统信息命令
- 新增 `app/app_version.h`:`APP_FW_VERSION "V1.00.01"`,启动 banner 与 `version`
命令均输出版本号。
- 新增 CLI 命令 `sysinfo`:输出运行时间(tick/秒)、任务数、heap 总量/当前空闲/
历史最小空闲,以及 `vTaskList` 任务表(名称/状态/优先级/栈高水位/编号,
栈单位为字)。`vTaskList` 行尾自带 `\r\n`,直接 DMA 输出。
- `FreeRTOSConfig.h` 新增 `configUSE_STATS_FORMATTING_FUNCTIONS 1`(`vTaskList`
依赖,同时需要 `configUSE_TRACE_FACILITY 1`,已具备)。
- 编译:0 Error / 0 Warning;体积 `Code=16944 RO-data=1020 RW-data=112 ZI-data=23880`。
---
日期:2026-09-01
## 15. BLE 接入:自定义 GATT 透传服务(CLS)+ FreeRTOS 整合
### 15.1 工程与链接布局
- 器件由 `N32WB031` 改为 `N32WB031KEQ6-2`(512KB Flash 型号);APP 链接到
**APP1 = 0x01008000,大小 0x38000(224KB)**(uvprojx 的 Cpu/IROM/OCR_RVCT4、
FlashDriverDll `-FL080000`、RegisterFile/SFDFile 同步修改)。IRAM 保持
`0x20000000/0xC000`。
- LDads Misc 加入 BLE ROM 符号表 `symbol_g15.obj`(相对路径
`..\..\nations-tec\N32WB03x_SDK_V2.0.0\middlewares\Nationstech\ble_library\ns_ble_stack\symdef\`)。
- 新增源码组:BLE_STACK(lib_att.c / rwip.c / rwip_driver.c)、BLE_PROFILE
(prf.c / prf_utils.c / rdtss.c / rdtss_task.c / rdts_common.c)、NS_LIB
(ns_ble.c / ns_ble_task.c / ns_sleep.c / ns_sec.c / ns_timer.c / ns_error.c)、
BLE_APP(app_ble.c / app_cls.c);FWLB 追加 `n32wb03x_qflash.c`(OTA 备用)与
`n32wb03x_exti.c`(ns_ble.c 的 EXTI_* 调用需要)。NS_LOG/LPUART、Crypto、
NS_DFU 未接入。
- Cads IncludePath 参照 rdtss.uvprojx 移植全部 BLE 相关目录(前缀改 2 层
`..\..`),`..\inc`→`..\app\ble`、`..\inc\app_profile`→`..\app\ble\app_profile`。
### 15.2 BLE 应用层(app/ble/)
- `app_user_config.h` / `app_profile/rwapp_config.h`:由 rdtss 精简。广播名
`CAIIC-MCM`;广播数据 = 128-bit 服务 UUID 列表(LSB first),扫描应答留空由
协议栈自动附加设备名(attach_name);连接参数 15/30ms、latency 0、超时 5000ms;
仅启用 `CFG_PRF_RDTSS`(复用 SDK rdtss profile 引擎),NS_LOG 全关。
- `app_profile/app_cls.c/.h`:自定义服务 CAIIC Link Service(属性表仿 app_rdtss.c):
- service `0000CA10-CA11-4B11-8000-CA11CA11CA11`
- 下行写特征(Write Without Response)`0000CA11-CA11-4B11-8000-CA11CA11CA11`
- 上行 notify 特征(Notify + CCCD)`0000CA12-CA11-4B11-8000-CA11CA11CA11`
沿用 SDK prf 框架(GAPM_PROFILE_TASK_ADD_CMD 建库、`ns_ble_prf_task_register`
注册消息表、`prf_get_itf_func_register` 挂 `rdtss_prf_itf_get`)。notify 发送为
单包接口:忙(上一包未收到 RDTSS_VAL_NTF_CFM)或未订阅时返回 -1,由上层重试。
- `app_ble.c/.h`:`ns_ble_stack_init`(注册 ble 消息回调)+ GAP/安全/广播参数 +
profile 注册 + `ns_ble_adv_start()`;连接/断开/MTU 更新事件经
`bsp_usart_tx_lock` 保护的 printf 输出;断开后自动重新开广播。对外接口:
`app_ble_init()` / `app_ble_notify_send()` / `app_ble_set_rx_callback()`。
- **BLE 调度任务**:`app_ble_init()` 内创建(栈 512 字、优先级 2),循环
`rwip_schedule(); vTaskDelay(pdMS_TO_TICKS(1));`。ke_msg/ke_timer 只允许在该
任务上下文运行(SDK 约束)。
- **不接 ns_sleep 低功耗**(与 FreeRTOS tick 冲突,代码内留 TODO);ns_sleep.c
仍需编译(ns_ble.c 引用 `ns_sleep_lock_acquire` 等符号),只是主循环不调用。
### 15.3 VTOR 处理(关键)
Cortex-M0 无 SCB->VTOR,本芯片用 `PWR->VTOR_REG`(bit31=EN,[30:0]=向量基址)。
APP 链接在 0x01008000,但 SDK `SystemInit()` 写成 0x81000000(指向 0x01000000
bootloader 区),`ns_ble_stack_init()` 内部(`NS_BLE_STACK_INIT`)又清 0。
处理:`main` 在 SystemCoreClockUpdate 之后、任何 NVIC 中断使能之前先设
`PWR->VTOR_REG = 0x80000000 | 0x01008000`;`app_ble_init()` 之后再重设一次
(`main.c` 中 `APP_VTOR_VALUE`)。
另:SDK 的 `ns_ble_stack_vtor_init()` 会把 `__Vectors`(本 APP 的 flash 向量表,
含 FreeRTOS 的 SVC/PendSV/SysTick 映射与 USART1_IRQHandler)复制到 RAM 重映射区
(0x200000e8 系统异常 / 0x200009c0 用户 IRQ),并把 BLE_FIFO/BLE_SLP/EXTI4_12
指向 ROM/RAM 内部 handler;rdtss 例程在栈初始化后保持 VTOR=0 即依赖该 RAM 重映射。
**本工程按设计要求重设 VTOR 到 flash;若实测 BLE 中断异常,回退方案是
app_ble_init 后保持 VTOR=0(RAM 重映射已覆盖全部中断向量)。** 见 15.5 风险。
BLE 中断不定义在 n32wb03x_it.c:启动文件的 BLE_*IRQHandler 均为 weak
Default_Handler,真正的 handler 由 symbol_g15.obj(ROM)+ RAM 重映射提供;
与 FreeRTOS 的 SVC/PendSV/SysTick 映射无冲突。BLE_SW/FIFO IRQ 优先级 0,
ISR 内不得调用 FreeRTOS FromISR API(本工程 BLE 事件均在 BLE 任务上下文处理)。
### 15.4 编译验证
- `UV4.exe -b embeddedSrc.uvprojx -j0 -o build.log`:**0 Error(s), 0 Warning(s)**
- 体积:`Code=34828 RO-data=6788 RW-data=8820 ZI-data=25572`
- RW+ZI = 34392 字节 < 48KB(0xC000),`configTOTAL_HEAP_SIZE` 保持 20*1024 不变;
剩余约 14.4KB 供 MSP 主栈。BLE ROM 栈变量经 symbol_g15.obj 绝对符号落在低地址
RAM,其占区由 rwip_driver.o/ns_ble.o 等的 .data 段原位保留(与 rdtss 布局一致)。
- 上板预期:复位后 banner(含 BLE 行)→ `caiic->` CLI 正常;手机 nRF Connect
能搜到并连接 `CAIIC-MCM`,可见自定义服务的写/notify 两个特征。**尚未实测。**
### 15.5 遗留风险
- VTOR 与 BLE 中断的最终取指向量路径需上板确认(见 15.3,回退方案已备)。
- `app_ble_notify_send()` 会从非 BLE 任务上下文触发 ke_msg_send(SDK 未保证
跨上下文安全),后续 CLI-over-BLE 协议接入时应评估改为向 BLE 任务投递消息。
- ns_sleep 低功耗未接;广播常开,功耗未优化。
---
日期:2026-09-01
## 16. CLI 传输解耦 + BLE 帧协议 + BLE OTA 接收器
### 16.1 CLI 核心与传输解耦
- 新增 `app/cli_core.c/.h`:命令表、全部命令 handler、分词执行从 app_cli.c 迁入。
输出经可切换的 `cli_out_fn` 回调(`cli_set_output`,NULL=默认 UART DMA)。
`cli_exec_line()` 返回 0/1(未知命令)。对外另有 `cli_cmd_count/cli_cmd_name`
供 UART 前端 Tab 补全。
- `app/app_cli.c` 保留 UART 前端(CliTask、行编辑、历史、Tab、Ctrl+Z、提示符),
分发前 `cli_set_output(NULL)` 再 `cli_exec_line`;前端自身的提示符/回显/重绘
固定走 UART(`cli_uart_write`),不受回调切换影响。
- 并发约定:UART 与 BLE 两个前端共用全局 out 回调与 TX 缓冲,采用"执行前设置、
执行完恢复"的简单保护(两端并发概率低,代码内已注释说明)。
- 新增 CLI 命令 `bankinfo`:打印当前运行 bank(经 armlink 符号
`Image$$ER_IROM1$$Base` 判定)与 bootsetting 记录内容。
- UART CLI 行为不变(回归项)。
### 16.2 BLE 帧协议前端(app/ble/app_ble_proto.c/.h)
帧格式(小端):`[0]=0xCA SOF, [1]=TYPE, [2]=SEQ, [3:4]=LEN(LE), [5..]=PAYLOAD,
末尾 CRC16-CCITT(初值 0xFFFF, poly 0x1021) 2 字节 LE,覆盖 TYPE..PAYLOAD`。
帧开销 7 字节(头 5 + CRC 2)。
- 帧类型:0x01 CLI_REQ / 0x02 CLI_RSP / 0x03 CLI_RSP_END{status} /
0x10 OTA_BEGIN{size,crc32,version} / 0x11 OTA_DATA{offset+data} /
0x12 OTA_END{crc32} / 0x1F OTA_RSP{cmd_echo,status,offset}。
OTA_RSP status:0 ok / 1 bad_frame / 2 bad_state(seq) / 3 size_too_big /
4 crc_fail / 5 flash_fail。
- 接收:字节流状态机(找 SOF→5 字节头→payload+CRC),接收缓冲 512 字节
(payload 上限 480);CRC 错误的帧静默丢弃(OTA 靠 offset/ack 重同步)。
- CLI_REQ:payload ≤63 字节拷入行缓冲,置 out 为 BLE 帧输出后 `cli_exec_line`,
结束发 CLI_RSP_END(status=cli_exec_line 返回值)。
- 发送分块:单帧 payload 上限 = min(att_mtu-3, 244) - 7;新增
`app_ble_max_payload()`(未连接返回 17)与 `app_ble_is_connected()`。
- notify 忙重试:512 字节 pending FIFO 存整帧,`app_ble_proto_poll()` 挂进
BleTask 循环每轮冲刷;FIFO 满则丢弃并计数;断连清空。
- 全部运行在 BleTask 上下文(rx 经 CLS 写指示 handler,poll 在任务循环),无锁。
### 16.3 OTA 接收器(app/app_ota.c/.h)
- 静态 4KB 扇区缓冲;`Image$$ER_IROM1$$Base` 判定当前 bank,目标=对侧 bank。
- OTA_BEGIN:校验 4..0x38000(否则 status=3)、首次 `Qflash_Init()`、记录会话、
回 OTA_RSP(0x10,ok,0)。
- OTA_DATA:offset 必须等于期望值(否则 status=2 附期望 offset,状态保持供重发);
攒满 4KB → 擦扇区+写 4KB → 回 OTA_RSP(0x11,ok,已写 offset) 作扇区级流控;
未满不回包。Qflash 操作内关中断数十 ms,BLE 靠 5s supervision timeout 容忍。
- OTA_END:残余 0xFF 补齐到 4 字节倍数后擦写该扇区;整镜像 `caiic_crc32` 回读校验
(同时比对 BEGIN 与 END 的 crc32);失败 status=4 中止不复位。成功则更新
bootsetting(无效记录清零重建、写 active_bank/bank{size,crc,version}/重算结构
CRC、擦写+回读比对),回 OTA_RSP(0x12,ok),vTaskDelay 200ms 后
`NVIC_SystemReset()`。
- 断连(`app_ble_proto_on_disconnect`)中止会话并清空 pending;OTA 仅在连接态有效。
- uvprojx:新增 DFU 组(`..\..\Boot\src\boot_crc32.c`),IncludePath 加
`..\..\Boot\src`。
### 16.4 编译验证
- `UV4.exe -b embeddedSrc.uvprojx -j0 -o build.log`:**0 Error(s), 0 Warning(s)**
- 体积:`Code=38000 RO-data=6856 RW-data=10600 ZI-data=31008`
- RW+ZI = 41608 字节 < 48KB,`configTOTAL_HEAP_SIZE` 保持 20*1024 不变。
- 上板预期:UART CLI 回归正常;nRF Connect 连接后发 CLI_REQ("help") 应收
CLI_RSP 分块 + CLI_RSP_END;OTA_BEGIN/DATA/END 把新 bin 写入对侧 bank 并复位,
bootloader 校验跳转后 `version`/`bankinfo` 显示新 bank。**尚未实测。**
---
日期:2026-09-01
## 17. 整片烧录包(bootloader + APP 合并)
新增顶层 `tools/` 目录:
- `merge_image.py` — 纯 Python(无第三方依赖)合并镜像:`Boot/MDK-ARM/bin/caiic_boot.bin`
(0x01000000)+ `mcm-ddc-04/MDK-ARM/bin/embeddedSrc.bin`(0x01008000,APP1)→
输出 `tools/out/caiic_full.hex`(Intel HEX,稀疏地址)与 `caiic_full.bin`
(0x01000000 起,空隙补 0xFF)。已做回读校验:hex 中 boot/APP 段与源 bin 逐字节一致。
- `make_package.bat` — 一键:UV4 构建 bootloader → UV4 构建 APP → 合并。任一步 0E0W
校验失败即中止。本机 `python` 是商店占位 stub,脚本自动回退到 Python312 全路径。
- `flash_package.bat` — 调 SDK 自带 `NSpyocd.exe`:`erase --chip` 后 `load` 合并 hex
(目标 n32wb031)。需 NS-LINK 接 SWD(PA4/PA5)+ 复位脚。
烧录流程:**首次**整片烧录用 `tools\flash_package.bat`(或 Keil 分工程分别烧录);
之后日常升级走 BLE OTA。bootsetting 首次为空,bootloader 向量检查后直接跳 APP1。
---
日期:2026-09-01
## 18. 修复 app_ble_init 崩溃(VTOR 根因)+ heap 调整
### 18.1 现象与根因
上板实测:程序在 `app_ble_init()` 中崩溃(卡死)。排查后确认**不是 heap 不足**,而是 VTOR:
- 该芯片 BLE 协议栈的中断分发机制:`ns_ble_stack_vtor_init()`(ns_ble.c)把 APP 的
`__Vectors`(链接器符号,即我们 0x01008000 处的向量表)拷贝到 RAM 中继区
(系统异常 → 0x200000e8 起,用户 IRQ → 0x200009c0 起 31 项),再把
BLE_FIFO/BLE_SLP/EXTI4_12 槽位改写为 ROM/RAM handler(BLE FIFO handler 是运行时
拷贝到 RAM 的代码 `rwip_fifo_isr_codeArray`,不可能出现在 flash 静态向量表里)。
- 因此 BLE 栈初始化后 **VTOR 必须保持 0**(走芯片 ROM 跳板 + RAM 中继),
这也是 rdtss/app_ota 等所有官方例程的实际状态。
- 此前我们在 `app_ble_init()` 之后把 VTOR 重设为 `0x80000000|0x01008000`,
导致 BLE FIFO 中断直接查 APP 的 flash 向量表 → weak `Default_Handler` 死循环 → 卡死。
### 18.2 修复
- 删掉 `main.c` 中 `app_ble_init()` 之后的 VTOR 重设(保留 main 开头那一次——
BLE 初始化前 USART1 RX 中断需要查 APP 自己的向量表)。
- 规则:**BLE init 前 VTOR=APP 基址;BLE init 后 VTOR=0,不再动**。
### 18.3 heap 检查与调整
核算 FreeRTOS heap(20KB)占用:5 个任务栈(1024×4 + 512 字 = 18944B)+ 6 个 TCB
(≈552B)+ 队列/互斥锁(≈370B)≈ 19.9KB,剩余仅 ~600B——未耗尽但余量偏小。
`configTOTAL_HEAP_SIZE` 由 20KB 调至 **22KB**(RW+ZI=43656B,主栈余量约 5.4KB)。
编译:0 Error / 0 Warning,`Code=37996 RO-data=6856 RW-data=10600 ZI-data=33056`。
---
日期:2026-09-02
## 19. Boot 跳转策略调整:校验移到升级过程,解决 Keil 下载无法调试
### 19.1 现象与根因
加了诊断代码重新编译后,Keil 下载/调试**能连上但全速跑永远到不了 main**。根因:
- Keil 下载只写 APP1 区(0x01000000 起的镜像由 Keil 的 FLM 按 axf 地址 0x01008000 写入),
**不会更新 bootsetting(0x01004000)里记录的 APP1 CRC32**——那是上次 `merge_image.py`
打整片包时算的。
- 复位链路:芯片 ROM → Boot → 校验 bootsetting → 校验 APP1 镜像 CRC → **不匹配,拒绝跳转**,
CPU 停在 Boot 的 `for(;;)`。Keil 的 "Run to main" 走的是自然启动,自然卡在 Boot。
- 所以任何一次 Keil 重新下载(不只是诊断代码)都会触发;引入 Boot 之前没有这层校验,故以前正常。
验证方法:调试状态 halt 看 PC,落在 0x01000000~0x01003FFF 即 Boot 空转。
### 19.2 调整内容
- **Boot(`Boot/src/main.c`)改为跳转时不校验镜像 CRC**:只检查 bootsetting 扇区自身完整性
(magic + 自身 CRC32)选出 active bank,再做向量表 sanity(MSP 在 SRAM、复位向量在 bank 内)
兜底后直接跳转;bootsetting 无效时默认 APP1。删除 `bank_crc_ok()` 及双 bank CRC 回退。
- **镜像 CRC 校验职责完全在升级过程**:`app_ota.c` OTA_END 已对新镜像整体 CRC32 比对,
通过才写 bootsetting 切 bank 复位——OTA 链路安全性不变。
- 新增 `MDK-ARM/debug_app.ini` 并挂到工程调试配置(uvoptx `<tIfile>`):调试器下载后强制
`SP/PC = APP1 向量表`,跳过 Boot 直进 APP。Boot 不校验后该脚本已非必需,保留为纯调试可选旁路。
- Boot 重建 0E0W(`Code=1352`,bin 2032B),`tools/make_package.bat` 重新出整片包
(caiic_full 79464B)。
### 19.3 影响与注意
- Keil 直接下载 APP 即可启动和调试,开发节奏恢复。
- 代价:OTA 流程之外的镜像损坏(如 Keil 下载中途断电)不再被 Boot 拦截——向量检查只能挡
全空/乱码。这是"简单"换来的取舍。
- **在板子上烧过其他 0x01000000 起步的程序(SDK 例程 / mcm-ddc-ble)会覆盖 Boot 和
bootsetting**,恢复方法:`tools\flash_package.bat tools\out\caiic_full.bin` 重烧整片包。
---
日期:2026-09-02
## 20. 串口波特率提升到 1Mbps
### 20.1 可行性核实
- USART1 挂 APB2=64MHz(`n32wb03x_usart.c` 中 USART1 取 Pclk2),波特率发生器为
16 倍过采样 + 小数分频(同 STM32F1 算法)。
- 1Mbps 分频值 = 64MHz / (16 × 1M) = **4.0,误差 0%**(2M/4M 同样零误差)。
- 驱动断言上限 `IS_USART_BAUDRATE` = 4Mbps(`n32wb03x_usart.h`),1Mbps 远在范围内。
### 20.2 改动
- `bsp_usart.h`:`BSP_USART_BAUDRATE` 115200 → **1000000**,相关注释与 AGENTS.md 同步。
- 构建 0E0W(`Code=38080 RO-data=6932 RW-data=10600 ZI-data=33056`)。
- 注意:串口助手需切到 1000000;USB 转串口适配器需支持 1Mbps(CH340/CH343/FTDI 均可)。
### 20.3 决策:RX 暂不切 DMA
评估了 RX 由 RXDNE 逐字节中断改为 DMA 循环缓冲 + IDLE 中断的方案,结论**维持现状**:
- CLI 是人工输入速度,1Mbps 下逐字节中断负载可忽略;RX DMA 的收益只在串口灌大数据
(有线烧录/批量注入)时才存在,属出现需求后再做。
- DMA RX 引入循环缓冲管理、IDLE 中断、4 通道合一的 DMA IRQ 与 FromISR 优先级约束等
新出错点;现 RXDNE 方案已上板验证。
- 对 CLI 语义无影响(保持 `bsp_usart_read_byte()` 字节流接口即可),但无收益不改动。
### 20.4 后续:波特率回退
实测后决定**改回 115200**(`BSP_USART_BAUDRATE` 恢复,注释与 AGENTS.md 同步还原)。
本节 20.1 的核实结论仍然有效:该芯片串口支持 1Mbps 且零误差,需要高速时随时可再开。
---
日期:2026-09-02
## 21. Boot 跳转后 BLE 失败的最终定位与修复 + bootsetting 携带跳转地址
### 21.1 根因(两层叠加,均已修复)
1. **PRIMASK 中断状态**:旧 Boot 在 `jump_to_app()` 里 `__disable_irq()`,APP 继承中断全关。
全 SDK 应用代码从不自己 `__enable_irq()`(自然复位 PRIMASK=0),FreeRTOS 要到
`vTaskStartScheduler()` 才开中断,而 `app_ble_init()` 在调度器之前跑,其中的 ROM 初始化
(如 LSI 校准 `calib_lsi_clk`,7.5ms)需要中断推进 → 卡死。修复:Boot 跳转前
`__enable_irq()`(Boot 自身无中断源,安全)。
2. **RAM 起始地址踩 ROM 保留区(主因)**:N32WB031 SRAM 为 **48KB+16KB** 结构,低 16KB
(0x20000000~0x20003FFF)由芯片 ROM/BLE 子系统占用——ROM 启动代码在其中预备好
rwip 堆描述符(0x20000118 起,全 SDK 无任何代码写它们)、patch 数组、中断中继表。
SDK 例程(rdtss/mcm-ddc-ble)RAM 执行区从 **0x20004000** 起;而 mcm-ddc-04 和 Boot
之前错配成 0x20000000 起(器件切到 KEQ6-2 时被 pack 默认值带偏),`__main` 的
.data/.bss 初始化把低 16KB 冲掉 → BLE 栈拿垃圾描述符初始化堆 → HardFault。
修复:两个工程 IRAM 全部改 **0x20004000/0xC000**(应用可用 48KB,
APP RW+ZI≈46.7KB 放得下)。
- 佐证:故障时打印的堆描述符为乱码(`env=4622910E/30777...`);官方 masterboot 的
`ns_dfu_boot_jump` 不动 VTOR/PRIMASK 只设 MSP,行为与我们修复后的 Boot 等效。
- 教训:**任何工程(包括裸机小工具)链接到本芯片都必须从 0x20004000 起**。
### 21.2 辅助手段
- `n32wb03x_it.c` 的 HardFault_Handler 增加异常栈帧打印(R0-R3/R12/LR/PC/xPSR,
EXC_RETURN bit2 选 MSP/PSP;ARMv6-M 无 CFSR/HFSR)。
- 恢复 main 开头 2s 上电延时(与官方例程一致,兼作 SWD 附着窗口)。
- `debug_app.ini` 已由用户移除,调试不再需要旁路。
### 21.3 bootsetting 携带跳转地址
- `dfu_layout.h`:`caiic_bank_t` 新增 `start_address`(结构体 44 字节)。
- Boot:校验 bootsetting 自身完整性(magic + 结构体 CRC32)后按 active bank 的
`start_address` 跳转;地址越界或记录无效回退 APP1。
- `app_ota.c`:OTA 写记录时填 `start_address`;`bankinfo` 命令同步打印。
- `tools/merge_image.py`:**生成缺省 bootsetting** 打进 0x01004000(magic、active=APP1、
bank1={start=0x01008000,size,crc32,version=0}、bank2 空、结构体 CRC32,与
`boot_crc32.c` 同为 IEEE CRC32,脚本用 zlib 实现);支持 `merge_image.py [app_bin]
[输出名]` 参数化。
- `tools/make_package.bat`:构建改 `-r` 全量重建,杜绝陈旧中间产物进产线包。
### 21.4 产物(均逐字段校验:向量表 / 结构体 CRC / bank1 CRC 与镜像吻合)
- `tools/out/caiic_full.{hex,bin}`(79732B)= Boot + mcm-ddc-04(APP1 46964B)
- `tools/out/caiic_ble_full.{hex,bin}`(62480B)= Boot + mcm-ddc-ble(APP1 29712B,
该工程已重链接到 0x01008000/0x38000)
---
日期:2026-09-02
## 22. mcm-ddc-ble 工程:创建、FreeRTOS、CLI 搬迁与 BLE 帧协议落地
mcm-ddc-04 在 Boot 跳转路径下仍有问题期间,新建 `mcm-ddc-ble/` 作为可工作的基座
工程并逐步补齐功能。本节汇总其全部演进。
### 22.1 工程创建(rdtss 蓝本)
- 以 SDK `projects/n32wb03x_EVAL/ble/rdtss` 为蓝本整目录复制(BLE 原始数据透传服务,
128-bit UUID,下行写 + 上行 notify,与 CLI-over-BLE 形态最接近)。
- uvprojx/uvoptx 中 SDK 相对路径全部重定向到 `..\..\nations-tec\N32WB03x_SDK_V2.0.0\`,
`nations-tec/` 只读未动。OTA_IMG_1/2 两个 target 保留不用。
- 引脚核对:LED1=PB0、LED2=PA6、USART1=PB6/PB7、LPUART 日志=PB1,无 PA4/PA5(SWD)
占用。
- `main.c` 加 `ns_sleep_lock_acquire()` 永久锁睡眠,SWD 全程可调试(例程默认进
deep sleep 会断开 SWD)。
### 22.2 FreeRTOS 接入 + 点灯任务
- SDK 自带 FreeRTOS V9(tasks/queue/list/timers + heap_4 + RVDS/ARM_CM0 port)加入
工程;`FreeRTOSConfig.h` 从 mcm-ddc-04 移植。
- 任务化:`ble_schedule_task`(512 字,优先级 2,rwip_schedule 循环,rwip 单上下文
约束)、`led_task`(128 字,LED1 500ms 翻转)。
- 踩坑:脚本写 uvprojx 路径时 `\t` 被 Python 解释成制表符(`Source\tasks.c` 变成
`Source<TAB>asks.c`),Keil 报 `cannot create command input file`;另 `<FileName>`
必须写纯文件名。
### 22.3 从 mcm-ddc-04 搬迁 CLI 框架 + UART 驱动
- 搬入:`bsp_usart.c/h`(printf 重定向 + DMA TX + RXDNE 中断队列 + TX 互斥锁,
新增 `bsp_usart_rx_inject()`)、`cli_core.c/h`、`app_cli.c/h`(提示符 `caiic->`、
Tab 补全、历史、四种行结束符)、`n32wb03x_it.c`(USART1_IRQHandler 转发 +
HardFault 栈帧打印)、`app_version.h`(V1.00.01)、新写 `ble_up.c/h`(CLI 应答的
BLE notify 上行:ke_msg 只允许 BLE 任务上下文,故 CLI 任务入队 512B 环形缓冲,
BLE 调度任务 20B/notify、cfm 节流发送)。
- 命令集:`help`/`version`/`sysinfo`/`led`(`log`、`bankinfo`、Ctrl+Z 依赖 04 的
日志/OTA 系统,未搬)。
- 拆除:`app_usart.c`(原透传 FIFO)、`ns_log_lpuart.c`(占用 `fputc` 与 bsp_usart
冲突;`NS_LOG_LPUART_ENABLE` 置 0)。
- main.c 增加 VTOR 预置(`0x80000000|0x01008000`,BLE init 前 USART1 RX 中断需要,
BLE init 后 VTOR 归 0 不动)与启动 banner。
- 踩坑:µVision 在 uvoptx 缺失/不同步时会把多个 target 的同名组文件清单合并,从
target 1 删掉的文件仍被编进链接——三个 target 的条目需全部删除;以
`Objects/*.lnp` 链接清单无残留为验证。
### 22.4 实现 ble_protocol.md 帧协议
- 新增 `app_ble_proto.c/h`(从 04 移植适配 rdtss 服务):字节流重组状态机
(SOF 0xCA → 头 → payload → CRC16-CCITT,跨多次 BLE 写入重组,坏帧静默丢弃并
重新找 SOF);CLI_REQ 执行命令,输出组帧 CLI_RSP 若干 + CLI_RSP_END{status}。
- 通道隔离:串口输入只回串口,BLE 帧输入只回 BLE 帧(set/exec/restore 输出切换)。
- OTA 帧类型(0x10~0x1F)保留不应答(本工程无 flash 升级)。
- CRC16 已用标准向量("123456789" → 0x29B1)验证。
- 协议文档 `docs/ble_protocol.md` 增加 1.2 节(rdtss 服务 UUID 表、差异点:默认
MTU 下每帧 payload 13B、OTA 不支持)与 `help` 命令线上字节示例
(`CA 01 00 04 00 68 65 6C 70 16 EC`)。
### 22.5 产物与测试入口
- 构建 0E/0W:`Code=31184 RO-data=3504 RW-data=1968 ZI-data=26912`(heap 20KB)。
- 整包:`tools\out\caiic_ble_full.{hex,bin}`(Boot + 缺省 bootsetting + 本工程 APP1)。
- 串口(115200):banner + `caiic->`;BLE:`CAIIC-MCM-20260902`,订阅 `...E0002`
notify 后往 `...E0001` 写 CLI_REQ 帧即可对话。
## 23. BLE 三业务补齐:OTA + 设备信息查询 + CLI 透传(2026-09-03)
mcm-ddc-04 目录已由用户删除,mcm-ddc-ble 成为唯一应用工程。本轮把 BLE 通道
从"仅 CLI 透传"补齐为协议 V1.1 的三业务:CLI 透传(已有)、设备信息查询
(新增)、双 bank 直写 OTA(从 04 git 历史 3135722 移植 app_ota.c/h)。
### 23.1 协议升级 V1.1(docs/ble_protocol.md 重写)
- 适配本工程实际:rdtss 服务(`...E0001` 下行写 / `...E0002` 上行 notify)、
广播名 `CAIIC-MCM-20260902`。
- 新增信息查询帧:INFO_QUERY(0x20, {item_id}*n,空/0xFF=查全部) /
INFO_RSP(0x21, TLV {id,len,value}*n)。信息项:0x01 固件版本 u32、
0x02 芯片温度 i16(0.1°C)、0x03 风扇转速 u16(0xFFFF=无硬件)、
0x04 电源电压 u16(mV)、0x05 运行时长 u32(s)、0x06 剩余堆 u32(B)。
- OTA 帧 0x10~0x12/0x1F 从"保留"变为实现,流程与 04 一致(扇区级 ack 流控、
END 时整镜像 CRC32 校验 + 更新 bootsetting + 200ms 后复位)。
### 23.2 OTA 移植(app_ota.c/h,368 行原样搬入)
- 4KB 扇区 RAM 缓存攒满即擦写对侧 bank,无 flash 中转区;`Image$$ER_IROM1$$Base`
判定当前 bank(本工程链接 0x01008000 = APP1,OTA 写 APP2 0x01040000)。
- `app_ble_proto_send_frame()` 由 static 改为导出供 OTA/INFO 应答;
OTA 状态码 BLE_OTA_ST_* 移入 app_ble_proto.h。
- 断连即 `app_ota_abort()`(挂在 app_ble_proto_on_disconnect)。
- uvprojx:USER 组加 app_ota.c / app_info.c / `..\..\Boot\src\boot_crc32.c`,
IncludePath 追加 `..\..\Boot\src`(dfu_layout.h / boot_crc32.h)。
与 app_ble_proto.c 一样只加 target "N32WB03x"。
### 23.3 设备信息查询(新模块 app_info.c/h)
- 温度:ADC CH7 内置温度传感器(`ADC_EnableTS` + 单发轮询 + 超时保护,
`ADC_ConverValueToTemperature` 用出厂 trim),参考 SDK 例程
`peripheral/ADC/ADC_Temperature`。
- 电压:ADC CH6(VCC),`ADC_ConverValueToVoltage`(600~3600mV 档 trim)。
- ADC 懒初始化(AUDIOPLL 时钟源 + bypass filter + 过采样 3),首次查询时配好。
- 风扇转速:`__weak app_fan_get_rpm()` 默认返回 0xFFFF(本板无测速电路),
产品代码重写该函数即可,协议不变。
- CLI 新增 `devinfo` 命令打印同组数据(方便无手机时串口验证)。
### 23.4 MTU 协商
- `app_ble_connected()` 里设备主动 `ns_ble_mtu_set(247)`(ns_ble 库提供,
官方 DFU 的 OTA_CMD_MTU_UPDATE 同款调用)。MTU 247 时一次写入 244B,
OTA_DATA 单帧可带 233B 数据;MTU 协商失败则退化为 20B/写(协议按字节流
重组,两种情形都正确)。上行 notify 仍固定 20B 块,兼容任意对端。
### 23.5 产物
- 构建 0E/0W:`Code=34488 RO-data=3592 RW-data=2004 ZI-data=31012`
(含 4KB OTA 扇区缓存;用户 RAM 48KB 占用 33016B)。
- APP bin = 39616B(≈38.7KB,bank 上限 224KB)。
- 整包:`tools\out\caiic_ble_full.{hex,bin}`(Boot + 缺省 bootsetting + 本工程
APP1,bootsetting crc32=0x69A005D5)。
- 待上板验证:BLE 连接后 MTU 交换、INFO_QUERY 应答、OTA 全流程(写 APP2 →
复位 → Boot 跳 APP2)。
## 24. OTA 去 4KB 扇区缓存,改直写(2026-09-03)
- 背景:23 节版本整包烧录后不运行。静态校验发现该包 APP1 向量表初始 SP 仍停在
旧版栈顶 0x2000B0D0,而新增 4KB OTA 缓存已把 ZI 顶到 0x2000B8F8——栈顶落在
ZI/堆区域内,启动即崩(增量构建产物不一致所致;教训:出包一律 `UV4 -r`
全量重建,并核对向量表 SP 与 map 的 `__initial_sp` 一致)。
- 应用户要求去掉 4KB 扇区 RAM 缓存设计,app_ota.c 重写为**直写**:
- 惰性擦除:数据首次落入某扇区时先擦该扇区(`ota_erase_up_to`);
- 收到 OTA_DATA 立即 `Qflash_Write` 写入(bulk 对齐部分直接用帧 payload,
不再经过大缓冲);
- 仅保留 4 字节对齐暂存(Qflash 写要求 4B 对齐,尾部 OTA_END 时 0xFF 补齐);
- 流控/进度不变:每写满一个 4KB 扇区回一帧 OTA_RSP(ok, offset)。
- 效果:ZI 从 31012 降回 26912(-4KB),本版 `Code=34564 RO=3592 RW=2016
ZI=26912`,APP bin 39692B,栈顶 0x2000B100 与 map `__initial_sp` 一致。
- 整包:`tools\out\caiic_ble_full.{hex,bin}`(bootsetting crc32=0xF15790D8)。
- 协议文档 §6 已同步直写描述(对外流程与帧格式不变)。
- 上板验证:整包烧录后正常启动,串口 banner + `caiic->` 正常(2026-09-03 确认)。
## 25. 出包流程同时产出 OTA 镜像(2026-09-03)
- `tools/merge_image.py` 扩展:每次生成整包时同步输出
`<包名>_ota.bin`(OTA 载荷 = APP bin 本体)和 `<包名>_ota.json`
(清单:file/size/crc32/version,手机端 OTA_BEGIN 直接取用;
version 用第三个命令行参数指定,如 `0x00010001`,缺省 0)。
- 本版产物:`caiic_ble_full_ota.bin`(39692B,crc32=0xF15790D8)+ json,
已校验与 Keil 产物 bin 逐字节一致;`make_package.bat` 流程自动获得同样产物。
- 协议文档 §7 已补充 OTA 产物说明。
## 26. 协议文档补齐手机端开发指南(2026-09-03)
- 用户将文档目录移到仓库根 `docs/`(mcm-ddc-ble/docs 已删除,以根目录为准)。
- `docs/ble_protocol.md` 新增第 8 节"手机端开发指南":
- 8.1 连接初始化步骤(扫描名/服务 UUID → CCCD 使能 notify → MTU 协商);
- 8.2 参考实现:CRC16-CCITT、组帧、字节流解帧(含坏帧重同步)、zlib CRC32,
Python 版可直接对照移植 Kotlin/Swift/JS;
- 8.3 字节级完整示例表(CLI/INFO/OTA 全业务,CRC 均为真实计算值);
- 8.4 时序与异常处理:CLI 串行执行、OTA 滑动窗口流控与断点重同步、
notify 跨包重组要求、温度/风扇特殊值约定。
## 27. BLE 打印开关 + 收发报文打印(2026-09-03)
- 新增 `blelog [on|off]` CLI 命令(缺省关),开关状态只存 RAM(复位即关)。
- 打开后:
- 每个完整收/发协议帧在串口 hex 打印:`[BLE RX]/[BLE TX] type= seq= len= + payload
预览(超 24B 截断打 ...`);RX 在 CRC 校验通过分发前打印,TX 在组帧入队前打印;
- 连接/断开事件打印(`[BLE] connected` / `disconnected, advertising restarted`)。
- 打印走 printf(fputc 轮询 UART),不经过 BLE 通道,避免 CLI-over-BLE 自激。
- 用途:手机 APP 联调时对照 ble_protocol.md §8.3 的示例帧逐字节核对。
- 本版 `Code=35104 RO=3672 RW=2016 ZI=26912`,整包/OTA 镜像已重新生成
(_ota.bin 40312B,crc32=0xCAE8E135),SP=0x2000B100 与 map 一致。
## 28. CLI 读写特征 + 只读参数特征 + 温度采样修复(2026-09-04,V1.00.02~V1.00.08)
- **CLI 读写特征 `...c72e0003`(V1.00.02)**:RD + WRITE_REQ(带响应写)。手机写原始
命令行(≤63B,不套 0xCA 帧),设备执行后读同一特征取回输出文本(≤512B,
ATT long read 由协议栈自动分段)。`app_ble_proto_cli_exec()` 捕获输出到静态缓冲,
`rdtss_value_req_ind_handler` 按 att_idx 分发读请求。整条链路不依赖 notify。
- **blelog 可见性增强(V1.00.02)**:rdtss 写回调原始 dump 从编译期剔除的
NS_LOG_DEBUG 改为 blelog 门控 printf(`[BLE] write ind: att_idx= len= ...`),
新增 CCCD 订阅/退订打印(`[BLE] notify enabled/disabled`);修正 CCCD 值解析
(`value[0]+value[1]` → 小端合成)。
- **只读参数特征 `...c72e0004`(V1.00.04)**:读出为全部信息项 TLV,与
INFO_RSP(all) 一致;`app_info.c` 抽出 `app_info_tlv_snapshot()` 与帧应答共用组包。
- **温度采样修复(V1.00.03/05/08)**:现象 `devinfo` 显示 287.6°C。定位过程:
首版加 5ms 稳定+丢弃首次转换(05 加到 20ms)无效;devinfo 加 `(adc= trim=)`
诊断后确认 adc≈115(贴近地的浮空读数,trim=681 正常)——**规律是每次开机后
第一次 TS 采样为垃圾值,之后全部正常**(V1.00.05 稳态 272 采样点 20~26°C)。
最终修复:ADC 初始化时做预热转换并丢弃(`info_adc_init` 尾部)。
- **教训记录(V1.00.06/07 → 08 撤回)**:曾按勘误表 7.1 条加
`*(uint32_t*)0x40011004 |= 0x40`(HSI 设计 ADC 时钟修复),但该地址是
AFEC+0x04,与 BLE 协议栈共享(rwip_driver.c 写 0x20,ns_sleep.c 保存/恢复,
pwr.c 的 bit6 是无 HSE 睡眠唤醒专用),误写导致首次 ADC 读取后 BLE 射频挂死、
设备停止广播。V1.00.08 已撤回该写操作。寄存器级勘误修复必须先在 SDK 源码
交叉验证地址用途。
- **工具**:新增 PC 端 bleak 测试客户端 `tools/ble_cli_test.py`(帧协议/读写特征
两种方式,venv `tools/.venv-ble`)、一键启动器 `tools/ble_cli.bat`、温度监测
`tools/ble_temp_watch.py`;`make_package.bat` 改指 mcm-ddc-ble(mcm-ddc-04 已删);
`merge_image.py` 出包自动从 `app_version.h` 取 `APP_FW_VERSION_NUM` 写入 OTA 清单。
- **版本号约定**(已写入 AGENTS.md):每次修改固件递增 `app_version.h` 的
`APP_FW_VERSION`/`APP_FW_VERSION_NUM`(0x00MMmmpp 补丁位 +1),文件名不变。
- 本版 V1.00.08 `Code=35864 RO=3760 RW=2020 ZI=27532`,整包/OTA 镜像已重新生成
(_ota.bin 41168B,crc32=0x26056D5B)。
- **已知未决**:notify 上行链路不通(帧协议 CLI/INFO_QUERY/OTA 应答收不到),
下行写入正常;新读写/只读特征已绕开 notify,OTA 仍依赖它,待后续定位。
## 29. bootsetting 读写 + APP_DATA 参数区(结构化命令,CLI + BLE,2026-09-04,V1.00.09)
需求:维护期读写 bootsetting 记录与 APP_DATA 保留区(0x01006000/8KB)。
**本次只提供结构化访问,不暴露任何裸读写命令**;经 CLI 命令实现,UART 与
BLE(CLI 读写特征 ...c72e0003,写命令行读应答)双通道可用。
- **新模块 `app_params.c/h`(APP_DATA 参数记录)**:区头放 `caiic_params_t`
(magic 0xCA12DA7A + layout_ver + led1_blink_ms + flags + pwm_duty_pct +
reserved[8] + crc32,52B)。`app_params_init()` 开机校验 magic+CRC,无效则
RAM 载入缺省(不落盘);`param set` 改 RAM 副本后重算 CRC、擦首扇区、编程、
读回校验。命令:`param`(打印全部+valid/default 状态)、
`param set <led1_blink_ms|pwm_duty_pct> <val>`、`param save`。
PWM 占空比本版只存参数不接外设;LED1 闪烁半周期由 led_task 每循环读参数
生效(另有 <50ms 兜底回 500ms,参数区为全 0xFF 时 magic 不匹配直接走缺省)。
- **新模块 `app_bootset.c/h`(bootsetting 结构化读写)**:`bsdump` 逐字段打印 +
magic/CRC 校验结果;`bsset <field> <val>`(active/b1addr/b2addr/b1size/b2size/
b1crc/b2crc/b1ver/b2ver)单字段修改后整记录重写(无效记录先清零重建),流程与
`app_ota.c::ota_update_bootsetting()` 一致。addr 钳位只允许 APP1/APP2 基址,
size ≤224KB,active 只允许 1/2。写错 active/addr 会导致 Boot 跳错,恢复靠
SWD 重烧整片包。
- Qflash 使用模式与 OTA 相同:各模块 static flag 保证 `Qflash_Init()` 只调一次;
擦写关中断数十 ms/扇区,BLE 靠 5s supervision timeout 维持。
- 构建 0 Error/0 Warning,V1.00.09 `Code=38352 RO=4192 RW=2028 ZI=27580`,
整包/OTA 镜像已生成(_ota.bin 44088B,crc32=0xE246E2B2)。
## 30. 参数命令改名 appget/appset(2026-09-04,V1.00.10)
`param [set <led1_blink_ms|pwm_duty_pct> <val>|save]` 拆成两条短命令,参数名缩短:
- `appget` — 打印全部参数(led = LED1 闪烁半周期 ms,pwm = PWM 占空比 %)+ 记录状态。
- `appset <led|pwm> <val>` — 修改并立即落盘(led 50~10000;pwm 0~100)。
取消独立 save 子命令(每次 set 即保存)。
- 构建 0 Error/0 Warning,V1.00.10 `Code=38224 RO=4232 RW=2028 ZI=27580`,
整包/OTA 镜像已生成(_ota.bin 44000B,crc32=0x51756221)。
## 31. CLI 新增 reset/factory/uartrst/uartinfo(2026-09-04,V1.00.11)
四条维护命令(UART 与 BLE CLI 读写特征 `...e0003` 均可用):
- `reset` — 复位单板:打印应答后延时 200ms(与 OTA 复位同一约定,
保证应答帧先出去),`NVIC_SystemReset()` 重启,Boot 按 active bank 跳转。
- `factory` — 恢复出厂设置:新增 `app_params_restore_defaults()`(装载缺省值 +
落盘 flash),随后同样延时 200ms 复位。**只恢复 APP_DATA 参数区,不动
bootsetting**(bootsetting 决定启动 bank,不是用户配置,动它有变砖风险)。
- `uartrst` — 复位串口:补上 `bsp_usart.h` 里早已声明但未实现的
`bsp_usart_set_baud()`(等 TXC 排空 → 关 USART → 按默认 115200 8N1 重 init →
xQueueReset 清空 RX 队列 → 重开 RXDNE 中断与 USART)。
- `uartinfo` — 查询串口参数:打印 TX=PB6/RX=PB7(AF4)、波特率/数据位/校验/停止位、
TX DMA CH1 + RX 中断队列形态。
构建 0 Error/0 Warning,V1.00.11 `Code=38840 RO=4492 RW=2028 ZI=27580`,
整包/OTA 镜像已生成(_ota.bin 44876B,crc32=0x3DB21431)。
## 32. help 改两级输出(2026-09-04,V1.00.12)
实测 BLE 读写特征跑 `help` 只收到 512B 被截断:14 条命令的完整帮助表约 840B,
超过读写特征应答的 ATT 属性值硬上限 512B(`CLI_RW_RSP_MAX`),排在表尾的
reset/factory/uartrst/uartinfo 整条丢失。
- `help`(无参)改为只列命令名(约 230B,任何 MTU 下都完整);
- `help <cmd>` 输出单条命令的完整帮助/usage;
- UART 与 BLE 两侧行为一致,Tab 补全不受影响(走 `cli_cmd_name()`)。
构建 0 Error/0 Warning,V1.00.12 `Code=38972 RO=4524 RW=2028 ZI=27580`,
整包/OTA 镜像已生成(_ota.bin 45040B,crc32=0x458CCBD6)。
## 33. CLI 读写特征改分包上报(2026-09-04,V1.00.13)
V1.00.12 的 help 紧凑列表是绕过 512B 上限的权宜之计;根因是 ATT 属性值硬上限
512B,而命令表全表已约 840B。本节把读写特征应答改为**分包上报**,彻底解除限制:
- **为什么不能靠 Read Blob 扩长**:SDK rdtss profile 的 `RDTSS_VALUE_REQ_IND`
不下发 blob 偏移(`rdtss_task.h` 结构体只有 conidx/att_idx),ROM GATT 收到
blob 读会再次问应用层要全量值再自行切片——应用层若按"每次读推进游标"返回下一片,
blob 偏移会切到错误的窗口。因此分片必须避开 blob:**单片 ≤ att_mtu−2,恒为短读,
对端协议栈不会发起 blob**;读到 0 长度包 = 应答结束。
- 固件(app_ble_proto.c):应答缓冲 512B→1024B;新增 `s_cliRspOff` 游标,
每次 `app_ble_proto_cli_rsp()` 返回下一片(片长 = `app_env.max_mtu`−2,
连接后默认 23→21B/片,MTU 247 后 245B/片);写新命令或断连重置游标。
- `help` 恢复完整帮助表(UART 体验不变),`help <cmd>` 单条查询保留。
- PC 工具 ble_cli_test.py 与 App `McmCli.ts` 同步改循环读(读到空包止,
各带 64 片兜底)。**客户端必须配套升级**:旧固件重复读返回同一缓冲,
循环读会重复拼接。
构建 0 Error/0 Warning,V1.00.13 `Code=38988 RO=4524 RW=2028 ZI=28092`,
整包/OTA 镜像已生成(_ota.bin 45056B,crc32=0xB2469318)。
## 34. 引入 cJSON(infra 层)+ 参数接口 JSON 化(2026-09-04,V1.00.14)
评估结论:JSON 的价值在**接口层**(App 原生 JSON.parse,新增参数旧客户端天然兼容),
不在 Flash 存储层——APP_DATA 记录保持二进制 `caiic_params_t`(magic+layout_ver+CRC32),
半写坏记录靠 CRC 识别回落缺省,比 JSON 文本(空格/键序敏感、解析失败路径难收敛)更稳。
- **infra 层落地**:vendor cJSON v1.7.18(MIT)到 `mcm-ddc-ble/src/infra/`,
`infra_cjson.c` 用 `cJSON_InitHooks` 把分配挂到 FreeRTOS `pvPortMalloc/vPortFree`
(惰性一次性初始化,命令首次用到时才挂钩子)。uvprojx 三个 target 均加 infra 组
与 `..\src\infra` include path。改动只在 mcm-ddc-ble 内,nations-tec 只读区未动。
- **MicroLIB 注意**:cJSON 数字序列化对整数值走 `%d` 快路径(valueint),不触发
MicroLIB 不支持的浮点 `%g`;解析侧 `strtod` MicroLIB 支持。本工程参数全为整数,
若未来引入浮点参数需先验证打印路径。
- **接口**:`appget json` 输出 `{"led":500,"pwm":0,"valid":1}`;
`appset json {"led":500,"pwm":50}`(CLI 分词后的 JSON 片段自动重组;未知键忽略 =
前向兼容;所有键先校验范围,任一越界则整体不改动;成功后落盘并回显 JSON)。
原纯文本 `appget`/`appset <led|pwm> <val>` 保持不变。
- 命令行长度假定 ≤79B(JSON 重组缓冲 80B),BLE 读写特征 63B 上限内可用紧凑 JSON。
构建 0 Error/0 Warning,V1.00.14 `Code=46912 RO=4604 RW=2060 ZI=28604`,
整包/OTA 镜像已生成(_ota.bin 53072B,crc32=0x603CBC54)。
## 35. appsw 命令:切换运行 bank(2026-09-04,V1.00.15)
`appsw [1|2]`(app_bootset.c):无参切到对侧 bank,带参切到指定 bank。
安全校验层层兜底,防止跳进坏 bank:
- bootsetting 记录必须 valid(无效直接拒绝,不像 bsset 那样从零重建);
- 目标 bank 记录一致性:`start_address` 必须等于该 bank 固定基址
(APP1=0x01008000 / APP2=0x01040000),`size` 在 1B~224KB 内;
- **复算目标 bank 镜像 CRC32 与记录比对**(软件 CRC 逐位实现,224KB 约数秒,
期间不关中断、BLE 不断连),不匹配则拒绝并提示先跑 OTA;
- 通过后 `bootset_commit` 改 `active_bank`(自动重算 CRC + 擦除编程 + 读回校验),
延时 200ms 让应答出去,`NVIC_SystemReset()`。Boot 端不校验镜像 CRC,
切换安全性全部由本命令的前置校验承担。
构建 0 Error/0 Warning,V1.00.15 `Code=47680 RO=4752 RW=2060 ZI=28604`,
整包/OTA 镜像已生成(_ota.bin 53988B,crc32=0xC0500D4E)。
## 36. 双 APP 整包脚本(appsw 测试包,2026-09-04,V1.00.15/APP2=V1.00.16)
`tools\make_dual_package.bat` 一键出**完整整片包** `tools/out/caiic_ble_dual.{hex,bin}`,
包含全部 5 个区域:Boot + bootsetting(双 bank 记录,size/crc32/version 均填实)
+ APP_DATA(缺省 `caiic_params_t` 52B 记录,led=500/pwm=0)+ APP1(V1.00.15,
链接 0x01008000)+ APP2(V1.00.16,链接 0x01040000)。烧录后 `appsw` 可在两个
真实镜像间来回切换,`version`/`devinfo` 读到的版本号即当前 bank。
实现要点:
- **APP2 必须链接在 0x01040000**:Cortex-M0 代码位置相关,直接把 APP1 的 bin
烧进 APP2 会因绝对地址/字库指回 0x01008xxx 而"偷跑"APP1 内容。uvprojx 新增
target **APP2**(克隆 N32WB03x target):IROM/OCR_RVCT4 改 0x01040000,
独立 `Objects-app2/`、`Listings-app2/`、`bin/mcm-ddc-ble-app2.bin`,
Define 追加 `APP_BASE_ADDR=0x01040000u, APP_FW_VERSION=\"V1.00.16\",
APP_FW_VERSION_NUM=0x00010010u`(字符串宏在 Define 里用 `\"` 转义,
`&quot;` 不会被还原,实测编译报 #20 后改)。**APP2 版本号同时写在 uvprojx
Define 与 make_dual_package.bat 的 APP2_VERSION,两处必须同步**。
- 固件配套改动(对 APP1 行为中性,故 APP1 版本不递增):`main.c` VTOR 基址由
宏 `APP_BASE_ADDR`(缺省 0x01008000)替代硬编码;`app_version.h` 两个宏加
`#ifndef` 允许 target 级覆盖。`app_ota.c` 本来就用 `Image$$ER_IROM1$$Base`
判当前 bank,APP2 target 天然正确(OTA 目标自动为 APP1)。
- `merge_image.py` 扩展:`--app2 <bin> --app2-version N --with-appdata`,
bootsetting 生成双 bank 记录,APP_DATA 生成缺省参数记录;位置参数兼容
原 `make_package.bat` 流程(单 bank 包行为不变)。
- **OTA 跨 bank 链接地址注意**:OTA 载荷必须链接在**目标 bank** 地址。当前
`_ota.bin` 是 APP1 链接的镜像——只有运行在 APP2 时 OTA(写 APP1)才是严格
正确的;运行在 APP1 时 OTA 写 APP2 的 APP1 链接镜像,boot 后名义 active=APP2
实际执行会滑回 APP1 flash(VTOR 也指向 APP1)。要完整支持双 bank OTA,需出
两份 bank 匹配的 OTA 包(本工程的 APP1/APP2 双 target 已具备这个能力)。
## 37. 实测:板载芯片 Flash 只有 256KB 可用(2026-09-04,重要)
双 APP 整包(§36)烧录后 `appsw 2` 被自身 CRC 校验拦下(记录 0xCCDBC8B0,
实算 0xD30FB2BD)。逐层定位:
1. 本地复算:`mcm-ddc-ble-app2.bin` 的 zlib CRC32 = 0xCCDBC8B0(与 bootsetting
记录一致),而 0xD30FB2BD 恰好等于 **dual 整包 [0x01000000, +54288) 区段**的
CRC——即固件(CPU)读 0x01040000 拿到的是 0x01000000 处的内容(256KB 回绕别名)。
2. NSpyocd(AHB-AP/SWD)读 0x01040000 直接 TransferFault;读 0x0103FF00 正常。
总线在 0x01040000 以上没有存储器。
3. NSpyocd 烧写 0x01040000 以上"成功"(FLM 未报错),但 CPU 读回证明数据实际
不可见;Boot/APP1 未被破坏说明写也未回绕到低区——上方写入被静默丢弃。
**结论:当前开发板上的芯片实际表现为 256KB Flash(0x01000000~0x0103FFFF),
与"N32WB031KEQ6-2 = 512KB"的预期不符。** 需核对板子实际丝印/物料(可能实装了
256KB 的 KCQ6-1)。受此影响:
- §36 的双 APP 整包/`appsw` 功能在这块板上不可用(APP2 地址无效);
脚本与 APP2 target 保留,待 512KB 芯片到位后可直接复用。
- **现有 512KB 双 bank 布局(dfu_layout.h)在此板上整体不成立**:BLE OTA 向
APP2(0x01040000)的直写会被静默丢弃,OTA"成功"后会切到无效 bank。
若确认板上就是 256KB 料,应回退到官方 256KB DFU 布局(APP1 0x01008000/112KB、
APP2 0x01020000/112KB,bootsetting/APP_DATA 相应下移),改动涉及
dfu_layout.h、Boot、app_ota.c、merge_image.py 与全部文档。
- flash_package.bat 已改用 DFP pack 的 n32wb031keq6_2 target(512KB 映射)
+ `-O smart_flash=false`(pack target 的预编程 diff 读 0x01000000 会 fault,
编程+校验正常);对 256KB 内的常规整包烧录同样适用(已实测)。
## 附:本次调试的正面结果
- `appsw` 的三层安全校验按设计拦下了无效 bank(记录 CRC ≠ 实算 CRC),
没有跳进坏镜像——校验逻辑本身验证通过。
- 分包读 CLI 应答(§33)实测正常:BLE 读 `version`/`appsw` 输出完整。
## 38. 回退 256KB 布局 + appsw 双 bank 实测通过(2026-09-04,V1.00.16/APP2=V1.00.17)
确认板载硅片为 256KB(§37)后的重排:Boot 16KB / bootsetting 8KB / APP_DATA 8KB
不动,余下 224KB 均分:**APP1 0x01008000/112KB(0x1C000),APP2 0x01024000/112KB**,
末尾 0x0103FFFF 对齐 256KB 终点;IMAGE_UPDATE 区取消(BASE 改为 0x01040000 作
flash 结束标记,SIZE=0,Boot 的 start_address 上界检查沿用该区间)。
改动点:
- `Boot/src/dfu_layout.h`:`CAIIC_APP_BANK_SIZE` 0x38000→0x1C000,
`CAIIC_APP2_BASE` 0x01040000→0x01024000,IMAGE_UPDATE 归零。Boot/APP 全量重建。
- uvprojx:target N32WB03x 的 IROM/OCR_RVCT4 size 0x38000→0x1c000;
target APP2 的 start 0x1040000→0x1024000、size 0x1c000,
Define `APP_BASE_ADDR=0x01024000u`、版本覆盖升至 **V1.00.17/0x00010011u**
(APP1 默认版本升 V1.00.16/0x00010010u;bat 的 APP2_VERSION 同步)。
- `merge_image.py`:`APP2_ADDR` 0x01024000。
- app_bootset.c 提示文案去掉硬编码地址(改指宏名)。
实测(NSpyocd 烧 `caiic_ble_dual.hex`,bleak 客户端操作):
- 烧录后跑 APP1:BLE `version` → **V1.00.16**;`appsw 2` → bank2 CRC 校验通过、
更新 active=2、复位;
- 重连后 `version` → **V1.00.17**(APP2,链接 0x01024000,BLE/CLI 全功能正常,
VTOR 宏化生效);`bsdump` 记录 valid,双 bank size/crc/ver 正确;
- 无参 `appsw` 切回 APP1,`version` 再次 → **V1.00.16**。双向切换全通。
- 已知小现象:设备在写应答 ACK 前后复位时,bleak 写特征偶报
WinError -2147023673(操作已取消),实际命令已送达执行——属 Windows BLE
栈对端复位的时序表现,非固件问题。
构建 0 Error/0 Warning:APP1 `Code=47704 RO=4752 RW=2060 ZI=28604`,
APP2 `Code=48004`(同源码、不同链接地址/版本号,体积差 300B 属正常链接差异)。
整包/双包/OTA 镜像均已重新生成。
## 39. OTA 双 bank 配套:CUR_BANK 信息项 + 双载荷出包 + 向量表防呆(2026-09-04,V1.00.17)
回答"不知道当前跑 APP1 还是 APP2、OTA bin 链接地址不匹配怎么办":
- **信息项 0x07 CUR_BANK**(u8,1=APP1/2=APP2,按链接基址 `Image$$ER_IROM1$$Base`
判定):`devinfo` 首行新增 `cur bank: APPn`,帧协议 INFO_QUERY 与只读特征
`...e0004` 的 TLV 同步包含。任何时刻客户端都能确定运行 bank,不依赖版本号。
- **双 OTA 载荷**:`make_package.bat`/`make_dual_package.bat` 现在都构建 APP2
target,`merge_image.py` 新增 `--ota-app2`:出 `<包名>_ota.bin`(target_bank=1)
与 `<包名>_ota_app2.bin`(target_bank=2),清单 json 增加 `target_bank` 字段。
客户端规则:读 CUR_BANK → OTA 目标 = 对侧 bank → 选对应载荷。
- **固件防呆**:OTA_END 整镜像 CRC 通过后,校验镜像向量表 Reset 地址落在目标
bank 范围内,否则回 OTA_RSP(status=6 bank_mismatch)、中止不切换。
(通知上行未修复,手机端 OTA 页仍停用;该校验 PC 帧协议客户端可达。)
- **APP2 target 取消版本号覆盖**:两个 bank 必须出同一 release(否则 OTA 清单
version 与镜像自报版本不一致)。bank 区分改由 CUR_BANK 承担——比"+1 版本号"
更通用(任意 OTA 之后仍有效)。`--app2-version` 缺省=主版本号。
- ble_cli_test.py 的 `param` 命令同步解码 cur_bank。
实测(双包烧录):APP1 devinfo `cur bank: APP1` → `appsw` → APP2 devinfo
`cur bank: APP2`(两 bank 同为 V1.00.17)→ e0004 TLV 含 `cur_bank` →
`appsw 1` 切回。构建 0 Error/0 Warning(双 target),整包/双包/双 OTA 载荷
均已重新生成。
## 40. SWD 单 bank 升级流程实测 + BLE OTA 待定(2026-09-04,V1.00.18)
**SWD 部分烧录升级(不动运行中的 bank)实测通过**,步骤:
1. 正常出包:`tools\make_package.bat`(构建 APP1/APP2 双 target,出整包 +
bank1/bank2 两份 OTA 载荷 + 合并包 `*_ota_dual.bin`);
2. 只写非活动 bank(假设当前跑 APP2,升级 APP1 区):
`NSpyocd load --pack <DFP> -M under-reset -f 1000000 -t n32wb031keq6_2
-O smart_flash=false mcm-ddc-ble.bin@0x01008000`
—— bin 后用 `@地址` 指定基址,pyocd 只擦写涉及的 14 个扇区,运行中的
APP2 不受影响(烧完复位仍回 active bank);
3. BLE/串口 CLI 修正该 bank 记录:`bsset b1size <size>` / `bsset b1crc <crc>`
/ `bsset b1ver <ver>`(值取 merge_image 输出或 _ota.json 清单);
4. `appsw 1` → 目标镜像 CRC 复算通过 → 复位切入新版本。
实测:bank1 SWD 写入 V1.00.18 → 修记录 → `appsw 1` → `devinfo` 报
**APP1 / V1.00.18**;bank2 仍保留 V1.00.17 可回切。此路径可作为 BLE OTA
修复前的生产升级手段,也是 OTA 链路的地面参照。
**BLE OTA 当前状态(未决)**:`tools/ble_ota_update.py`(合并包解析、
CUR_BANK 选包、扇区擦除窗口避让的节流发送、双次重试)已写好并实机试跑,
但设备端在 OTA_END 后未复位(中途应有帧丢失/状态拒绝,notify 上行不通
拿不到 OTA_RSP,定位需要 UART blelog)。该脚本标记为实验性,待 notify
修复或 UART 抓帧后再调通。
## 41. 发布命名整理 + OTA 单文件包(2026-09-04,工具链,固件 V1.00.18 不变)
- 生产整片包改名 **`mothercup_ble_prod.{hex,bin}`**(Boot+bootsetting+APP_DATA
缺省记录+APP1);`flash_package.bat` 缺省镜像同步改名。
- OTA 发布件改名 **`mothercup_ble_ota.bin`**,头部从 40B 扩到 **52B**:
新增 hdr_len、total_size(整文件字节数)、payload_crc(两载荷整体 CRC32)——
原清单 json 的信息全部收进头部,**只发这一个文件**(单 bank 载荷与 json
仍生成,仅供调试)。格式表写入 ble_protocol.md §7(供 App/UART 升级开发)。
- `merge_image.py` 新增 `--combo-name`,`make_package.bat` 传
`--combo-name mothercup_ble_ota`;`ble_ota_update.py` 按新头解析并校验
hdr_len/total_size/payload_crc/各 bank crc,实包解析验证通过
(V1.00.18,bank1 54204B / bank2 54504B,total=108760B 与文件一致)。
- 旧命名 caiic_ble_full_* 产物已从仓库删除;双 APP 测试包仍叫 caiic_ble_dual。
## 42. OTA 传输层通道无关化 + 帧 CRC 根因修复(2026-09-04,V1.00.19)
**本仓库最大遗留问题(notify 上行不通)的根因找到并修复了**:固件 0xCA 帧的
CRC16 只覆盖了 `3+payLen` 字节(TYPE/SEQ/LEN + 除最后一字节外的 payload),
而协议文档与 PC 端实现都是 `4+payLen`(TYPE..PAYLOAD 全量)——**所有帧在设备侧
CRC 校验失败被静默丢弃**,表现为"下行写入正常、没有任何应答",此前一直以为
是 notify 上行链路问题。修复 4 处(app_ble_proto.c TX/RX、app_ota.c 信道
TX/RX 各一处 `3u`→`4u`)后,notify 上行立即恢复(`ble_cli -f version` 实测
收到 CLI_RSP 帧流 + CLI_RSP_END),e0005 OTA 信道也随之打通。
本次内容:
- **OTA 传输层通道无关设计**(ble_protocol.md §6.6):0xCA 帧 + OTA 消息集
(BEGIN/DATA/END/ABORT→OTA_RSP)与物理通道解耦;`app_ota.c` 应答改为
通道注册的 sink 出口(`app_ota_rsp_sink_fn`),NULL 时回落旧 notify 路径;
通道侧自含字节流重组器(只收 OTA 类型)。
- **BLE 独立 OTA 特征 `...e0005`**(Write With Response + Read,不与 CLI/参数
特征混用):写携带一帧,读同一特征取回 OTA_RSP(读即消费;读要先于设备
处理完成返回空时主机需轮询,实测需要)。
- **UART OTA 通道**:CLI 命令 `ota` 进入二进制帧模式(无回显、3s 空闲自动
退出回 CLI 并中止会话),RSP 帧直接从 TX 发出。
- **流控改为逐帧锁步**:原"每写满 4KB 扇区才回 ack"导致锁步主机在第一个
DATA 帧就等不到应答;改为每帧回 OTA_RSP(ok, 新 offset),ack 往返天然覆盖
扇区擦除窗口(旧 WWR 盲发丢帧问题同时消解)。
- PC 端 `ble_ota_update.py` 重写为双通道(BLE 默认 / `--uart COMx`),
`ble_ota.bat` 包装;BEGIN/DATA/END 逐帧校验 echo/status/offset,
END 的 ack 丢失(设备先复位)按"等重启复核"处理。
实测:BLE e0005 通道完整 OTA 通过(55KB @ ~1.9kB/s,35s,复位后重连核对
CUR_BANK 翻转 + 版本正确);notify 帧协议通道恢复(CLI_RSP 正常);
UART 通道待串口线恢复后验证。
## 43. OTA_END 应答可靠送达:延迟复位(2026-09-04,V1.00.20)
**问题**:OTA 成功后设备在发出 END 的 ok 应答后仅固定延时 200ms 就
`NVIC_SystemReset()`。V1.00.19 锁步信道下,BLE 通道的 OTA_RSP 只是写入
e0005 读回缓冲,**主机还没发起 ATT 读**,200ms 后设备已复位——PC 端永远
收不到 END ack(实测报 `END ack lost WinError -2147023673`,只能靠重启后
重连复核兜底)。UART 通道同理存在复位抢在应答读完之前的窗口。
**修复**(`app_ota.c`):END 成功后不再原地延时复位,改为**延迟复位**:
- 新增"应答已消费"标志 `s_rsp_consumed`:BLE 通道在
`app_ota_chan_rsp_ble()` 读取消耗(主机读到非空 RSP)时置位;UART 通道
在 `ota_uart_sink()` 阻塞式 DMA TX 返回(字节已出移位寄存器)时置位。
- END ok 应答发出后置 `s_reset_pending` + 时间戳即返回;**不能在
`ota_on_end()` 里死等**——e0005 的 ATT 读处理本身也要 BLE 调度任务继续
跑 `rwip_schedule()`,阻塞会导致永远等不到读。
- BLE 调度任务循环新增 `app_ota_reset_poll()`:应答已消费且过了 300ms
宽限(让控制器真正把 ATT read response 发出空中)即复位;2s 超时无条件
兜底复位(防旧客户端/异常下设备挂死)。旧 notify 路径顺带获益:等待窗口
内 `ble_up_poll()` 持续排空 notify 缓冲。
实测:`ble_ota.bat` 完整 OTA 后**直接收到 END ack**(输出 `OTA_END done`,
不再出现 ack lost),设备复位进 APP2,重连核对 CUR_BANK 翻转 + 版本正确,
PASS。基线:Code=48840 RO-data=4868 RW-data=2076 ZI-data=29116。
## 44. UART OTA 通道调通:模式切换机制 + 锁步重传容错(2026-09-04,V1.00.21)
UART OTA 首轮实测失败("no OTA_RSP on UART"),排查过程与最终设计如下。
**模式切换机制(来回切换的完整答案)**:串口是 CLI 文本通道,切二进制
需要明确的状态机,否则主机与设备模式错位、二进制帧被文本行编辑器吃掉
(帧内 0x0D 会触发执行垃圾命令)。定案(ble_protocol.md §6.6):
- 进入握手:`ota` 命令回标记行 `[ota] binary mode ON`,主机必须等到标记
再发帧(此前 PC 工具靠固定 sleep 猜,时序上不可靠);
- 退出三路:END 成功→复位(V1.00.20 延迟复位保证 ack 送达);OTA_ABORT→
回完 ack 立即回 CLI(新增,`app_ota_chan_rx_uart()` 返回值驱动前端退出);
10s 空闲兜底(原 3s 太短,必须大于主机重传窗口);
- 再同步:主机随时发 `\r`,CLI 态立即回提示符;二进制态无响应,等兜底退出;
- 会话占用保护:OTA 会话全局唯一,他通道 BEGIN 回 BAD_STATE(此前会踩状态)。
**实测抓到的两个真 bug**:
1. `otaIdleMs` 进入二进制模式时未清零——第一次 `ota` 正常,**第二次进入
必在 100ms 内被"空闲超时"踢出**(上一段会话残留的计数值 ≥ 阈值)。
表现为帧字节走文本路径被逐字符回显(只有可打印字符回显,这是定位
突破口)。修复:每次进入 while(s_otaMode) 前清零。
2. 丢整帧:约 1~3% 的 233B 数据帧在主机 write() 成功后设备侧一个字节都
收不到(设备 3s 空闲退出、CLI 仍活为证),随机发生——**物理链路接触
不良**(用户已预警过串口线问题)。协议必须容忍:PC 工具实现锁步重传
(ack 2s 超时直接重发同帧,≤3 次);利用协议已有的 offset 报告语义做
再同步——重复 DATA 回 BAD_STATE+期望 offset,若等于本帧结束位置说明
设备早收到只是 ack 丢了,视为 ack 继续;重复 BEGIN 视为会话已建立。
设备侧配套:100ms 字节间断自动丢弃重组器半帧状态,保证重发帧重新对齐。
**调试教训**:排查时在逐字节路径加 `printf("[rx %02X]")` 观测,结果打印
本身(轮询 TX ~500µs/字节)把 RX 消费拖慢 6 倍,64 深队列在 ~80 字节处
溢出丢尾——**观测手段本身制造了新的丢帧**,误以为 CRC/协议问题。高速
链路排查禁用逐字节打印。
实测(release 固件):UART OTA 全程 PASS(55.7KB/41s,途中 4~5 次丢帧
全部自动重传恢复,END ack 正常,复位进 APP2 复核通过);BLE OTA 回归
PASS(APP1,30s)。基线:Code=48872 RO-data=4980 RW-data=2076
ZI-data=29116。遗留:串口物理链路建议用户更换/重插,协议层已能自愈。
## 45. OTA 工具选错 bank 事故与三重防护(2026-09-04,V1.00.22,工具侧修复)
用户实测 V1.00.22 包时工具报 `FAIL: OTA_END rejected, status=6
(bank_mismatch)`,但现场查询发现设备**已在运行 V1.00.22@APP2**、
bootsetting 记录完整——即前一次运行其实已成功,这次 FAIL 是重复运行时
的误选 bank 被设备防呆拦截。
**根因(PC 工具 bug)**:`enter_ota_mode` 读 `devinfo` 时用
"cur bank:" 前缀做读完标记,标记命中时 "APP2" 字样可能尚未到达,解析
落入 `else bank=1` 分支——把运行在 APP2 的设备误判为 APP1,于是把
APP2 链接的镜像发向 APP1 区。设备端 END 校验(向量表 Reset PC 必须落在
目标 bank 内)正确拒绝(status=6),不变更 bootsetting、不复位——
**防呆设计成功阻止了一次错刷**。
**修复(ble_ota_update.py,三重防护)**:
1. bank 探测改为读到提示符的完整输出 + 严格正则 `cur bank: APP([12])`,
读不到直接报错退出(删除"读不到就默认 APP1"的危险兜底);
2. **传输前预检** `check_blob_bank()`:载荷向量表 Reset PC 必须落在目标
bank 地址范围内,选错包在发送前即拒绝(BLE/UART 两通道都加);
3. UART 通道新增**复位后自动复核**(与 BLE 通道对齐):END 后等重启、
重连 CLI 读 devinfo 比对 bank+版本,输出确定性的 PASS/FAIL,杜绝
"显示 FAIL 实际成功"的歧义。
实测:修复后 UART OTA(APP2→APP1,V1.00.22)全程 PASS 并自动复核通过;
顺带修复了误刷进 APP1 区的内容。固件无改动(仅版本号 V1.00.21→V1.00.22)。
## 46. V1.00.23 + OTA 工具进度细化(2026-09-04,工具侧)
- 固件版本号 V1.00.22→V1.00.23(0x00010017,无代码改动),供用户做
BLE/UART OTA 全流程验证:两通道均 PASS(用户自测 BLE:APP1→APP2;
本机复测 BLE:APP2→APP1,自动复核版本/bank 正确)。
- `ble_ota_update.py` 进度显示细化:tty 下帧级进度条原地刷新(24 格
bar + 百分比 + 字节数 + 速率 + 最近一帧 ACK 往返 rtt,~10fps 节流);
非 tty(重定向/管道)自动退化为每 5% 一行,避免日志被 `\r` 刷乱。
重传/再同步提示前补换行不与进度条混行;END 总结新增
`N resent, N resynced` 统计(链路质量一目了然:BLE 实测 0 次,
接触不良的串口线 4~6 次)。BEGIN/END 阶段切换增加明示打印。
## 47. UART OTA 流式传输层 + RX 改 DMA 环形缓冲(2026-09-05,V1.00.24)
- **动机**:UART OTA 逐帧锁步实测仅 ~1.5 kB/s(55KB 要 39s),瓶颈是每帧
一个 RTT(设备处理 + 主机调度抖动),不是波特率。
- **UART RX 改 DMA 环形缓冲**(bsp_usart.c):DMA_CH2 循环模式 + 2KB ring,
USART1_RX remap;RXDNE 逐字节中断关闭,改开 IDLEF 中断只做信号量唤醒
(`bsp_usart_read_byte()` 接口不变,20ms 切片等信号量兜底连续流无 IDLE
的情形;BLE 下行注入 CLI 改走独立的 64B 注入队列,优先于 ring 消费)。
**关键收益:DMA 在关中断的扇区擦除窗口(数十 ms)内继续收字节**
(2KB ≈ 173ms@115200),这是流式不丢包的前提——中断驱动 RX 在擦除期间
必丢字节,正是当年只能锁步的根因。CLI 文本模式行为不变。
- **流式传输层(ble_protocol.md §6.7)**:OTA_BEGIN 扩展 16B(+flags u32,
bit0=STREAM,仅 UART 通道生效);DATA 不再逐帧 ack,只在跨越 4KB 扇区
边界(擦除已完成)或最后一字节时回 ack;主机 8KB 窗口流水线发送;
丢帧靠既有的 BAD_STATE+期望 offset 立即回执回退续传(go-back-N,设备侧
节流为对齐前只报一次);3s 无 ack 进展主机自动回退到最后的 ack 重发;
END/ABORT 语义不变。12B BEGIN=锁步,新旧固件/主机任意组合兼容(旧固件
对 16B BEGIN 回 bad_frame,PC 工具自动回退;`--lockstep` 强制旧模式)。
- PC 工具 `ble_ota_update.py` 新增 `ota_session_uart_stream()`;
BLE 路径与 App(e0005)路径完全不受影响。
- 预期速率:115200 下 ~10 kB/s(55KB 约 6s,原 39s)。
### §47 补记:V1.00.24 首版烧录即锁死——SRAM 顶部悬崖(同日下午修复)
- **现象**:首版 V1.00.24 烧录后板子无 banner 无广播。SWD 连上看:CPU 处于
lockup,LR 落在 HardFault_Handler 内(fault 中再 fault)。
- **定位**:用 NSpyocd commander 逐地址探测发现 **SRAM 从 0x2000C000 起读即
TransferFault**(复位后立刻探也一样),最后一个可访问字是 0x2000BFFC——
该 256KB 硅片 APP 可用 SRAM 实为 **32KB(0x20004000~0x2000BFFF)**,
顶部 16KB 块并不可用(IRAM 配置里 0xC000=48KB 的上限是虚的)。V1.00.23
的栈顶是 0x2000B9D8 恰好在线内;本次新增的 2KB DMA ring 把 ZI 顶高,
栈顶漂到 0x2000C1E8 越线 → 上电第一批压栈即炸。**教训:任何 ZI/RW
膨胀都必须复查 map 的 `__initial_sp`**。
- **修复**:FreeRTOS heap 20KB→18KB(实测余量 12.6KB,够用),栈顶回到
0x2000B9E8;`merge_image.py` 增加硬卡:bin 向量表首字(初始 SP)不在
(0x20004000, 0x2000C000] 内直接出包失败,距悬崖 <1KB 告警。
- **验证**:重烧后 SWD 观测 reset→go→延时,CPU 持续 Running、PC 在 APP1
正常代码区推进,HardFault 断点不再命中。
- AGENTS.md "RAM 铁律"已改写为双侧悬崖(低 16KB ROM 占用 + 顶部
0x2000C000 悬崖)。
### §47 再补记:流式首测无 ack 的两个叠加 bug(当日下午二修,已实测通过)
- **现象**:V1.00.25 首版流式 OTA 主机侧 0 ack,反复 stall 回退。
- **定位过程**(SWD 在线取证,值得记一笔):commander 读 `s_offset`/
`s_erased`/`s_prog` 发现设备把 4800B 突发全部正确收写(环形缓冲内容
与主机发送字节流逐字节一致,DMA RX 无丢字节);OTA 状态机正常、erase
正常、TX DMA 通道空闲未挂死——唯一没发生的就是 ack 本身。
- **根因 1(V1.00.24 版)**:扇区 ack 写成"`s_offset` 恰好是 4096 倍数才
ack",240B/帧时偏移序列 4080→4320 永远踩不中边界。
- **根因 2(V1.00.25 首版)**:改成跨界判断时用了 `dlen`——但 `dlen`
在上面的 4B 对齐暂存逻辑里已被消耗成 0~3 的尾巴,`(s_offset-0)` 与
`s_offset` 永远同扇区,ack 永不触发。修复:用帧原始长度 `chunk` 做
跨界判断(`app_ota.c ota_on_data`)。
- **实测**:双向各跑一次,均 5.9s 完成(**9.5 kB/s,0 rewinds**,锁步时
39s/1.4kB/s),复位复核 PASS(APP1→APP2→APP1,版本 V1.00.25)。
- 附:flash_package 改 python 包装(tools/flash_package.py)流式转发
NSpyocd 输出并替换横幅为 "CAIIC NSLINK UMP";块状读取修进度条 \r
不刷新问题;模式替换需在拼接缓冲上做(跨块边界会劈开模式串)。
## 48. UART 波特率提升 115200→460800(2026-09-05,V1.00.26)
- 固件 `BSP_USART_BAUDRATE` 460800(64MHz/460800 分频误差 +0.03%);
`uartrst`/`uartinfo` 走宏自动跟随,帮助文本同步。
- **环形缓冲 2KB→3KB**:460800 下 2KB 只能覆盖 44ms,小于擦扇区关中断
窗口(~45ms);3KB ≈ 69ms。注意 SRAM 悬崖(§47):栈顶上移 1KB 至
0x2000BDE8,merge_image.py 硬卡给出 <1KB 告警(属预期内)。
- PC 工具默认波特率同步(ble_ota_update.py UartTransport、uart_cap.py)。
- 实测:流式 OTA 55832B **2.6s(23 kB/s)**,途中丢 1 帧由 go-back-N
自动回退续传( rewind 45360→38160),复位复核 PASS;CLI 文本模式
在 460800 下正常(devinfo/ota 握手/复位后复核全走文本 CLI)。
- 对比:115200 锁步 39s → 115200 流式 5.9s → 460800 流式 2.6s。
- 注意:串口终端/工具必须同步改 460800,否则 CLI 全是乱码。
## 49. tools 自包含生产包(2026-09-05,无固件改动)
- 目标:tools/ 打包即可发布到任何 Windows 产线电脑,无需安装任何环境。
- **内嵌 Python 运行时**:`tools/python-embed/`(python-3.12.10-embed-amd64,
华为云镜像下载;python312._pth 启用 Lib/site-packages + import site,
bleak 3.0.2/pyserial 3.5 用 pip --target 预装,29MB)。bat 优先级:
python-embed → .venv-ble(开发机自动建)→ 系统 Python。
- **烧录器收编**:NSpyocd.exe + DFP pack 复制进 tools/(flash_package.py
优先用 tools 内副本,找不到才回退仓库 nations-tec 路径)。
- **一键打包**:`make_release.bat`(PowerShell Compress-Archive,排除
.venv-ble/__pycache__,zip 暂存 %TEMP% 再移入 out/——直接写 out/ 会
自读自写报错)。
- **坑**:bat 必须 CRLF 行尾——LF-only 时 cmd 对括号块/%~dp0 的解析会
错乱(standalone 复测时 venv 分支被误触发且 PYREAL 为空),本次全部
转 CRLF。zip 用 Windows 资源管理器/Expand-Archive 解压(Info-ZIP
unzip 对 Compress-Archive 的反斜杠分隔符不兼容)。
- 验证:zip 解到仓库外独立目录,ble_ota.bat --uart COM4 全流程 PASS
(2.2s,27 kB/s,0 重传);flash_package.bat 用包内 NSpyocd 烧录 PASS。
## 50. 独立看门狗 IWDG(2026-09-05,V1.00.27)
- 起因:此前 CLI 卡死事故中 `reset` 命令无从执行(任务已死),系统无自救
手段。固件原本没有看门狗。
- 复位路径复查:当前固件 `reset`/`factory`/`appsw`/OTA 延迟复位均实测正常
(NVIC_SystemReset 有效);用户报告的"复位没生效"发生于 CLI 卡死状态,
命令根本无法到达执行路径——这正是看门狗的场景。
- 设计(main.c / app_wdt.h,驱动 n32wb03x_iwdg.c 已加入全部 4 个 target):
- IWDG:LSI 32kHz /128 → 250Hz,reload 1000 = **4s 超时**;
- **喂养策略**:FreeRTOS idle hook 只在"3s 内有任务心跳"时喂狗;BLE 调度/
LED/CLI 三个任务循环里打点 `app_wdt_heartbeat()`。任何任务死锁/死循环
(CLI 独占 CPU 饿死 idle,或全体阻塞 idle 空转无心跳)都会在 ≤4s 复位;
- `DBG_ConfigPeriph(DBG_IWDG_STOP, ENABLE)`:SWD halt 时冻结 IWDG,
调试会话不会被看门狗打断;
- 启动 banner 打印复位原因(RCC IWDGRSTF)并清标志。
- 新增调试命令 `hang`(CLI 任务忙等挂起,验证看门狗点火用)。
- 实测:正常喂狗系统稳定(12s+ 无重启、CLI 正常);`hang` 后 4.4s 复位,
新 banner 打印 "Last reset: IWDG watchdog!"。
- 附带修正:`uartinfo` 输出里 RX 描述更新为 DMA ring(此前还是 RXDNE 队列
的旧文案)。
## 51. Boot download 模式:设计定稿(2026-09-05,代码未落地)
- 目标:Boot 增加 USART1 功能,把 APP 的 CLI 与 UART OTA 能力移植进 Boot,
支持 **download 模式**(该模式下有 CLI + OTA 帧升级),用于救砖与生产
下载。本节先记录摸底结论与设计决策,实现见后续章节。
- 摸底结论(复用性评估):
- Boot 现状:仅 4 个源文件(startup/system/main/boot_crc32),ROM 占用
2KB,预算 16KB(`CAIIC_BOOT_SIZE`),IRAM 0x20004000 起(栈顶须远离
0x2000C000 SRAM 悬崖,§47);
- `app_ota.c`(741 行)**通道无关**:帧重组/惰性擦扇区/4B 暂存/CRC32 校验/
bootsetting 更新全部可复用;耦合点仅 FreeRTOS tick(延迟复位)、
`app_ble_proto.h`(帧常量 + legacy notify 应答)、`bsp_usart.h`
(UART sink)。用 `BOOT_FIRMWARE` 宏做双构建隔离,**不复制副本**,
避免两份 OTA 引擎日后漂移;
- `app_bootset.c`(bsdump/bsset/appsw)几乎无耦合一并复用;
`cli_core.c`/`app_cli.c` 与 FreeRTOS/BLE/各 app 模块深耦合,**不搬**,
Boot 侧写一个精简的轮询行编辑器 + 命令子集;
- Qflash 驱动 = 常量数组 memcpy 到 RAM(0x324B)+ 关中断执行,裸机可用;
- flash 擦除关中断数十 ms,**RX 必须用 DMA 环形缓冲**(同 APP 方案,
裸机轮询 DMA 写指针即可,无需 IDLE 中断),否则 460800 下擦除窗口丢字节。
- PC 脚本兼容面(`tools/ble_ota_update.py` UART 流程,决定 Boot CLI 协议面):
提示符 `caiic->`;`devinfo` 输出须含 `cur bank: APP<n>`(脚本据此选
非活动区载荷);`ota` 命令须打印 `[ota] binary mode ON` 标记行后进入
二进制帧模式。**Boot CLI 全部照此实现,脚本零改动即可对 Boot 升级**。
- download 模式设计:
- 入口:① 每次复位后 Boot 初始化 USART1(460800 8N1)打印 boot banner,
**2s 窗口**内收到字节即进入;② bootsetting 无效且 APP1 向量表
(SP/复位 PC) sanity 检查不过 → 无镜像可跳,自动进入(救砖路径);
- 模式内:裸机 superloop(无 RTOS),SysTick 1ms 时基;命令子集:
help / version / devinfo / bsdump / bsset / appsw / ota / reset /
uartinfo;`ota` 进入 0xCA 二进制帧模式(复用 app_ota 引擎,语义与
APP 完全一致:OTA_ABORT 立即退出 / 3s 空闲退出 / OTA_END 成功后延迟
复位——应答被取走 +300ms 或 2s 超时兜底);
- **目标 bank 规则**:写 bootsetting 非活动区并翻转到新区;bootsetting
无效时写 APP1 并置活动。boot 的 `devinfo` 按此规则报告 "cur bank"
(无效记录时报告 APP2,使脚本选 APP1 载荷,与 boot 目标一致);
- download 模式内开 IWDG 兜底(superloop 喂养),跳转 APP 前不动 IWDG
(APP 自理,§50)。
- 风险提示:Boot 16KB 尺寸预算紧张(printf + USART/DMA/GPIO/RCC 驱动 +
qflash + OTA 引擎 + CLI),构建后核对 map 是否越界,越界则裁剪命令。