资讯动态

AD7745电容测量芯片驱动开发:I2C时序与寄存器配置实践

发布时间:2026/9/13 9:07:58 来源:尧图企业网站定制
简介AD7745高精度24位Σ-Δ ADC官方驱动与参考例程主要面向需要实现精密数据采集、传感器接口与工业测量功能的嵌入式开发者尤其适合希望直接调用成熟驱动、规避底层通信调试问题的用户。资源包为RAR格式整体仅14KB共5个文件包含2个C源文件、2个头文件以及1个zip示例压缩包源码覆盖AD7745/AD7746的底层通信接口、寄存器读写、连续/单次转换模式及中断管理等模块头文件注释较完整便于快速移植到MCU工程中。已有710人学习下载可用于数据采集、温度测量、压力检测等场景。压缩包内不仅提供官方驱动函数还附带通用参考示例能够帮助开发者根据具体应用需求调整增益、滤波与电源管理设置正确处理转换结果与错误状态显著提升高精度模拟量采集的可靠性并减少底层驱动开发的重复工作量。1. AD7745 拿到手先别急着读数据驱动分层决定了后续开发效率第一次调 AD7745 时我直接拿万用表量 CIN1 引脚对地电容再读寄存器读数却在一百多 pF 上下乱跳。折腾了半个下午才明白问题不在芯片而在驱动里少了一步对状态寄存器的处理。AD7745 是带电容测量前端的 24 位 Σ-Δ ADCI2C 接口电容通道能做到约 4 fF 有效分辨率常用于环境湿度、电容式液位、材料形变这类微弱电容变化场景。官方驱动压缩包把 Communication.c 和 AD7746.c 分得很开这套分层逻辑看懂之后不只 AD7745 能用后续换 AD7746、AD7747 甚至其他 Σ-Δ ADC 都能沿用。对刚接手这类器件的嵌入式工程师来说最值得花时间的不是调通 I2C而是把寄存器时序和驱动骨架理清楚。2. AD7745 的 I2C 从机协议与 Communication.c 读写实现2.1 7 位地址与重复起始位的读时序AD7745 的 I2C 从机地址是固定的7 位地址 0x48不含读写方向位。换算成总线上的字节写方向是 0x90读方向是 0x91。很多人在手写 I2C 驱动时习惯把 0x90 直接发给硬件结果收不到 ACK原因就是某些 MCU 的 I2C 外设比如 STM32 HAL 库只接受 7 位地址由库内部再补一位方向位。地址一旦写成 0x90HAL 库会认为你要访问地址 0x24总线上一片死寂。读取单个寄存器的标准时序是发起起始位发送写方向从机地址发送寄存器地址再发一次重复起始位然后切读方向收数据。这里必须用重复起始位而不是先 STOP 再 START因为 AD7745 在 STOP 之后会结束当前传输上下文某些固件版本下会导致下一次读操作返回上一笔数据。官方驱动 Communication.c 里通常就把这套逻辑封装成一个函数。下面是按位操作方式实现的版本#define AD7745_I2C_ADDR 0x48 /* 7 位从机地址 */ uint8_t AD7745_ReadReg(uint8_t reg_addr) { uint8_t val 0; i2c_start(); I2C_SendByte((AD7745_I2C_ADDR 1) | 0x00); /* 写方向0x90 */ I2C_SendByte(reg_addr); /* 寄存器地址 */ i2c_start(); /* 重复起始位 */ I2C_SendByte((AD7745_I2C_ADDR 1) | 0x01); /* 读方向0x91 */ val I2C_RecvByte(0); /* 0 表示回 NACK */ i2c_stop(); return val; }代码里 I2C_RecvByte 的参数 0 表示接收最后一个字节后主机回 NACK这是单字节读的标准收尾方式。如果回 ACK从机还会继续把下一寄存器的数据往 SDA 上推总线状态就乱了。这个函数是后续所有高级操作的地基建议单独编译进驱动层不要和应用代码混在一起。2.2 地址自增的批量读取方式电容转换结果是 24 位数据存放在 0x01、0x02、0x03 三个连续寄存器中高位在前。AD7745 支持地址自增主机连续读时内部地址自动加一。利用这个特性一次突发读三字节比三次单字节读省掉两次从机地址交互更重要的是避免在读与读之间被更高优先级的中断插入导致三字节数据跨了两轮转换周期拼出一个错误值。void AD7745_ReadCapData(uint32_t *cap_raw) { uint8_t buf[3] {0}; i2c_start(); I2C_SendByte((AD7745_I2C_ADDR 1) | 0x00); I2C_SendByte(0x01); /* 电容数据起始地址 */ i2c_start(); I2C_SendByte((AD7745_I2C_ADDR 1) | 0x01); buf[0] I2C_RecvByte(1); /* 回 ACK */ buf[1] I2C_RecvByte(1); /* 回 ACK */ buf[2] I2C_RecvByte(0); /* 最后一字节回 NACK */ i2c_stop(); *cap_raw ((uint32_t)buf[0] 16) | ((uint32_t)buf[1] 8) | (uint32_t)buf[2]; }注意 buf 内字节是大端排列buf[0] 是最高字节buf[2] 是低字节。拼接成 32 位无符号数后bit23 对应 buf[0] 的最高位。这个拼接顺序如果写反读数会出现类似“数值每 256 跳变一次”的规律性错误排查起来非常隐蔽。2.3 与单片机硬件 I2C 对接时的注意事项如果直接用 STM32 的 HAL 库不必自己拼重复起始位I2C_Mem_Read 内部会处理好。但需要留意两个参数MemAddress 传寄存器地址DevAddress 传 7 位地址 0x48千万别传 0x90。另一个问题是 DMA 模式下HAL 会把寄存器地址当作第一个发送字节写入总线如果本地缓冲区没有先清零首次 DMA 读取会在最高位字节里混入上一次内存里的残留数据。我一般会在每次读取前先 memset 缓冲区或者在 DMA 完成回调里丢弃第一包数据只保留后续报文。3. AD7746.c 初始化序列寄存器映射、工作模式与通道配置3.1 AD7745/AD7746 寄存器映射速览AD7745 与 AD7746 寄存器布局完全一致所以官方驱动把源文件命名成 AD7746.c 一点不奇怪。先看寄存器地图后面初始化代码才读得懂。地址寄存器名称功能说明0x00STATUS转换完成标志、激励错误标志0x01-0x03CAP_DATA电容通道转换结果24 位大端0x04-0x06VT_DATA温度 / 电压通道结果24 位大端0x07CAP_SETUP电容通道使能、PGA 增益设置0x08VT_SETUPVT 通道模式片内温度、外部温度、VDD、外部电压0x09EXC_SETUP激励输出电平、时钟使能0x0ACFG转换模式、斩波、同步控制0x0BCAPDACA正向电容补偿 DAC0x0CCAPDACB负向电容补偿 DAC0x0D-0x0ECAP_OFFSET电容偏移校准字0x0F-0x10CAP_GAIN电容增益校准字CAP_DATA 是驱动最常读的寄存器初始化阶段反而很少动它。VT_DATA 是 24 位温度或电压数据具体代表什么物理量由 0x08 寄存器决定。3.2 初始化步骤从空闲模式到连续转换AD7745 上电后默认处于空闲模式必须先完成配置再启动转换。常见初始化顺序是先读一次状态寄存器清掉上电残留标志然后配置电容通道使能和增益再配置激励源设置转换模式最后关闭 CAPDAC 或按需求设置补偿值。void AD7745_Init(void) { uint8_t status; status AD7745_ReadReg(0x00); /* 清除上电残留状态 */ (void)status; /* CAP_SETUPbit71 使能电容通道bit6:500 增益 1 倍 */ AD7745_WriteReg(0x07, 0x80); /* EXC_SETUPbit11 激励输出使能bit5:20010 激励电压 VDD/2 */ AD7745_WriteReg(0x09, 0x84); /* CFGbit1:010 连续转换模式bit21 使能斩波 */ AD7745_WriteReg(0x0A, 0x06); /* 先关掉 CAPDAC让数据裸奔一遍确认基线 */ AD7745_WriteReg(0x0B, 0x00); AD7745_WriteReg(0x0C, 0x00); }这段初始化里 CFG 寄存器写成 0x06含义是连续转换加斩波使能。斩波CAPCHOP位推荐打开它能抑制运放失调和低频噪声对 24 位 ADC 来说收益非常明显。代价是每次转换时间会变长一点具体延迟取决于数字滤波器配置数据手册给出的典型转换时间在 20ms 到 90ms 之间。如果你的应用需要更高刷新率可以关掉斩波但读数噪声会明显变大对后续数据处理要求更高。3.3 CAPDAC 与激励电压的配置逻辑这是 AD7745 驱动里最值得深入讲的部分。芯片输入端有一个约 4 pF 的固定寄生电容而测量量程只有 ±4 pF单端或 ±8 pF差分。当待测传感器静态电容是 10 pF 或 20 pF 时直接测肯定超量程。CAPDAC 的作用就是把静态电容“顶掉”相当于给测量结果做一次数字层面的电平搬移。CAPDAC 是 6 位可编程 DAC每个 LSB 约 21 fF正负方向由符号位决定。初始化时估算一下传感器静态电容再设置 CAPDACA 的数值/* 假设传感器在无负载状态下电容约 12.5 pF */ float cap_offset_pf 12.5f; uint8_t capdac_code; capdac_code (uint8_t)(cap_offset_pf / 0.021f); /* 21 fF 对应 0.021 pF */ if (capdac_code 63) capdac_code 63; AD7745_WriteReg(0x0B, 0x80 | capdac_code); /* bit71 表示正方向 */0x0B 寄存器最高位是方向控制低 6 位是补偿数值。补偿精度不需要太高残留的偏差可以通过后面的软件校准消除。注意 CAPDAC 设置完成后要重新校准后才能保证线性度官方驱动里一般会在初始化末尾读几次当前输出把平均值作为软件基线保存下来。4. AD7745 数据读取路径、中断管理与错误恢复4.1 单次转换与连续转换的读取路径AD7745 支持单次转换和连续转换两种模式驱动里最常用的切换点就在 CFG 寄存器的 bit1:0。单次转换适合电池供电的设备把模式位设为 01启动转换后芯片在完成转换后自动切回空闲典型电流消耗比连续模式低很多。连续转换则适合需要持续监测的场景比如湿度记录仪配置成模式 10 后芯片不停转换主机按自己的节奏来读。数据读取路径上我通常的做法是主循环里轮询状态寄存器uint8_t AD7745_StartSingle(void) { uint8_t cfg AD7745_ReadReg(0x0A); cfg ~0x03; /* 清掉模式位 */ cfg | 0x01; /* 置为单次转换 */ AD7745_WriteReg(0x0A, cfg); return 0; } uint8_t AD7745_ReadCapDataBlocking(uint32_t *raw, uint32_t timeout_ms) { uint32_t tick 0; while (tick timeout_ms) { uint8_t st AD7745_ReadReg(0x00); if ((st 0x08) 0) { /* bit3 RDYCAP0 表示转换完成 */ AD7745_ReadCapData(raw); return 0; } DelayMs(1); tick; } return 1; /* 超时 */ }状态寄存器 0x00 的 bit3 是 RDYCAP转换完成后自动清零。这个轮询函数在 while 循环里调用 AD7745_ReadReg 去读状态每次 I2C 交互大概耗时几百微秒对大多数传感器应用足够了。如果系统里还有 Modbus 帧接收、其他传感器轮询任务可以把这个超时值设成 100ms避免阻塞主任务太久。4.2 外部 RDY 引脚触发中断读取AD7745 有一颗 RDY 输出引脚转换完成后拉低。这颗引脚可以直接接到单片机的 EXTI 外部中断输入端让中断服务函数去读取转换结果。相比轮询这种方式在连续转换模式下能把 CPU 占用降到最低同时保证每次数据都能及时取走不会出现轮询间隔过密或过疏导致的读数抖动。void EXTI2_IRQHandler(void) /* 假设 RDY 接在 GPIO2 */ { uint32_t raw; if (EXTI_GetITStatus(EXTI_Line2)) { EXTI_ClearITPendingBit(EXTI_Line2); AD7745_ReadCapData(raw); /* 中断里只读寄存器 */ cap_raw_latest (uint32_t)raw; /* 放入全局变量 */ } }把 volatile 修饰的全局变量 cap_raw_latest 留给主循环消费中断服务函数里不要做滤波、格式转换这类耗时操作。24 位数据的拼接是几条位运算指令在中断里执行没有风险。但 I2C 读取本身如果走的是阻塞等待要注意优先级分配如果 I2C 中断优先级低于 EXTI而 EXTI 服务函数里又调用了需要等下 I2C 中断完成的阻塞读死锁条件就成立了。解决办法是只设置一个标志位把真正的 I2C 读放到主循环里做或者确认 I2C 中断优先级比 EXTI 高。4.3 错误标志与超时恢复机制AD7745 状态寄存器里还有一个错误标志位负责提示激励配置问题。ECERR 位被置 1 时通常意味着激励输出配置与输入通道不匹配比如把激励关掉了但电容通道还开着。驱动里需要给这个标志位处理留一条路径检测到错误后强制软件复位重新执行初始化序列而不是继续读数据。否则芯片会一直输出无意义的铺底数据从应用层看就是“读回的值漂移很大”。超时恢复也是驱动不可少的部分。I2C 总线偶尔会因为外部干扰进入异常状态从机不响应 ACK这时如果驱动直接卡死在读操作里整个任务就挂了。我之前在批量测试里遇到过一次一个机械接触不良的电容夹具导致 I2C 总线每次读到固定地址就 NACK整条产线的测试程序全部卡死。后来在驱动里给所有 I2C 操作加了超时计数和复位逻辑问题才解决。void AD7745_Recovery(void) { /* 尝试释放总线产生 9 个 SCL 时钟脉冲 */ for (uint8_t i 0; i 9; i) { i2c_start(); i2c_stop(); } AD7745_WriteReg(0x0A, 0x00); /* 切回空闲模式 */ AD7745_Init(); /* 重新初始化 */ }这段恢复逻辑的思路是总线卡死时通过 9 个起始-停止脉冲让从机内部的位同步状态机复位然后重新按正常流程初始化。实际项目中我还会在恢复前后各读一次状态寄存器把内容打印到日志里方便现场定位是硬件还是软件问题。5. AD7745 驱动移植到 STM32 时最容易被忽略的四个坑5.1 移植前先把 Communication.c 隔离出来官方驱动里的 Communication.c 直接操作寄存器移植到 STM32 时不要改这个文件的逻辑而是新增一个 stm32_i2c_adapter.c把里头的 I2C_Start、I2C_SendByte、I2C_RecvByte 全部映射到 STM32 HAL 库函数上。这样 AD7746.c 上层逻辑一行不用动后续换 NXP、GD32 或者 ESP32 平台只需要重写 adapter 层的六个函数。5.2 用逻辑分析仪验证首笔读时序移植完成后第一件事不是读数据而是抓 I2C 波形。逻辑分析仪触发条件设为 Start 条件抓完整笔初始化序列检查几个关键点从机地址字节的 ACK 是否出现、寄存器地址是否正确、重复起始位后读方向字节的 ACK、以及最后一个字节是不是 NACK。常见问题是 HAL 库在 I2C_Mem_Read 时对地址的处理和官方驱动不同导致读出来的全是 0xFF。5.3 地址移位、字符合并与 RDY 电平边沿地址移位问题前面 2.1 节提过这里补充一个验证方法直接读 0x00 状态寄存器如果返回 0x04 或 0x20 这类非 0xFF 的数值说明地址配置没问题。如果一直是 0xFF优先怀疑地址位和方向位搞反了。虚拟机场景下如果碰到 I2C 总线插入外部设备导致地址冲突可以直接用 i2cdetect 扫一遍挂载在总线上的所有设备地址确认 0x48 没有被同一总线上的其他外设占用。RDY 引脚边沿选择上注意 AD7745 的 RDY 是开漏输出外部必须接上拉电阻配置 EXTI 时选下降沿触发。如果选的上升沿中断永远不会来因为引脚被拉低后保持低电平。5.4 用已知电容验证测量链路最后我一般会在传感器焊盘上并一个标称值 5 pF、精度 1% 的贴片电容然后隔一小时读一百次取平均看偏差有没有超过 0.1 pF。这个验证方法能同时检验驱动程序、I2C 时序和电源噪声水平。如果读数跳动超过 0.2 pF先检查激励电压设置是否稳定再量 VDD 纹波最后才怀疑驱动代码。整个验证过程不改一行驱动代码只通过调整 EXC_SETUP 和 CAPDAC 两个寄存器来排查这也是官方驱动把寄存器访问接口单独暴露出来的意义所在。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价