尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

ADS131A02与DAC8552共享SPI总线的模拟信号链驱动设计

发布时间:2026/9/24 22:16:29

资讯中心
01
ARTICLE

ADS131A02与DAC8552共享SPI总线的模拟信号链驱动设计

ADS131A02与DAC8552共享SPI总线的模拟信号链驱动设计
简介面向嵌入式开发与高精度测量应用资源打包了TI公司ADS131A02 16位Σ-Δ型ADC与DAC8552双通道DAC的完整驱动代码适合需要实现高精度模拟信号采集与输出的电子设计项目。代码基于STM32F4平台提供了ADC采样率配置、参考电压设置、读取转换结果以及DAC初始化、输出电压控制等核心函数方便开发者快速集成。资源共195个文件以C源码、H头文件为主包含STM32标准外设库、编译生成的目标文件、调试配置及示例工程压缩包整体约6.34MB目录结构清晰便于直接查阅。目前已有909人学习下载适合从事医疗、工业控制、数据采集系统的开发者参考使用。通过阅读这些代码可以掌握两器件与MCU的通信时序、寄存器配置和数据处理流程减少自行调试时间。无论是用于遥测系统、自动化仪表还是教学实验这一资源都能提供有价值的参考。1. 这个压缩包想要拼起一条完整模拟信号链ADS131A02读进来DAC8552送出去打开这个压缩包的人大概率正站在一块模拟采集板的调试现场ADS131A02要把传感器信号读进来DAC8552要把计算结果送出去。很多工程师第一次看到这两个型号放在一起会以为是两个独立驱动实际上它们拼起来是一条完整的模拟信号链24 位 ADC 负责把微弱电压变成数字码16 位双通道 DAC 负责把数字设定值变成可输出的模拟电压。这套组合常见于测控仪表、电源闭环、电池化成设备、HIL 仿真板卡。适合谁正在调多通道高精度采集、又不想在 DAC 和 ADC 两条链路上来回折腾的人。需要先说透一件事这两个芯片单独跑都不难难的是它们共享同一根 SPI 总线、同一颗基准、同一块地平面时怎么把干扰和时序理顺。2. 先搞懂两个芯片的角色为什么是 ADS131A02 配 DAC8552而不是一颗 SOC2.1 ADS131A02 强在哪里Delta-Sigma 架构与 24 位的含金量ADS131A02 是 TI 的 24 位双通道 Delta-Sigma ADC。说它“含金量高”不是因为它标称 24 位分辨率而是因为它在低数据速率下能把有效位数做到 20 位以上。常用的 MCU 内置 ADC 大多是 12~16 位逐次逼近型实际有效位数往往只有 12 位左右做一次 16 位精度测量都要靠过采样硬凑。ADS131A02 这类 Delta-Sigma 芯片靠的是过采样加数字滤波把量化噪声推到了测量带宽之外在直流和低频场景下信噪比优势非常明显。我在选型时最看重它的三件事双通道同步采样、内置可编程增益放大器以及可配置的过采样率。双通道同步意味着两个通道由同一个时钟驱动通道间相位差极小做功率计算或两路传感器差值计算时不用另外对齐时间戳。PGA 则让芯片能直接接毫伏级传感器信号不需要在外部堆仪表放大器。OSR 参数决定了数据速率和噪声的权衡OSR 越高数据速率越低但噪声越小这就是“想要多少位有效数字先想清楚你要多快的刷新率”。2.2 DAC8552 的定位双通道、16 位、单极性输出但帧格式有讲究DAC8552 是 16 位双通道、单极性输出的 SPI 接口 DAC内置输出缓冲可以直接驱动后级电路。它不算快也不算贵但胜在稳定16 位分辨率在 0~2.5V 量程下一个 LSB 约等于 38uV对大多数闭环控制场景已经够用。它的数据写入格式是 24 位一帧其中高 2 位是控制位、中间 16 位是数据、低 6 位必须写 0。很多人第一次写驱动时想当然地认为是 16 位一帧结果低 6 位带了随机电平输出完全对不上。DAC8552 的输出是单极性的输出范围从 0 到基准电压。这一点在选型时要提前想清楚如果你的系统需要输出正负电压就必须在 DAC 后面加偏置电路或运放。我通常会把 DAC8552 的数据手册页码贴到代码注释里因为控制位的含义和输出极性这类细节总会有人几个月后再看时记混。2.3 用同一根 SPI 串起两片芯片片选分工与模式统一两个芯片挂在同一根 SPI 总线上是这套方案里最需要提前规划的部分。ADS131A02 有四线 SPISCLK、SDI、SDO、CSDAC8552 是写数据为主SCLK、DIN、SYNC它没有数据输出脚。共用总线的关键是片选分开命令帧分开。信号ADS131A02 引脚DAC8552 引脚MCU 侧时钟SCLKSCLK共用 SPI SCLK数据输入SDIDIN共用 SPI MOSI数据输出SDO无SPI MISO只接 ADC片选CSSYNC两个独立 GPIO状态信号DRDY无独立 GPIO可接中断SPI 模式我一般固定用 Mode 1也就是 CPOL0、CPHA1。ADS131A02 数据手册支持 Mode 1 或 Mode 2DAC8552 的数据在 SCLK 下降沿被锁存Mode 1 刚好让主机在上升沿更新数据、下降沿输出稳定电平两个芯片不需要切换模式。时钟速度建议先从 1MHz 起步跑通后再往上加很多所谓“通信不稳定”其实是上来就把频率拉满导致的。3. 把 ADS131A02 的初始化写成 C 函数上电时序、寄存器自检和固定帧读取3.1 初始化前先处理上电时序RESET 不是随便拉一下就行ADS131A02 的复位过程直接决定了 DRDY 引脚能不能正常出来。常见做法是先让时钟稳定再拉低 RESET保持一段时间然后释放之后等待芯片完成内部初始化。我用示波器踩过坑MCU 和 ADC 一起上电时RESET 引脚如果没被明确拉低而是靠上电自然复位芯片可能进入不确定状态表现为 DRDY 一直不拉低、读寄存器全是 0xFF。正确的上电顺序是先让 CLKIN 时钟起振并稳定然后拉低 RESET 至少 10ms再拉高等待至少 20ms。释放 RESET 后不要急着配置寄存器先读一次状态类寄存器确认 SPI 通信正常。这一步像开机自检能省掉后面至少半小时的排查时间。我把这段顺序写进驱动注释里防止换人维护时把顺序打乱。3.2 寄存器配置与自校准可直接抄的初始化函数ADS131A02 的寄存器是 16 位宽操作命令由高 2 位操作码和低 5 位寄存器地址组成。写寄存器命令是 0x60 加上地址读寄存器命令是 0x20 加上地址。下面这段是我惯用的初始化骨架寄存器地址以你手上数据手册的映射表为准代码里用宏占位提醒核对/* ADS131A02 寄存器操作命令字节 操作码 | 地址 */ #define A2_CMD_WREG 0x60 /* 写寄存器 */ #define A2_CMD_RREG 0x20 /* 读寄存器 */ /* 寄存器地址占位填你手册里对应的地址别凭记忆写 */ #define A2_REG_CLK1 0x00 #define A2_REG_CLK2 0x01 #define A2_REG_ADC1 0x02 #define A2_REG_CH1 0x04 #define A2_REG_CH2 0x05 static void a2_cs_low(void) { /* 拉低 CS */ } static void a2_cs_high(void) { /* 拉高 CS */ } static void a2_write_reg(uint8_t addr, uint16_t val) { uint8_t cmd A2_CMD_WREG | (addr 0x1F); a2_cs_low(); spi_write_byte(cmd); /* 写命令 地址 */ spi_write_byte((val 8) 0xFF);/* 数据高字节 */ spi_write_byte(val 0xFF); /* 数据低字节 */ a2_cs_high(); } static uint16_t a2_read_reg(uint8_t addr) { uint16_t val; uint8_t cmd A2_CMD_RREG | (addr 0x1F); a2_cs_low(); spi_write_byte(cmd); spi_write_byte(0x00); /* 空时钟等芯片把数据送出来 */ val (uint16_t)spi_read_byte() 8; val | spi_read_byte(); a2_cs_high(); return val; } void ads131a02_init(void) { /* 1. 时钟就绪后复位 */ clkin_on(); delay_ms(5); a2_reset_pin(0); delay_ms(10); a2_reset_pin(1); delay_ms(20); /* 2. 读寄存器自检返回全 0xFF 说明 SCLK 极性或复位有问题 */ if (a2_read_reg(A2_REG_CLK1) 0xFFFF) { /* 自检失败先检查 SPI 波形别继续往下写 */ return; } /* 3. CLK2OSR 先选大的把数据速率压到几 kSPS */ a2_write_reg(A2_REG_CLK2, 0x0000); /* 4. 两个通道 PGA 先设 1 倍跑通后再调增益 */ a2_write_reg(A2_REG_CH1, 0x0001); a2_write_reg(A2_REG_CH2, 0x0001); /* 5. 使能两个 ADC 通道 */ a2_write_reg(A2_REG_ADC1, 0x0011); }这段代码的逻辑是先确认通信再配时钟再配输入通道最后使能 ADC。顺序反了容易出玄学问题比如寄存器写进去了但数据模式起不来。OSR 先选大值是我的习惯因为低数据速率下芯片有效位数更高调试时看波形更干净等信号链逻辑正确了再逐步提高速率。PGA 从 1 倍起步避免一开始增益太大、微弱干扰被放大到不可收拾。3.3 固定帧读取数据拼出两个通道的 24 位有符号数初始化完成之后DRDY 引脚会按数据速率周期性地拉低提示 MCU 去读转换结果。ADS131A02 的数据输出是固定帧格式一次连续读回一帧帧里面按位置放着状态字和两个通道的转换码。每个通道的转换码是 24 位补码需要手动扩展到 32 位有符号数/* 把 24 位补码扩展成 int32_t */ static int32_t a2_sign_extend(uint8_t hi, uint8_t mid, uint8_t lo) { int32_t v ((int32_t)hi 16) | ((int32_t)mid 8) | lo; return (v 0x800000) ? (v | ~0xFFFFFF) : v; } void a2_read_channels(int32_t *ch1, int32_t *ch2) { uint8_t f[8]; a2_cs_low(); for (int i 0; i 8; i) f[i] spi_read_write_byte(0x00); a2_cs_high(); /* 帧内偏移以手册帧图为准这里假设状态字在前、CH1/CH2 依次在后 */ *ch1 a2_sign_extend(f[2], f[3], f[4]); *ch2 a2_sign_extend(f[5], f[6], f[7]); }为什么必须做符号扩展因为 24 位补码的最高位是符号位如果只赋值给 int32_t不做高位填充负数会被当成正数。转换成物理量时用转换码乘以基准电压再除以 2^23再除以 PGA 增益float volt_ch1 (float)(*ch1) * 2.5f / 8388608.0f / pga_gain;这里的 2.5V 是 ADC 内部基准电压除以 2^23 是因为 24 位补码的满量程对应正负基准电压。如果你用了外部基准换成实际基准值就行。PGA 增益变量必须和寄存器里的配置保持一致否则电压值会成倍地错。4. DAC8552 在同一根 SPI 总线上做双路输出24 位帧计算与上电防毛刺4.1 24 位帧只有 18 位有效控制位和低 6 位最容易被忽略DAC8552 的每次写入是一帧 24 位数据结构是固定的高 2 位是控制位中间 16 位是 DAC 数据低 6 位必须写 0。控制位决定这次数据写到哪个通道控制位目标通道00写 DAC-A01写 DAC-B10同时写 DAC-A 和 DAC-B11掉电控制日常不碰很多人第一次写驱动时直接把 16 位数据放进一个 24 位变量然后发送忘了把数据左移 6 位结果低 6 位的内容窜到了数据区输出码和预期差了一大截。反过来如果控制位设置成了 11DAC 会进入掉电模式输出端呈现高阻万用表量不到电压容易被误判成芯片烧了。我一般会在驱动里把控制位用宏定义写清楚而不是在代码里散落裸数字。4.2 一段模拟 SPI 的 DAC 写函数贴寄存器又贴总线DAC8552 是纯写设备不占用 MISO。下面的函数用软件模拟 SPI 完成 24 位帧发送方便移植到任意平台/* ch 0 写 DAC-Ach 1 写 DAC-Bch 2 两路同步写 */ void dac8552_write(uint8_t ch, uint16_t code) { uint32_t frame; switch (ch) { case 0: frame 0x000000UL; break; /* 控制位 00 */ case 1: frame 0x400000UL; break; /* 控制位 01 */ default: frame 0x800000UL; break; /* 控制位 10 */ } frame | ((uint32_t)code) 6; /* 16 位数据左移到 21:6 */ dac_sync_low(); for (int i 23; i 0; i--) { dac_sdin((frame i) 0x01); dac_sclk_high(); dac_sclk_low(); } dac_sync_high(); /* SYNC 上升沿锁存数据 */ }这个函数的逻辑是按通道构造控制位把 16 位数据左移 6 位放进帧中间然后从最高位开始逐位输出。SCLK 的低电平和高电平之间不要插入延时让 GPIO 翻转速度自然决定时钟频率一般在 1MHz 以下稳定优先。数据在 SYNC 拉高的那一瞬间被 DAC 锁存所以发送过程中 SYNC 必须全程保持低电平发送完再拉高。用硬件 SPI 也能做核心逻辑不变只是把逐位循环换成 3 字节发送void dac8552_write_hw(uint8_t ch, uint16_t code) { uint8_t buf[3]; uint32_t frame; frame (ch 0) ? 0x000000UL : (ch 1) ? 0x400000UL : 0x800000UL; frame | ((uint32_t)code) 6; buf[0] (frame 16) 0xFF; buf[1] (frame 8) 0xFF; buf[2] frame 0xFF; dac_sync_low(); spi_write_bytes(buf, 3); dac_sync_high(); }参数上要注意 code 的范围是 0 到 655350 对应 0V65535 对应基准电压。如果要从电压值换算成码uint16_t dac_voltage_to_code(float volt, float vref) { if (volt 0) volt 0; if (volt vref) volt vref; return (uint16_t)(volt / vref * 65535.0f 0.5f); }这里 0.5f 是四舍五入抹掉浮点计算的小数尾巴别小看这半个 LSB在闭环系统里累积起来会变成静态误差。4.3 上电毛刺与共用基准为什么第一次上电 DAC 就冒尖DAC8552 最常见的上电翻车现场是MCU 程序还没跑起来DAC 输出端已经出现一个电压尖峰。原因不是 DAC 坏了而是 MCU 的 GPIO 在复位阶段处于高阻态DIN、SCLK、SYNC 三个引脚的电平完全随机DAC 把这段随机电平当成了一帧数据锁存进去了。解决这个问题要同时做三件事硬件上在 DAC 输出端串一个 1kΩ 电阻对地、并一个 1nF 电容把毛刺削掉软件上在系统启动的最早期先把 DIN 拉低、SCLK 拉低、SYNC 拉高再初始化 SPI 外设然后上电后第一件事就是给两个通道各写一次 0x0000把输出明确拉到 0V。这套方案还有一个容易忽略的点DAC8552 和 ADS131A02 共用基准电压时基准芯片必须先稳定DAC 再输出。如果基准还在爬坡就把 DAC 的满量程写进去输出会跟着基准一起爬下游电路可能因此误动作。我的习惯是基准芯片输出端加一个运放缓冲再分别供给 ADC 和 DAC两个芯片的瞬态电流就不会互相拉扯。5. 双芯片联调排查4 个让人翻车的现象按现象-原因-解决逐条拆5.1 DRDY 一直不拉低ADC 像黑匣子一样没有响应现象RESET 拉高后等了很久DRDY 引脚一直保持高电平用示波器量不到任何周期信号。读寄存器返回全 0xFF 或全 0x00芯片完全不回应。原因分三类一是 CLKIN 根本没起振或者时钟频率和手册要求不符芯片内部逻辑停摆二是 SCLK 空闲电平反了SPI 模式配置成了 Mode 0 或 Mode 3芯片收到的指令全是乱码三是 RESET 引脚时序不对上电后一直悬空或拉高芯片进入不了正常模式。解决先用示波器确认 CLKIN 引脚有波形再确认 RESET 曾经被拉低过最后把 SPI 统一切到 Mode 1。这三步按顺序查九成问题都在这三处。我还遇到过一种情况MCU 的 SDI 引脚初始化晚于 CS 拉低芯片在 CS 低电平期间读到了浮空电平把命令字节当成噪声吞掉了。解决方法是先配好所有 GPIO 方向和初始电平再拉低 CS。5.2 ADC 读数随 DAC 输出一起跳动信号链互相串扰现象单独测 ADC 时最后几位稳定一旦 DAC8552 输出变化ADC 读数立刻开始跳跳变幅度和 DAC 输出的电压幅度相关。原因最典型的是共地阻抗问题。DAC 的负载电流流过了 ADC 的模拟地在地线上产生压降这个压降直接叠加到 ADC 的输入信号上。另外如果 DAC8552 和 ADS131A02 共用的基准没有加去耦DAC 切换瞬间的电流会把基准电压拉偏ADC 的参考也随之抖动。解决把模拟地和数字地单点汇接DAC 输出端加 RC 滤波基准引脚加 10uF 和 0.1uF 两级去耦。如果还压不住把 DAC 的电源用磁珠单独隔离。注意一个原则地不要断开成两个孤岛而是用一根短粗的走线单点连接电源和地都别在芯片之间绕圈。5.3 SPI 波形示波器看着正常寄存器读回来却不对现象逻辑分析仪抓到的波形命令字节、寄存器地址、数据字节都符合预期片选时序也对着但读回的数据就是不对或者写进去再读出来不一致。原因最常见的是字节之间缺少间隔。很多 SPI 主机的 FIFO 连续发送字节与字节之间几乎无缝连接ADS131A02 对片选低电平期间的数据字节间隔有一定要求间隔太短时芯片会把前后两个字节当成一个整体解析。另一种情况是相位配置差了一个边沿示波器上电压波形看起来一样但采样点恰好落在数据翻转沿上。解决在每发送一个字节后加一个极小延时比如delay_us(1)把字节边界隔开。同时把 SCLK 频率降到 1MHz 再试排除高频振铃干扰。这套“降速 加间隔”的土办法能解决至少一半的 SPI 疑难杂症。5.4 DAC 自然码正确、电压对不上问题出在 DAC8552 外面那一圈现象写 0x8000 之后DAC 输出按比例应该是基准电压的一半但万用表量出来偏了百分之几而且不同通道偏差方向还不一样。原因DAC 本身没问题问题在基准电压不准或者输出端负载太重。DAC8552 的输出是单极性Vout Vref × code / 65536如果 Vref 实际是 2.48V 而不是 2.5V全量程都会跟着偏。还有一种情况是 DAC 输出引脚接了太低阻值的负载内置输出缓冲驱动不动电压被拉低。解决先把基准电压用万用表实测再按实测值回算 code。如果基准没问题检查 DAC 输出端对地电阻别低于 10kΩ。最后确认一点DAC8552 是单极性输出别指望它输出负压量出 0V 不一定是芯片坏了可能数据本来就是 0。6. 信号链闭环验证用零点、增益和方波响应给这套方案收尾6.1 三步完成零点与增益标定信号链焊完、驱动跑通先别急着追求精度按三步把它“驯服”。第一步把 ADC 输入短接到模拟地连续读 100 次转换码取平均得到零点偏移值 offset。第二步给 ADC 输入一个已知电压比如 1.000V读码取平均得到该点的转换码 code算出增益系数 gain (code - offset) / 1.0V。第三步把这两个参数固定下来后续所有测量用同一套公式换算float y (float)(adc_code - offset) / gain;6.2 用 DAC8552 的输出反接回 ADS131A02测闭环线性度标定完两端把 DAC8552 的 DAC-A 输出经过分压和 RC 滤波后接到 ADS131A02 的通道输入端。DAC 从 0 码开始每次步进 4096直到满码每个点记录 ADC 的读数。理想情况下ADC 读数应该对应 DAC 输出码的线性比例关系。如果中间某个区间的跳变量超过了 1 个 LSB回到第 5 章的供电和共地检查项去查。这一步不需要高精度仪器两个芯片互相验证能暴露大部分接线和配置问题。6.3 留一个“寄存器快照”的调试习惯我做这类双芯片联调时最后一个固定动作是在代码里留一个全局结构体每次写完寄存器、每次读完一帧数据都把原始值和解析值存进去。遇到问题时把这个结构体原样打印出来比盯着示波器猜要快得多。这个习惯救过我不止一次有一次 DAC 输出总差半个码打印出来发现是控制位被中断服务函数里的变量覆盖了这类问题光看波形是看不出来的。希望这个习惯也能帮到你少走一次弯路。本文还有配套的精品资源点击获取
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。