- Boot/src/boot_cli.c: CmdHelp 无参分支在命令表后打印 keys 提示串(对齐 APP 的 help) - 撤回 ready banner 临时加的 (TAB = complete),提示只放 help,与 APP 风格一致 - Boot/src/boot_version.h: V1.01.02 -> V1.01.03 - docs/开发日志.md: 新增 §55
89 KiB
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. 烧录与运行
- Keil 打开
embeddedSrc/MDK-ARM/embeddedSrc.uvprojx,F7 编译 - NS-LINK 接 SWD(PA4/PA5),F8 下载
- 串口助手接 PB6(TX)/PB7(RX),115200 8N1
- 复位后预期输出:启动 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,由上层重试。
- service
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 向量表 → weakDefault_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.cOTA_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_BAUDRATE115200 → 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 根因(两层叠加,均已修复)
- 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 自身无中断源,安全)。 - 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,订阅...E0002notify 后往...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)。
- 每个完整收/发协议帧在串口 hex 打印:
- 打印走 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(帧协议/读写特征 两种方式,venvtools/.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 5010000;pwm 0100)。 取消独立 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把分配挂到 FreeRTOSpvPortMalloc/vPortFree(惰性一次性初始化,命令首次用到时才挂钩子)。uvprojx 三个 target 均加 infra 组 与..\src\infrainclude path。改动只在 mcm-ddc-ble 内,nations-tec 只读区未动。 - MicroLIB 注意:cJSON 数字序列化对整数值走
%d快路径(valueint),不触发 MicroLIB 不支持的浮点%g;解析侧strtodMicroLIB 支持。本工程参数全为整数, 若未来引入浮点参数需先验证打印路径。 - 接口:
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_t52B 记录,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 里用\"转义,"不会被还原,实测编译报 #20 后改)。APP2 版本号同时写在 uvprojx Define 与 make_dual_package.bat 的 APP2_VERSION,两处必须同步。 - 固件配套改动(对 APP1 行为中性,故 APP1 版本不递增):
main.cVTOR 基址由 宏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)。逐层定位:
- 本地复算:
mcm-ddc-ble-app2.bin的 zlib CRC32 = 0xCCDBC8B0(与 bootsetting 记录一致),而 0xD30FB2BD 恰好等于 dual 整包 [0x01000000, +54288) 区段的 CRC——即固件(CPU)读 0x01040000 拿到的是 0x01000000 处的内容(256KB 回绕别名)。 - NSpyocd(AHB-AP/SWD)读 0x01040000 直接 TransferFault;读 0x0103FF00 正常。 总线在 0x01040000 以上没有存储器。
- 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_SIZE0x38000→0x1C000,CAIIC_APP2_BASE0x01040000→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_ADDR0x01024000。- 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)实测通过,步骤:
- 正常出包:
tools\make_package.bat(构建 APP1/APP2 双 target,出整包 + bank1/bank2 两份 OTA 载荷 + 合并包*_ota_dual.bin); - 只写非活动 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); - BLE/串口 CLI 修正该 bank 记录:
bsset b1size <size>/bsset b1crc <crc>/bsset b1ver <ver>(值取 merge_image 输出或 _ota.json 清单); 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:
otaIdleMs进入二进制模式时未清零——第一次ota正常,第二次进入 必在 100ms 内被"空闲超时"踢出(上一段会话残留的计数值 ≥ 阈值)。 表现为帧字节走文本路径被逐字符回显(只有可打印字符回显,这是定位 突破口)。修复:每次进入 while(s_otaMode) 前清零。- 丢整帧:约 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,三重防护):
- bank 探测改为读到提示符的完整输出 + 严格正则
cur bank: APP([12]), 读不到直接报错退出(删除"读不到就默认 APP1"的危险兜底); - 传输前预检
check_blob_bank():载荷向量表 Reset PC 必须落在目标 bank 地址范围内,选错包在发送前即拒绝(BLE/UART 两通道都加); - 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% 一行,避免日志被6 次)。BEGIN/END 阶段切换增加明示打印。\r刷乱。 重传/再同步提示前补换行不与进度条混行;END 总结新增N resent, N resynced统计(链路质量一目了然:BLE 实测 0 次, 接触不良的串口线 4
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_BAUDRATE460800(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 下擦除窗口丢字节。
- Boot 现状:仅 4 个源文件(startup/system/main/boot_crc32),ROM 占用
2KB,预算 16KB(
- PC 脚本兼容面(
tools/ble_ota_update.pyUART 流程,决定 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 是否越界,越界则裁剪命令。
52. Boot download 模式:实现与实测(2026-09-05,Boot V1.01.00 / APP V1.00.28)
- 实现(设计见 §51,一处变更:Boot 内不启用 IWDG——download 模式遗留的
IWDG 会在 APP 启动早期(app_wdt_init 之前)误杀,APP 看门狗由 §50 自管;
download 为有人值守场景,卡死靠断电):
Boot/src/boot_usart.c/h:USART1 460800 8N1 裸机驱动——TX 轮询、RX DMA CH2 环形 3KB(裸机轮询 DMA 写指针,无中断;扇区擦除关中断窗口 不丢字节,流式 OTA 的前提)、SysTick 轮询 millis(COUNTFLAG,无中断);Boot/src/ota_wire.h:0xCA 帧类型/OTA 状态码的单一来源共享头, APP 的app_ble_proto.h改为包含它(协议常量不再两处定义);mcm-ddc-ble/src/app_ota.c、app_bootset.c:不复制副本,加BOOT_FIRMWARE宏双构建——隔离 FreeRTOS tick(boot 用 millis)/ BLE legacy 应答路径/BLE 通道函数/bsp_usart(boot 用阻塞轮询 TX); Boot 下 OTA 目标 bank = bootsetting 非活动区(记录无效时 APP1);Boot/src/cli_core.h(shim,仅 cli_write/cli_printf)+boot_cli.c: 轮询行编辑器(回显/退格)+ 迷你 printf(无 libc printf 依赖,支持 %s/%-8s/%08lX 等)+ 命令子集(help/version/devinfo/bsdump/bsset/appsw/ ota/reset/uartinfo,后三个复用 app_bootset.c);Boot/src/main.c:banner([boot] CAIIC-BOOT V1.01.00 - Enter within 2s: download mode)→ 2s 回车窗口;bootsetting 无效且 APP1 向量表 sanity(SP∈0x20004001~0x2000C000、复位 PC∈APP1 bank)不过 → 自动进入 download 模式;否则按原逻辑跳转;- uvprojx:加 boot_usart/boot_cli + 共享 app_ota/app_bootset + 5 个 SDK
驱动(rcc/gpio/usart/dma/qflash),宏
BOOT_FIRMWARE,include 路径..\src先于..\..\mcm-ddc-ble\inc(shim 生效); merge_image.py:boot bin 超 16KB 硬卡 + boot 栈顶悬崖校验。
- PC 脚本零改动兼容的关键:boot CLI 提示符同为
caiic->、devinfo按"OTA 目标的对侧"打印cur bank: APP<n>、ota打印同一[ota] binary mode ON标记行——ble_ota_update.py --uart的探活/选包/ 握手流程原样命中。 - 尺寸:Boot
Code=10254 RO-data=2574 RW-data=96 ZI-data=6648,bin 12924B (16KB 区余 3.4KB);__initial_sp=0x20005A58,远离 0x2000C000 悬崖。 APP 行为不变(V1.00.28,基线与 V1.00.27 相同)。 - 实测(COM4 460800 + SWD)全部 PASS:
- 正常启动:窗口超时自动跳 APP(SWD 读 xTickCount 持续增长);
- 窗口内回车 → download 模式,help/version/devinfo/bsdump 全部正常;
- download 模式下
ble_ota_update.py --uart流式写 APP2: 27.3 kB/s、2.3s、0 回退,复位后运行 APP2(PASS); - 再次 download 模式写 APP1(往返):同样 PASS,板子回到 APP1;
- 救砖路径:SWD 擦除 bootsetting + APP1 首扇区模拟变砖 → 复位后 自动进 download 模式(devinfo 报 "bootsetting: INVALID - OTA targets APP1")→ 脚本写入 APP1 并重建 bootsetting → 复位进 APP1(PASS)。
- 协议文档:
docs/ble_protocol.md§6.8。注意 Boot 本身不能被 OTA 更新 (OTA 只写 APP bank),升级 Boot 只能 SWD 整片包;已出厂设备的旧 Boot 无 download 模式,功能不受影响。
53. Boot 新增 3.3V 拉高下载 strapping 机制(2026-09-06,Boot V1.01.01)
- 需求:在不动原有 2s 回车窗口 / 救砖自动进入两条入口的前提下,新增一条 纯硬件入口——上电/复位时把某一 IO 脚接 3.3V(板载 VDD 轨),Boot 即进入 download 模式,无需 PC 串口在 2s 内回车,方便产线/现场用一根跳线强制进入。
- 引脚选型(依据 STB 开发板原理图
nations-tec/N32WB031_STB_V1.3+ SDK EVAL 例程按键定义):- 占用脚(必须避开):SWD=PA4/PA5、LED1=PB0(D1)、LED2=PA6(D2)、
USART1=PB6(TX)/PB7(RX)、32.768K 晶振=PB8/PB9、用户键 KEY1=PB1/KEY2=PB2
(SDK
central_hogprh/EXTI/KeyInterrupt等例程均定义 PB1/PB2); PA0 为 WKUP 唤醒脚,也避开。 - 选定 PB5:空闲、纯 GPIO、无板载按键/LED/晶振/SWD/串口占用,且在 排针 J3 上与 PB6(TX)/PB7(RX) 相邻,接跳线最方便。
- 占用脚(必须避开):SWD=PA4/PA5、LED1=PB0(D1)、LED2=PA6(D2)、
USART1=PB6(TX)/PB7(RX)、32.768K 晶振=PB8/PB9、用户键 KEY1=PB1/KEY2=PB2
(SDK
- 实现(
Boot/src/main.c):- 新增宏
BOOT_DL_STRAP_GPIO=GPIOB/BOOT_DL_STRAP_PIN=GPIO_PIN_5/BOOT_DL_STRAP_CLK=RCC_APB2_PERIPH_GPIOB; boot_dl_strap_active():使能 GPIOB 时钟 → 配为输入 + 内部下拉 (GPIO_MODE_INPUT+GPIO_PULL_DOWN)→ 读GPIO_ReadInputDataBit, 为高返回 1。不接跳线时内部弱下拉把脚拉低 → 正常启动;外接 3.3V 强驱动 覆盖弱下拉 → 读高 → 进 download;- 主流程:banner 后先查 strap(置 enter_dl 并打印
[boot] strap pin HIGH (3V3): download mode),strap 生效时跳过 2s 回车窗口直接进 download;未生效则继续走原有 2s 窗口 + 救砖判断, 两条旧入口行为不变。
- 新增宏
- 版本:
Boot/src/boot_version.hBOOT_FW_VERSIONV1.01.00 → V1.01.01。 - 用法:板子断电 → 用跳线把 J3 的 PB5 接到 3.3V(J3 上有 VDD_U1/3.3V 脚)→
上电/复位 → 看到
strap pin HIGH (3V3)即进入 download 模式(CLI + OTA)。 - 注意:PB5 与 LPUART 无冲突(本工程未用 LPUART 日志);若日后产品板复用 PB5,只需改上述三个宏即可换脚。
54. Boot download CLI 移植 TAB 命令补全(2026-09-06,Boot V1.01.02)
- 从 APP 的
mcm-ddc-ble/src/app_cli.c把 TAB 命令名自动补全移植到Boot/src/boot_cli.c(Boot 侧为裸机轮询行编辑器,命令表是本地s_cmds[], 直接引用本地表而非 cli_core 的枚举函数):boot_cli_tab_complete():只补全首 token(已含空格则不补);单匹配→补全 命令名并追加一个空格;多匹配→先扩展到公共前缀,再按 TAB 列出候选;boot_cli_redraw():多匹配列出候选后重画caiic->提示符 + 当前行 (Boot 无历史导航,shownLen恒等于当前行长);boot_cli_run():switch 新增CLI_KEY_TAB(0x09)分支,len由uint8_t改uint16_t;ready banner 提示(TAB = complete)。
- 行为与 APP 一致:
b+TAB → 扩到bs(bsdump/bsset 公共前缀),再 TAB 列出bsdump bsset;d+TAB →devinfo;空行+TAB → 列出全部命令。 - 版本:
Boot/src/boot_version.hV1.01.01 → V1.01.02;构建 0E/0W,Code=10894 RO-data=2590 RW-data=96 ZI-data=6648,bin 13580B(16KB 区内)。
55. Boot download CLI help 补全提示串对齐 APP(2026-09-06,Boot V1.01.03)
- 问题:APP 的
help末尾有一行keys: TAB = complete, UP/DOWN = history(cli_core.c的 CmdHelp 打印,是 APP 运行时显示的字符串);Boot 的help只有命令表,没有这行提示,与 APP 不一致。 - 修改(
Boot/src/boot_cli.c):CmdHelp无参数分支在命令表后追加keys: TAB = complete(Boot 只移植了 TAB 补全,无 UP/DOWN 历史,故提示串如实只写 TAB);- 撤回 ready banner 里临时加的
(TAB = complete),保持与 APP banner 风格 一致(提示只放在help里)。
- 版本:
Boot/src/boot_version.hV1.01.02 → V1.01.03;构建 0E/0W,Code=10922 RO-data=2574 RW-data=96 ZI-data=6648。