简介:这份资源面向嵌入式开发工程师与单片机驱动开发者,提供AD717X系列多路复用模数转换器的C语言驱动源码,覆盖AD7177-2、AD7175-2、AD7172-2等型号,可用于工业采集、精密测量等需要高精度ADC的嵌入式项目。压缩包共5个文件,包含3个h头文件与2个c源文件,整体约12KB,其中头文件负责寄存器定义与通信接口声明,源文件实现芯片初始化、寄存器读写及I2C通信逻辑,并给出I2C_Write、I2C_Read等底层读写函数原型,便于移植到不同MCU平台。目前已有642人学习下载,说明该驱动在同类芯片开发中具有一定参考价值。读者可据此快速理解AD717X系列的多路复用配置流程与寄存器操作方式,减少从数据手册到可运行代码的摸索时间,适合作为项目驱动开发的起点或二次封装的基础。
1. AD717X 驱动源码拆包:从寄存器手册到能跑的 C 代码
如果你手头正用 AD7177-2、AD7175-2 或 AD7172-2 做多路复用采集,大概率经历过这个场景:芯片手册翻了三遍,寄存器地址背得滚瓜烂熟,可一到写驱动就卡在通道切换、建立时间、数据就绪判断这几步上。这份 AD717X 系列驱动 C 源码包,解决的就是从「看懂手册」到「跑通采集」之间那段最磨人的距离。它覆盖 AD7177-2、AD7175-2、AD7172-2 三款多路复用模数转换器的底层驱动,包含寄存器配置、通道扫描、SPI 读写时序和转换结果解析。适合正在做精密数据采集、称重、温度巡检的嵌入式工程师,也适合想拿一份能直接移植的参考实现来对照自己代码的人。下面按「资源是什么、怎么接、怎么配、坑在哪」的顺序拆开讲。
2. 驱动架构与 SPI 时序:先搞清它怎么跟 MCU 说话
2.1 源码目录结构与模块划分
拿到压缩包解压后,常见做法是先扫一遍文件树,别急着往工程里塞。这类 AD717X 驱动通常按「芯片抽象层 + 通信层 + 应用示例」三层组织。芯片抽象层负责寄存器定义和位域操作,通信层封装 SPI 读写,应用层给出单通道和多通道扫描的调用示例。你要做的第一件事是确认哪几个文件跟你的 MCU 平台无关,哪几个需要改。
| 文件/目录 | 作用 | 移植时是否需改 |
|---|---|---|
| ad717x.h | 寄存器地址、位掩码、结构体定义 | 不改 |
| ad717x.c | 寄存器读写、通道配置、转换控制 | 改 SPI 回调 |
| ad717x_regs.h | 各寄存器位域宏 | 不改 |
| example/ | 单通道/多通道采集示例 | 参考,按需改 |
| platform/ | SPI 与延时接口 | 必须改 |
我一般会先把 platform 层单独拎出来,把 SPI 收发和微秒级延时换成自己 MCU 的 HAL 或寄存器操作,其余文件原样保留。这样做的原因是:驱动核心逻辑跟平台无关,改得越少,后面出问题越容易定位。
2.2 SPI 模式与读写时序的关键参数
AD717X 系列走的是标准 4 线 SPI,但有几个参数不能想当然。CPOL 和 CPHA 的组合、SCLK 空闲电平、数据在时钟的哪个边沿采样,这些在手册时序图里写得很清楚,但代码里如果配错,现象往往是「读回来全是 0x00 或 0xFF」,而不是报错。
/* SPI 初始化关键参数,以 STM32 HAL 为例 */ hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_MASTER; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; /* CPOL = 0 */ hspi1.Init.CLKPhase = SPI_PHASE_2EDGE; /* CPHA = 1 */ hspi1.Init.NSS = SPI_NSS_SOFT; /* 片选手动控制 */ hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_32; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;逻辑说明:AD717X 在 SCLK 下降沿输出数据、上升沿采样,对应 CPOL=0、CPHA=1。片选必须用软件控制,因为一次寄存器读操作是「拉低 CS → 发命令字 → 读数据 → 拉高 CS」的完整事务,硬件 NSS 会在字节间自动翻转,导致事务被打断。波特率预分频先给保守值,等通信稳定后再往上提。
参数说明:DataSize 固定 8 位,因为命令字和寄存器数据都按字节传输。FirstBit 必须是 MSB,AD717X 不支持 LSB 优先。如果你用的是软件模拟 SPI,注意在下降沿之前把数据准备好,上升沿之后延时至少几十纳秒再读,具体看你的主频。
2.3 寄存器读写事务的完整流程
单次寄存器读写的流程是固定的:先发 1 字节命令字(最高位区分读/写,低 6 位是寄存器地址),然后跟 1 到 3 字节数据。命令字的位定义在 ad717x.h 里通常有宏,别自己手算。
/* 寄存器写:命令字 + 数据 */ static void ad717x_write_reg(ad717x_dev *dev, uint8_t reg, uint32_t data, uint8_t len) { uint8_t cmd = AD717X_COMM_WRITE | (reg & 0x3F); AD717X_CS_LOW(); spi_transfer(cmd); while (len--) { spi_transfer((data >> (8 * len)) & 0xFF); } AD717X_CS_HIGH(); } /* 寄存器读:命令字 + 读回数据 */ static uint32_t ad717x_read_reg(ad717x_dev *dev, uint8_t reg, uint8_t len) { uint8_t cmd = AD717X_COMM_READ | (reg & 0x3F); uint32_t data = 0; AD717X_CS_LOW(); spi_transfer(cmd); while (len--) { data = (data << 8) | spi_transfer(0x00); } AD717X_CS_HIGH(); return data; }逻辑说明:写操作先发命令字,再按寄存器长度从高字节到低字节依次发出。读操作发完命令字后,继续发空字节把数据时钟出来。注意 CS 拉高之前不能插入其他 SPI 事务,否则芯片会认为事务未结束。
参数说明:reg 是 6 位地址,len 是该寄存器的字节数(多数配置寄存器是 2 字节,ID 寄存器是 2 字节,数据寄存器是 3 字节)。如果你读回来的 ID 寄存器不是预期值,先查 CS 时序和 CPHA,再查命令字地址有没有写错。
3. 通道配置与多路复用扫描:把 16 个通道跑起来
3.1 通道寄存器与建立时间的关系
AD717X 的多路复用不是「切了就能读」。每个通道有独立的配置寄存器,里面包含增益、基准源选择、滤波参数,还有一个容易被忽略的字段:通道使能位。你要扫描几个通道,就得把对应通道的使能位打开,并且给每个通道配好建立时间。
建立时间的本质是:模拟前端切换通道后,内部滤波器和 PGA 需要一段时间稳定,如果没等稳定就启动转换,读出来的码值会偏。手册里给的建立时间跟输出数据速率(ODR)和滤波器类型有关,常见做法是按手册推荐值再留 20% 余量。
/* 配置通道 0 和通道 1,使能并设置建立时间 */ ad717x_write_reg(dev, AD717X_REG_CH0, AD717X_CH_EN | AD717X_CH_SETUP(0) | AD717X_CH_AIN0P_AIN1N, 2); ad717x_write_reg(dev, AD717X_REG_CH1, AD717X_CH_EN | AD717X_CH_SETUP(1) | AD717X_CH_AIN2P_AIN3N, 2); /* 配置 setup 0 的滤波器和增益 */ ad717x_write_reg(dev, AD717X_REG_SETUP0, AD717X_SETUP_BIPOLAR | AD717X_SETUP_GAIN_1 | AD717X_SETUP_REF_INT, 2); ad717x_write_reg(dev, AD717X_REG_FILTER0, AD717X_FILTER_ODR_1000SPS | AD717X_FILTER_SINC5, 2);逻辑说明:CH 寄存器里 EN 位必须置 1,SETUP 字段指向该通道用哪组 setup 配置。多个通道可以共用一组 setup,也可以各用各的。滤波器寄存器决定 ODR 和 sinc 阶数,ODR 越低、阶数越高,噪声越小但建立时间越长。
参数说明:GAIN_1 表示增益 1,如果你接的是小信号传感器,可以改成 GAIN_16 或 GAIN_64,但要注意输入共模范围会收窄。REF_INT 用内部基准,如果精度要求高,建议切外部基准并相应改配置。ODR 从 1000SPS 起步,稳定后再往下降。
3.2 单次转换与连续转换模式的选择
驱动通常支持两种模式:单次转换和连续转换。单次转换适合低功耗巡检,每次切换通道后启动一次,读完就停。连续转换适合高速采集,芯片按 ODR 自动输出,MCU 只管在 DRDY 中断里读数据。
/* 单次转换:切换通道后启动,等 DRDY 再读 */ void ad717x_single_convert(ad717x_dev *dev, uint8_t ch) { ad717x_write_reg(dev, AD717X_REG_ADC_MODE, AD717X_MODE_SINGLE | AD717X_MODE_CH(ch), 2); while (ad717x_drdy_read(dev) == 1); /* 等 DRDY 拉低 */ dev->last_code = ad717x_read_reg(dev, AD717X_REG_DATA, 3); }逻辑说明:ADC_MODE 寄存器里写 SINGLE 模式并指定通道号,芯片开始一次转换。DRDY 引脚在转换完成后拉低,读走数据后自动拉高。注意 DRDY 是开漏输出,硬件上要接上拉电阻。
参数说明:CH(ch) 是通道号,范围 0 到 15,具体看芯片型号。AD7177-2 是 4 通道差分,AD7175-2 是 8 通道,AD7172-2 是 4 通道,写通道号之前先确认你的芯片支持到几。读 DATA 寄存器固定 3 字节,因为它是 24 位结果。
3.3 多通道扫描的代码组织方式
多通道扫描有两种写法:一种是在主循环里轮询切换,另一种是用 DRDY 中断驱动状态机。轮询写法简单但浪费 CPU,中断写法效率高但要注意中断里不能做太长的事。
/* 中断驱动的通道扫描状态机 */ volatile uint8_t scan_ch = 0; volatile int32_t scan_result[4]; void drdy_isr(void) { scan_result[scan_ch] = ad717x_read_reg(dev, AD717X_REG_DATA, 3); scan_ch = (scan_ch + 1) % 4; ad717x_write_reg(dev, AD717X_REG_ADC_MODE, AD717X_MODE_SINGLE | AD717X_MODE_CH(scan_ch), 2); }逻辑说明:每次 DRDY 中断读走当前通道数据,切到下一个通道并启动转换。这样通道切换和转换启动都在中断里完成,主循环只负责处理 scan_result 数组。
参数说明:scan_result 用 int32_t 存 24 位有符号码值,方便后续转电压。通道数按你的实际接线改,别照抄 4。中断里调用的 ad717x_read_reg 必须是阻塞式 SPI 读,不能用 DMA 异步,否则状态机会乱。
4. 避坑与排查:那些手册不会明说的翻车点
4.1 读回 ID 不对,但 SPI 波形看着正常
现象:读 ID 寄存器返回 0x00 或 0xFF,示波器看 SCLK 和 MOSI 都有波形。原因通常是 CPHA 配反了,数据在错误的边沿被采样。解决:把 CLKPhase 从 2EDGE 改成 1EDGE 试一次,或者反过来。AD717X 手册时序图里数据在 SCLK 下降沿变化、上升沿稳定,对应 CPHA=1。如果还不对,检查 CS 是不是在字节间被拉高了。
4.2 通道切换后第一组数据总是偏大
现象:多通道扫描时,刚切过去的通道读出来的码值比稳定值大一截。原因是建立时间不够,内部滤波器还没稳定就启动了转换。解决:在切换通道后加延时,或者把 ODR 降一档。我一般会在单次转换模式里,切换通道后先丢弃第一次转换结果,从第二次开始取。
4.3 DRDY 一直不拉低
现象:配置完 ADC_MODE 后等 DRDY,结果一直等不到。原因可能是 ADC_MODE 寄存器没写对,或者通道没使能。解决:先读回 ADC_MODE 确认写入成功,再读 CH 寄存器确认 EN 位是 1。还有一种可能是 DRDY 引脚没接上拉,开漏输出没有上拉就是浮空,MCU 读到的电平是随机的。
4.4 内部基准下噪声比预期大
现象:用内部基准时,码值跳动比手册标称的噪声大不少。原因是内部基准的温漂和噪声在低 ODR 下更明显,而且内部基准带负载能力有限。解决:如果精度要求高,切外部基准,比如 ADR445 这类低噪声基准。切换后记得改 SETUP 寄存器里的 REF 字段,并且给外部基准足够的建立时间。
4.5 多通道共用 setup 导致配置互相覆盖
现象:两个通道配了不同的增益,结果读出来两个通道增益一样。原因是两个通道的 SETUP 字段指向了同一个 setup 编号,后写的配置覆盖了先写的。解决:每个需要独立配置的通道分配不同的 setup 编号,AD717X 一般有 4 组 setup,够用。如果通道多且配置相同,共用一组反而省事。
5. 从能读到读准:校准与数据验证的实操技巧
5.1 内部零点和满量程校准的执行顺序
驱动跑通、能读到数之后,下一步是校准。AD717X 支持内部零点和满量程校准,但顺序不能反。常见做法是:先配好通道和滤波器,启动内部零点校准,等校准完成后再启动满量程校准。校准期间输入要接对应的参考电压,零点校准接零伏,满量程校准接接近满量程的电压。
/* 内部零点校准 + 满量程校准 */ void ad717x_calibrate(ad717x_dev *dev) { /* 零点校准:输入短接或接零伏 */ ad717x_write_reg(dev, AD717X_REG_ADC_MODE, AD717X_MODE_INT_ZERO_CAL, 2); while (ad717x_drdy_read(dev) == 1); /* 满量程校准:输入接满量程参考 */ ad717x_write_reg(dev, AD717X_REG_ADC_MODE, AD717X_MODE_INT_FS_CAL, 2); while (ad717x_drdy_read(dev) == 1); }逻辑说明:校准模式写下去后,芯片内部执行校准,完成后 DRDY 拉低。校准结果存在芯片内部寄存器里,掉电不保存,所以每次上电都要重新校准。
参数说明:零点校准的输入电压要接近 0V,满量程校准的输入要接近你设定的满量程值。如果校准后读数仍然偏,检查基准电压是否准确,以及输入是否在 PGA 的共模范围内。
5.2 码值转电压的公式与符号处理
AD717X 输出的是 24 位有符号码值,双极性模式下码值范围是 -2^23 到 2^23-1。转电压的公式是:电压 = 码值 × VREF / (增益 × 2^23)。注意码值是二进制补码,读回来如果是 0xFFFFFF 开头的,要按有符号数处理。
/* 24 位补码转电压 */ float ad717x_code_to_voltage(int32_t code, float vref, uint8_t gain) { /* 先做符号扩展:24 位补码转 32 位有符号 */ if (code & 0x800000) { code |= 0xFF000000; } return (float)code * vref / (gain * 8388608.0f); }逻辑说明:24 位码值读回来存在 int32_t 的低 24 位,如果最高位是 1,说明是负数,需要把高 8 位补 1 做符号扩展。然后按公式转电压。
参数说明:vref 是基准电压,内部基准通常是 2.5V,外部基准按实际值填。gain 是 PGA 增益,跟 SETUP 寄存器里配的一致。8388608 是 2 的 23 次方。
5.3 用已知电压验证采集链路
校准完别急着上传感器,先用基准电压源或分压电路给几个已知电压,看读回来的值对不对。我一般会测三个点:接近零伏、中间值、接近满量程。三个点都对得上,说明采集链路没问题。如果中间值对、两端偏,查增益和基准;如果整体偏一个固定值,查零点校准。
验证的时候注意:输入电压不能超过 AVDD 和 AVSS 的范围,差分输入的共模电压也要在手册规定范围内。曾经有一次读回来的值一直偏大,查了半天发现是输入共模电压超了,PGA 输出饱和,这种问题看波形看不出来,只能查数据手册的共模范围表。
5.4 数据就绪中断与 FIFO 的配合
AD717X 有 FIFO 模式,可以缓存多次转换结果,减少 MCU 中断次数。但 FIFO 用不好会丢数据。常见做法是:FIFO 水位设到一半,中断里读走全部数据,别等到满。如果 ODR 很高,中断处理时间要算进去,SPI 读 3 字节加状态机切换,在几十兆主频的 MCU 上大概几微秒,ODR 超过 10kSPS 就要留意了。
从那以后我每次拿到新的 AD717X 板子,都强制走一遍「读 ID → 配通道 → 单次转换 → 校准 → 三点验证」的流程,不跳过任何一步。这套驱动源码把前面几步都封装好了,你只需要把 platform 层的 SPI 和延时对接上,剩下的按示例改通道号和 ODR 就能跑。希望帮到你。
本文还有配套的精品资源,点击获取