CubeMX 生成 ADC1(9 通道)+ 复原重生成定制,FreeRTOS 加固迁 L1、钩子落 USER CODE 4/5,门禁豁免生成文件空白改写

This commit is contained in:
2026-10-10 00:53:20 +08:00
parent 718d399f70
commit 5eed2151de
25 changed files with 10250 additions and 551 deletions
+12 -1
View File
@@ -60,18 +60,29 @@ git commit
|---|---|
| **触发条件** | 文件任意行末尾含空格或 Tab |
| **处理方式** | **自动修复**(直接改写文件,删除行尾空白) |
| **影响范围** | 所有被 git 跟踪的文件 |
| **影响范围** | 项目自有代码;**豁免 CubeMX / Keil 生成文件**(见下方"豁免清单") |
| **典型报错** | `Trim Trailing Whitespace` → `Fixing path/to/file.c` |
| **修复方式** | 无需手动修复,钩子已自动改写;重新 `git add` 即可 |
> **注意**:首次对全库运行时,可能批量修改大量文件的行尾空格。这是正常行为,不会改变代码逻辑。
> **豁免清单(2026-10-09 定,`trailing-whitespace` 与 `end-of-file-fixer` 共用同一份,
> 配置里以 YAML 锚点 `&cubemx-generated` 定义)**:`Src/*.{c,h}`、`Inc/*.{c,h}`、
> `Drivers/CMSIS/`、`Drivers/STM32F4xx_HAL_Driver/`、`Middlewares/`、
> `MDK-ARM/*.{uvprojx,uvoptx,sct,map}`、`.mxproject`、`*.ioc`。
> 理由:这些文件的内容事实源是 `.ioc` 与工具链,每次重生成都按工具自己的风格写回
> (行尾空白、文件末尾不带换行)——2026-10-09 实测每次重生成后 `Src/`/`Inc/` 下 10+ 个
> 文件各被补 1 行末尾空行,纯属提交抖动。
> ⚠ **只豁免"改写型"钩子**:`cppcheck` / 乱码 / `cubemx-survival` / `task-table` /
> `mem-numbers` 等**只读检查仍覆盖** `Src/`、`Inc/`(存活审计必须能看到这些文件)。
### 2.2 end-of-file-fixer — 文件尾换行
| 项 | 说明 |
|---|---|
| **触发条件** | 文件最后一行不是换行符(`\n`) |
| **处理方式** | **自动修复**(在文件尾部追加换行) |
| **影响范围** | 项目自有代码;**豁免 CubeMX / Keil 生成文件**(同 2.1 豁免清单) |
| **典型报错** | `Fix End of Files` → `Fixing path/to/file.c` |
| **修复方式** | 无需手动修复,钩子已自动改写;重新 `git add` 即可 |
@@ -36,10 +36,10 @@
<!-- MEM_SNAPSHOT:BEGIN generated by scripts/check_mem_numbers.py -->
| 区域 | 用量 | 容量 | 占比 |
|---|---|---|---|
| ER_IROM1 | 266,632 B (260.38 KB) | 1,048,576 B (1024 KB) | 25.4% |
| RW_IRAM1 | 84,960 B (82.97 KB) | 131,072 B (128 KB) | 64.8% |
| ER_IROM1 | 267,740 B (261.46 KB) | 1,048,576 B (1024 KB) | 25.5% |
| RW_IRAM1 | 85,032 B (83.04 KB) | 131,072 B (128 KB) | 64.9% |
| RW_CCM | 49,184 B (48.03 KB) | 65,280 B (64 KB) | 75.3% |
| **Flash (ROM 合计)** | **267,372 B (261.1 KB)** | 1,048,576 B (1024 KB) | **25.5%** |
| **Flash (ROM 合计)** | **268,480 B (262.2 KB)** | 1,048,576 B (1024 KB) | **25.6%** |
<!-- MEM_SNAPSHOT:END -->
各区的容量与常量来源:
+20 -6
View File
@@ -818,6 +818,13 @@ external pin → "pin reset" (新增)
> 真发生溢出/耗尽时才起作用,且 `HardFault_Handler` 被打回空死循环后
> **崩溃时连现场都打不出来**(只剩 IWDG 兜底复位,但归因信息全丢)。
> 故本次已把这三个宏搬进 `USER CODE BEGIN FREE_RTOS_CONFIG_HARDENING` 区。
>
> **2026-10-09 第三次重生成(启用 ADC1):同一批定制再次全被抹,并暴露两个新事实**
>
> 1. **"自造区不可信"再次成立**:`FREE_RTOS_CONFIG_HARDENING` 连标记带内容**第二次**消失。已按 §10.2 改走 **L1**——`.ioc` 的 `FREERTOS.configCHECK_FOR_STACK_OVERFLOW=2` / `FREERTOS.configUSE_MALLOC_FAILED_HOOK=1`(GUI 里就能设,见上),自造区删除,审计两项也从"校验自造区"升级为"校验 `.ioc`"。
> 2. **新坑**:在 GUI 里启用这两个钩子后,CubeMX 会在 `Src/freertos.c` 生成 `USER CODE BEGIN 4 / 5` 区(默认内容是**空桩**),与 `Application` 区的真实实现**重复定义 → 编译错**(这是本项目唯一一次重生成后直接编译不过)。**最终落点:整个钩子函数写进 4 / 5 区**(见 §10.2),`Application` 区的旧实现搬走;配套 helper `hook_ptr_ok()` 须在钩子之前 → 放 `USER CODE BEGIN PD` 区。
> 3. `Src/iwdg.c` 与 4 个 Fault Handler(L3)**第三次**被抹,锚点全部检出并按 HEAD 复原;`Src/freertos.c` 三处栈注释再次丢失(栈**值**未丢)→ 第四次重生成又丢一次,已**集中成一段"任务栈核算依据"放进 `USER CODE BEGIN Variables` 区**(L2,重生成不丢);各 `.stack_size` 旁不再留注释——那里在生成区之外,补了也留不住。
> 4. 审计清单 18 → **21 项**(新增 `adc1-hal-enabled` 与钩子落点三项 `stack-overflow-hook-impl`(区 4) / `malloc-failed-hook-impl`(区 5) / `stack-overflow-hook-helper`(区 PD),两项加固检查升级为 `.ioc` 校验),现 21 通过 / 1 警告 / 0 失败。
> **每步改完必须 `@build` 0 错误 0 警告**(`AGENTS.md` 铁律)。
@@ -887,6 +894,8 @@ external pin → "pin reset" (新增)
| 事件组 `sys_evt` | `FREERTOS.Events01` | ✅ |
| FreeRTOS 堆大小 | `FREERTOS.configTOTAL_HEAP_SIZE=49152` | ✅ |
| FPU 使能 | `FREERTOS.configENABLE_FPU=1` | ✅ |
| **栈溢出检测(模式 2)** | `FREERTOS.configCHECK_FOR_STACK_OVERFLOW=2` | ✅ **2026-10-09 迁入 L1**(原先写在 `FreeRTOSConfig.h` 的自造区里,**两次重生成两次被抹**)。GUI 位置:FreeRTOS 页 → **Config parameters** 标签 → **Hook function related definitions** 组 |
| **堆分配失败钩子** | `FREERTOS.configUSE_MALLOC_FAILED_HOOK=1` | ✅ 同上(同一组,可选 0/1) |
| IWDG 分频 / 重载 | `IWDG.IPParameters` + `IWDG.Prescaler` / `IWDG.Reload` | ✅ 本次新增,见 §2.2 |
| 时钟树(含 LSI) | `RCC.*` | ✅ |
| `KeepUserCode=true` | `ProjectManager.KeepUserCode` | ✅ **这个必须为 true**,否则 L2 整体失效 |
@@ -895,12 +904,15 @@ external pin → "pin reset" (新增)
| 定制 | 现状 | 建议 |
|---|---|---|
| `configCHECK_FOR_STACK_OVERFLOW` | 在 `USER CODE BEGIN FREE_RTOS_CONFIG_HARDENING` 区(L2) | ⚠️ CubeMX 的 FreeRTOS 界面**没有**这一项的勾选框,只能靠 L2 + 审计。**保持现状** |
| `configUSE_MALLOC_FAILED_HOOK` | 同上 | 同上 |
| `configASSERT` 定义 | 在 `USER CODE BEGIN 1` 区(L2) | 同上。CubeMX 无对应界面项 |
| `configASSERT` 定义 | 在 `USER CODE BEGIN 1` 区(L2) | CubeMX 无对应界面项,保持 L2 + 审计 |
| 4 个 Fault Handler 实体 | `stm32f4xx_it.c` 标记区**外**(L3) | ⚠️ CubeMX 为这些函数**不生成** `USER CODE` 区,无法 L2。见下方"存量清单" |
| `fault_handler_enable()`(SHCSR 使能三位) | `Src/stm32f4xx_it.c` 的 `USER CODE BEGIN 1` 区(L2) | ❌ **CubeMX 无此配置项**(已查模板源码证实,见下) |
> ✅ **2026-10-09 更正**:本节原先写"CubeMX 的 FreeRTOS 界面**没有** `configCHECK_FOR_STACK_OVERFLOW` / `configUSE_MALLOC_FAILED_HOOK` 这一项的勾选框"——**判断有误,该项一直在 GUI 里**:FreeRTOS 页 → **Config parameters** 标签 → **Hook function related definitions** 组(DB 证据:`db/mcu/IP/FREERTOS-*-v*_Cube_Modes.xml` 里两个 `RefParameter`,可选值 0/1/2 与 0/1)。现已迁入 `.ioc`(见上一表),`Inc/FreeRTOSConfig.h` 里自造的 `FREE_RTOS_CONFIG_HARDENING` 区**随之删除**——它正是"自造区名不可信"的又一例(两次重生成两次被抹,2026-09-29 与 2026-10-09 各一次)。
>
> ⚠️ **连带坑与最终落点(2026-10-09 实测)**:一旦在 GUI 里启用这两个钩子,CubeMX 会在 `Src/freertos.c` 生成 `USER CODE BEGIN 4 / 5` 区,默认内容是**空桩**——若实现仍留在 `USER CODE Application` 区,就出现**同名函数两处定义 → 编译错**。
> **正确做法:整个钩子函数(含签名)写进 4 / 5 区**,`Application` 区里的旧实现删掉。这两个区名由 CubeMX 当前模板生成,且**其存在由 `.ioc` 启用钩子保证**(L1 兜住 L2),比"每次重生成后删桩"更省事也更不易漏。签名与生成原型保持一致(`xTaskHandle` → `TaskHandle_t`,即 `void *`)。配套 helper `hook_ptr_ok()` 必须在钩子之前定义 → 放 `USER CODE BEGIN PD` 区。审计项 `stack-overflow-hook-impl`(区 4)/ `malloc-failed-hook-impl`(区 5)/ `stack-overflow-hook-helper`(区 PD)分别校验。
##### 为什么 `fault_handler_enable()` 写不进 `.ioc`(2026-10-01 查模板源码证实)
用户问"能否通过 CubeMX 配置",逐条查证 CubeMX 安装目录
@@ -926,14 +938,16 @@ external pin → "pin reset" (新增)
> 与 `SHCSR` 使能位无关。**不要误以为勾了 CubeMX 的 Enabled 就等于使能了故障。**
> **判断口诀**:能在外设/中间件的图形界面里找到对应输入框的 → 一定写 `.ioc`;
> 找不到(如 FreeRTOS 的两个加固宏、断言定义、故障异常处理函数体)→ 只能 L2/L3。
> 找不到(如 `configASSERT` 定义、故障异常处理函数体、`SHCSR` 使能位)→ 只能 L2/L3。
> ⚠️ 但**别凭印象断言"界面里没有"**——先翻一遍各标签页或查 `db/mcu/IP/*_Modes.xml`:
> FreeRTOS 那两个加固宏就一直躺在 "Config parameters" 标签页里,因此白丢了两次。
#### L2:`USER CODE` 区的可用位置(本项目实测)
| 文件 | 可用标记区 | 已用于 |
|---|---|---|
| `Inc/FreeRTOSConfig.h` | `Header` / `Includes` / `FREE_RTOS_CONFIG_HARDENING` / `MESSAGE_BUFFER_LENGTH_TYPE` / `1` / `Defines` | 加固宏(HARDENING)、`configASSERT`(1) |
| `Src/freertos.c` | 共 25 个,含 `Includes` / `Application` / `Variables` / `PD` / `PTD` / … | 钩子实现(`Application`)、`timeSyncTask` 属性定义(`Variables`) |
| `Inc/FreeRTOSConfig.h` | `Header` / `Includes` / `MESSAGE_BUFFER_LENGTH_TYPE` / `1` / `Defines` | `configASSERT`(`1`)。加固宏已迁 L1(写 `.ioc`、由模板生成),不再占标记区 |
| `Src/freertos.c` | 共 25 个,含 `Includes` / `PD` / `PTD` / `Variables` / `4` / `5` / `Application` / … | 栈溢出钩子(`4`)、malloc 失败钩子(`5`)、helper `hook_ptr_ok`(`PD`)、**任务栈核算依据 + `timeSyncTask` 属性定义(`Variables`)**、断言钩子及其余应用代码(`Application`) |
| `Src/stm32f4xx_it.c` | 共 52 个,含文件头 `0`(标准保护位)+ 各中断函数**内部**的 `<Func>_IRQn 0` / `W1_<Func>_IRQn 0` | 崩溃报告实现(`0`) |
| `Src/main.c` | 共 19 个,含 `Init` / `SysInit` / `2` / `PV` / `PFP` | 复位原因锁存(`Init`) |
| `Src/iwdg.c` | 共 7 个,含 `IWDG_Init 0/1/2` | ❌ **本次改动用不上**(见下方特例) |
+1 -1
View File
@@ -70,7 +70,7 @@
| 序 | 任务 | 前置 | 说明 |
|---|---|---|---|
| **B0** | **产品定义确认**(阻塞项) | — | 需甲方明确:① 采集是否持续 ② 采样率(**通道语义已于 2026-10-09 确认并固化 → `docs/硬件平台/ADC通道映射说明.md`**:UA/UB/UC/UN=`AIN1~4`、IA/IB/IC/IN=`AIN8~11`、V_ZL2=温度、其余 8 路备用;主板只做监视/辅助测量,计量在子板) ③ 转发目标(MODBUS RTU?上行 TCP?)④ `APP_ADC_ENABLE`/`APP_RS485_ENABLE` 语义。**B1/B2 的规模完全取决于此** |
| **B1** | **ADC 采集链路实现**(消化 P1-1、E-58) | B0 + **E-59 门禁** | 周期 CONVST 重触发 + `adc_task` 消费 `get_buf`;**先修 E-58**(原子快照 API,否则接入即撕裂);栈预算核算(两任务未入 `freertos.c` 栈表);**取数必须按 `docs/硬件平台/ADC通道映射说明.md`**(TPAFE `buf[2]=IA`/`buf[5]=UN`/`buf[6]=UA`;STM32 侧 9 路含 UB/UC/IB/IC/IN/温度,需先启用 `HAL_ADC_MODULE_ENABLED`,网名 ADC17=PC4 用 `ADC_CHANNEL_14`) |
| **B1** | **ADC 采集链路实现**(消化 P1-1、E-58) | B0 + **E-59 门禁** | 周期 CONVST 重触发 + `adc_task` 消费 `get_buf`;**先修 E-58**(原子快照 API,否则接入即撕裂);栈预算核算(两任务未入 `freertos.c` 栈表);**取数必须按 `docs/硬件平台/ADC通道映射说明.md`**(TPAFE `buf[2]=IA`/`buf[5]=UN`/`buf[6]=UA`;STM32 侧 ADC1 **已于 2026-10-09 生成**:`hadc1` + `MX_ADC1_Init()`、9 通道 Rank1~9、144 cycles、ADCCLK=21 MHz、软件触发,网名 ADC17=PC4 用 `ADC_CHANNEL_14`;剩活只剩消费者与 `adc_task` 建任务) |
| **B2** | **RS-485 采集链路实现**(消化 E-50、E-49) | B0 + **E-59 门禁** | 双缓冲 + 序号(防包撕裂)+ DE 收尾(超时路径等 TC)+ 协议状态机(`App/protocol/` 正是为此预留) |
| **B3** | **E-59 门禁固化入 `AGENTS.md`** | — | 把"接消费者前必须先修 E-36/E-44/E-45/E-58 + 栈预算"写成强制门禁,与"改栈前跑 `stack_report.py`"同级。**这是防止 B1/B2 带缺陷上线的关键** |
+4 -3
View File
@@ -101,7 +101,7 @@ STM32(U17)为 LQFP144 脚号。
> ⚠️ **`ADC17` 网名 ≠ `ADC_CHANNEL_17`**。PC4 在 F407 上是 `ADC123_IN14`,代码必须写 `ADC_CHANNEL_14`;`ADC_CHANNEL_12` 是 **PC2(pin 30)**,写错就读到另一只脚。
**CubeMX 侧标签(2026-10-09 已写入 `STM32F407-Demo.ioc`,格式 `板级网名_语义`)**:`ADC9_RES` / `ADC10_IC` / `ADC11_IN` / `ADC12_UC` / `ADC13_RES` / `ADC14_TEMP` / `ADC15_IB` / `ADC16_RES` / `ADC17_UB`(9 脚信号已设为 `ADC1_IN0..IN7` + `ADC1_IN14`)。
**CubeMX 侧标签(2026-10-09 已写入 `STM32F407-Demo.ioc` 并生成代码,格式 `板级网名_语义`)**:`ADC9_RES` / `ADC10_IC` / `ADC11_IN` / `ADC12_UC` / `ADC13_RES` / `ADC14_TEMP` / `ADC15_IB` / `ADC16_RES` / `ADC17_UB`(9 脚已配到 ADC1 的 IN0..IN7 + IN14,生成代码里对应 `main.h` 的 `ADC9_RES_Pin`…`ADC17_UB_Pin`)。⚠ PA0 标签首次配置时误写成 `AD9_RES`(少个 C),已改回 `ADC9_RES`(`.ioc` + `Inc/main.h` + `Src/adc.c` 三处同步)。
**引脚可用性核查(2026-10-09)**:`STM32F407-Demo.ioc` 中 PA0~PA7、PC4 **此前均未分配**(PC5 = LED1,与 ADC 无关)→ 这 9 路做模拟输入无冲突,现已配给 ADC1。PA4/PA5 兼 `DAC_OUT1/2`,作模拟输入期间须保持 DAC 关闭(当前固件未启用 DAC ✓);PA4/PA5/PA6/PA7 的 SPI1 复用未使用(SPI1 走 PB3/PB4/PB5)✓。
@@ -118,8 +118,8 @@ STM32(U17)为 LQFP144 脚号。
| 项 | 现状 | 影响 |
|---|---|---|
| STM32 ADC 外设 | **`.ioc` 已配 ADC1(9 通道 / Rank 1~9 / 采样 144 cycles / 软件触发 / Scan=ENABLE),但尚未生成代码**:`Inc/stm32f4xx_hal_conf.h` 的 `HAL_ADC_MODULE_ENABLED` 仍是注释,`Src/adc.c` 与 HAL adc 驱动(`stm32f4xx_hal_adc.c/_ex.c`)不在仓库(HAL 为裁剪子集) | 运行时仍采不到;须在 CubeMX 6.17.0 + FW_F4 V1.28.3 里 Generate Code,再把新文件注册进 `MDK-ARM/*.uvprojx` 并 `@build` |
| ADC 时钟(生成后注意) | CubeMX 对 F4 固定生成 `ADC_CLOCK_SYNC_PCLK_DIV2` → ADCCLK = APB2/2 = **42 MHz**,超 DS 表 67 的 36 MHz 上限 | 代码侧改 `__HAL_RCC_ADC_CONFIG(RCC_ADCPCLK2_DIV4)`(21 MHz,放 `USER CODE` 区或 `MX_ADC1_Init()` 之后);按 144+12 cycles 算 ≈ 7.4 µs/通道、9 通道一轮 ≈ 67 µs |
| STM32 ADC 外设 | **已生成(2026-10-09,CubeMX 6.17 + FW_F4)**:`Src/adc.c`/`Inc/adc.h` 已入库并挂进 `MDK-ARM/*.uvprojx`;`Inc/stm32f4xx_hal_conf.h` 的 `HAL_ADC_MODULE_ENABLED` 已生效;HAL `stm32f4xx_hal_adc.c/_ex.c` + `stm32f4xx_ll_adc.h` 已补入仓库(HAL 原本是裁剪子集);`Src/main.c` 里 `MX_ADC1_Init()` 排在 `MX_IWDG_Init()` 之后。生成配置核对无误:9 通道 / Rank 1~9 / 全部 144 cycles / `ScanConvMode=ENABLE` / `ContinuousConvMode=DISABLE` / `ExternalTrigConv=ADC_SOFTWARE_START` + `EDGE_NONE` / `EOCSelection=ADC_EOC_SINGLE_CONV` / `DataAlign=RIGHT` / 12 位;`HAL_ADC_MspInit()` 已把 PA0~PA7+PC4 配成 `GPIO_MODE_ANALOG` | 硬件通路已打通(`hadc1` 可用);**仍无消费者**——见下面两行 |
| ADC 时钟 | CubeMX 的 F4 ADC 面板**有** Clock Prescaler 一项(此前"面板没有该项、固定 DIV2"的判断有误),已选 **PCLK2/4 → ADCCLK = 21 MHz** ✓。面板里 **DIV2 是灰的、选不到**:DB 里 `FadcConstraint Max=36000000`,APB2 = 84 MHz → DIV2 = 42 MHz 超限,该候选值被 `Action="DISABLE"` 禁掉,超规值进不来 | **不设额外防护(2026-10-09 决定)**:约束已由 CubeMX 把关;`.ioc` 虽不保存 `ADC1.ClockPrescaler` 键,重生成也只会回落到约束允许的值。按 144+12 cycles 算 ≈ 7.4 µs/通道、9 通道一轮 ≈ 67 µs |
| `adc_task` | **空桩**:`App/task/adc_task.c` 只 `osDelay(1000)`,且任务未在 `Src/freertos.c` 创建 | 无消费者,数据路径死(登记簿 P1-1) |
| TPAFE 采样触发 | `tpafe5160_start_conv_irq()` 仅在 `app_main_init` 调一次,**无周期 CONVST 重触发** | 只采到一帧,无连续波形 |
| 前端偏置 | 每路 1k 串联 + 1k 到 `*_GND` + 滤波 C,**无中点偏置** | STM32 侧只能测 **0~3.3 V 单极性**,交流量负半周被削顶;主板侧只做监视可接受,若要算 RMS/功率需硬件补偏置或明确算法口径 |
@@ -141,3 +141,4 @@ STM32(U17)为 LQFP144 脚号。
| 日期 | 内容 |
|---|---|
| 2026-10-09 | 建档:固化 V0.1 语义分配(UA/UB/UC/UN、IA/IB/IC/IN、V_ZL2=温度、其余备用)与 17 路端到端映射;确立"主板只做监视/辅助测量、计量在子板"口径;勘误 PC4 通道号(IN12→IN14)与 PB0 定性(=IN8) |
| 2026-10-09 | **CubeMX 生成 ADC1(9 通道)落地**:新增 `Src/adc.c`/`Inc/adc.h` + HAL adc 三文件并挂进 uvprojx、`HAL_ADC_MODULE_ENABLED` 生效、`MX_ADC1_Init()` 入 `main.c`;分频 PCLK2/4 = 21 MHz——面板里 DIV2 已被 `FadcConstraint`(36 MHz)约束禁掉,**故不加 `.ioc` 键 / 代码守卫 / 审计项等额外防护**(曾加过,按决定撤除,理由见 §7"ADC 时钟"行);修正 PA0 标签笔误 `AD9_RES`→`ADC9_RES`。⚠ 本次重生成打回了 5 处定制(IWDG 延迟启动锚点、HardFault + 3 个 Fault 手工 Handler、`FreeRTOSConfig.h` 加固区、`freertos.c` 三处栈核算注释),已全部复原;存活审计新增 1 项 `adc1-hal-enabled`(共 19 项),现 19 通过 / 1 警告(timeSyncTask 手工定义)/ 0 失败,`@build` 0 错 0 警 |