970e574433
- CubeMX 加 TIM6(PSC=83 / ARR=999 / ARR 预装载 / NVIC 抢占优先级 6) - Src/main.c 回调加 TIM6 分支触发 CONVST(USER CODE 区) - app_main.c 用 HAL_TIM_Base_Start_IT(&htim6) 替换单次触发 - adc_task.c 退化为纯消费者(1 ms 轮询仅作消费节奏) - @build 0 错 0 警;台架 @test 验收待做 - 同步 ADC采样节拍设计说明 / 继电保护复现规划 / AGENTS / TPAFE README
204 lines
11 KiB
Markdown
204 lines
11 KiB
Markdown
# ADC 采样节拍设计说明(1 kHz 硬件定时器触发)
|
||
|
||
> **状态**:**已实现(2026-10-10)**——TIM6 更新中断(1 kHz)触发已落地(`Src/main.c` 回调 +
|
||
> `app_main.c` 启动 + `adc_task.c` 退化为纯消费者),`@build` 0 错 0 警;**台架 `@test` 验收(§7)待做**。
|
||
> **上位需求**:`docs/算法/继电保护功能复现规划.md` §3.2 L1-1(周期 CONVST 重触发 1000 Hz,
|
||
> 与保护节拍同源)。**驱动契约**:`Drivers/BSP/TPAFE5160/README.md`。
|
||
> **背景**:B1(2026-10-10)已把 `adc_task` 由空桩改为真实消费者,但采样节拍是
|
||
> **任务周期软触发**(`osDelayUntil`)——CONVST 边沿 = 任务被调度到的时刻,抖动大、
|
||
> 且与 FreeRTOS tick 同源。本项把节拍源换成**硬件定时器**。
|
||
|
||
---
|
||
|
||
## 1. 目标与现状
|
||
|
||
| 项 | 改造前(B1 软触发) | 改造后(TIM6 硬件定时器,2026-10-10 已落地) |
|
||
|---|---|---|
|
||
| CONVST 边沿时刻 | 任务被调度时刻(抖动 = 调度延迟,可达数十 µs~ms) | TIM 更新事件 + ISR 入口延迟(亚 µs~数 µs) |
|
||
| 周期精度 | 依赖 FreeRTOS tick(TIM7) | 定时器硬件分频,**晶振级** |
|
||
| 丢拍 | 任务被长时抢占会漏拍 | 硬件自动重载,不漏拍 |
|
||
| 等周波调频 | 需改 tick 周期(不可行) | 运行期写 ARR 即可(§8) |
|
||
|
||
**判据(L1-1)**:周期 CONVST 重触发 1000 Hz;示波器见 U3 `BUSY` 连续脉冲;
|
||
`tpafe5160_frame_seq()` 持续递增(每秒 +1000)。
|
||
|
||
---
|
||
|
||
## 2. 方案选型
|
||
|
||
### 2.1 方案 A(**推荐**):TIM6 更新中断 → ISR 内脉冲 CONVST
|
||
|
||
- TIM6 配成 1 kHz 更新中断;ISR 内调 `tpafe5160_start_conv_irq()`(现成的 GPIO 脉冲)。
|
||
- **零引脚改动**:PC9 保持 `GPIO_Output`,驱动与 `tpafe5160_init()` 首帧自检**完全不动**。
|
||
- BUSY(PC7)EXTI 读帧路径**完全不动**。
|
||
- 周期精度 = 定时器硬件(晶振级);CONVST 边沿抖动 = TIM6 ISR 入口延迟(见 §5)。
|
||
- 运行期改 ARR 即改变采样率 → 天然支持等周波调频。
|
||
|
||
### 2.2 方案 B(备选,暂不采用):TIM3_CH4/TIM8_CH4 硬件 PWM 直驱 PC9
|
||
|
||
- PC9 复用为定时器通道,硬件直接产生 CONVST 方波,**零软件抖动**。
|
||
- **代价**:① 需把 PC9 由 `GPIO_Output` 改为 AF(`.ioc` 引脚改动);
|
||
② 驱动的 `TPAFE5160_CONVST_*` 宏(GPIO 写)全部失效 → `tpafe5160_init()` 的
|
||
"确保 CONVST 低"与首帧自检(E-44)都要重写;③ 上电初值/空闲电平语义变化。
|
||
- **结论**:收益(把抖动从"数 µs"降到"0")不足以抵消改动面与回归风险。
|
||
**仅当 §5 实测抖动超过算法容限时再考虑**。
|
||
|
||
> 下文只展开**方案 A**。
|
||
|
||
---
|
||
|
||
## 3. CubeMX 配置规格(照此配置)
|
||
|
||
**前提**:SYSCLK = 168 MHz / HCLK = 168 MHz / APB1 = 42 MHz(÷4)→ **APB1 定时器时钟 = 84 MHz**;
|
||
HAL 时基 = TIM7(已占用);**TIM1~TIM6、TIM8~TIM14 当前全部空闲**。
|
||
|
||
| 步骤 | 位置 | 值 | 说明 |
|
||
|---|---|---|---|
|
||
| 1 | Timers → **TIM6** → *Activated* | ✅ 勾选 | 基础定时器,**无输出引脚**(不占用任何 IO) |
|
||
| 2 | Prescaler (PSC) | **83** | 84 MHz ÷ (83+1) = **1 MHz** 计数时钟(1 µs 分辨率) |
|
||
| 3 | Counter Period (ARR) | **999** | 1 MHz ÷ (999+1) = **1.000 kHz**(= 1000 µs 周期) |
|
||
| 4 | Counter Mode | Up | 向上计数 |
|
||
| 5 | **auto-reload preload** | **Enable** | 运行期写 ARR 在下一个更新事件生效,不产生毛刺(§8 等周波调频要用) |
|
||
| 6 | Clock Division | No Division | 与采样无关 |
|
||
| 7 | Trigger Event Selection | Reset(默认) | 不用 TRGO |
|
||
| 8 | NVIC Settings → **TIM6 global interrupt** | ✅ 勾选;**Preemption Priority = 6** | 与 BUSY EXTI(`EXTI9_5_IRQn` = 6)**同级**:两者互不抢占,避免"读 RD 时序被 CONVST 脉冲打断"(§5);且 6 ≥ `configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY`(=5),将来 ISR 里发任务通知也安全 |
|
||
|
||
**生成后**:CubeMX 会新建 `Src/tim.c` + `Inc/tim.h`(含 `htim6` 与 `MX_TIM6_Init()`)、
|
||
在 `Src/stm32f4xx_it.c` 生成 `TIM6_IRQHandler()`、在 `Src/main.c` 的 `main()` 里插入 `MX_TIM6_Init()`。
|
||
⚠ **`MX_TIM6_Init()` 只做配置,不启动计数**——启动由用户代码负责(§4 第 4 条)。
|
||
|
||
> **为何不用 TIM7 那种"1 MHz 计数 + ARR=999"以外的心跳**:本配置与 HAL 时基 TIM7
|
||
> 的构造完全一致(同为 84 MHz / 1 MHz / 1000 计数),便于对照排查;PSC 取 83 使 ARR 与
|
||
> "微秒"一一对应,等周波调频时可直接 `ARR = 周期µs - 1`。
|
||
|
||
---
|
||
|
||
## 4. 代码改造清单
|
||
|
||
> 约定:**生成区不改**(CubeMX 重生成会覆盖);所有手工代码落 `USER CODE` 保护区(L2)。
|
||
|
||
### 4.1 `Src/main.c`(CubeMX 生成;改 `USER CODE` 区)
|
||
|
||
- `USER CODE BEGIN Includes` 增加 `#include "tpafe5160.h"`。
|
||
- `HAL_TIM_PeriodElapsedCallback()`(**注意:该回调被 TIM7 时基共用**)增加 TIM6 分支:
|
||
|
||
```c
|
||
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
|
||
{
|
||
if (htim->Instance == TIM7) { HAL_IncTick(); } /* 既有:HAL 时基 */
|
||
/* USER CODE BEGIN Callback 0 / 1 —— 加在既有 TIM7 分支之后 */
|
||
if (htim->Instance == TIM6)
|
||
{
|
||
tpafe5160_start_conv_irq(); /* 1 kHz 采样节拍:ISR 内脉冲 CONVST(仅 GPIO 写,亚 µs) */
|
||
}
|
||
}
|
||
```
|
||
|
||
⚠ **不得**破坏既有 `HAL_IncTick()` / `task_wd_tick_probe()` 两条 TIM7 分支。
|
||
|
||
### 4.2 `Src/tim.c` / `Inc/tim.h` / `Src/stm32f4xx_it.c`
|
||
|
||
- **CubeMX 自动生成,勿手改**(`TIM6_IRQHandler` → `HAL_TIM_IRQHandler(&htim6)`)。
|
||
|
||
### 4.3 `App/app/app_main.c`
|
||
|
||
- 增加 `#include "tim.h"`。
|
||
- 第 213 行的**单次** `tpafe5160_start_conv_irq();` 替换为**启动周期触发**:
|
||
|
||
```c
|
||
/* 6) 启动 1 kHz 采样节拍(TIM6 更新中断 → ISR 内触发 CONVST) */
|
||
(void)HAL_TIM_Base_Start_IT(&htim6);
|
||
```
|
||
|
||
- ⚠ **必须在 `tpafe5160_init()` 成功之后**再启动 TIM6,否则 ISR 会在驱动自检
|
||
(首帧有效性探针,E-44)期间乱发 CONVST。
|
||
|
||
### 4.4 `App/task/adc_task.c`
|
||
|
||
- **删除**主循环里的 `tpafe5160_start_conv_irq();`(触发已交给 TIM6)。
|
||
- 本任务退化为**纯消费者**:`tpafe5160_snapshot()` + 帧统计 + 10 s 心跳 + 看门狗心跳。
|
||
1 ms 节拍(`osDelayUntil`)保留,仅用于"消费节奏"。
|
||
- 更新文件头与函数注释(说明节拍由 TIM6 提供,本任务不再触发)。
|
||
- **看门狗不变**:`TASK_WD_ADC` 心跳仍在循环内每拍调用。
|
||
|
||
### 4.5 文档同步(2026-10-10 已执行)
|
||
|
||
- `docs/算法/继电保护功能复现规划.md`:§3.2 L1-1 由 🔶 改为 ✅(TIM6 硬件触发)、§1.2 采样软件行 + 变更记录同步。
|
||
- `docs/待处理内容.md` §4:TPAFE 自动扫描模式待确认项标注"已不影响"(TIM6 方案不依赖它)。
|
||
- `AGENTS.md` 架构要点 `adcTask` 条 + `app_main_init` 序列:改为"TIM6 硬件触发 + 纯消费者"。
|
||
- `Drivers/BSP/TPAFE5160/README.md`:周期重触发改为"由 TIM6 更新中断提供"。
|
||
|
||
---
|
||
|
||
## 5. 时序与优先级分析
|
||
|
||
**单周期时序(1 kHz,周期 1000 µs)**:
|
||
|
||
```
|
||
t=0 TIM6 更新事件 → ISR → CONVST 脉冲(LOW→HIGH 上升沿)
|
||
t≈0+ε TPAFE5160 开始转换(BUSY 拉高)
|
||
t≈23 µs BUSY 拉低 → PC7 EXTI(优先级 6) → 读 8 通道(≈0.5 µs)→ seq++
|
||
t=1000 µs 下一个 TIM6 更新事件
|
||
```
|
||
|
||
- **读窗口与下一次 CONVST 相隔 ~977 µs**(转换仅 ~23 µs),互不重叠。
|
||
- **TIM6 与 BUSY EXTI 同级(6)→ 互不抢占**:读 RD 时序(8 次 RD 翻转)不会被打断,
|
||
避免"读到一半被新转换刷新数据总线"。这是选 6 而非 5 的原因。
|
||
- **触发抖动**:TIM6 是最高优先级之一(5 为 USART,7 为 SPI/DMA);被更高优先级 ISR
|
||
(USART,~数 µs)挡住的概率与时长都有限 → 最坏抖动约数 µs,占 1000 µs 的 <0.5%。
|
||
- **ISR 时长**:`tpafe5160_start_conv_irq()` 只做两次 `HAL_GPIO_WritePin`(寄存器写)
|
||
→ 亚 µs,不拖累其它中断。
|
||
|
||
---
|
||
|
||
## 6. 风险与注意事项
|
||
|
||
1. **共用回调**:`HAL_TIM_PeriodElapsedCallback` 同时服务 TIM7(HAL 时基)与 TIM6 →
|
||
分支必须并列、不得互相干扰;改动后必须确认开机日志时间戳正常(时基未坏)。
|
||
2. **启动顺序**:TIM6 必须在 `tpafe5160_init()` 之后启动(§4.3)。
|
||
3. **不新增任务/栈**:本项**零新任务、零新栈、零动态分配** → 不动 `stack_report.py` 预算。
|
||
4. **CubeMX 生存**:新增生成文件 `Src/tim.c` / `Inc/tim.h`;改后跑一次
|
||
`python scripts/check_cubemx_survival.py`,若有新增生成文件被登记,按提示重建基线。
|
||
5. **Keil 工程**:从 CubeMX 重生成工程(含 MDK-ARM)以把 `tim.c` 纳入编译;随后
|
||
`@build` 必须 0 错 0 警。
|
||
6. **`mem-numbers`**:本项改 Flash/RAM 极小但非零 → 若 `check_mem_numbers.py` FAIL,
|
||
跑 `python scripts/check_mem_numbers.py --write` 刷新快照。
|
||
7. **消费端与生产端同频**:TIM6(84 MHz 派生)与 FreeRTOS tick TIM7(同源)都恰好 1 kHz,
|
||
相位固定 → 消费端每拍恰好取到 1 帧。**一旦进入等周波调频(采样率≠1 kHz),
|
||
1 ms 轮询会与生产端失配 → 消费端须改为"帧就绪事件驱动"**(见 §8)。
|
||
|
||
---
|
||
|
||
## 7. 验收判据
|
||
|
||
| # | 判据 | 手段 |
|
||
|---|---|---|
|
||
| 1 | `@build` 0 错 0 警,且镜像无源文件新于 hex | `MDK-ARM/build.bat` + `find -newermt` |
|
||
| 2 | PC9 `CONVST` 周期 = 1.000 kHz(±0.1%) | 示波器 / 逻辑分析仪 |
|
||
| 3 | PC7 `BUSY` 每周期一个脉冲,与 CONVST 相位固定 | 示波器双通道 |
|
||
| 4 | `frame_seq()` 每秒 +1000(每 10 s +10000),严格递增 | `@test` 串口 `[ADC_TASK] frames=... seq=...` |
|
||
| 5 | `frst_err=0`、`idle` 基本为 0 | 同上 |
|
||
| 6 | 开机日志时间戳正常(TIM7 时基未坏) | 串口日志 |
|
||
| 7 | 无 HardFault / `[ERR]` / 看门狗复位 | `@test` Forbidden 判定 |
|
||
|
||
---
|
||
|
||
## 8. 后续:等周波调频(不在本项范围)
|
||
|
||
参考系统按 **20 点/周波**采样:电网频率漂移时需让采样率跟随(50.0 Hz → 1000 Hz;
|
||
49.5 Hz → 990 Hz),以保持傅氏全波窗对齐一个工频周期。
|
||
|
||
- 本配置已为此预留:**1 µs 分辨率** + **ARR 预装载使能** → 运行期
|
||
`__HAL_TIM_SET_AUTORELOAD(&htim6, 周期µs - 1)` 即可无毛刺改频。
|
||
- 需要配套:① 测频源(过零/DFT 相位);② 把当前采样率同步给保护算法(相量/定值);
|
||
③ **消费端改事件驱动**(§6.7)——否则 1 ms 轮询在非 1 kHz 采样率下会丢帧。
|
||
|
||
---
|
||
|
||
## 9. 变更记录
|
||
|
||
| 日期 | 变更 | 说明 |
|
||
|---|---|---|
|
||
| 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` 验收待做 |
|