Table of Contents
- 已知陷阱
- 1. 中文源码与 BOM(最容易被工具误伤)
- 2. osKernelStart 之前不能创建 RTOS 对象
- 3. 就绪位被消费 → FTP 静默不监听
- 4. 自研代码禁用 C 库堆分配
- 5. FatFs 追加写与短写
- 6. FatFs 只支持 8.3 短名
- 7. CH395F SPI 事务必须成对包裹, 分频固定为 8
- 8. ADC 取数只能用原子快照
- 9. F4 ADC 的四个硬约束
- 10. V0.1 的 8 路电气量不能跨转换器运算
- 11. net_msg_t 错误码要靠任务通知回传
- 12. EOF 时不要 net_close 监听 fd
- 13. CubeMX 重生成会覆盖 USER CODE 区外的一切
- 14. 烧录必须 -Rst -Run, 烧后必须回读校验
- 15. FTP 单客户端与默认口令
- 16. RS-485 尚未接线, 不要假设它可用
- 17. 新增任务/栈/缓冲先过内存预算
- 18. 新源文件必须双端注册
- 19. uvprojx 抖动不要提交
- 20. 启动段没有看门狗保护
已知陷阱
本页只收“高频、难查、后果重”的坑。每条给出规则与详情入口; 改代码前先扫一遍, 尤其是驱动、启动、存储和烧录。
详细台账:
docs/问题记录/CH395F_Trap_Records.md、GD5F2GQ5UE_Trap_Records.md、SD2506_Trap_Records.md、错误码传播审计.md。
1. 中文源码与 BOM(最容易被工具误伤)
规则:
- 改含中文的源码只用 Edit 工具, 绝不用
Get-Content/Set-Content/cmd整文件重写; - 带 BOM 的文件(
Drivers/BSP/UI/*、App/task/ui_task.c、App/cfg/cfg_file.c、Lib/lv_conf.h)被 Edit 后会静默丢 BOM; - 丢 BOM 后 ARMCC 按 CP936 解码, 中文字面量报
#870-D: invalid multibyte character sequence; - 只补 3 字节
EF BB BF, 不要整文件重写; - 怀疑乱码先统计
EF BF BD字节数, 不要急着git checkout(会丢未提交工作)。
字符串字面量必须纯 ASCII: 无 BOM 文件里奇数个汉字会吞掉结束引号。中文只写注释。
2. osKernelStart 之前不能创建 RTOS 对象
症状: 启动直接死掉, 或行为不可预测。
规则: osKernelStart() 之前禁止任何 FreeRTOS / CMSIS-RTOS 创建类 API(osMessageQueueNew、osThreadNew 等)。内核前只能用纯寄存器/HAL 轮询 + 无锁静态缓冲; 内核启动后再惰性创建真队列。
详见 docs/架构设计/项目启动顺序管理.md。
3. 就绪位被消费 → FTP 静默不监听
症状: ftpTask 永久阻塞, 21 端口从未监听, 零日志零报错。
根因: osEventFlagsWait 默认消费所等位(xClearOnExit=pdTRUE)。netTask / uiTask / timeSyncTask 先消费掉 NET_READY / STORAGE_READY, ftpTask 的 WaitAll(bit0|bit1) 永不成立。
规则:
- 状态位(
NET_READY/STORAGE_READY)等待必须osFlagsNoClear; - 命令位(
CFG_SAVE/NET_RECONFIG)保持消费型。
4. 自研代码禁用 C 库堆分配
症状: 模拟器正常, 真机 malloc 返回 NULL。
根因: MicroLIB 的 C 堆由 startup_stm32f407xx.s 的 Heap_Size EQU 0x200 限定为 512 B; KB 级分配必然失败, 而 PC 模拟器用宿主 CRT 堆, 照不出来。
规则: 只用 pvPortMalloc/vPortFree, 或把分配收进平台后端(*_mcu.c / *_sim.c)。
5. FatFs 追加写与短写
陷阱 A: 该移植的 FA_OPEN_APPEND 不含 FA_WRITE。只带 FA_OPEN_APPEND 打开的文件, f_write 恒返 FR_DENIED。
f_open(&fp, path, FA_WRITE | FA_OPEN_APPEND);
陷阱 B: f_write 簇分配失败时可能返回 FR_OK + 短写(无错误码)。必须校验写回字节数:
UINT wrote = 0;
if (f_write(&fp, buf, len, &wrote) != FR_OK || wrote != len) { /* 失败 */ }
6. FatFs 只支持 8.3 短名
FF_USE_LFN=0 → 只支持严格 8.3 短名, 不得以点开头。.fschk 这类探测名会恒返 FR_INVALID_NAME。自愈逻辑严禁把名字/参数类错误码归入“卷损坏触发集”, 否则会误格式化。
7. CH395F SPI 事务必须成对包裹, 分频固定为 8
规则:
- 每次 SPI 事务用
ch395f_spi_begin()/ch395f_spi_end()包裹; - SPI 分频系数必须为 8(PCB 设计约束, 勿“优化”);
- 所有 CH395F 中断在
net_poll()内经GET_GLOB_INT_STATUS集中读取清除, 不得在 ISR 或其他地方单独处理; - 非
netTask任务只调net_*API, 不直接碰 CH395F 驱动。
8. ADC 取数只能用原子快照
规则:
- 只用
tpafe5160_snapshot()(临界区原子快照); - 旧的
get_buf()/data_ready()/clear_ready()已删除(两步之间被抢占会拷回两帧混合数据,s_ready有三写者竞态); - 消费者统一经
adc_input_poll()/adc_input_raw()按语义取数, 不写死下标; - ISR 侧只能走
adc_tick_irq_notify()。
9. F4 ADC 的四个硬约束
- ADC1 外部触发只认 TIM1/2/3/4/5/8 的 CC 或 TRGO; TIM6/TIM7 的 TRGO 只接 DAC → 用 TIM3。
- ADC1 在 DMA2 只挂 Stream0 或 Stream4 → 选 Stream4, 避免挤走 SPI1/USART1 RX; 换流后优先级会被重置为 0, 必须回 NVIC 核对(外设中断 ≥5)。
DMAContinuousRequests必须 ENABLE; 关掉会一轮 9 通道只发一次请求, 静默丢 8 路。- DMA 目的缓冲必须在 SRAM(DMA2 不可达 CCM); ADC 上电必须自校准(CubeMX 不生成,
adc1_calibrate()手写)。
10. V0.1 的 8 路电气量不能跨转换器运算
8 路电气量分属 TPAFE5160 与 STM32 ADC1 两片转换器, 没有共同采样触发、精度/量程不一致:
- V0.1 上禁止跨转换器做相位、功率、功率因数、矢量运算;
- 主板侧只做逐通道独立监视;
- 功率/功率因数/相角取子板计量模块结果;
- V0.2 要求 8 路全部接 TPAFE。
11. net_msg_t 错误码要靠任务通知回传
net_msg_t 是按值拷入队列的, netTask 改的是自己那份副本, 调用方看不到。错误码必须搭 xTaskNotify() 通知值回来; 不要给 net_msg_t 加“回传用”字段(调用方只会读到栈垃圾)。
12. EOF 时不要 net_close 监听 fd
CH395F 单连接模型下, net 层在 EOF 已 auto_relisten。应用层若在 EOF 时 net_close 该 fd, 会把监听一起毁掉, 端口变成“连接被拒”。仅当数据 fd ≠ 监听 fd(模拟器多连接后端)时才关。
13. CubeMX 重生成会覆盖 USER CODE 区外的一切
- 自定义代码只写
USER CODE BEGIN/END之间; - 重生成后立刻跑
scripts/check_cubemx_survival.py; .ioc的KeepUserCode=true只保护 USER CODE 区;- 4 个 Fault Handler、IWDG 延后启动等生成区外定制靠
cubemx-survival钩子审计。
14. 烧录必须 -Rst -Run, 烧后必须回读校验
- 裸
-P可能显示Programming Complete, 但目标核处于死循环/异常时写入不落地; - 烧后必须
-CmpFile回读,No difference found才算真正写入; -CmpFile连接时会复位目标, 必须排在采日志之后;- 固件身份戳用于确认“芯片里跑的就是这份构建”。
15. FTP 单客户端与默认口令
- 同一时刻只服务 1 个会话; 旧会话异常断开后约 30 s 才恢复监听;
- 不要用裸 TCP 工具探测 21 端口(会占掉唯一会话);
- 测试脚本必须内置重连;
- 出厂默认
admin/admin, 现场必须修改; FTP 明文传口令, 不挡网络嗅探。
16. RS-485 尚未接线, 不要假设它可用
rs485_init()已调用, 但rs485_task未创建;- RS-485 采集按用户决定冻结(2026-10-12);
- 接线前还有单缓冲包撕裂、发送超时未等发送完成就拉低方向控制两条驱动问题未修;
- 接线前必须确认驱动契约已修, 并在建任务前后各跑一次
stack_report.py。
17. 新增任务/栈/缓冲先过内存预算
- 新增任务、抬栈、加大缓冲前先核算 RAM;
- 改栈前后各跑一次
scripts/stack_report.py; scripts/ram_report.py看.map静态占用;mem-budget钩子拦截 RW_IRAM1 / RW_CCM >92%;- 新增任务还要纳入 IWDG
required_mask与心跳点检。
18. 新源文件必须双端注册
- Keil
.uvprojx+simulator/CMakeLists.txt都要注册; - 新文件落在新目录时, 还要把目录加进
uvprojx的<IncludePath>; - 否则报错会落在消费者 TU 上(
cannot open source input file), 容易误判; - 用
new-source-fileskill 并跑registration-check。
19. uvprojx 抖动不要提交
每次 @build 后 UV4 可能自动改写 MDK-ARM/*.uvprojx(XML 归一化、PackID 降版本)。这类改动不入库; 只有真正增删源文件时才提交工程文件。
20. 启动段没有看门狗保护
app_main_init() + bring-up 阶段不喂狗, 也没有看门狗保护。启动卡死时先看最后一条 DBG_INFO, 启动段每一步都有结果日志。
STM32F4-Base 百科
入门
子系统
开发与验证
参考
仓库
STM32F4-Base 百科 · 基于仓库 master 分支整理 · 最后更新: 2026-11-01
内容以源码、AGENTS.md 与 docs/ 为准; 发现页面过期, 请在 维护指南 中按流程更新。