ARTICLE DETAIL

资讯详情

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

STM32F4驱动ADS131A02与DAC8552:SPI与信号链详解

STM32F4驱动ADS131A02与DAC8552:SPI与信号链详解 简介压缩包面向电子设计中的高精度模拟信号采集与输出场景整合了TI ADS131A0216位Σ-Δ型ADC和DAC8552双通道DAC的驱动代码可用于STM32F4等嵌入式平台帮助开发者解决器件初始化、寄存器配置、数据读取和模拟电压输出等问题。包内共195个文件、约6.34MB以48个.c源文件和47个.h头文件为主体同时包含编译生成的axf、hex、map文件、Keil工程配置uvprojx/uvoptx以及清理脚本可直接用MDK打开工程查看或重新编译。从文件结构中可看出代码基于STM32F4标准外设库编写覆盖定时器、RTC、RCC、ADC、Flash、CAN等模块便于理解ADS131A02与DAC8552在实际MCU工程中的挂接和调用方式。已有908人学习下载。包内保留的调试配置、链接脚本和辅助文件既适合作为驱动移植范本也可作为排查时序与转换异常的参考。1. 别被“高精度ADC”这三个字带偏做信号链的工程师拿到一块 ADS131A02 和 DAC8552 的驱动代码多半会先翻数据手册对着寄存器看半天。这个经验在 STM32F4 上并不成立——真正花时间的从来不是器件本身而是 SPI 总线上的帧格式、DRDY 电平时序以及一批 Keil 工程里特有但没什么人解释的文件。你手上这份ADS131A02和DAC8552.zip就是一个典型的 MDK-ARM 工程ADC.uvguix.Administrator是界面布局缓存ADC.axf是上一次编译好的可执行镜像keilkilll.bat用来清理中间文件真正的驱动代码则混在stm32f4xx_adc.c这类标准外设库文件里。这篇文章把它拆开讲先补两个器件的选型逻辑和时序模型再给出可复现的驱动写法、参数配置和移植到其他板子时的坑适合正在做数据采集、传感回环或闭环输出的开发者。2. ADS131A02 驱动拆开看Σ-Δ ADC 不是普通 SPI 设备2.1 为什么这个场景非选 Σ-Δ 架构不可ADS131A02 是 TI 出的一颗双通道 24 位工程里以 16 位模式使用Σ-Δ 型 ADC最高采样率可达 250 ksps。Σ-Δ 架构的核心思路是“过采样 噪声整形”用远高于奈奎斯特频率的采样率把量化噪声推到高频段再通过数字滤波器把带外噪声滤掉。这套机制带来的直接好处有两个——其一在 50Hz/60Hz 工频干扰较强的工业环境里配合数字滤波器可以做出很高的信噪比其二失调电压和增益漂移远小于逐次逼近型SARADC对于长时间运行的采集系统来说基线漂移是致命的Σ-Δ 在这点上优势明显。但使用 Σ-Δ ADC 要付出的代价是时延。数字滤波器有一个群延迟通常等于滤波器阶数除以输出数据率。如果你把 ADS131A02 用在闭环控制里带宽不能做太宽否则相位裕度会吃掉控制稳定性。所以拿到代码后第一件事不是看 SPI 读写而是确认当前配置下 OSR过采样率对应的输出数据率是多少这直接决定了你的控制环能跑多快。2.2 寄存器和 SPI 帧的前 8 位命令ADS131A02 的 SPI 协议和普通外设不太一样。它的所有读写帧都是 24 位对齐的前 8 位是指令头命令字节后 16 位是数据部分。读取转换结果时主机需要至少连续发送两个 24 位帧第一帧返回的是器件状态和 DRDY 相关的 flag第二帧才是真正的通道数据。命令帧格式拆开看RREG和WREG的编码规则是0000 rrrr读寄存器rrrr是寄存器地址紧接着必须跟一个 dummy 字节0010 rrrr写寄存器数据字节随命令帧后移地址自动步进0xA0进入待机模式0x20等用于配置 CLOCK2 寄存器从这段代码的源码来看驱动里最常见的操作是先写CLOCK寄存器定好时钟分频再写CLOCK2选通内部基准然后等 DRDY 拉低取数据。需要注意芯片默认上电后通道是关闭的必须显式配置PGA和CHANNEL寄存器把对应通道打开否则读回来永远是 0。以下是驱动中常见的寄存器初始化代码static void ADS131A02_WriteReg(uint8_t addr, uint8_t value) { uint8_t buf[3]; buf[0] 0x20 | (addr 0x1F); // WREG 命令头0x20 表示写操作 buf[1] 0x00; // 寄存器数据前的保留字节必须为 0 buf[2] value; // 要写入的寄存器值 GPIO_ResetBits(ADC_CS_GPIO_PORT, ADC_CS_PIN); // 拉低片选开始一次传输 SPI_I2S_SendData(ADC_SPI, buf[0]); while (SPI_I2S_GetFlagStatus(ADC_SPI, SPI_I2S_FLAG_TXE) RESET); SPI_I2S_SendData(ADC_SPI, buf[1]); while (SPI_I2S_GetFlagStatus(ADC_SPI, SPI_I2S_FLAG_TXE) RESET); SPI_I2S_SendData(ADC_SPI, buf[2]); while (SPI_I2S_GetFlagStatus(ADC_SPI, SPI_I2S_FLAG_RXNE) RESET); (void)SPI_I2S_ReceiveData(ADC_SPI); // 丢弃有效位非应答信号 GPIO_SetBits(ADC_CS_GPIO_PORT, ADC_CS_PIN); // 拉高片选 delay_us(1); // 保证相邻帧之间至少一个 SPI 时钟间隔 }这段代码后两处参数值得留意。第一addr 0x1F只取了低 5 位地址ADS131A02 的寄存器空间能覆盖到但如果你的驱动是从 ADS131M02 改过来的地址位宽不同直接套会写到一个不存在的寄存器上。第二delay_us(1)不是随便加的这保证了片选释放后到下一帧开始前有一个时钟周期的死区这是 Σ-Δ ADC 对 SPI 时序的硬性要求STM32F4 的 SPI 外设本身不会替你处理这个时间。读数据帧相对简单向芯片连续发送两个 0x00 帧等第二个帧返回时读取数据即可。若总线上还有别的从机片选必须全程保持低电平中途拉高会导致 ADC 判定当前帧传输中止数据直接丢弃。2.3 初始化顺序与关键参数表初始化时要先复位芯片再写寄存器这和多数 TI ADC 一致。复位后 ADS131A02 会自己拉低 DRDY之后主机才能开始配置否则配置命令可能被忽略。驱动中的初始化顺序通常是拉低 RESET 引脚保持至少 10 个系统时钟周期 → 拉高 → 等待 DRDY 有效 → 写编码器配置。配置时几个关键参数直接决定采集质量寄存器位域默认值建议值说明CLOCKPLL 分频0x06按所需采样率计算决定调制器时钟频率。采样率 fCLKIN / (分频系数 × OSR)CLOCK2基准电压选择0x00内部基准 1.2V 或外部 2.4V外部基准精度更高但会增加引脚开销PGA增益0x00 即 1 倍按信号幅度选择增益过高会让 Σ-Δ 调制器进入饱和输出出现“粘滞”现象CONFIG数据率配置0x00根据带宽选 OSROSR 越大有效位数越高但时延线性增大内部基准电压选 1.2V 时输入范围约 ±1.2V / 增益选 2.4V 输入范围更大但 LSB 变大。若你的信号本来就在 0-5V 区间前端没有电平调理直接进 ADC那 PGA 就得放小且偏置必须抬到共模范围内否则器件工作点就不对了。采样率计算的口诀是输出数据率 调制器时钟频率 / OSR。而调制器频率由 CLOCK 寄存器和外部时钟源决定外部晶振是 8.192MHz 时分频到 2.048MHz 再除以 16 倍 OSR得到的就是 128ksps 附近的输出率具体数值要看代码里实际写入 CLOCK 寄存器的分频值。3. DAC8552 输出链路一个函数把数字量变成 0~5V3.1 双通道输出为什么够用DAC8552 是 TI 的单片双通道 DAC输出范围 0~VREF电源电压即参考电压上限串行接口写入即刷新输出端。和并行 DAC 相比它引脚少、占板面积小、也不存在数据总线竞争问题和 I2C 接口的 DAC 比SPI 的优势是写入速率没有时钟拉伸更新频率可以推得更高。这套 ADCDAC 的组合常见于信号回放、波形发生器和闭环输出场景。ADS131A02 采到的数字量经 MCU 处理后由 DAC8552 重建为模拟电压输出整个过程不需要外部挂运放做 I/V 转换DAC 的输出引脚直连即可。3.2 写寄存器与输出电压的代码实现DAC8552 的 SPI 帧是 24 位第 23~22 位控制通道选择和数据格式第 21~20 位控制增益与输出缓冲低 16 位是数据。数据按二进制补码格式写入满量程对应 0xFFFF零输出对应 0x8000。如果不做编码换算直接用原码会让输出在零点上下跳变。void DAC8552_WriteValue(uint8_t channel, uint16_t value) { uint32_t frame 0; uint16_t tmp 0; // 帧组织bit23~22 通道选择bit21 增益bit20 输出缓冲 // channel 0 0b00channel 1 0b10 tmp (uint16_t)((channel 14) | (0x01 13) | (value 0x1FFF)); frame ((uint32_t)tmp 8) | (value 0xFF); GPIO_ResetBits(DAC_CS_GPIO_PORT, DAC_CS_PIN); // 先发高 16 位再发低 8 位 SPI_I2S_SendData(DAC_SPI, (uint16_t)(frame 16)); while (SPI_I2S_GetFlagStatus(DAC_SPI, SPI_I2S_FLAG_TXE) RESET); SPI_I2S_SendData(DAC_SPI, (uint16_t)(frame 0xFFFF)); while (SPI_I2S_GetFlagStatus(DAC_SPI, SPI_I2S_FLAG_RXNE) RESET); GPIO_SetBits(DAC_CS_GPIO_PORT, DAC_CS_PIN); // SYNC 上升沿时锁存数据因此必须保证发送完再拉高片选 delay_us(1); }这段代码中值得解释的参数有三个。第一channel左移 14 位是因为通道选择位在 bit23~22对应到 16 位暂存器里就是高两位。第二注释里特意留了 bit21 置 1这是增益选择位置 1 表示输出不加缓冲、直接由外部基准驱动。若你的负载阻抗很低必须改这个位启用内置输出缓冲否则带载后电压会跌掉几十毫伏。第三发送顺序是先高 16 位后低 8 位如果把两个半字交换DAC 会解析到错误的帧边界输出直接乱跳。用逻辑分析仪看波形时最明显的特征是 SYNC 保持低电平的时间跨越完整 24 个时钟沿差一位都不行。3.3 与 ADS131A02 的信号链接线在实际工程里ADC 和 DAC 都挂在同一条 SPI 总线上分时使用两套片选。典型的接线方式如下信号ADS131A02DAC8552STM32F4SPI_SCLKSCLKSCLKSPI1_SCKSPI_MOSIDINDINSPI1_MOSI片选CSSYNCPB12 / PB13数据就绪DRDY-外部中断引脚或 GPIO 查询把 DRDY 接到 STM32F4 的外部中断引脚上比在主循环里轮询要省 CPU。AD 转换完成拉低中断ISR 里读走数据、计算、再写 DAC整个环路的延迟只有软件执行时间和 DAC 的建立时间。DAC8552 的典型建立时间在微秒级和 ADC 的数据率不在一个量级上不会成为瓶颈。4. STM32F4 工程移植:SPI 复用、时钟方向和那些隐藏文件4.1 与 STM32 标准外设库对接的 SPI 配置这个工程从文件列表能看出来用的是 ST 标准外设库。stm32f4xx_spi.c属于外设库源码stm32f4xx_gpio.c对应管脚配置。ADC 和 DAC 共用 SPI1常用的引脚是 PA5(SCK)、PA7(MOSI)片选分别接普通 GPIO。初始化时需注意 SPI 的时钟极性和相位ADS131A02 和 DAC8552 都要求 SPI Mode 1——即 CPOL0、CPHA1字符长 8 位。若直接用默认的 Mode 0ADC 的命令帧能发出去但返回的数据会整体错位一位。void SPI1_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE); RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1, ENABLE); GPIO_InitStructure.GPIO_Pin GPIO_Pin_5 | GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode GPIO_Mode_AF; GPIO_InitStructure.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType GPIO_OType_PP; GPIO_InitStructure.GPIO_PuPd GPIO_PuPd_NOPULL; GPIO_Init(GPIOA, GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource5, GPIO_AF_SPI1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource7, GPIO_AF_SPI1); SPI_InitStructure.SPI_Direction SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode SPI_Mode_Master; SPI_InitStructure.SPI_DataSize SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL SPI_CPOL_Low; SPI_InitStructure.SPI_CPHA SPI_CPHA_2Edge; SPI_InitStructure.SPI_NSS SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler SPI_BaudRatePrescaler_16; SPI_InitStructure.SPI_FirstBit SPI_FirstBit_MSB; SPI_Init(SPI1, SPI_InitStructure); SPI_Cmd(SPI1, ENABLE); }SPI 时钟分频选择了SPI_BaudRatePrescaler_16即主频 168MHz 时 SPI 时钟为 10.5MHz。ADS131A02 手册标识 SPI 最高可达 25MHzDAC8552 稍低一些。这里刻意没有往极限上跑原因是两个器件共用一条总线时钟太快时帧间干扰会把 DAC 的 SYNC 边沿弄脏。需要提速时优先把 DAC8552 放到独立的 SPI 外设上而不是单纯调大分频系数。若你的板子上这两个器件挂在 SPI2/SPI3 上只需把 GPIO 复用配置和 SPI 外设名替换掉代码逻辑不用动。但要注意SPI2 和 SPI3 挂在 APB1 总线上时钟上限是 42MHz分频后实际速率比 SPI1 低一截。4.2 Keil 工程里那些不被解释的文件打开这个 zip 包会看到一堆名字看起来无足轻重的文件实际上它们各有用途。ADC.uvguix.Administrator是 uVision 图形界面的布局配置文件保存了上次打开工程的窗口位置和断点状态你的用户名不是 Administrator 时Keil 会忽略它并重新生成。ADC.axf是编译链接后生成的可执行镜像里面带了调试符号烧录用的是 axf 提取出的 hex/bin如果在没源码的机器上想直接看固件逻辑可以用fromelf工具反汇编。kernel相关的keilkilll.bat是一个批处理脚本双击它会删除当前目录下的中间文件只保留源码和工程文件方便重新完整编译也可以用它在压缩包里清掉自己的编译缓存防止发给别人时打包进无关的 .o 文件。stm32f4xx_adc.c这类文件是标准外设库的驱动源码和题目里主角芯片无关主要用来配置 STM32 自家的 ADC不少初学者会在这里面翻找 ADS131A02 的寄存器定义其实搜索的头绪完全错了。4.3 三类实测问题与定位方法移植过程中最容易踩的是三个点。第一读回的数据全是 0x7FFFFF 或 0x800000 这种中间值基本可以断定 SPI 的 CPOL/CPHA 配错了数据在时钟沿采样位置不对输出经过滤波器落到中点。第二DAC8552 的某个通道输出始终为 0CS 波形测量正常多半是 SYNC 拉低后总线字节序反了需要对照逻辑分析仪检查 24 位帧里的高低位顺序。第三ADC 的 DRDY 引脚长时间无下降沿先查复位引脚的时序再查 CLOCK 寄存器是否写了有效分频值最后考虑外部时钟源是否起振。这三点排查顺序按成本从低到高排列先看波形再改代码别一上来就把驱动全部推翻重写。5. 进阶用一条信号回环验证 ADC 与 DAC 的配合单独验证两个器件能读写并不难难的是确认整条链路是通的。这里有一个非常实用的自测方法把 ADC 的某一通道输入短接到 DAC8552 的输出端程序里不断读取转换结果再做一次数字映射写回 DAC。若链路无误差示波器上看到的 DAC 输出波形应该与原输入信号波形幅值和形状一致。这样做的价值在于ADC 的滤波器和 DAC 的建立时间会同时体现到波形上任何一端配置错误都会让波形形态失真。void SignalLoop_Test(void) { int32_t adc_value 0; uint16_t dac_value 0; // 等待 DRDY 引脚拉低表示新的转换结果已经就绪 if (GPIO_ReadInputDataBit(DRDY_PORT, DRDY_PIN) RESET) { adc_value ADS131A02_ReadChannel(0); // 读取通道 0 原始值 // 符号位直接保留幅度做 1:1 映射到 DAC 输出 // 0xFFFF 对应 VREF0x8000 对应 0V dac_value (uint16_t)((adc_value 8) 0x8000); DAC8552_WriteValue(0, dac_value); // 写回到 DAC 通道 0 } }这段回环代码中有两个重要参数。adc_value 8是把 16 位 ADC 结果压缩到 DAC 的 8 位有效数据位去掉低 8 位噪声代价是分辨率降低但信号完整性验证阶段完全够用。 0x8000是偏移量调整把 ADC 双极性输出转换到 DAC 的单极性输出范围内这样才能保证零点处两边的电压是一一对应的。回环测试跑通后建议再做一次校准给 ADC 输入端加一个标准电压比如 1.000V然后调整代码里的增益系数使得 DAC 输出电压等于输入电压。记录此时 ADC 读数对应 DAC 编码的比值这就是整套信号的增益误差。把误差值和失调值固化在配置结构体里每块板子生产时单独校准一次比靠元器件精度硬扛要稳定不少。本文还有配套的精品资源点击获取
返回列表