adc: 节拍源 TIM6->TIM3 + ADC1 走 DMA 乒乓双缓冲,消除 2 ms 忙等(P1-5 单拍硬上界办结)
用户选定最彻底方案(B2):DMA + 定时器 TRGO 硬件触发 + 消费上一拍 已完成的半区。对应 P1-5 拆出的"单拍硬上界",现已办结。 硬件事实更正(查 CubeMX 器件库 + 本仓 HAL 头证实):F4 上 ADC1 的 外部触发清单里没有 TIM6/TIM7 TRGO(只接 DAC)。故 1 kHz 节拍源由 TIM6 改为 TIM3(PSC 83 / ARR 999,TRGO=UPDATE),同一个更新事件 同时 ① ISR 脉冲 TPAFE CONVST ② 经 TRGO 触发 ADC1 一轮 9 通道 ③ 置位 ADC_WAKE_FLAG 唤醒 adcTask。TIM6 从 .ioc 整体移除。 代码: - Src/tim.c/h:新增 MX_TIM3_Init(含 MasterSlaveMode),删 htim6 - Src/adc.c:ExternalTrigConv=T3_TRGO + EDGE_RISING + DMAContinuousRequests=ENABLE;MspInit 挂 hdma_adc1(DMA2_Stream4 循环模式)。DDS 位关掉 = 一轮只发一次请求,静默丢 8 路 - Src/dma.c:DMA2_Stream4 NVIC 优先级 6(CubeMX 换流会把它重置为 0, 优先级 0 会抢占 FreeRTOS 临界区,违反"外设中断 ≥5") - Src/main.c:周期回调加 TIM3 分支;stm32f4xx_it.c 向量同步 - App/algo/adc_input.c:stm32_scan() → stm32_take(),逐通道 PollForConversion 全删;乒乓半区由完成计数奇偶推出(只读一个 32 位量 → 与 ISR 无竞态);STM32 侧数据固定滞后 1 拍 - App/task/adc_task.c:心跳日志拆出四类计数 观测性:adc1_seq(完成计数)/ adc1_gap(seq 前进 >1 = 丢帧)/ adc1_err(连续 ≥2 拍无新帧 = 停摆)/ adc1_hw(HAL 错误回调)。 单拍无新帧是消费者相位伪影(DMA 完成落在唤醒后 ≈67 µs),不计故障。 验证: - 主机单测 79 → 101 全绿;反向验证四连均咬(半区奇偶取反 11 条、 停摆拍置 valid 4 条、STALL_RUN_MAX 改 1 为 6 条、gap 阈值 1 条) - @build 0 错 0 警,Code 256872 → 257660 - @test PASS:adcp 122 µs → 8 µs、adc1_err=0、adc1_hw=0、 adc1_gap=27/20002 拍、adc1_seq 与 seq 恒定偏移 2 - stack_report 7/7 PASS,adcTask 832 B / 余 59%(不变) - cubemx-survival 新增 5 项审计(TIM3 分支 / T3_TRGO / DDS / DMA 挂载 / 流优先级),28 通过 0 失败 文档:ADC 采样节拍设计说明新增 §11;看门狗停摆分析 §4.4 与 §8 收口; 算法规划 L1-1/L1-3、通道映射、测试说明、各 README 同步。
This commit is contained in:
@@ -307,6 +307,55 @@ CHECKS = [
|
||||
"`USER CODE BEGIN 1` 区。本仓库 HAL 无 HAL_ADCEx_Calibration_Start、"
|
||||
"CMSIS 头无 ADC_CR2_CAL,两者都是裁剪造成的,勿以为『标准 HAL 里有』。",
|
||||
),
|
||||
# ---------------- ADC 采样节拍 + DMA(2026-10-10 v2.8) ----------------
|
||||
# 节拍源 TIM3:同一个更新事件 ① ISR 脉冲 TPAFE CONVST ② 经 TRGO 触发 ADC1
|
||||
# ③ 置位 ADC_WAKE_FLAG。丢了 ② 不报编译错,只是两片转换器不再同源(相位差自由漂移)。
|
||||
dict(
|
||||
id="adc-tick-tim3-branch",
|
||||
kind="protected",
|
||||
path="Src/main.c",
|
||||
region="Callback 1",
|
||||
must_contain=["TIM3", "tpafe5160_start_conv_irq()", "adc_tick_irq_notify()"],
|
||||
hint="HAL_TIM_PeriodElapsedCallback 的 TIM3 分支必须同时做三件事:判 TIM3、脉冲 CONVST、"
|
||||
"通知消费者。⚠ 节拍源**不能**换回 TIM6/TIM7——F4 上 ADC1 的外部触发清单里没有 "
|
||||
"TIM6/TIM7 TRGO(只接 DAC),TIM3 的 TRGO 才是 ADC1 能收到的那个。",
|
||||
),
|
||||
dict(
|
||||
id="ioc-adc-trig-t3trgo",
|
||||
kind="ioc",
|
||||
key="ADC1.ExternalTrigConv",
|
||||
want="ADC_EXTERNALTRIGCONV_T3_TRGO",
|
||||
hint="ADC1 外部触发必须是 T3_TRGO(与节拍源 TIM3 配对)。改回软件触发 = 每拍由任务启动"
|
||||
"转换,DMA 与 TPAFE 不再同源对齐,且重新引入忙等。",
|
||||
severity="FAIL",
|
||||
),
|
||||
dict(
|
||||
id="ioc-adc-dma-continuous",
|
||||
kind="ioc",
|
||||
key="ADC1.DMAContinuousRequests",
|
||||
want="ENABLE",
|
||||
hint="DMAContinuousRequests(DDS 位)必须 ENABLE:关掉后一轮 9 通道只发**一次** DMA 请求"
|
||||
"→ 静默丢 8 路,不报任何编译/运行错误。",
|
||||
severity="FAIL",
|
||||
),
|
||||
dict(
|
||||
id="adc1-dma-link",
|
||||
kind="generated",
|
||||
path="Src/adc.c",
|
||||
must_contain=["hdma_adc1", "__HAL_LINKDMA", "DMA2_Stream4", "DMA_CIRCULAR"],
|
||||
hint="ADC1 的 DMA 挂载(Stream4 + 循环模式 + __HAL_LINKDMA)须在位。"
|
||||
"ADC1 在 DMA2 上只挂 Stream0 或 Stream4;选 Stream0 会连锁搬走 SPI1_RX/USART1_RX。",
|
||||
),
|
||||
dict(
|
||||
id="adc1-dma-irq-priority",
|
||||
kind="generated",
|
||||
path="Src/dma.c",
|
||||
must_contain=["HAL_NVIC_SetPriority(DMA2_Stream4_IRQn, 6, 0)"],
|
||||
hint="⚠ CubeMX 在**换 DMA 流**时会把该流的 NVIC 优先级静默重置为 **0**(实测两轮)。"
|
||||
"优先级 0 高于 configMAX_SYSCALL_INTERRUPT_PRIORITY(5) → 会抢占 FreeRTOS 临界区,"
|
||||
"违反本仓『外设中断 ≥5』。每次改 DMA 流后必须回 NVIC 页核对。",
|
||||
severity="FAIL",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ ROLE = {
|
||||
"testTask": "硬件测试入口",
|
||||
"uiTask": "LVGL UI 界面",
|
||||
"timeSyncTask": "墙钟时间同步",
|
||||
"adcTask": "TPAFE5160 采样(TIM6 1kHz 事件驱动 + 原子快照)",
|
||||
"adcTask": "TPAFE5160 采样(TIM3 1kHz 事件驱动 + 原子快照 + STM32 ADC DMA)",
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user