我已经完成了这个功能,但是我是在自己的代码环境中写的代码,我的代码没有使用HAL库和LL库。是我自己写的所有驱动, 所以各位只能当作参考。不能直接使用。
AD7606 FMC+DMA 采集方案 — 正确配置与避坑指南版本: Ver1.0 日期: 2026-06-25 作者: cuijunling 适用芯片: STM32H743IIT6 配套代码: ChipDriver/Src/ad7606_fmc.c 状态: 全部配置经 2026-06 实测验证(dump 寄存器值 + 示波器波形双重确认)
0. 本文档的定位本文档记录 AD7606 DBM 双缓冲采集方案的正确配置和已踩过、必须避开的坑。 旧文档 ad7606_fmc_driver.md 中有若干结论被本次调试推翻(主要是"EXTI0 无需中断即可触发 DMAMUX"、"FMC=200MHz/5ns"等),附录 A 列出了旧文档的错误纠正清单。两份文档冲突时,以本文档为准。 1. 方案总览与信号链路1.1 架构TIM1_CH1(PA8) ──PWM──→ CONVST ──→ AD7606 开始转换 │ BUSY↓ (PA0) │ ┌───────────┴───────────┐ │ EXTI0(下降沿, IT模式) │ ← 必须开中断! (见坑1) │ IRQ: 清 PR + 计数 │ └───────────┬───────────┘ │ exti_exti0_it 锁存输出 ▼ DMAMUX1 Request Generator 0 SIG_ID=EXTI0, GPOL=下降沿 GNBREQ=8 │ 每次 BUSY↓ 产生 8 个 DMA 请求 ▼ DMA Stream (DBM 双缓冲) DMAREQ_ID=1 (监听 RG0) PAR=FMC 0x60003000 M0AR/M1AR = SDRAM Buf A/B │ 8 次读 FMC → 8 通道数据 NDTR→0 自动切换 A/B → TC 中断1.2 关键事实(每一条都实测过)事实 值 / 说明
触发源AD7606 BUSY 下降沿 = 数据就绪
EXTI 信号 DMAMUX1 监听的是 exti_exti0_it(EXTI 锁存/PR 输出),不是引脚原始信号
必须开中断 是。否则 PR 挂着不清,RG0 永不触发(见坑1)
DMA 请求源 DMAREQ_ID = 1(DMAMUX1_REQ_GENERATOR0)
每次触发 RG0 产生 8 个 DMA 请求 = 读 8 个通道
数据通路 DMA1/DMA2 + DMAMUX1(不能用 BDMA,见 1.3) 1.3 为什么用 DMA1/DMA2 + DMAMUX1,不用 BDMA + DMAMUX2AD7606 挂在 FMC(D1 域),缓冲区在 SDRAM(D1 域)。BDMA 只能访问 D3 域,读不到 FMC、写不进 SDRAM。所以数据通路必须走 DMA1/DMA2(挂 DMAMUX1)。 ⚠️ 注意:ST 官方的 DMAMUX_RequestGen 例程和网上的 GIPO_DMA 例程用的是 BDMA + DMAMUX2,它的"EXTI0 无中断触发"模型不能照搬到 DMAMUX1(见坑1)。这是最容易踩的坑。 2. 已验证的正确配置(实测寄存器值)2.1 硬件接线信号 STM32 引脚 功能
CONVSTPA8 / TIM1_CH1PWM 输出,周期启动转换
BUSY PA0 / EXTI0 数据就绪(下降沿),触发 DMAMUX1 RG0
FMC_NE1 PD7 FMC 片选(AF12)
FMC_NOE PD4 读使能(RD)
FMC_NWE PD5 写使能
FMC_D0~D15 PJ0~PJ15 等 16-bit 数据总线
RESET / RANGE / OS0~2 见 board_init 配置 普通推挽 GPIO 输出 CONVST 实际由 bsp_timer 产生,PWM 在 PA8 上(debug 中监控的是 TIM1->CR1)。 2.2 BUSY 引脚(PA0)— 方案B:必须用 bsp_gpio_exti_initstatic void ad7606_fmc_config_busy_pin(AD7606_FMC_CONFIG *config){ EXTI_CONFIG exti_cfg; exti_cfg.gpio_group = config->busy_port; // GPIOA exti_cfg.pin = config->busy_pin; // GPIO_Pin_0 exti_cfg.type = EXTI_Trigger_Falling; // BUSY↓ = 数据就绪 exti_cfg.priority = 5; exti_cfg.pull = GPIO_PuPd_UP; bsp_gpio_exti_init(&exti_cfg, ad7606_fmc_busy_exti_callback);}bsp_gpio_exti_init 一步配齐:SYSCFG(PA0→EXTI0) + EXTI(IT 下降沿) + NVIC 使能 + 注册非空调调。回调 ad7606_fmc_busy_exti_callback 非空(仅计数),故 EXTI0_IRQHandler 清 PR 后调用它不会空指针(见坑2)。 ⚠️ 不要用裸 bsp_exti_enable_* 宏 + 不开 NVIC 的旧写法。那是基于"EXTI0 无中断触发 DMAMUX1"的错误认知,实测不工作(见坑1)。 2.3 DMAMUX1 Request Generator 0(RG0)实测 RGCR = 0x003D0106,字段分解(H7 真实位段,见坑4 的位段纠正): 字段 位 值 含义
SIG_ID4:06触发源 = EXTI0(DMAMUX1_REQ_GEN_EXTI0)
OIE 8 1 Overrun 中断使能(仅状态位,NVIC 未开)
GE 16 1 RG 使能(最后置位)
GPOL 18:17 10 下降沿
GNBREQ 23:19 7 产生 8 个请求(编码 = N-1) 配置顺序(改 RGCR 前必须先关 GE,最后再开): bsp_dmamux1_disable_request_gen(rg); // 1. 先关 GEbsp_dmamux1_clear_flag_RGOx(...); // 2. 清历史 overrunbsp_dmamux1_set_request_signalID(rg, EXTI0); // 3. SIG_ID=6bsp_dmamux1_set_request_gen_polarity(rg, FALLING); // 4. GPOL=下降沿bsp_dmamux1_set_gen_request_Nb(rg, 8); // 5. GNBREQ=8(宏内 N-1)bsp_dmamux1_set_overrun_enable(rg); // 6. OIEbsp_dmamux1_enable_request_gen(rg); // 7. 最后开 GE2.4 DMA Stream(DBM 双缓冲)字段 值 说明
DMAREQ_ID(DMAMUX channel CCR)1监听 RG0 输出(DMAMUX1_REQ_GENERATOR0)
DIR 外设→内存 从 FMC 读
DBM 1 双缓冲,M0AR/M1AR 自动切换
MINC 1 内存地址自增
PSIZE/MSIZE 16-bit AD7606 每路 16-bit
PL Very High 最高优先级
TCIE 1 Buffer 填满触发 TC 中断
NDTR buf_size/2 half-word 个数(16KB → 8192) DMA stream 由 bsp_dma_init 动态分配(返回 index 0~15)。index 同时就是 DMAMUX1 通道号(sg_dma_stream[] 按 DMA1×8+DMA2×8 排列),bsp_dma_init 内部 DMAMUX1 + index 自动写到对应 channel CCR。 2.5 FMC 时序(BTR1)实测 BTR1 = 0x02100323: 字段 位 值 含义
ADDSET3:03RD 高 ≈ 30ns(≥ AD7606 22ns)
ADDHLD 7:4 2 Mode A 下不生效
DATAST 15:8 3 RD 低 ≈ 30ns(≥ AD7606 21ns)
BUSTURN 19:16 0 连续读同 Bank,省去
CLKDIV 23:20 1 kernel clock 分频
ACCMOD 29:28 0 Mode A(异步) AD7606 读时序下限:RD 低 ≥ 21ns、RD 高 ≥ 22ns。当前 30/30ns 有余量,不可再降到 2(20ns < 21ns 会读到错误数据)。 2.6 CONVST / PWMbsp_timer_pwm_init(convst_channel=0, 1000us, 10% 占空) → PA8 输出 PWM 周期启动转换。采样率由 ad7606_fmc_start_record(freq) 设定,内部 bsp_timer_pwm_change_cycle 改周期。 3. 关键避坑要点(按严重程度排序)🔴 坑1(最致命) MAMUX1 的 EXTI0 必须开中断清 PR现象:EXTI0 明明检测到 PA0 下降沿(dump PR1=1),但 DMA 不触发(NDTR 不减、PD7 无波形)。RG0 配置全对。 根因(ST 官方答复,community.st.com /td-p/219513): - DMAMUX1(D2 域)的 EXTI0 输入信号 = exti_exti0_it,是 EXTI 的锁存(PR)输出。PR 挂起期间持续为高,只有在中断里清 PR 才会拉低 → 这个下降沿被 RG0 的 GPOL=falling 捕获 → 触发 DMA。
- DMAMUX2(D3 域,BDMA)的 EXTI0 输入 = exti_syscfg_exti0(引脚原始信号,自动清除),无需中断。GIPO_DMA 例程正是这套,所以从不清 PR 也能跑。
- 旧设计"不开 NVIC、纯硬件触发"对 DMAMUX1 行不通
R 一直挂着 → exti_exti0_it 无下降沿 → RG0 永不触发。
正确做法:用 bsp_gpio_exti_init 使能 EXTI0 NVIC,IRQ 清 PR(方案B)。代价:每 BUSY 下降沿进一次中断(10kHz = 1 万次/秒),中断体仅清 PR + 计数,开销可忽略。 调试判据:dump 中 busy_irq_cnt 持续增长 = 中断通了、清 PR 生效。 🟠 坑2:EXTI0_IRQHandler 空指针 HardFaultbsp_gpio.c 的 EXTI0_IRQHandler: void EXTI0_IRQHandler(void) { bsp_exti_clear_flag_0_31(EXTI_LINE_0); // 清 PR sg_exti_fun[0].Exit_Function((void *)EXTI_LINE_0); // 直接调回调,无 NULL 检查!}若使能 NVIC 却没注册回调,进中断就解引用空指针 → BLX 0x0 → HardFault(就是早先"进 ISR 挂死"的真因)。 正确做法:使能 NVIC 前必须经 bsp_gpio_exti_init 注册非空调调(方案B 注册了 ad7606_fmc_busy_exti_callback)。 🟠 坑3:sg_register_gpio 占用冲突导致静默失败bsp_gpio_set_in_up_op 和 bsp_gpio_exti_init 都调 sg_register_gpio 占用引脚。若先调前者占了 PA0,后者内部的 sg_register_gpio(PA0) 会静默返回 false(不报错),导致回调没注册、NVIC 没开。 正确做法:config_busy_pin 里只调 bsp_gpio_exti_init(它内部会注册 PA0 + 配 GPIO),不要先调 bsp_gpio_set_in_up_op。 🟡 坑4 MAMUX RGCR 位段别按老资料写H7 的 DMAMUX_RGxCR 真实位段(20 个 H7 头文件一致,GPOL_Pos=17、GNBREQ_Pos=19): SIG_ID[4:0] OIE[8] GE[16] GPOL[18:17] GNBREQ[23:19]旧文档写的 GPOL[17:16]、GNBREQ[20:16] 是错的(那是别的系列/笔误)。BSP 宏 DMAMUX_RGxCR_GPOL_Pos=17 等已正确,直接用宏即可,不要手填位段。 🟡 坑5:OF0=0 不代表"RG0 没被触发"RGSR.OF0 是 Overrun 标志(触发太密、请求没被及时取走才置位),不是"触发过"标志。DMA 正常服务请求时 OF0 永远是 0。 真判据:NDTR 是否递减。NDTR 不减 = DMA 没收到请求 = RG0 没产生请求(或 DMA 没响应)。别被 OF0=0 误导成"没触发"。 🟢 坑6 verflow 持续涨 = 应用层没消费 bufferdump 里 ovf 贴着 tc 涨(TC 时上一个 buffer 还没被释放)。这是应用层没调 ad7606_fmc_get_dbm_buffer → 处理 → ad7606_fmc_release_dbm_buffer 的消费循环。和 DMA 触发无关,但数据其实没被用起来。 4. FMC 时序与时钟真相(实测)4.1 时钟链(2026-06 实测寄存器值)HSE ──→ PLL1 (M=18, N=192, P=2) ──→ PLL1P ≈ 133MHz (SYSCLK) │ D1CPRE /2 (D1CFGR=0x48) ▼ HCLK3 ≈ 66MHz (T = 15ns) │ FMCSEL=0 (D1CCIPR, PLL2R case 被注释没切) ▼ FMC kernel = HCLK3 ≈ 66MHzPLL2 配置异常慢(N=120,M=33,R=3 → PLL2_R≈30MHz),但 FMCSEL=0 没用它,不影响 AD7606。这是 [memory: sdram-debug-status] 里 "  LL2 diff pending" 的来源,属遗留问题,与采集无关。 4.2 单次 FMC 读周期Mode A 异步读 ≈ (ADDSET + DATAST + 同步开销) 个 kernel 周期: 当前: (3 + 3 + 2) × 15ns ≈ 120ns/读8 读 ≈ 1us ← 与示波器实测一致4.3 优化空间(谨慎)项 当前 提 HCLK3 到 240MHz 后
HCLK366MHz (15ns)240MHz (4.17ns)
单次读 ~120ns ~33ns
8 读 ~1us ~270ns
BTR 是否要改 否(3/3 已最优) 否(仍满足 AD7606 21/22ns) 但提 HCLK3 是系统级大改:CPU、SDRAM、所有外设波特率全变,需重新验证整板。当前 10kHz 采样下 1us 占空才 1%,不建议为此动系统时钟。等采样率冲 100kHz+ 再考虑。 5. 调试方法5.1 ad7606_fmc_debug_dump() 判据表start_record() 之后调用 ad7606_fmc_debug_dump(),按表从上往下看,第一项异常的就是断点: 打印项 正常值 异常含义 断点位置
PA0采样 高电平=NN 在 0~2000 跳动0 或 2000信号没进 PA0(短接/虚焊)
[EXTI] PR1 line0 0(中断清掉) 一直=1 没进中断清 PR(坑1)
[ISR] busy_irq_cnt 持续增长 0 NVIC 没开 / 回调空指针(坑2/3)
[SYSCFG] EXTICR1 src 0(PA) ≠0 PA0 没路由到 EXTI0
[DMAMUX] RGSR OF0 (参考) — OF0 不是触发判据(坑5)
[DMAMUX] ch? DMAREQ_ID 1 ≠1 DMA 监听错请求源
DMA NDTR 递减 恒定 RG0 没触发 / DMA 没启动
[FMC] BTR1 0x02100323 别的值 BTR 没写入(检查 FMCEN 时序)
[RCC] FMCSEL 0 — FMC=HCLK3(坑6)
PD7 波形 每次触发 8 脉冲 无 上游某一环断了 5.2 分段调试技巧(无 AD7606 硬件时)把 PA8(CONVST PWM)用杜邦短接到 PA0(BUSY),用 PWM 边沿模拟 BUSY 下降沿,可单独验证 EXTI0 → RG0 → DMA → FMC 链路,不必接真实 AD7606。 注意:此时读的是悬空 FMC,数据无意义,只验证时序链路(NDTR 递减、PD7 波形)。接回真实 AD7606 后才能验证数据正确性。 5.3 验证数据正确性(必须做)FMC 时序压太狠的典型失败是波形还在、数据已采错。给 AD7606 某路输已知直流电压,确认 8 路读数换算正确,才算配置真 OK。 6. 当前工作配置快照(2026-06-25 实测)项 值
采样率10 kHz(boot 测试)
8 读耗时 ~1us(示波器实测)
占空 1%(1us / 100us 周期)
DMA stream index 0(DMA1_Stream0)
DMAREQ_ID 1
RG0 RGCR 0x003D0106
FMC BTR1 0x02100323
HCLK3 ~66MHz
FMCSEL 0(FMC=HCLK3)
busy_irq_cnt 随采样率增长
数据正确性 待接真实 AD7606 验证 结论:当前配置在 HCLK3=66MHz 下已是 BTR 能做到的最快,数据时序合规。1us 是时钟决定的正常结果,非故障。无需进一步优化(除非提系统时钟)。 附录 A:关联资料
|