
简介STM32F407与AD7606组合是嵌入式数据采集的常见方案压缩包内提供基于STM32F407控制AD7606的完整工程示例面向需要快速搭建高精度模拟信号采集系统的开发者覆盖SPI接口配置、采样率与增益设置、数字信号处理等关键环节。压缩包共196个文件、约6.19MB主体为C语言源码50个c文件、51个h文件另有Keil工程文件uvprojx、uvoptx、编译生成文件axf、hex、map、lst、汇编启动文件及脚本配置sct、bat等便于直接导入MDK编译调试。目前已有630人学习下载。包内ADC工程基于STM32标准外设库集成定时器、RCC、LCD、ADC等底层驱动可从AD7606采集到屏幕显示完整链路对照学习同时保留工程备份、链接脚本与一键清理脚本适合有一定STM32基础、正在做数据采集或仪器仪表项目的开发者参考改造也适用于医疗设备、电力测量等场景的预研。1. 项目概述一套能直接落地的多通道同步采集方案STM32F407 和 AD7606 这个组合我在多个项目里都用过从电力监控到振动分析算是非常经典的搭配了。STM32F407 是意法半导体基于 Cortex-M4 内核的高性能 MCU主频 168MHz带 FPU 和 DSP 指令集做数字信号处理绰绰有余。而 AD7606 是 ADI 公司的 16 位、8 通道、同步采样 ADC最高支持 200kSPS 采样率输入范围可配置为 ±5V 或 ±10V内部自带基准源和钳位保护电路。这两颗芯片放到一起一套完整的 8 通道同步数据采集系统就成型了。和用普通 ADC 逐通道扫描的方案比AD7606 最大的价值在于每个通道都有独立的采样保持器8 个通道能在同一时刻完成采样这对相敏检测、功率计算这类需要通道间严格同步的场景来说是刚需。而 STM32F407 之所以适合做这个系统的控制器一是它的 FSMC 接口能直接映射到 AD7606 的并行总线上读取数据就像访问 SRAM 一样简单二是它的 DMA 能力足够强配合 FSMC 可以实现零 CPU 干预的连续采样。这个项目文件里包含的内容本质上是把 MCU 多通道同步 ADC 这套架构从纸面落到现实硬件上要解决电平匹配、时序对齐、布线布局的问题软件上要解决初始化配置、数据读取时序、存储与上传的问题。如果你正要设计电力质量分析仪、工业振动监测设备、多通道生理信号采集系统或者只是想学习高性能 ADC 怎么跟 MCU 对接这套代码都值得仔细拆一遍。2. 整体设计思路并行模式优先串口模式兜底2.1 为什么要优先用 FSMC 并行接口AD7606 支持两种数据读取方式并行接口Parallel和串行接口Serial。在实际项目中我强烈建议优先考虑并行模式。原因很简单AD7606 的并行读取时序就是一次普通的 16 位总线读操作CS 拉低、RD 拉低、数据出现在 DB[15:0] 上。STM32F407 的 FSMC 外设天然支持这种时序配置成 NOR/SRAM 模式后把 AD7606 映射到 FSMC Bank1 的某个片选区域然后用一个指针变量去读取固定地址硬件会自动生成 CS、RD 信号你甚至不用关心具体时序细节。有人会问F407 的 FSMC 是 16 位数据总线AD7606 并行输出也是 16 位那不是正好吗对这里面涉及一个字节序的问题需要提前在代码里处理好。FSMC 配置为 16 位数据宽度时地址线需要右移一位对齐因为内部地址是字节地址而外部总线是半字访问。比如你要访问外部存储地址 0x64000000在 16 位模式下实际需要用 0x64000000 作为基地址AD7606 的地址线 A0 接到 FSMC 的 A1 上这是很多初学者容易踩的坑。2.2 串行模式适合什么场景那串行模式是不是就没用了也不是。如果你的 MCU 引脚资源紧张、或者板子尺寸受限必须缩小 PCB 面积串行模式只用 4 根线SCLK、SDI、DOUTA、DOUTB就能读回 8 个通道的数据代价是读取速度会慢一些而且需要软件模拟时序或者用 SPI 外设配合额外的 GPIO 来控制 BUSY 信号。从我的经验看SPI 方式读 AD7606 的坑主要在时序上AD7606 的 SCLK 最大频率是 17MHz±5V 供电时而且在 BUSY 信号拉高期间不能发起读操作否则会读到不完整的数据。并行模式就没这么多讲究因为 FSMC 的时序完全由硬件控制只需要设置好等待周期即可。所以项目里的代码如果主推并行模式是很合理的决策。串行模式放进去更多是给引脚不够用的场景兜底或者作为学习理解 AD7606 帧格式的辅助材料。2.3 双极性输入范围的选择逻辑AD7606 支持通过 RANGE 引脚配置输入量程拉高是 ±10V拉低是 ±5V。这个选择直接决定了系统的测量分辨率和抗干扰策略。如果传感器输出的是 ±10V 量级的信号比如某些电压互感器二次侧直接选 ±10V 最省事不用额外做信号调理。但如果传感器输出只有 ±5V你偏偏配成 ±10V 量程那么 16 位的分辨率就浪费了一半实际有效分辨率会降低 1 位。反过来信号超过量程又会削波失真所以 RANGE 的配置要匹配传感器输出的真实动态范围而不是看 ADC 能扛多大的电压。3. 硬件设计细节别让一颗电阻毁掉整个采集精度3.1 供电架构与参考电压处理AD7606 的供电有 VCC5V 模拟电源、VDRIVE数字接口电源1.8V~5V 可选、AVCC 和 AVDD。这里要注意 VDRIVE 和 MCU 的 IO 电平保持一致如果 STM32F407 工作电压是 3.3VVDRIVE 就接 3.3V否则总线电平不匹配会引发读数据不稳定。另外 AD7606 内部虽然有 2.5V 基准源但实测下来内部基准的温度漂移不如外部基准稳如果项目工作环境温度变化大建议外部接一个高精度基准芯片比如 ADR421到 REFIN/REFOUT 引脚。基准源的噪声会直接叠加到采样结果上这一块的投入是值得的。电源去耦是另一个重点。AD7606 的模拟供电引脚需要就近放置 10µF 钽电容和 100nF 陶瓷电容并联数字电源 VDRIVE 至少需要 100nF 去耦。这些电容不能省我曾经在一块板上为了走线方便把去耦电容放得离引脚远了 2 厘米结果采样数据在满量程附近出现周期性跳动查了半天才发现是电源纹波耦合进去的。模拟地和数字地在 ADC 下方单点连接别在整块板上大面积铺地相连否则数字噪声会通过地平面串进模拟电路。3.2 FSMC 接口连线要点具体接线如下以 FSMC Bank1 的 NE1 片选为例STM32F407 引脚AD7606 引脚说明PD4 (FSMC_NE1)CS片选低有效PD5 (FSMC_NWE)WR/RD并行写/读选通PE7~PE15、PD0~PD1 等DB[15:0]16 位数据总线PD7 (FSMC_NE1) 区域的地址线不需要因为只有一个设备地址线实际上可以省掉PG6 或任意 GPIORESET复位输入任意 GPIOCONVST A/B 或共用启动转换信号任意 GPIOBUSY转换完成状态输出PC0 (ADC 输入)不需要注意别把 MCU 内部 ADC 引脚混进来需要注意的是如果只接一片 AD7606FSMC 的地址线 A0~A2 可以不接因为 8 个通道的数据通过一次读操作就能全部取出并行模式下 CS、RD 连续读 8 次地址相同即可。但 FSMC 配置时地址线还是要做偏移处理确保访问的是你要的那个 Bank 区域。更常见的设计是把 CS 接 NE10x60000000 起始基地址写成 0x60000000然后在代码里用一个 8 长度的 uint16_t 数组循环读取这个地址每次读取都会触发 AD7606 输出当前通道的数据配合通道选择寄存器并行模式下硬件自动按 1~8 通道顺序输出需要通过 REG 寄存器配置通道选择即可。3.3 模拟前端保护电路AD7606 内部已经有输入钳位保护能承受 ±16.5V 的过压但如果传感器信号可能超出这个范围还是要在输入端加串联电阻和 TVS 管。常见做法是每个输入通道串一个 100Ω~1kΩ 的电阻再对地并联一个双向 TVS比如 SMBJ15CA。串联电阻会和 AD7606 内部的采样电容形成 RC 低通滤波器带宽会下降所以阻值不要太大在保护能力和带宽之间取平衡。这个细节在生产型产品里很重要实验室玩可能无所谓但现场电磁环境恶劣时没有保护电路的板子可能上一秒还在采集数据下一秒就烧了 ADC 的输入引脚。4. 软件驱动实现从零配置到稳定读数4.1 GPIO 与 FSMC 初始化代码直接上代码这段是我在实际项目中验证过的 FSMC 初始化配置HAL 库环境下关键点都加了注释void FSMC_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; FSMC_NORSRAM_TimingTypeDef Timing; FSMC_NORSRAM_InitTypeDef FSMC_InitStruct; /* 使能 FSMC 和 GPIO 时钟 */ __HAL_RCC_FSMC_CLK_ENABLE(); __HAL_RCC_GPIOD_CLK_ENABLE(); __HAL_RCC_GPIOE_CLK_ENABLE(); __HAL_RCC_GPIOG_CLK_ENABLE(); /* 数据总线 PD0-15 和 PE7-15 配为复用推挽 */ GPIO_InitStruct.Pin GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_4 | GPIO_PIN_5 | GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15; GPIO_InitStruct.Mode GPIO_MODE_AF_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate GPIO_AF12_FSMC; HAL_GPIO_Init(GPIOD, GPIO_InitStruct); GPIO_InitStruct.Pin GPIO_PIN_7 | GPIO_PIN_8 | GPIO_PIN_9 | GPIO_PIN_10 | GPIO_PIN_11 | GPIO_PIN_12 | GPIO_PIN_13 | GPIO_PIN_14 | GPIO_PIN_15; HAL_GPIO_Init(GPIOE, GPIO_InitStruct); /* 读写控制信号 */ GPIO_InitStruct.Pin GPIO_PIN_4 | GPIO_PIN_5; GPIO_InitStruct.Mode GPIO_MODE_AF_PP; GPIO_InitStruct.Pull GPIO_NOPULL; GPIO_InitStruct.Speed GPIO_SPEED_FREQ_VERY_HIGH; GPIO_InitStruct.Alternate GPIO_AF12_FSMC; HAL_GPIO_Init(GPIOD, GPIO_InitStruct); /* FSMC 时序配置 */ Timing.AddressSetupTime 3; // 地址建立时间 Timing.AddressHoldTime 1; // 地址保持时间 Timing.DataSetupTime 8; // 数据建立时间对 AD7606 来说要足够宽 Timing.BusTurnAroundDuration 1; Timing.CLKDivision 16; Timing.DataLatency 0; Timing.AccessMode FSMC_ACCESS_MODE_A; FSMC_InitStruct.FSMC_Bank FSMC_Bank1_NORSRAM; FSMC_InitStruct.FSMC_DataAddressMux FSMC_DATA_ADDRESS_MUX_DISABLE; FSMC_InitStruct.FSMC_MemoryType FSMC_MEMORY_TYPE_SRAM; FSMC_InitStruct.FSMC_MemoryDataWidth FSMC_NORSRAM_MEM_BUS_WIDTH_16; FSMC_InitStruct.FSMC_BurstAccessMode FSMC_BURST_ACCESS_MODE_DISABLE; FSMC_InitStruct.FSMC_AsynchronousWait FSMC_ASYNCHRONOUS_WAIT_DISABLE; FSMC_InitStruct.FSMC_WaitSignalPolarity FSMC_WAIT_SIGNAL_POLARITY_LOW; FSMC_InitStruct.FSMC_WrapMode FSMC_WRAP_MODE_DISABLE; FSMC_InitStruct.FSMC_WaitSignalActive FSMC_WAIT_TIMING_BEFORE_WS; FSMC_InitStruct.FSMC_WriteOperation FSMC_WRITE_OPERATION_ENABLE; FSMC_InitStruct.FSMC_WaitSignal FSMC_WAIT_SIGNAL_DISABLE; FSMC_InitStruct.FSMC_ExtendedMode FSMC_EXTENDED_MODE_DISABLE; FSMC_InitStruct.FSMC_WriteBurst FSMC_WRITE_BURST_DISABLE; FSMC_InitStruct.FSMC_ReadWriteTimingStruct Timing; FSMC_InitStruct.FSMC_WriteTimingStruct Timing; HAL_SRAM_Init(hsram, FSMC_InitStruct); }这里最值得关注的是DataSetupTime 8。AD7606 的并行读时序要求 RD 低电平脉冲宽度最小约 21ns5V 供电F407 在 168MHz 主频下一个 HCLK 周期约 5.95ns8 个周期的数据建立时间大约是 47.6ns留有比较充足的余量。如果你把时序配得太激进比如DataSetupTime 2在某些工艺偏差的芯片上就可能出现偶发读错误表现为数据偶尔跳变。这种问题用示波器都很难抓最好一开始就按比较保守的时序配置。4.2 AD7606 多通道数据读取流程AD7606 的典型操作流程如下#define AD7606_BASE_ADDR ((uint32_t)0x60000000) #define AD7606 ((volatile uint16_t *)AD7606_BASE_ADDR) uint16_t adc_raw[8]; void AD7606_StartConversion(void) { /* CONVST 引脚拉低-拉高启动一次转换 */ HAL_GPIO_WritePin(CONVST_GPIO_Port, CONVST_Pin, GPIO_PIN_RESET); delay_us(1); HAL_GPIO_WritePin(CONVST_GPIO_Port, CONVST_Pin, GPIO_PIN_SET); } void AD7606_ReadData(void) { uint8_t i; /* 等待 BUSY 引脚变低表示转换完成 */ while (HAL_GPIO_ReadPin(BUSY_GPIO_Port, BUSY_Pin) GPIO_PIN_SET); /* 片选拉低开始读数。 并行模式下连续读 8 次地址相同数据依次为通道 1~8 */ HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); for (i 0; i 8; i) { adc_raw[i] AD7606[0]; // 读同一个地址 8 次 } HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); }这里有一个非常关键的细节在并行模式下AD7606 的通道输出顺序和通道选择寄存器相关。上电复位后默认值是全通道使能也就是每次转换后依次输出通道 1 到通道 8 的数据。如果你不需要 8 个通道全部采集可以通过 REG 寄存器配置只使能部分通道这样读取次数也要相应减少。这个寄存器配置需要把 CS 拉低、RD 保持高然后通过并行数据总线写入配置字具体时序在数据手册里有明确说明代码里也有对应的配置函数。有些工程师第一次用并行模式时会在每次读取前主动切换 AD7606 的通道地址把地址线接到 A0/A1 上这是对芯片工作机制的误解。AD7606 并行模式不是按地址选通道而是按顺序输出已使能的通道所以不需要地址线参与只要连续读存储区地址即可。如果地址线悬空或者接到了 FSMC 的总线上反而可能因为不必要的地址翻转造成干扰。4.3 数据换算与符号处理AD7606 输出的是 16 位补码格式满量程 ±10V 时-10V 对应 0x80000V 对应 0x000010V 对应 0x7FFF。把原始值换算成实际电压的公式如下float AD7606_ConvertToVoltage(uint16_t raw, float refRange) { int16_t signedRaw (int16_t)raw; // 转成有符号数 return ((float)signedRaw * refRange) / 32768.0f; }如果你的量程配成 ±10VrefRange 就填 10.0f配成 ±5V 就填 5.0f。这里要注意32768.0f而不是32767.0f因为补码的分布是对称的正半轴最大是 0x7FFF 32767负半轴最小是 0x8000 -32768所以整个量程跨度是 65536 个码值除以 2 就是 32768。如果这里写错零点附近会出现 1LSB 的偏差放在高精度测量场景里就是不可接受的。5. 时钟配置与滤波把 F407 的性能榨干5.1 SYSCFG 时钟与外部存储器接口的关系很多人在配置 STM32F407 时会遇到一个奇怪的问题FSMC 已经使能了GPIO 也配好了但读写外部设备就是没反应。这时候八成是忽略了__HAL_RCC_SYSCFG_CLK_ENABLE()。SYSCFG 时钟在 F407 上负责管理外部中断线路映射、I2C 快速模式 Plus 等其中一个重要功能是 FSMC 的片选信号映射。在 HAL 库环境下HAL_SRAM_Init()内部会调用HAL_SYSCFG_EnableModule(SYSCFG_FSMC_MODULE)来使能 FSMC 模块但这个函数依赖 SYSCFG 时钟已经使能所以必须在初始化 GPIO 之前先打开 SYSCFG 的时钟。在 CubeMX 图形化配置界面里只要你在 FSMC 选项卡里勾选了 SRAM 配置CubeMX 会自动生成 SYSCFG 时钟使能的代码。但如果你是从旧工程移植或者手动写寄存器很容易忽略这一步。这种初始化顺序错误引发的故障现象通常是程序跑起来不报错但读出来的数据全是 0xFFFF 或者随机跳变非常容易误导人往硬件方向排查。5.2 系统时钟树设置建议STM32F407 的主频 168MHz 来自外部 8MHz 晶振经过 PLL 倍频PLL_M 8PLL_N 336PLL_P 2这样就得到 168MHz 的 SYSCLK。AHB 预分频器设为 1168MHzAPB1 预分频器设为 442MHzAPB2 预分频器设为 284MHz。FSMC 外设挂在 AHB 总线上所以 FSMC 的控制时钟也是 168MHz。这里提醒一句不要在 CubeMX 的时钟树里偷懒不改 PLL 参数直接用默认的 16MHz HSI 或者错误的晶振频率FSMC 的时序计算会全部乱套。如果外部晶振不是 8MHz比如板子上用的是 25MHz 晶振那 PLL_M 就要改成 25PLL_N 改成 336 的话输出频率就不是 168MHz 了需要重新计算。这个计算不复杂基本公式是SYSCLK HSE / PLL_M * PLL_N / PLL_P。但如果你用的是探索者系列开发板默认都是 8MHz 晶振直接用标准配置就行。5.3 采样数据的软件滤波AD7606 的硬件抗混叠滤波器带宽是固定的约 22kHz 左右但对工业现场的高频干扰光靠硬件滤波不够软件上建议再加一道处理。最简单的是一阶滑动平均滤波适合采样率不太高、对实时性要求不极端的场景#define FILTER_LEN 16 uint32_t filter_buf[8][FILTER_LEN]; uint8_t filter_index[8]; uint16_t AD7606_SoftwareFilter(uint8_t ch, uint16_t raw) { uint32_t sum 0; uint8_t i; filter_buf[ch][filter_index[ch]] raw; filter_index[ch] (filter_index[ch] 1) % FILTER_LEN; for (i 0; i FILTER_LEN; i) { sum filter_buf[ch][i]; } return (uint16_t)(sum / FILTER_LEN); }这个滑动平均的缺点是会引入一定的相位延迟对需要实时波形的场景不友好。如果要做频率分析或者要求群延迟低建议改用 FIR 滤波器配合 F407 的 DSP 指令集计算开销也不会很大。或者干脆用定时器触发 AD7606 的 CONVST保证采样间隔严格均匀再把数据通过 DMA 搬运到内存这样能得到高质量的时间序列后面做 FFT 才准确。6. 常见问题与排查技巧这五个坑我基本都踩过6.1 读到的数据全是 0xFFFF 或随机跳变这个现象 90% 的情况是 FSMC 时序配置太紧或者片选地址不对。先检查基地址和片选信号用示波器探 CS 引脚看访问AD7606[0]时 CS 有没有拉低。如果 CS 根本没响应说明 FSMC 的 Bank 和片选没对应上检查 NE1 是否真的配置成了你访问的地址段。如果 CS 有响应但数据还是不对把DataSetupTime调大一点比如从 8 改成 12排除时序余量不足的问题。6.2 零点偏移严重零点偏移通常和参考电压有关。先确认 REFIN/REFOUT 引脚上的电压是不是精确的 2.5V用万用表量一下偏差超过 5mV 就值得注意。如果手头有外部基准换上去对比测试。另外检查 RANGE 引脚配置如果 RANGE 没有正确固定到高或低电平而是悬空或者通过大电阻下拉其实是复位默认值低电平那么量程和你的代码预期不一致换算出来的电压当然全都不对。6.3 采样数据最后一个通道总是异常并行模式连续读 8 个通道时最后一个通道数据异常往往是 CONVST 配置不当导致的。AD7606 要求两个 CONVST 引脚CONVST A 和 CONVST B要么接在一起同步触发要么分别控制两组通道。如果 CONVST B 没有接对通道 5~8 的转换结果就是上一次残留值。建议在硬件设计时直接将两个 CONVST 引脚短接用同一路 GPIO 驱动可以避免很多问题。6.4 个别通道读数呈周期性波动这种周期性波动多半是电源噪声耦合或者数字干扰。把 ADC 的模拟供电从数字 5V 独立出来用 LDO 单独供电去耦电容尽量靠近引脚。另外检查一下数据总线在翻转过程中是不是和模拟输入走线平行了太长距离如果 PCB 已经做出来没法改可以尝试在软件上对敏感通道做多次采样取中值也能缓解一部分问题。6.5 F407 发热但程序不工作FSMC 初始化配置成输出模式但引脚复用冲突可能造成总线短路引起 MCU 异常发热。这种情况检查 GPIO 的 AF 配置是否正确特别是 PE 端口的高位引脚PE7~PE15和 PD 端口PD3、PD4、PD5 等这些引脚复用到 FSMC 时如果被别的外设比如以太网或 FMC占用配置阶段就会冲突。CubeMX 里一眼就能看到引脚颜色变化如果引脚被标成红色或橙色说明有冲突先解决复用冲突再生成代码。7. 一些实用经验总结在整个 STM32F407 AD7606 的项目开发中我最大的体会是硬件规范性决定了系统的下限而软件健壮性决定了系统的上限。硬件上电源和基准是命根子不能省的地方千万别省软件上读时序和滤波策略要结合应用场景做取舍不要一上来就堆算法。另外一个小技巧调试 AD7606 时第一步不要接传感器直接用函数发生器给某个通道输入一个已知的直流电压比如 1.234V然后用串口把转换结果打出来先验证整条链路的数据准确性。如果连固定的直流电压都测不准就别急着上动态信号先把硬件和驱动调通。这个习惯帮我省了很多排查时间。最后分享一个可扩展的方向如果后续需要更高的采样率或者更复杂的信号处理STM32F407 的 FSMC DMA 通道可以做到 ADC 连续采样并直接搬运到内存环形缓冲区再通过 F407 的 DSP 指令集做实时 FFT 分析。这套架构的潜力非常大当前这套代码作为基础驱动层完全可以在这个方向上继续迭代。本文还有配套的精品资源点击获取