简介:AD7745官方驱动程序压缩包面向需要快速上手高精度24位Σ-Δ ADC的嵌入式开发者,以及工业与医疗领域的数据采集、传感器接口和精密测量场景工程师,用于解决芯片初始化配置、转换结果读取和主机通信对接等问题。包内共5个文件,以C语言源文件与对应头文件为主,包含驱动主体与底层通信模块,另附一个ZIP通用例程包,整体约14KB,结构简洁清晰。目前已有710人学习下载,适合正在做温度测量、压力检测、电流监控等项目的开发者。通过这份资料,读者可以获得完整的官方驱动代码与示例工程,覆盖初始化设置、24位数据读取、错误处理、中断管理以及低功耗切换等功能模块,能够直接移植到STM32等常用MCU平台。同时,驱动程序的跨平台兼容设计也为后续Linux、Windows或RTOS环境下的集成提供了参考。
1. 先搞清楚 AD7745 驱动程序到底在驱动什么
AD7745 是 ADI 公司的 24 位电容数字转换器,一颗芯片走 I2C 总线,既能测一路电容,也能同时测一路温度和一路电压。它常出现在液位传感器、湿度变送器、接近感应和压力变送器这类对微小电容变化敏感的场合。很多人第一次搜“AD7745官方驱动程序”,会以为它像显卡驱动一样下载即装,但实际上这是芯片级驱动,需要你自己在 MCU 或嵌入式 Linux 上把 I2C 读写、寄存器配置、等待转换完成、校准和误差补偿这一整套逻辑打通。官方提供的参考程序通常只给你一个可移植的骨架,真正要适配的时序和保护逻辑还得自己做。这篇文章从芯片的通信协议讲起,一直讲到 Linux IIO 框架接入,再把调试中常见的坑剖开看,目标是让你拿到一块 AD7745 之后,不是对着数据手册逐字节抄,而是能快速改出一版能用的驱动。
2. I2C 通信与寄存器模型:所有 AD7745 程序都绕不开的地基
2.1 AD7745 的 I2C 地址与事务细节
AD7745 的 7 位 I2C 从机地址是 0x48,对应 8 位写地址 0x90,读地址 0x91。设计上 ADI 没有给这颗芯片留地址引脚,所以一条 I2C 总线上只能挂一个 AD7745,这一点在系统规划时要先确认。驱动里常见的写法是把地址左移一位,配合 HAL 库的I2C_MEMADD_SIZE_8BIT参数,让硬件自动拼出读写的类型位。
芯片的所有操作都是“向某个 8 位寄存器地址写入或读取若干字节数据”。注意,AD7745 的寄存器地址是单字节,和 EEPROM 那种先写地址再传数据的流程很像,但寄存器数量很少,总共不到 32 个,其中真正用到的配置寄存器只有十几个。驱动开发的第一步不是写数据解析,而是把 I2C 读写封装成干净的函数,让上层逻辑只关心寄存器名和值,不再直接面对总线时序。
I2C 速率建议先按 100 kbps 跑通,再去尝试 400 kbps。虽然数据手册标称支持 400 kbps,但实际布线和上拉电阻不佳时,高速率下容易在写配置后立刻读状态时丢字节。驱动代码里把超时时间留足,比如 HAL 的 timeout 参数设置 100 ms,不要在 master 侧频繁重试,而是让出 CPU 等下一次轮询。
2.2 寄存器映射:驱动全凭一张表在操作
把寄存器梳理清楚,驱动就完成了一大半。下面这张表是我在移植驱动时最常对照的清单,覆盖了初始化、校准和数据读取所需的核心寄存器。其中数据寄存器地址在不同时期的数据手册中标注略有差异,实际写驱动时以你手头手册为准,或者直接沿用第三方开源驱动里验证过的宏定义。
| 寄存器名 | 地址 | 方向 | 作用 |
|---|---|---|---|
| STATUS | 0x00 | 只读 | 转换完成位、错误位 |
| CAP_EXC_A | 0x02 | 读写 | 激励源 A 配置 |
| CAP_EXC_B | 0x03 | 读写 | 激励源 B 配置 |
| CAP_SETUP | 0x04 | 读写 | 电容转换使能、输入选择 |
| CAP_CFG | 0x0B | 读写 | 转换模式、迟滞、内部 DAC |
| CFG | 0x0C | 读写 | 电压/温度转换选择、主模式配置 |
| CAP_DATA[23:0] | 0x10 起 | 只读 | 24 位电容原始数据 |
STATUS 寄存器里的就绪位是轮询的依据。不同手册对 RDY 位的极性定义不一样,有的版本写“为 0 时数据就绪”,有的写“为 1 时转换完成”。为了避免移植时踩坑,建议先在寄存器层把你的目标位提取成宏,比如AD7745_STATUS_RDY_C,这样后续代码不依赖具体数值,出现问题只需要回头改这一个宏。
2.3 用 HAL 封装读写函数
在 STM32 上驱动 AD7745,最直接的做法是使用 HAL 库的内存读写接口。下面这段代码是驱动底层的最小封装,所有后续操作都建立在它之上:
#include "ad7745.h" #define AD7745_I2C_ADDR (0x48 << 1) /* 7 位地址左移成为 8 位地址 */ #define AD7745_REG_STATUS 0x00 #define AD7745_REG_CAP_SETUP 0x04 #define AD7745_REG_CAP_CFG 0x0B #define AD7745_REG_CFG 0x0C #define AD7745_REG_CAP_DATA 0x10 /* 连续 3 字节的电容数据起始地址 */ static int ad7745_write_reg(ad7745_t *dev, uint8_t reg, uint8_t val) { return HAL_I2C_Mem_Write(dev->hi2c, AD7745_I2C_ADDR, reg, I2C_MEMADD_SIZE_8BIT, &val, 1, 100) == HAL_OK ? 0 : -1; } static int ad7745_read_regs(ad7745_t *dev, uint8_t reg, uint8_t *buf, size_t len) { return HAL_I2C_Mem_Read(dev->hi2c, AD7745_I2C_ADDR, reg, I2C_MEMADD_SIZE_8BIT, buf, len, 100) == HAL_OK ? 0 : -1; }这段代码里有几个参数需要说明。I2C_MEMADD_SIZE_8BIT通知 HAL 库,内存地址也就是寄存器地址是 8 位宽度。100是超时毫秒数,在首次上电和长时间待机后建议加大到 200,避免系统时钟紧张时误报超时。返回值统一为 0 表示成功,负一表示失败,上层驱动不需要关心底层的HAL_StatusTypeDef枚举,这样代码移植到别的平台时只要重写这两个函数即可。
2.4 为什么驱动里要有延时和重试
很多初次接触 AD7745 的人会把驱动写成“先写配置,立刻读数据”,结果读回来的永远是 0 或上一次的值。原因是 AD7745 的转换不是同步完成的,电容转换需要几十毫秒时间。在单次转换模式下,写 CFG 寄存器触发转换后,芯片需要经过 t_conv 时间才把结果锁存到数据寄存器。此时 STATUS 寄存器里的就绪位会发生变化,驱动必须等待这个位到达指定状态后才能读取数据。
常见做法是写一个带超时的轮询循环,避免程序卡死在等待里。超时次数可以设为 50 到 100 次,每次延时 1 ms。如果你发现轮询次数经常接近上限,就要检查 I2C 通信是否出错,或者转换配置是否真的把芯片拉进了正确的工作模式,而不是简单地把超时调大。延时和重试的本质,是把芯片的异步行为隔离成同步接口,让上层调用者感觉就像在读一个普通寄存器一样。
3. 最小驱动实现:初始化、单次转换与数据读取
3.1 初始化序列:一组保守能启动的组合
初始化 AD7745 的难点在于寄存器之间有依赖关系。先要配置激励源,再打开电容转换,最后设置转换模式。如果你把激励源留成上电默认值,某些输入信号过弱的传感器会导致转换值跳变。下面这组初始化参数是我在一款液位传感模组上验证过的起点:
int ad7745_init(ad7745_t *dev) { HAL_Delay(10); /* 等待内部上电复位完成 */ ad7745_write_reg(dev, 0x02, 0x00); /* CAP_EXC_A: 激励A默认输出 */ ad7745_write_reg(dev, 0x03, 0x00); /* CAP_EXC_B: 激励B默认输出 */ ad7745_write_reg(dev, AD7745_REG_CAP_SETUP, 0x80); /* CAPEN=1,使能电容转换 */ ad7745_write_reg(dev, AD7745_REG_CAP_CFG, 0x00); /* 单端输入,内部激励 */ return 0; }0x80写入 CAP_SETUP,只把最高的 CAPEN 位置 1,其他位保持默认。这样芯片不会进入差分输入模式,外部传感器接在 CIN1 和地之间就能工作。CAP_CFG写 0 表示先不启用内部 DAC,也不设置迟滞,对大多数测量场景足够。真的需要调整激励幅度时,再去操作CAP_EXC_A/B寄存器的低三位,它们的值对应激励电压的分档。
3.2 单次转换流程:触发、等待、读数据
单次转换模式适合电池供电和低速测量场景,每触发一次读一个点。流程简单,也便于在驱动里加入失败重试。以下是读取一次电容原始值的完整函数:
int ad7745_read_cap_single(ad7745_t *dev, int32_t *raw) { uint8_t status = 0; uint8_t buf[3]; ad7745_write_reg(dev, AD7745_REG_CFG, 0x02); /* 写 CFG,低两位 0b10 触发单次转换 */ for (int i = 0; i < 100; i++) { ad7745_read_regs(dev, AD7745_REG_STATUS, &status, 1); if (status & AD7745_STATUS_RDY_C) { /* 轮询到转换完成 */ break; } HAL_Delay(1); } if (!(status & AD7745_STATUS_RDY_C)) { return -2; /* 超时未完成 */ } ad7745_read_regs(dev, AD7745_REG_CAP_DATA, buf, 3); *raw = ((int32_t)buf[0] << 16) | ((int32_t)buf[1] << 8) | (int32_t)buf[2]; return 0; }代码中AD7745_STATUS_RDY_C是我在前面提过的状态位宏,你需要根据芯片手册确认它对应的掩码。读取电容数据时,连续读 3 个字节拼成 24 位有符号数,是因为 AD7745 支持正负电容差值测量,所以数据被编码为二进制补码。如果只做相对测量,即使最高位当符号位处理,也不会影响变化量的计算。
3.3 换算到真实电容值:校准才是关键
原始码和真实电容之间不是简单的比例关系。AD7745 的输入范围可以理解为一定的 pF 量程,但内部增益、PCB 寄生电容、传感器线缆电容都会让结果偏移。要得到绝对值,必须先做零点校准和增益校准。
驱动里常见的做法是预留两个校准系数,offset_cal和gain_cal。零点校准时把传感器从测量端断开,读到的原始码记录为 offset;增益校准则接入一个已知容值的标准电容,读到的原始码减去 offset 后,除以标准电容值得到每 LSB 对应的 pF。之后每次测量都执行:
float capacitance_pf = ((float)(raw - offset_cal) * scale_pf_per_lsb);两个校准系数建议放在非易失存储里,上电初始化时读出并写入芯片的 OFFSET_CAL 和 GAIN_CAL 寄存器。数据手册里对这两个寄存器给了计算公式,实际调试时也可以先跳过寄存器校准,用应用层软件校准,效果往往更直观。
3.4 连续转换模式与速率控制
连续模式把 CFG 的低两位设为 0b01,芯片会自动按固定速率更新数据,适合需要高频采样的场景。驱动在这种模式下不需要反复触发,只需要轮询 STATUS 看看有没有新数据。这里有个容易忽略的点:即使不读数据,芯片也会持续刷新数据寄存器。所以驱动要记录上一次的完成标志,只有当 STATUS 里的就绪位发生“从没就绪到就绪”的跳变时,才去读一次数据,否则同一组原始码会被重复读取,造成波形不平滑。
连续模式的转换速率主要由芯片内部时钟决定,外部无法调节。如果你的系统需要更高采样率,可以牺牲精度把滤波器设置为低分辨率模式,这会缩短转换时间。但我不建议一上来就调这个,先跑通默认模式,把数据拿回来看看噪声底,再决定要不要降分辨率。
4. 在 Linux 里接入 AD7745:设备树与 IIO 驱动复用
4.1 内核里已经存在成熟的 AD7745 驱动
如果你的产品跑的是嵌入式 Linux,那完全没有必要从零写驱动。内核的 IIO 子系统中已经有 AD7745 的驱动程序,它基于工业 I/O 框架,把电容数据、温度数据都抽象成标准属性。官方驱动不是一堆散落的 .c 文件,而是已经合入内核主线的drivers/iio/adc/ad7745.c,这给移植带来很大便利。你只需要让设备树里出现正确的节点,驱动就会自动 probe,剩下的工作是在用户空间用 sysfs 读写。
使用内核官方驱动有个前提:你使用的内核版本要开启CONFIG_IIO和CONFIG_AD7745。在某些精简的 BSP 里,这两个选项默认是关闭的,需要重新配置内核。
4.2 设备树:让驱动找到 AD7745
最简单的设备树节点写法如下,假设 AD7745 挂在 i2c1 总线上:
&i2c1 { ad7745@48 { compatible = "adi,ad7745"; reg = <0x48>; vdd-supply = <&vdd_3v3>; }; };compatible必须写成adi,ad7745,这与内核驱动的 OF 匹配表严格对应,不能简写成ad7745。reg是 7 位 I2C 地址,由于设备树里地址本来就代表从机地址,不需要再左移一位。vdd-supply不是必须,但如果你的板子用独立 LDO 给 AD7745 供电,建议补上这个属性,驱动 probe 时会自动控制电源。
配置好设备树后重新编译内核或使用 dtb overlay,启动系统后检查/sys/bus/iio/devices/目录下有没有出现新的iio:deviceX。如果设备节点注册失败,先看 dmesg 里有没有ad7745: probe failed的报错,多数情况是 I2C 地址不对或者供电电压没起来。
4.3 通过 sysfs 读取数据和配置采样频率
IIO 驱动把原始值放在in_capacitance_raw属性里,把校准后的偏移和比例放在in_capacitance_calibbias和in_capacitance_calibscale。读数据只是简单的文件读取:
cat /sys/bus/iio/devices/iio:device0/in_capacitance_raw执行后会返回一个十进制整数,比如123456,这就是 24 位原始码。连续读几次可以观察噪声。如果你想让驱动进入连续采样模式,通过trigger机制配置;单次读取则直接读属性文件,每次打开和关闭文件都会触发一次转换。这种方式和单片机上的驱动逻辑没有本质区别,只是把轮询和等待交给了内核驱动完成。
想拿到以 pF 为单位的真实电容,需要把原始码和calibbias、calibscale组合计算。内核驱动默认会把in_capacitance_calibscale设置为一个接近每 LSB 对应 pF 数倒数的值,但真正准确的校准还得自己标定。可以用echo命令修改in_capacitance_calibbias,把零点误差压到最低。
4.4 裸机驱动与 Linux 驱动在寄存器层是相通的
在内核 IIO 驱动里,你依然能看到对 CAP_SETUP、CAP_CFG、CFG 这些寄存器的操作,只是它们被封存在ad7745_write_reg函数中,不再暴露给用户空间。这给开发者的启示是:调试 Linux 下的问题,完全可以借助前面裸机驱动的那套寄存器读写逻辑,用 i2c-tools 直接在 shell 里操作。比如用i2cdump -y 1 0x48查看当前寄存器内容,比对 Linux 驱动初始化后的寄存器和数据手册里的预期值,能快速定位是设备树没匹配上,还是驱动配置被平台数据覆盖。
5. 稳定性进阶:状态轮询、软件滤波与温漂补偿
5.1 状态轮询比固定延时更可靠
很多驱动示例里写的是“触发后延时 90 ms 再读”,这在实验室环境没问题,但它掩盖了一个事实:芯片的转换时间会随着配置和供电电压变化。把延时写死成固定值,等到环境温度变化或者供电纹波变大时,可能读到的还是上一次的数据,而你毫不知情。正确做法是像第 3 章那样,用状态寄存器做轮询,配合超时保护。这样无论转换时间怎么变,驱动总能拿到最新一帧数据,不会滑帧。
5.2 滑动平均滤掉机械抖动和电源噪声
AD7745 的原始码在外部环境稳定时也有 ±2 到 ±5 个 LSB 的跳动,这是正常的。做接近感应或液位检测时,这种跳动经过阈值判断后会变成误触发。实用的办法是在驱动上层加一个 N 点滑动平均,我一般取 8 或 16 点。N 太大会让响应变慢,N 太小则滤不干净。下面这段 C 代码实现了一个不耗内存的环形平均:
static int32_t filter_buf[16]; static uint8_t filter_idx; int32_t ad7745_filtered_value(int32_t raw) { static int32_t sum; sum -= filter_buf[filter_idx]; filter_buf[filter_idx] = raw; sum += raw; filter_idx = (filter_idx + 1) % 16; return sum / 16; }这段代码每读到一个新原始值,就替换窗口中最旧的样本,再求平均,全程只需要一次除法。注意sum是静态变量,初次调用前 buffer 里全是 0,所以前 16 次输出的平均值是偏小的。驱动刚开始运行的几百毫秒内,可以丢弃前 16 个输出,或者先把filter_buf全部填充为首个原始值。
5.3 温漂补偿的快速实现
AD7745 内部自带温度传感器,官方驱动可以同时输出in_temp_raw,这个数据刚好可以拿来做温漂补偿。实用思路是:在恒温箱里记录 25℃ 和 60℃ 两个温度点下的零点原始码,拟合出一条线性关系,然后在驱动里读温度,按比例修正电容原始值。补偿公式通常写成:
int32_t temp_compensated = raw - (int32_t)(k_temp * (temp_raw - temp_ref));temp_raw是内部温度传感器的原始码,temp_ref是标定时的基准温度码,k_temp是温度系数,单位是 LSB/温度码。这个系数可以通过两组标定数据求出。如果你的产品工作温区比较窄,比如 15℃ 到 35℃,甚至可以忽略温漂,因为 AD7745 在窄温区的温漂指标本身就不大,过度补偿反而会把系统噪声引入测量结果。
5.4 用 i2c-tools 做驱动的“最后一根救命稻草”
无论裸机还是 Linux 环境,调试到最后如果读数还是不对,直接看寄存器最直观。在 Linux 系统里安装 i2c-tools 后,用i2cdetect -y 1查总线 1 上有没有 0x48 这个地址,若有但驱动读不到数据,就用i2cget -y 1 0x48 0x00读状态寄存器,比对 STATUS 位是否符合预期。这一招也适用于检查芯片是否一直处于初始化失败状态,比反复改设备树参数快得多。
本文还有配套的精品资源,点击获取