简介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 寄存器映射驱动全凭一张表在操作把寄存器梳理清楚驱动就完成了一大半。下面这张表是我在移植驱动时最常对照的清单覆盖了初始化、校准和数据读取所需的核心寄存器。其中数据寄存器地址在不同时期的数据手册中标注略有差异实际写驱动时以你手头手册为准或者直接沿用第三方开源驱动里验证过的宏定义。寄存器名地址方向作用STATUS0x00只读转换完成位、错误位CAP_EXC_A0x02读写激励源 A 配置CAP_EXC_B0x03读写激励源 B 配置CAP_SETUP0x04读写电容转换使能、输入选择CAP_CFG0x0B读写转换模式、迟滞、内部 DACCFG0x0C读写电压/温度转换选择、主模式配置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); /* CAPEN1使能电容转换 */ 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 { ad774548 { 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 位是否符合预期。这一招也适用于检查芯片是否一直处于初始化失败状态比反复改设备树参数快得多。本文还有配套的精品资源点击获取