mothercup/docs/开发日志.md

44 KiB
Raw Blame History

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)。