拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

STM32CubeMX配置AD7606串行模式:SPI时序调试与DMA多通道采集实战

STM32CubeMX配置AD7606串行模式:SPI时序调试与DMA多通道采集实战 1. 项目缘起与整体设计思路AD7606 这颗芯片在电力监控、电机控制、振动采集这些领域出镜率极高。8 通道、16 位、真双极性、200kSPS 同步采样单颗芯片就能顶掉一堆运放加多路开关的组合。但它有个让新手很头疼的地方并行接口好懂SPI 接口的时序却总调不通读回来的数据要么全 0要么高低字节错位要么通道之间串数据。我前后用 STM32CubeMX 配过五六版 AD7606 的驱动从 F103 到 F407 再到 H743踩过的坑基本能凑成一本小册子。这篇就把整套配置流程和 SPI 时序调试的门道完整梳理一遍从 CubeMX 的引脚规划一直讲到 DMA 搬运和时序验证尽量让第一次上手的人少走弯路。先说清楚这个项目要解决什么问题。AD7606 支持并行、串行、并行字节三种接口模式串行模式下只用几根线就能读出 8 个通道的数据对引脚紧张的板子非常友好。但串行模式的时序约束比并行严格得多尤其是CS 与 SCLK 的配合关系、转换启动信号 CONVST 的脉冲宽度、BUSY 信号的下降沿判定这几处稍有不慎就读出错数据。STM32CubeMX 能帮我们把 SPI 外设、GPIO、DMA 的初始化代码生成出来但 AD7606 的时序逻辑必须自己写CubeMX 不会替你做这件事。这套方案适合谁如果你手上有 AD7606 模块或者自己画的板子主控是 STM32 任意带 SPI 和 DMA 的型号想用最少的引脚实现 8 通道同步采集那这篇内容基本可以照着抄。我默认你有一定的 C 语言基础和 STM32 HAL 库使用经验但即使你刚接触 CubeMX跟着步骤走也能配出来。核心关键词会贯穿全文STM32CubeMX 配置、AD7606 串行模式、SPI 时序调试、多通道同步采样、DMA 搬运。整体设计上我采用的思路是CubeMX 负责外设骨架手写驱动负责时序灵魂。具体来说SPI 配置成主机模式、CPOL1/CPHA1模式 3用硬件 NSS 或者软件片选都行但推荐软件片选以便精确控制 CS 的拉低和拉高时机CONVST 和 RESET 用普通 GPIO 输出BUSY 用 GPIO 输入或者外部中断数据读取用 SPI DMA避免 CPU 在 8 个通道 16 位数据的搬运上耗费时间。这个架构的好处是 CPU 占用极低采集 8 通道一轮下来 CPU 只需要处理一次 DMA 完成中断剩下的时间可以跑算法或者通信。为什么选 SPI 模式 3 而不是模式 0 或模式 1这是 AD7606 数据手册明确规定的。AD7606 在 SCLK 下降沿输出数据主机在上升沿采样对应 CPOL1空闲高电平、CPHA1第二个边沿采样。如果你配成模式 0数据会在错误的边沿被锁存读回来就是乱的。这一点我在早期调试时吃过亏示波器一看波形才发现采样点和数据变化沿重合了。再解释一下为什么用 DMA 而不是中断逐字节读取。AD7606 一次转换产生 8 个通道 × 16 位 128 位数据加上前导的通道地址位实际串行帧长度是 8 × 16 128 个 SCLK 周期部分模式还有额外的状态位。如果用中断方式每个字节进一次中断128 位就是 16 次中断在 200kSPS 的采样率下 CPU 根本扛不住。DMA 方式下SPI 每收一个字节自动搬到内存收完 16 个字节触发一次 DMA 完成中断CPU 负担降低一个数量级。实测在 F103 上DMA 方式下 200kSPS 连续采集 CPU 占用不到 5%中断方式直接飙到 40% 以上。2. 核心细节解析与 CubeMX 实操配置2.1 AD7606 串行模式引脚功能梳理在动手配置 CubeMX 之前先把 AD7606 在串行模式下用到的引脚理清楚不然后面配到一半发现少配了一个脚返工很麻烦。AD7606 是 64 脚 LQFP 封装串行模式下关键引脚如下引脚名方向功能说明STM32 侧连接CONVST A/B输入转换启动上升沿触发采样GPIO 输出可并联RESET输入复位高电平有效GPIO 输出CS输入片选低有效GPIO 输出软件片选SCLK输入串行时钟SPI SCKDOUTA输出串行数据输出SPI MISOBUSY输出转换中指示高电平表示正在转换GPIO 输入或 EXTIFRSTDATA输出首通道数据指示可悬空或接 GPIORANGE输入量程选择低±5V高±10VGPIO 输出OS[2:0]输入过采样倍率选择GPIO 输出或接地这里有几个细节值得展开。CONVST A 和 CONVST B控制两组通道的采样启动如果只做 8 通道同步采样把两个脚并联到一起用一个 GPIO 同时驱动即可。RANGE 引脚决定输入量程接低电平是 ±5V接高电平是 ±10V这个要根据你的传感器输出范围来定配错了读回来的电压值会差一倍。OS[2:0]是过采样设置不做过采样就全部接地需要过采样的话用 GPIO 控制过采样能显著降低噪声但会降低吞吐率200kSPS 下开 64 倍过采样实际输出率会降到 3kSPS 左右。FRSTDATA这个脚很多人忽略它用来指示串行帧的第一个通道数据。在读取时FRSTDATA 变高表示下一个 SCLK 下降沿输出的是通道 1 的数据。如果你不做通道对齐校验可以不管它但调试阶段建议接一个 GPIO 上去用示波器或者逻辑分析仪看它和 DOUTA 的相位关系能快速判断时序对不对。2.2 CubeMX 中 SPI 外设的配置要点打开 STM32CubeMX选好你的芯片型号进入 Pinout 视图。先找到 SPI 外设比如 SPI1把它设为Full-Duplex Master或者Receive Only Master。AD7606 只往外吐数据不需要主机发数据所以 Receive Only Master 更贴切能省一根 MOSI 线。但有些 HAL 库版本对 Receive Only 模式支持不太好如果你遇到 DMA 收不到数据的情况换成 Full-Duplex Master 把 MOSI 悬空也能用。SPI 参数配置是重头戏逐项说明Frame Format选 Motorola这是标准 SPI 格式AD7606 兼容。Data Size8 Bits。AD7606 串行帧按字节传输虽然数据是 16 位但 SPI 层面按 8 位收发驱动里再拼成 16 位。Clock Polarity (CPOL)High。空闲时 SCLK 为高电平。Clock Phase (CPHA)2 Edge。第二个边沿采样即上升沿采样。NSS Signal Type选 Software用 GPIO 手动控制片选。硬件 NSS 在连续读取多帧时容易出问题软件片选更可控。Baud Rate Prescaler这个要算一下。AD7606 的 SCLK 最高频率是 23MHzVDRIVE5V 时但实际用的时候建议留余量。STM32F103 的 SPI1 挂在 APB2 上72MHz 主频分频系数选 8 得到 9MHz选 4 得到 18MHz。我一般先用 9MHz 调通再往上提。分频系数太小导致 SCLK 太快AD7606 输出数据还没稳定就被采样了读回来就是错的。First BitMSB First。AD7606 高位先出。配置完 SPI 后别忘了在 DMA Settings 里给 SPI 的 RX 通道加一条 DMA 流。方向选 Peripheral To Memory模式选 Normal单次或者 Circular循环。Normal 模式适合按需触发采集Circular 模式适合连续采集。数据宽度都选 Byte因为 SPI 按 8 位收发。DMA 的优先级设为 High避免被其他外设打断导致数据丢失。2.3 GPIO 配置与时序相关细节GPIO 配置看着简单但时序相关的几个脚有讲究。CONVST和RESET配成推挽输出、无上下拉、高速模式。BUSY配成输入模式如果需要用中断触发读取把它配成 EXTI 下降沿触发。CS配成推挽输出初始电平设为高不选中。这里有个容易踩的坑CS 的拉低时机。AD7606 要求 CS 在 SCLK 第一个下降沿之前至少拉低 10ns在最后一个 SCLK 下降沿之后至少保持 10ns 再拉高。如果你用 HAL 库的HAL_SPI_Receive_DMA函数它内部会在启动 DMA 后立即开始产生 SCLK如果你在调用这个函数之后才拉低 CS第一个时钟沿可能已经过去了导致第一个字节丢失。正确的做法是先拉低 CS再启动 SPI DMA 接收顺序不能反。另一个细节是CONVST 脉冲宽度。AD7606 要求 CONVST 高电平至少保持 25ns实际用 HAL 库的HAL_GPIO_WritePin加HAL_Delay或者简单的空循环就能满足。但要注意CONVST 上升沿触发转换后BUSY 会在最多 25ns 内变高转换时间取决于过采样设置不做过采样时约 4μs。如果你在 CONVST 拉高后立即去读 BUSY可能读到还没变高的状态所以要么加个小延时要么用 EXTI 等 BUSY 下降沿。3. 实操过程与核心环节实现3.1 完整采集流程的代码实现CubeMX 生成代码后在 main.c 里补充 AD7606 的驱动逻辑。先定义几个宏和全局变量#define AD7606_CS_LOW() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET) #define AD7606_CS_HIGH() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET) #define AD7606_CONVST_HIGH() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET) #define AD7606_CONVST_LOW() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET) #define AD7606_RESET_HIGH() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET) #define AD7606_RESET_LOW() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET) uint8_t ad7606_rx_buf[16]; int16_t ad7606_data[8]; volatile uint8_t ad7606_dma_done 0;初始化函数里先复位 AD7606再配置量程和过采样void AD7606_Init(void) { AD7606_CS_HIGH(); AD7606_CONVST_LOW(); AD7606_RESET_HIGH(); HAL_Delay(1); AD7606_RESET_LOW(); HAL_Delay(1); // RANGE 和 OS 引脚根据实际硬件配置 }启动一次转换并读取数据的函数void AD7606_StartConvAndRead(void) { AD7606_CONVST_HIGH(); // 保持至少 25ns这里用几个空指令 __NOP(); __NOP(); __NOP(); __NOP(); AD7606_CONVST_LOW(); // 等待 BUSY 下降沿表示转换完成 while (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_2) GPIO_PIN_SET); // 先拉低 CS再启动 DMA 接收 AD7606_CS_LOW(); ad7606_dma_done 0; HAL_SPI_Receive_DMA(hspi1, ad7606_rx_buf, 16); }DMA 完成回调里拼数据void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi-Instance SPI1) { AD7606_CS_HIGH(); for (int i 0; i 8; i) { ad7606_data[i] (int16_t)((ad7606_rx_buf[i*2] 8) | ad7606_rx_buf[i*21]); } ad7606_dma_done 1; } }这段代码里最关键的是CS 拉低在 DMA 启动之前以及BUSY 等待在 CS 拉低之前。顺序错了就读不到正确数据。另外注意ad7606_rx_buf是 16 字节对应 8 个通道 × 2 字节。AD7606 串行帧里每个通道 16 位高位在前所以拼数据时第一个字节左移 8 位再或上第二个字节。3.2 SPI 时序调试的示波器实战代码写完了不代表能跑通SPI 时序调试才是真正的战场。我强烈建议手边有一台示波器或者逻辑分析仪至少能看四路信号CS、SCLK、DOUTA、BUSY。没有的话用 STM32 的 GPIO 翻转配合串口打印也能凑合调但效率低很多。先看CS 和 SCLK 的关系。正确的波形是CS 拉低后SCLK 才开始翻转第一个下降沿输出第一位数据。如果你看到 CS 和 SCLK 几乎同时变化说明 CS 拉低太晚了第一个 bit 可能丢了。解决办法就是在HAL_SPI_Receive_DMA之前加一句AD7606_CS_LOW()并且确保这句和 DMA 启动之间没有其他耗时操作。再看SCLK 和 DOUTA 的相位。AD7606 在 SCLK 下降沿更新数据主机在上升沿采样。用示波器双通道同时抓 SCLK 和 DOUTA你应该看到 DOUTA 在 SCLK 下降沿之后变化在下一个上升沿之前稳定。如果 DOUTA 的变化沿和 SCLK 上升沿重合说明 CPHA 配错了改成模式 0 试试虽然手册说模式 3但不同批次的芯片或者不同厂家的模块可能有差异实测为准。BUSY 信号的判定也很关键。BUSY 在 CONVST 上升沿后变高转换完成后变低。如果你用轮询方式等 BUSY 变低要注意 BUSY 变低到数据准备好之间还有一小段延时典型 25ns直接读可能读到旧数据。稳妥的做法是 BUSY 变低后再等一个微秒再拉低 CS。用 EXTI 方式的话在中断里拉低 CS 启动 DMA响应更快。3.3 多通道同步采样的数据对齐AD7606 的 8 个通道是真正同步采样的内部有 8 个采样保持器同时工作。但串行读出时是逐个通道输出的所以数据对齐指的是读出的 8 个 16 位数据分别对应哪个通道。默认情况下CS 拉低后的第一个通道是通道 1依次到通道 8。但如果你开了过采样或者用了 FRSTDATA 做对齐顺序可能不同。我一般用 FRSTDATA 来验证对齐。把 FRSTDATA 接到一个 GPIO在 DMA 接收过程中用示波器看它和 DOUTA 的关系。FRSTDATA 变高表示下一个 SCLK 下降沿输出的是通道 1 数据。如果 FRSTDATA 变高时 DOUTA 上已经是通道 2 的数据了说明你少读了一个字节或者多读了一个字节需要调整 DMA 接收长度。数据拼好后转换成电压值的公式是电压 (int16_t 数据 / 32768.0) × 量程量程是 ±5V 时满量程 32768 对应 5V量程是 ±10V 时对应 10V。注意 AD7606 输出的是二进制补码负数表示负电压。如果你读回来的数据全是正数检查一下是不是把符号位丢了或者拼数据时用了uint16_t而不是int16_t。4. 常见问题与排查技巧实录4.1 读回数据全 0 或全 0xFF 的排查路径这是最常见的问题我遇到过至少三种原因。第一种是 CS 没拉低SPI 从机没被选中DOUTA 处于高阻态STM32 读回来就是 0 或者 0xFF。用万用表量一下 CS 引脚在读取时的电平如果是高电平检查代码里 CS 控制的那句是不是被条件编译跳过了。第二种是 SCLK 没有输出可能是 SPI 外设没使能或者 DMA 没启动。在HAL_SPI_Receive_DMA后面加个断点看返回值是不是 HAL_OK如果是 HAL_ERROR 或者 HAL_BUSY说明 SPI 状态不对。第三种是 AD7606 没复位芯片处于未知状态。在初始化时确保 RESET 引脚有一个完整的高电平脉冲宽度至少 50ns我一般给 1ms 保险。还有一种隐蔽的情况SPI 的 GPIO 复用功能没配。CubeMX 里如果只配了 SPI 外设但没把对应的引脚设为 Alternate Function生成的代码里引脚还是普通 GPIOSCLK 根本出不来。检查MX_GPIO_Init和MX_SPI1_Init里有没有对 SCK 和 MISO 引脚做HAL_GPIO_Init的复用配置。4.2 数据高低字节错位的修正方法读回来的数据如果通道 1 的值跑到了通道 2或者高低字节颠倒基本是数据拼接的问题。AD7606 串行输出是 MSB First每个通道 16 位先出高 8 位再出低 8 位。如果你拼数据时写成了(buf[i*21] 8) | buf[i*2]高低字节就反了。正确的写法是(buf[i*2] 8) | buf[i*21]。通道错位的话检查 DMA 接收长度。8 个通道需要 16 个字节如果你只收了 15 个或者 17 个通道就会串。用逻辑分析仪数一下 SCLK 的脉冲数8 通道 × 16 位 128 个 SCLK如果数出来是 120 个或者 136 个说明 SPI 配置的数据位宽或者 DMA 长度不对。4.3 SPI 通信不生效的硬件检查清单软件查完了还不行就得看硬件。我整理了一个速查表现象可能原因排查方法完全无数据供电缺失量 AD7606 的 AVCC 和 VDRIVE数据跳变剧烈参考电压不稳量 REFIN/REFOUT 引脚特定通道异常输入悬空检查对应通道输入是否接地或接信号源高速时出错SCLK 太快降低分频系数降到 1MHz 试偶尔出错电源纹波大示波器看电源纹波加去耦电容提示AD7606 的 VDRIVE 引脚决定数字接口电平如果 VDRIVE 接 3.3VSCLK 和 DOUTA 的电平就是 3.3V和 STM32 直连没问题。如果 VDRIVE 接 5VDOUTA 输出 5V 电平直接接 STM32 的 MISO 可能损坏引脚需要电平转换。4.4 提升采集稳定性的几个实操心得第一个心得是在 CONVST 和 BUSY 之间加死区。有些批次的 AD7606 在 CONVST 上升沿后 BUSY 变高的延迟比较大如果你用轮询方式立即读 BUSY可能读到低电平以为转换完成了。我的做法是 CONVST 拉高后延时 1μs 再开始等 BUSY 变低这样不会误判。第二个心得是DMA 缓冲区用 32 字节对齐。STM32 的 DMA 在访问非对齐内存时效率会降低虽然功能上没问题但在高采样率下可能丢数据。用__attribute__((aligned(32)))修饰缓冲区实测稳定性有提升。第三个心得是过采样和采样率的权衡。开过采样能降噪但转换时间成倍增加。不做过采样时 8 通道全读一遍约 4μs 转换加 14μs 传输总共 18μs理论最高约 55kSPS。开 64 倍过采样后转换时间变成 256μs实际采样率降到 3.7kSPS。如果你的应用需要高采样率就别开过采样靠外部模拟滤波来降噪。第四个心得是用定时器触发 CONVST。如果要精确控制采样率别用HAL_Delay用 TIM 的 PWM 输出或者更新事件来触发 CONVST。TIM 触发精度到微秒级HAL_Delay的精度只有毫秒级而且会被中断打断。配置一个 TIM周期设为 1/采样率在更新中断里拉高 CONVST这样采样间隔非常均匀。5. 从 CubeMX 到量产代码的优化建议5.1 中断优先级与 FreeRTOS 下的配置如果你在 FreeRTOS 环境下用 AD7606SPI DMA 的中断优先级要特别注意。FreeRTOS 的configMAX_SYSCALL_INTERRUPT_PRIORITY决定了哪些中断可以调用 FreeRTOS 的 API。SPI DMA 完成中断里如果调用了xSemaphoreGiveFromISR之类的函数中断优先级必须低于这个阈值。在 CubeMX 的 NVIC 配置里把 DMA 中断的抢占优先级设成 5 或更高数值越大优先级越低确保它不会打断 FreeRTOS 的临界区。另外AD7606 的采集任务建议单独放一个任务用信号量或者任务通知来同步 DMA 完成事件。不要在中断里做数据拼接和电压转换这些耗时操作放到任务里做。中断里只做一件事给出信号量。5.2 从轮询到 DMA 再到双缓冲的演进刚开始调的时候用轮询方式读 SPI 最简单每收一个字节等一次标志位代码好懂但效率低。调通之后换成 DMA 单缓冲CPU 占用大幅下降。如果要做连续采集单缓冲会有问题DMA 正在搬数据的时候你不能去读缓冲区否则读到一半的数据。这时候用双缓冲DMA 搬缓冲区 A 的时候 CPU 处理缓冲区 B搬完了切换。STM32 的 DMA 支持双缓冲模式Double Buffer Mode在 CubeMX 里把 DMA 模式设成 Circular然后使能双缓冲指定两个内存地址硬件自动切换。双缓冲的配置稍微复杂一点但收益很明显。在 200kSPS 连续采集的场景下单缓冲需要 CPU 在 DMA 完成后立即处理数据处理慢了下一轮 DMA 就覆盖了。双缓冲给了 CPU 一整轮的时间来处理从容很多。5.3 校准与误差补偿的实用技巧AD7606 的增益误差和偏移误差在数据手册里有标称值但实际板子上受参考电压和布线影响误差可能更大。如果对精度要求高建议做两点校准输入一个已知的负满量程电压和一个正满量程电压记录读回的码值算出增益和偏移修正系数在软件里补偿。校准的代码很简单float gain (float)(code_pos - code_neg) / (voltage_pos - voltage_neg); float offset code_neg - voltage_neg * gain; // 使用时 float voltage (raw_code - offset) / gain;但要注意校准时的输入电压要用高精度的源万用表至少 5 位半否则校准出来的系数还不如不校准。我一般用 AD7606 内部参考电压2.5V来反推但内部参考的初始精度只有 ±0.1%要求高的话得用外部基准。5.4 常见问题速查表问题排查方向解决方法数据全 0CS/SCLK/复位检查 GPIO 和 SPI 使能数据错位拼接顺序/DMA 长度调整字节顺序和接收长度通道串扰采样保持时序检查 CONVST 脉冲宽度高速丢数DMA 带宽/中断延迟降低 SCLK 或提高 DMA 优先级噪声大参考电压/布线加去耦电容缩短模拟走线温漂大参考电压温漂换低温漂基准源注意AD7606 的模拟输入范围是 ±5V 或 ±10V但输入电压超过 AVCC 或者低于 AGND 会触发内部保护二极管导通长期过压会损坏芯片。在输入端加钳位二极管和限流电阻是标准做法别省这几个元件。6. 个人实操体会与后续扩展方向这套 AD7606 加 STM32CubeMX 的方案我前后迭代了五六版从最开始用 F103 的硬件 SPI 调不通到后来发现是 CPHA 配错再到换成 DMA 双缓冲跑满 200kSPS每一步都是踩坑踩出来的。最大的体会是AD7606 的时序不复杂但容错率低任何一个边沿对不上就读出错数据。示波器或者逻辑分析仪是必备工具没有的话调试效率至少打对折。另一个体会是 CubeMX 生成的代码只是起点SPI 的 DMA 配置、GPIO 的翻转顺序、中断优先级的安排这些都得根据 AD7606 的时序要求手动调整。CubeMX 不会告诉你 CS 要在 DMA 启动前拉低也不会告诉你 BUSY 变低后要等 25ns 再读数据这些细节只能从数据手册里抠或者从踩坑中积累。后续如果要扩展有几个方向可以考虑。一是把采集的数据通过 USB 或者以太网传到上位机用 Python 或者 LabVIEW 做实时波形显示。二是加一片 DAC 做闭环控制AD7606 采集STM32 算 PIDDAC 输出构成一个完整的控制环路。三是用多片 AD7606 级联通过菊花链方式扩展通道数16 通道、24 通道甚至更多适合振动阵列或者电力多路监控的场景。菊花链模式下 DOUTA 接下一片的 DAINSCLK 和 CS 共用数据帧长度翻倍DMA 缓冲区也要相应扩大。最后分享一个小技巧调试阶段在 DMA 完成回调里翻转一个空闲 GPIO用示波器看这个 GPIO 的翻转频率就能算出实际采样率。这个方法比串口打印靠谱不会因为打印耗时影响采集时序。等采样率符合预期了再把翻转 GPIO 的代码去掉换成正式的数据处理逻辑。
返回列表