docs(adc): Task #32 P1-5 事件驱动消费方案 a 规划入档
用户决策"ISR 只发唤醒 + 计算留任务 + 提优先级"(方案 a),拒绝 ISR 内计算; 附下一版硬件 DMA 双缓冲建议(方案 b)。代码待实施。 - 新增 §10:决策依据、拟改造清单(main.c/freertos.c/adc_task.c/adc_task.h)、 预期收益、残留风险(P1-5 硬化)、DMA 硬件建议(§10.5) - 状态头与 §9 变更记录同步"规划/待实施"口径 - 未改代码;消费端当前仍为 1ms 轮询
This commit is contained in:
@@ -4,6 +4,9 @@
|
||||
> (`Src/main.c` 回调 + `app_main.c` 启动 + `adc_task.c` 退化为纯消费者),`@build` 0 错 0 警;
|
||||
> 台架 `@test` **PASS**(`frame_seq` 每 10 s 精确 +10000 = 1000.0 Hz、`frst_err=0`、回读一致;
|
||||
> 实测见 §7.1)。
|
||||
> **事件驱动消费改造方案 a 已规划入档(Task #32 P1-5,2026-10-10,待实施)**:用户决策
|
||||
> "ISR 只发唤醒 + 计算留任务 + 提优先级"(方案 a),拒绝"ISR 内计算";并附下一版硬件
|
||||
> DMA 双缓冲建议(§10.5)。详见 §10;消费端当前仍为 1 ms 轮询,待该方案排期落地。
|
||||
> **上位需求**:`docs/算法/继电保护功能复现规划.md` §3.2 L1-1(周期 CONVST 重触发 1000 Hz,
|
||||
> 与保护节拍同源)。**驱动契约**:`Drivers/BSP/TPAFE5160/README.md`。
|
||||
> **背景**:B1(2026-10-10)已把 `adc_task` 由空桩改为真实消费者,但采样节拍是
|
||||
@@ -224,3 +227,76 @@ t=1000 µs 下一个 TIM6 更新事件
|
||||
| 2026-10-10 | 建立本文档 | B1(ADC 采集链路)遗留项"精确 1 kHz 硬件定时器触发"的规划:方案选型(A/B)、CubeMX 配置规格、代码改造清单、时序与优先级分析、风险与验收判据、等周波调频预留 |
|
||||
| 2026-10-10 | **实现(方案 A)** | CubeMX 加 TIM6(PSC=83 / ARR=999 / ARR 预装载 / NVIC 抢占优先级 6);`Src/main.c` 回调加 TIM6 分支触发 CONVST;`app_main.c` 用 `HAL_TIM_Base_Start_IT(&htim6)` 启动节拍;`adc_task.c` 退化为纯消费者。`@build` 0 错 0 警;台架 `@test` 验收待做 |
|
||||
| 2026-10-10 | **台架 `@test` 验收 PASS** | 真机闭环:`seq` 10005→20005→30005(每 10 s +10000 = 1000.0 Hz)、`frst_err=0`、`idle` 每 10 s +5(0.05%,轮询抖动)、回读 `No difference found`、零 `[ERR]`/HardFault、`reset cause` 仅 1 次。证据 `boot_log_data/20261010_prod_adc_tim6_boot_log.txt` |
|
||||
| 2026-10-10 | **事件驱动消费方案 a 规划入档(Task #32 P1-5)** | 记录用户决策与改造清单(TIM6 ISR 置位 `ADC_WAKE_FLAG` 唤醒 `adcTask` + `adcTask` 提至 `osPriorityAboveNormal`),并附下一版硬件 DMA 双缓冲建议(§10.5)。代码待实施,消费端仍为 1 ms 轮询 |
|
||||
|
||||
---
|
||||
|
||||
## 10. 事件驱动消费改造(Task #32 P1-5,2026-10-10 规划:用户决策方案 a,待实施)
|
||||
|
||||
> 上位背景:Task #32 暴露"UI 任务看门狗停摆"候选占用者之一是 `adcTask` 的 1 ms 轮询——
|
||||
> 原 `osDelayUntil(1ms)` 把消费唤醒**钉在 FreeRTOS tick(1 ms)边界**,带来 0~1 ms 随机
|
||||
> 相位抖动 + 累积漂移风险,且 `adcTask` 与 `uiTask` 同为可被长时抢占的对象。用户决策:
|
||||
> **方案 a(ISR 只发唤醒 + 计算仍在任务 + 提优先级)**,并拒绝"ISR 内计算"方案。
|
||||
> 本方案**尚未实施**:消费端当前仍为 1 ms 轮询,改造清单见 §10.2,待排期落地。
|
||||
|
||||
### 10.1 决策:为什么选 a,不选"ISR 内计算"
|
||||
|
||||
- **拒绝"ISR 内计算"**:把相量/压环搬进 TIM6 ISR 是反模式——
|
||||
① `adc_input_poll()` 内含 `HAL_ADC_PollForConversion` 的 **2 ms 忙等**(`ADC_INPUT_POLL_TIMEOUT_MS=2U`),
|
||||
绝不可入 ISR(会冻结系统,最坏 9 通道 ×2 ms = 18 ms 全系统中断关闭);
|
||||
② 相量/采样环是任务级共享数据,ISR 内改写需额外临界区,破坏 `tpafe5160_snapshot()` 的
|
||||
"仅任务上下文"铁律;③ `HAL_TIM_PeriodElapsedCallback` 是 CubeMX 生成区回调,承载 L2 计算
|
||||
违反"生成区不改"约定,重生成即丢。
|
||||
- **选定方案 a**:TIM6 ISR **只置位唤醒标志**(`osThreadFlagsSet`),所有计算留在 `adcTask`;
|
||||
并把 `adcTask` 优先级由 `osPriorityNormal` 提到 `osPriorityAboveNormal`,使唤醒后**立即抢占**
|
||||
其余 Normal 任务,固定 µs 级延迟。计算量、数据共享、生成区约定全部不变。
|
||||
|
||||
### 10.2 拟改造内容(待实施)
|
||||
|
||||
| 文件 | 拟改动 |
|
||||
|---|---|
|
||||
| `Src/main.c` | `HAL_TIM_PeriodElapsedCallback` 的 TIM6 分支:在 `tpafe5160_start_conv_irq()` 后加 `(void)osThreadFlagsSet(adcTaskHandle, ADC_WAKE_FLAG);` |
|
||||
| `App/task/adc_task.c` | 主循环尾 `osDelayUntil(1ms)` 改为 `(void)osThreadFlagsWait(ADC_WAKE_FLAG, osFlagsWaitAny, osWaitForever);`;删除 `ADC_TASK_BEAT_MS` 与 `next` 变量 |
|
||||
| `Src/freertos.c` | `adcTask_attributes.priority`:`osPriorityNormal` → `osPriorityAboveNormal`(=32,高于其余 Normal=24;`configMAX_PRIORITIES=56` 有裕量) |
|
||||
| `App/task/adc_task.h` | 新增 `#define ADC_WAKE_FLAG 0x0001U` 与 `extern osThreadId_t adcTaskHandle;` |
|
||||
|
||||
- **ISR 安全性(设计校验)**:TIM6 抢占优先级 = 6 ≥ `configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY`(=5) →
|
||||
`osThreadFlagsSet` 在 ISR 内走 `xTaskNotifyFromISR` + `portYIELD_FROM_ISR`(CMSIS-RTOS V2 的
|
||||
`osThreadFlagsSet` 实现),合法且会立即触发上下文切换。`configUSE_OS2_THREAD_FLAGS=1` 已开,符号已编入。
|
||||
- **看门狗不变**:`TASK_WD_ADC` 心跳仍在循环内每拍调用,`required_mask` 与心跳位集合一致,
|
||||
优先级调整不影响掩码。
|
||||
- **验证前置**:实施后须 `@build` 0 错 0 警 + 台架 `@test` 复核 `idle` 是否降至 ≈0(§7.1)。
|
||||
|
||||
### 10.3 预期收益与语义
|
||||
|
||||
1. **唤醒延迟固定 µs 级**:原 `osDelayUntil(1ms)` 把唤醒量化到 tick 边界 → 0~1 ms 随机相位抖动 +
|
||||
一旦超拍即"永久落后/空转"风险(即 P0-2 的成因);事件驱动走 TIM6 硬件节拍,唤醒 = 生产时刻 + ISR/调度延迟(亚 µs~数 µs),**无 tick 量化、无累积漂移**。
|
||||
2. **idle 趋近 0**:原轮询与生产的 ±1 帧相位抖动(§7.1 每 10 s +5)消失——唤醒与生产同源锁定。
|
||||
待台架复核(§7.1)。
|
||||
3. **电平型标志的健壮性**:若单拍偶超 1 ms,TIM6 多发脉冲合并为一次唤醒,`adc_input_poll()` 返回
|
||||
`r==0`(无新帧)记 `idle`,**不重复处理同帧**,与 P0 修复(仅 `r==1` 压环)口径一致,无 DFT 窗污染。
|
||||
|
||||
### 10.4 残留风险与后续硬化(P1-5,规划中)
|
||||
|
||||
- **§4.4 的 2 ms 忙等仍在 L1 层**:`adc_input_poll()` 内的 `HAL_ADC_PollForConversion`
|
||||
(`ADC_INPUT_POLL_TIMEOUT_MS=2U`)是任务级忙等。改造后 `adcTask` 若提至 AboveNormal,若 STM32 ADC
|
||||
偶发超时,该 2 ms 忙等会在**高优先级**下阻塞其余 Normal 任务(比现状更敏感)。**硬化方向(待做)**:
|
||||
超时即**整拍跳过**(不轮询 9 通道),或在 `adc_input.c` 改用非阻塞查询 + 标志,彻底消除忙等占用。
|
||||
- **启动 fail-safe(设计)**:TIM6 未启动前 `adcTask` 永久阻塞于 `osThreadFlagsWait` → 无心跳 → 看门狗
|
||||
`TASK_WD_ADC` 触发复位。属安全 fail-safe,非缺陷(确保"节拍源缺失"不被静默放过)。
|
||||
|
||||
### 10.5 下一版硬件建议:DMA + 双缓冲(方案 b,用户要求记录)
|
||||
|
||||
当前硅片(STM32F407)**STM32 ADC1 已支持 TIM6-TRGO → ADC1 + DMA**;瓶颈在 **TPAFE5160 接口**,
|
||||
须先以逻辑分析仪确认其接口类型再定型:
|
||||
|
||||
- **若 TPAFE 为 SPI + DRDY**(推荐先验):
|
||||
- 单 1 kHz 主时钟:TIM6 `TRGO` 同时驱动 ADC1 扫描(`ADC_ExternalTrigConv`=TIM6_TRGO)与 TPAFE `CONVST`;
|
||||
- TPAFE 数据由 **SPI-RX-DMA** 搬运;`DRDY` 下降沿 → DMA **HT/TC 中断** 实现**乒乓双缓冲**;
|
||||
- 任务只在"半满"时做后处理(压环 + 相量),CPU 完全脱离逐点搬运。
|
||||
- **若 TPAFE 为并行接口**:用 **FMC** 把 TPAFE 数据总线映射为异步 SRAM,DMA 从 FMC 读 → 内存;
|
||||
须确认 FSMC/FMC 地址/时序兼容 TPAFE 读时序(开销大于 SPI 方案)。
|
||||
- **统一收益**:CPU 脱离轮询/搬运 → 采样率可上探(如 4 k / 8 k 点/周波)、相量窗更密、腾出
|
||||
AboveNormal 算力给保护算法;与 §8 等周波调频天然契合(ARR 改频 + DMA 自动续传)。
|
||||
- **验收前置**:先以逻辑分析仪确认 TPAFE 接口类型与 `DRDY` 时序,**再**定型下一版硬件 DMA 方案,
|
||||
避免返工。
|
||||
|
||||
Reference in New Issue
Block a user