简介:面向STM32微控制器的ADS8688模数转换芯片驱动代码包,重点解决八通道十六位高速采集与串行外设接口通信的整合问题。代码采用C语言实现,覆盖串行接口初始化、片选控制、内部寄存器配置、通道切换、启动转换及结果读取等关键环节,兼容硬件抽象层与低层库两种开发方式,也方便在裸机工程中直接调用。资源包共包含两个文件,即源程序文件与头文件,压缩后整体大小约3KB,代码量精炼、接口划分清晰,适合有一定嵌入式基础的中高级开发者快速移植到数据采集项目。已有五千五百七十七人学习下载,说明该驱动方案受到较多工程师关注和验证。压缩包内附带的底层驱动涵盖串行时钟极性与相位设置、多通道轮询采样、转换完成标志判断等实用细节,配合芯片手册可以更快掌握ADS8688的寄存器读写流程,有效减少高速模拟量采集模块的调试时间。 做工业采集项目时,我最常被问到的一类问题就是:“多通道、高精度ADC到底怎么选?”如果预算没有紧张到必须用国产替代,ADS8688基本是闭眼入的方案之一。这颗芯片是TI的16位、8通道、500kSPS逐次逼近型ADC,内置可编程增益放大器和4.096V基准,单5V供电就能直接采集±10.24V的工业信号,连前端调理运放都省了。这篇文章我就把自己在STM32上驱动ADS8688的完整过程整理出来,包括硬件连接、SPI时序、寄存器配置和代码实现,以及调试时踩过的几个典型的坑。想抄作业的可以直接看第三节的代码,想搞懂原理的建议从头读。
1. 项目背景与整体设计思路
1.1 ADS8688核心特性梳理
先说清楚这颗芯片到底解决了什么问题。我们平时测工业现场信号,比如压力变送器的4-20mA采样电阻上的电压、伺服驱动器的模拟量输出、或者PLC的0-10V控制信号,量程经常是±10V、±5V这个级别。普通STM32内置ADC输入范围只有0-3.3V,而且12位分辨率在弱信号场景下明显不够用。以往的做法是先用运放做电平平移和衰减,再进MCU的ADC,电路复杂不说,温漂和噪声指标也很难控制。
ADS8688把前端信号链几乎全部集成进去了。它的输入范围可以通过寄存器配置,支持±10.24V、±5.12V、±2.56V、±1.024V、0-10.24V、0-5.12V、0-2.56V共7种量程;输入阻抗高达1MΩ,直接并联在信号源上基本不影响原电路;内部基准不用外挂,省了一路参考电压的布局空间。而且8个通道全部是单端输入,多路采集时不需要外部多路开关,一颗芯片搞定。
1.2 方案选型:SPI接口和单电源供电是关键
很多新手第一次看到ADS8688的供电要求会被吓到——AVDD是5V,但DVDD可以接1.8V到5V。这说明它的数字接口电平是灵活可配的,和3.3V的STM32直接相连完全没问题。接口方面用的是标准SPI,最高时钟频率手册标注得很高,实际工程中我习惯控制在10MHz以内,留足时序余量。
选它而不是ADS1256这类Δ-Σ型ADC,主要看应用场景。ADS1256是24位精度,但采样率到了高位数时只有几十SPS,而且需要外部基准、输入范围也需要自己调。ADS8688是SAR型ADC,没有流水线延迟,500kSPS的采样率在电机控制、电力监控、振动采集这类需要一定实时性的场景里明显更合适。16位分辨率配合可编程增益,应对绝大多数工业信号已经足够了。
1.3 适合谁来参考
这篇内容适合有几类人阅读:第一类是在做数据采集卡、PLC模拟量模块、工业变送器校准设备的人,需要稳定可靠的多通道ADC方案;第二类是学校实验室做仪表类课题的学生,STM32加ADS8688是比较经典的组合;第三类是纯粹想搞清楚SPI从设备怎么驱动的入门者,可以先拿这颗芯片练手,寄存器不算复杂,时序也有代表性。
2. 硬件连接与接口设计要点
2.1 ADS8688与STM32引脚连接
ADS8688的封装是TSSOP-24,引脚不算密,手工焊接有点基础就能搞定。我常用的接法如下:
| STM32引脚 | ADS8688引脚 | 说明 |
|---|---|---|
| PA5 / SPI1_SCK | SCLK | SPI时钟 |
| PA6 / SPI1_MISO | SDO | 从机数据输出,接单片机MISO |
| PA7 / SPI1_MOSI | SDI | 从机数据输入,接单片机MOSI |
| PA4 / GPIO | CS | 片选信号,软件控制 |
| PB0 / GPIO | RST | 硬件复位,低有效 |
| — | AVDD | 接5V |
| — | DVDD | 接3.3V |
| — | REFIO/REFCAP | 基准引脚,按手册接旁路电容 |
这里有个容易犯的错:SDO和SDI不要接反。ADS8688作为SPI从机,SDO是它的输出,必须连到STM32的MISO;SDI是输入,连到MOSI。我第一次画板时把这两个信号对调了,结果读回来的数据全是0xFF,排查了半天。
2.2 电源与去耦细节
AVDD建议用独立的5V LDO供电,不要直接从电机驱动板或开关电源上拉,否则转换结果底噪会明显增大。我实测过,AVDD纹波从50mV降到10mV之后,读取到的数据跳动范围能从±12个LSB降到±3个LSB以内。去耦电容在AVDD引脚附近放一个10μF钽电容加一个0.1μF陶瓷电容并联,REFCAP基准输出电容用2.2μF以上的X7R陶瓷电容,这个值不能太小,否则基准建立时间不够,上电后前几次转换结果会偏。
数字部分DVDD接3.3V,和STM32同一路电源即可。ADS8688内部有电平转换电路,所以不用额外加电平转换芯片。如果系统里还有其它3.3V外设,建议在DVDD入口处也加一颗0.1μF去耦。
2.3 SPI模式与接线注意事项
ADS8688支持的是SPI Mode 1,也就是CPOL=0、CPHA=1,数据在时钟上升沿准备、下降沿采样。
我见过不少人在HAL库配置里把模式搞反,配成了Mode 0,读回来的数据偶尔正确偶尔错乱。原因就是采样沿和芯片输出时序对不上。下面这段是标准库的SPI配置片段:
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; // 模式1 SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;接线方面注意SPI信号线尽量短,尤其是SCLK,不要在线上串太大的电阻。如果SCLK频率超过5MHz,串接的RC滤波电容会直接把时钟沿变缓,导致数据采样错位。
3. STM32驱动代码实现
3.1 驱动代码整体结构
ADS8688的驱动我习惯拆成三个层次:底层是SPI读写,中间层是寄存器配置,上层是通道读取和数据处理。这样的好处是换MCU平台时只改底层,上面的业务逻辑不用动。
ADS8688的命令帧是16位,由8位命令/地址和8位数据组成。写寄存器时SPI连续发送两个字节:第一个字节是寄存器地址加读写标志,第二个字节是要写入的数据。读转换结果时,需要先发送读命令,然后在后续的时钟周期内接收数据输出。具体细节以数据手册的时序图为准。
3.2 驱动代码完整实现
下面是完整的驱动头文件,主要暴露初始化和读通道接口:
// ads8688.h #ifndef __ADS8688_H #define __ADS8688_H #include "stm32f1xx_hal.h" // ADC输入范围定义,对应数据手册范围寄存器 typedef enum { ADS8688_RANGE_PM_10_24V = 0x00, ADS8688_RANGE_PM_5_12V = 0x01, ADS8688_RANGE_PM_2_56V = 0x02, ADS8688_RANGE_PM_1_024V = 0x03, ADS8688_RANGE_0_10_24V = 0x05, ADS8688_RANGE_0_5_12V = 0x06, ADS8688_RANGE_0_2_56V = 0x07 } ADS8688_Range; uint8_t ADS8688_Init(SPI_HandleTypeDef *hspi); void ADS8688_SetRange(ADS8688_Range range); int16_t ADS8688_ReadChannel(uint8_t channel); float ADS8688_ReadVoltage(uint8_t channel); #endif驱动源文件的重点在初始化函数。上电后必须给RST引脚一个低脉冲,然后等待至少100μs让内部基准稳定,再写配置寄存器。这一步不能省,直接上电就读数据,经常能读到0x0000或者0xFFFF这种异常值。
// ads8688.c #include "ads8688.h" #include "gpio.h" static SPI_HandleTypeDef *ads8688_spi; static ADS8688_Range current_range; static void ADS8688_WriteReg(uint8_t reg, uint8_t data) { uint8_t tx[2]; tx[0] = reg; tx[1] = data; HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(ads8688_spi, tx, 2, 100); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); } uint8_t ADS8688_Init(SPI_HandleTypeDef *hspi) { ads8688_spi = hspi; // 硬件复位 HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_RESET); HAL_Delay(1); HAL_GPIO_WritePin(RST_GPIO_Port, RST_Pin, GPIO_PIN_SET); HAL_Delay(10); // 配置输入范围为默认±10.24V ADS8688_WriteReg(0x0A, 0x00); ADS8688_WriteReg(0x0B, 0x00); current_range = ADS8688_RANGE_PM_10_24V; return 0; }读通道函数需要注意一点:ADS8688的读时序是命令帧和结果帧连续完成,CS在整个过程中一直保持低电平。连续两次SPI传输之间不要释放CS,否则芯片会认为是一次新的命令,读出来的数据就错位了。我建议把两字节命令和两字节读取放在同一个CS低电平区间内:
#define ADS8688_CMD_READ 0x20 // 读转换结果命令,具体按手册定义 int16_t ADS8688_ReadChannel(uint8_t channel) { uint8_t tx[2]; uint8_t rx[2] = {0, 0}; uint16_t raw = 0; if (channel > 7) channel = 7; // 命令帧:读命令 + 通道地址 tx[0] = ADS8688_CMD_READ | ((channel & 0x07) << 1); tx[1] = 0x00; HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_RESET); HAL_SPI_Transmit(ads8688_spi, tx, 2, 100); // 紧跟着读取16位转换结果,期间CS保持低电平 HAL_SPI_Receive(ads8688_spi, rx, 2, 100); HAL_GPIO_WritePin(CS_GPIO_Port, CS_Pin, GPIO_PIN_SET); raw = ((uint16_t)rx[0] << 8) | rx[1]; // 16位ADC,双极性模式,符号扩展 return (int16_t)raw; }如果觉得SPI配置成8位模式收发两个字节太繁琐,也可以把SPI数据宽度直接设成16位。但实际上8位模式的兼容性更好,而且很多STM32型号在16位模式下DMA传输时字节序容易搞混,8位模式反而省心。
3.3 数据换算与量程选择
ADS8688输出的是二进制补码格式。以±10.24V量程为例,满量程对应0x7FFF,负满量程对应0x8000。换算成真实电压的公式是:
float ADS8688_ReadVoltage(uint8_t channel) { int16_t raw = ADS8688_ReadChannel(channel); float full_scale = 10.24f; // 根据设置的量程修改 return (float)raw * full_scale / 32768.0f; }这里有个容易踩的坑:很多新手会直接用raw乘以量程再除以65536,这等于把双极性数据当成单极性处理,算出来的电压值整个偏移了半个量程。16位ADC的补码范围是-32768到32767,正负满量程分别对应正负最大值,所以分母必须是32768而不是65536。
量程选择上,如果没有特殊需求,我一般建议选±10.24V。信号幅度接近满量程时,量化误差占的比例最小。如果实际信号最大只有1V,那就配置成±1.024V量程,把精度用足。比如测一个0-1V的传感器输出,用±10.24V量程时1V只占约9.7%的满量程,有效分辨率不到12位;换到±1.024V量程后,同样一个信号能用到98%的满量程,实际有效位数接近16位。
4. 调试中的典型问题与排查技巧
4.1 常见问题速查
我在几个项目里从零调试ADS8688,把遇到的典型问题整理成了表格:
| 现象 | 可能原因 | 处理方法 |
|---|---|---|
| MISO一直无输出,读回0xFF或0x00 | SDO接错、SPI模式配置错误 | 检查接线,确认SPI配置为Mode 1 |
| 只有第一个通道读数正确,其它通道不对 | 通道选择命令未生效,寄存器配置被覆盖 | 每次读通道前重新发送通道选择命令 |
| 电压读数整体偏大或偏小 | 量程寄存器配置与实际信号不匹配 | 核对当前量程,修改换算公式中的满量程值 |
| 数据跳动超过±5个LSB | 电源纹波大、基准电容太小、信号源内阻过高 | 改善AVDD去耦,增加REFCAP电容,输入端加RC滤波 |
| 上电后第一次读数始终是0 | 没有正确复位芯片,或者命令帧超时 | 初始化时增加硬件复位,检查CS时序等待时间 |
| 采样率提上去后偶尔丢帧 | SPI时钟过快或CS时序不满足要求 | 降低SPI分频,使用DMA时注意CS释放时机 |
4.2 排错思路与实测心得
如果你遇到MISO完全没有输出的情况,别急着怀疑芯片坏了。先用逻辑分析仪或示波器抓CS、SCLK、SDI三根线,确认初始化代码有没有真正执行到SPI发送。很多情况下是STM32的SPI时钟没有使能,或者GPIO复用功能没配置对。我就有一次忘了在CubeMX里勾选SPI1的MISO引脚复用,结果代码跑得欢,数据线上一片死寂。
另外建议把所有ADC通道都接地,先读一遍原始数据,看能不能稳定在读到一个接近0x0000的固定值。这能快速判断SPI链路和芯片基本功能是否正常。再给某个通道接一个精准的10V电压源,对照换算出来的电压值,误差应该在±10mV以内。如果偏差较大,优先检查量程寄存器是不是真的写进去了——可以用SPI回读寄存器内容确认。
自动扫描模式我也提一句。ADS8688支持配置自动扫描序列,把8个通道全部加进序列后,一次读取命令就能循环输出所有通道结果。这个模式配合DMA用很香,能够让MCU在采集期间去处理其它任务。但初始化时的序列配置寄存器一定要按手册设置清楚,少配一个通道或顺序配错,读回来的数据就不是你预期的通道顺序。我在某个项目里需要用500kSPS采样率连续采集4个通道,用自动扫描加DMA双缓冲之后,MCU的负载率从原来的86%骤降到23%,效果非常明显。
最后再给一个小建议:ADS8688的手册里关于寄存器位的描述表格要仔细看,不同版本的数据手册对某些保留位的说明可能不一样。写驱动的时候把寄存器地址用宏定义单独放在一个头文件里,加上注释说明对应手册页码,过几个月回头看代码时能省不少时间。
本文还有配套的精品资源,点击获取