news 2026/9/13 6:19:00

I2C接口16位ADC ADS112C04驱动开发与调试指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
I2C接口16位ADC ADS112C04驱动开发与调试指南

简介:一套完整的芯片驱动工程,基于官方固件库,可直接在主流集成开发环境中打开使用。它所驱动的模拟数字转换器具有十六位分辨率、四个输入通道,内置可编程增益放大器,适用于工业测量与传感器信号采集场景,也适合医疗设备和科学仪器使用。压缩包内共有一百九十二个文件,大小约八点八五兆字节,包含芯片数据手册、驱动源码、工程配置文件以及编译过程中生成的中间文件,目录按内核、外设库、用户代码等模块划分清晰。目前已有五百六十一人学习下载。借助现成的引脚图表,硬件工程师可以快速确认接线方式;跟随示例代码,开发者可以掌握寄存器初始化、数据读取和错误处理的完整流程,大大减少驱动调试时间。

1. ADS112C04:I2C 接口的 16 位 ΔΣ ADC 到底解决什么问题

设想你手上有一颗 PT100、一只称重传感器和一个只剩 I2C 引脚的单片机。ADS112C04 就是为这种场景准备的:16 位分辨率、4 路模拟输入、内部可编程增益放大器、内部温度传感器,全部通过 I2C 访问。它与知名度更高的 ADS1120 属于同一系列,后者走 SPI,它把通信引脚压到 SDA/SCL 两根,代价是吞吐率更低,所以定位很明确:低速高精度采集。下面从引脚图开始,依次讲驱动层、读取程序和调试手段,覆盖寄存器配置、I2C 时序、数据换算和常见故障。适合正在画传感器板或把现有 SPI ADC 迁移到 I2C 的工程师。

2. ADS112C04 引脚图对照与最小硬件连接

2.1 引脚图上有五类信号,先按功能域分组

拿到 ADS112C04 的引脚图,不要先背引脚号,按功能域分组记忆会快很多。整个器件可以分成电源、模拟输入、参考电压、I2C 通信、地址/状态五组。按这个顺序去对照手册里的引脚图,基本不会漏接。

功能域引脚/网络接法说明常见坑
模拟电源AVDD2.7V~5.5V,靠近引脚放 100nF与数字电源共用时噪声会被采集进去
数字电源DVDD与 AVDD 同电压DVDD 波动会导致 I2C 电平不稳
AGND / DGND单点汇合,AGND 下方铺完整地DGND 电流大时参考电压会漂
模拟输入AIN0~AIN3差分接传感器,未用输入接 AGND悬空输入会读成随机跳动
参考输入REFP0 / REFN0外部低噪声基准;验证阶段可以改内部参考直接用 AVDD 做参考会损失精度
I2CSDA / SCL必须开漏 + 外部上拉推挽配置会让总线锁死
地址选择AD0接 GND 或 DVDD 切换地址同总线多个 ADC 会冲突
状态输出DRDY/ALERT可上拉后接 MCU 中断不接就按最坏转换时间延时等待

注意:不同封装的丝印顺序有差异,画 PCB 时以数据手册里的封装图为准,不要直接套用网上引脚图上的编号。

2.2 SDA/SCL 开漏为什么需要外部上拉

ADS112C04 的 SDA/SCL 是开漏输出,这是 I2C 的标准做法。MCU 侧必须配置成开漏模式,并接上拉电阻到 DVDD。上拉电阻常见取值 2.2kΩ 到 4.7kΩ;如果总线上挂了多个从机,上拉电阻要相应减小,否则上升沿太慢,高速通信会出现随机错误。STM32 的引脚配置是这样的:

/* I2C1: PB6=SCL, PB7=SDA */ GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_I2C1_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_6 | GPIO_PIN_7; GPIO_InitStruct.Mode = GPIO_MODE_AF_OD; /* I2C 必须开漏 */ GPIO_InitStruct.Pull = GPIO_PULLUP; /* 内部上拉做兜底 */ GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Alternate = GPIO_AF4_I2C1; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

关键在GPIO_MODE_AF_OD。如果误配成推挽输出,SDA/SCL 会在总线空闲时主动输出高电平,与开漏从机冲突,表现就是 I2C 总线卡死、SCL 波形异常。内部上拉只是兜底,不能替代外部电阻,因为 MCU 内部上拉一般在 40kΩ 左右,驱动快 I2C 总线时上升沿会明显变缓。

2.3 电源和参考电压是读数稳不稳的根源

AGND 和 DGND 通常建议单点连接,ADC 下方铺完整地平面。AVDD 和 DVDD 都要加 100nF 电容,位置尽量靠近电源引脚。如果传感器距离远,模拟输入端再加 RC 低通,时间常数按目标信号频率选,比如 PT100 的响应本身很慢,RC 取 1kΩ + 100nF 不会影响测量。

参考电压对读数影响最大。验证阶段最省事的方法是使用内部参考,不接 REFP0/REFN0;要追求精度时再接外部基准。第一次上电调程序时,变量越少越好,内部参考能排除一大类接线问题。

2.4 硬件先查这几个点再写程序

不要上电后直接调寄存器。先用示波器看 SCL 空闲电平是否为 DVDD,用万用表量 AVDD/DVDD 是否都在规格内,再确认 SDA/SCL 上确实有外部上拉电阻。这三个点出问题,后面程序无论怎么写都是白费。

3. ADS112C04 驱动的 I2C 命令层与四个配置寄存器

3.1 RREG/WREG 命令怎么发

ADS112C04 的寄存器操作通过 I2C 写命令字节实现。读一个寄存器时,单片机先写 RREG 命令和寄存器号,然后从设备返回该寄存器内容;写一个寄存器时,直接写 WREG 命令、寄存器号和要写入的值。下面这个封装是后面所有驱动的基础:

#define ADS112C04_I2C_ADDR 0x40 /* 7-bit 地址,AD0 接 GND */ #define CMD_RREG 0x20 /* 0b0010rrrr */ #define CMD_WREG 0x40 /* 0b0100rrrr */ #define CMD_START 0x08 /* 开始单次/连续转换 */ #define CMD_RESET 0x06 /* 软件复位 */ #define CMD_RDATA 0x10 /* 读取最新转换结果 */ static uint8_t ads112c04_i2c_addr = ADS112C04_I2C_ADDR << 1; /* HAL 需要 8-bit 地址 */ uint8_t ads112c04_write_reg(uint8_t reg, uint8_t val) { uint8_t buf[3]; buf[0] = CMD_WREG | (reg & 0x0F); /* 高 4 位是命令,低 4 位是寄存器地址 */ buf[1] = 0x00; /* 单寄存器写,寄存器数 N-1 = 0 */ buf[2] = val; return HAL_I2C_Master_Transmit(&hi2c1, ads112c04_i2c_addr, buf, 3, 100) == HAL_OK; } uint8_t ads112c04_read_reg(uint8_t reg) { uint8_t cmd[2] = { CMD_RREG | (reg & 0x0F), 0x00 }; uint8_t val; HAL_I2C_Master_Transmit(&hi2c1, ads112c04_i2c_addr, cmd, 2, 100); HAL_I2C_Master_Receive(&hi2c1, ads112c04_i2c_addr, &val, 1, 100); return val; }

这里最容易被坑的是地址左移。STM32 HAL 的DevAddress参数要求 7 位地址左移 1 位,如果直接把 0x40 传进去,总线上实际发的是 8 位地址 0x81,芯片永远不会 ACK。寄存器地址只有 0 到 3,所以reg & 0x0F完全够用。

3.2 四个配置寄存器一张表说清楚

ADS112C04 的寄存器不多,但每个位域的含义都直接影响测量结果。上电后先按这张表逐项核对,可以少走很多弯路:

寄存器地址主要位域作用典型值
CONFIG00x00MUX[1:0]、GAIN[2:0]、PGA_BYPASS、VREF_SEL[1:0]输入通道、增益、PGA、参考源0x00
CONFIG10x01MODE、DR[2:0]、CM转换模式、数据率、比较器0x04
CONFIG20x02TS[1:0]、VREF_INT、LOW_SIDE温度传感器开关、内部参考、低侧开关0x10
CONFIG30x03IDAC[2:0]、I1MUX[2:0]、I2MUX[2:0]恒流源激励电流和输出脚0x00

典型值只代表一种常见配置,不是固定答案。比如把 CONFIG0 改成其他 MUX 值,通道就切到 AIN2/AIN3;把 CONFIG2 的温度传感器位打开,读取的数据就变成裸片温度。

3.3 初始化序列先复位再写寄存器

驱动初始化一定要先发软件复位,再逐个写寄存器。否则芯片可能停留在上次的未知配置里,读回来的寄存器值和你设的完全对不上。初始化函数如下:

uint8_t ads112c04_init(void) { uint8_t cmd = CMD_RESET; HAL_Delay(10); /* 复位后等待内部上电稳定 */ if (HAL_I2C_Master_Transmit(&hi2c1, ads112c04_i2c_addr, &cmd, 1, 100) != HAL_OK) return 1; /* AIN0-AIN1 差分输入,PGA=1,内部参考 */ ads112c04_write_reg(0x00, 0x00); /* 单次转换模式,数据率 20 SPS */ ads112c04_write_reg(0x01, 0x04); /* 关闭内部温度传感器,启用内部参考 */ ads112c04_write_reg(0x02, 0x10); /* 关闭 IDAC */ ads112c04_write_reg(0x03, 0x00); return 0; }

每个参数都有讲究。CONFIG0 写成 0x00,表示差分输入 AIN0-AIN1、PGA=1、参考源由 VREF_SEL 决定;CONFIG1 的 0x04 把芯片置于单次转换模式,数据率约 20 SPS,这一个档位对 50Hz 工频有抑制作用;CONFIG2 的 0x10 打开内部 2.048V 参考,不依赖外部基准;CONFIG3 为 0x00 时 IDAC 关闭,避免恒流源电流流进信号源。

3.4 单次转换和连续转换的配置差异

单次转换的流程是:发 START 命令,等转换完成,读数据,芯片自动回到空闲模式。这个模式功耗低,多个传感器轮询时逻辑最可控。连续转换是把 CONFIG1 的模式位改成连续,发一次 START 后芯片自己转,之后每次 RDATA 都拿最新结果。高速采样时用连续模式,但要注意连续模式下中途读寄存器可能对转换有干扰,所以轮询多通道时我一般用单次转换。

4. ADS112C04 读取程序:原始码到物理量的换算与滤波

4.1 先跑通单次转换读取函数

一个能用的读取函数要处理三件事:发启动命令、等待转换完成、读回两个字节。代码可以这样写:

int16_t ads112c04_read_raw(void) { uint8_t cmd = CMD_START; uint8_t data[2] = {0}; HAL_I2C_Master_Transmit(&hi2c1, ads112c04_i2c_addr, &cmd, 1, 100); HAL_Delay(60); /* 20 SPS 对应 50ms,多留 10ms 余量 */ uint8_t rcmd = CMD_RDATA; HAL_I2C_Master_Transmit(&hi2c1, ads112c04_i2c_addr, &rcmd, 1, 100); HAL_I2C_Master_Receive(&hi2c1, ads112c04_i2c_addr, data, 2, 100); return (int16_t)((data[0] << 8) | data[1]); }

等待时间由 CONFIG1 里的数据率决定。20 SPS 时一个周期是 50ms,留 60ms 余量;把数据率调到更高档,延时可以缩短:

目标数据率最小等待时间代码建议值
20 SPS50ms60ms
90 SPS11ms15ms
330 SPS3ms5ms
1000 SPS1ms2ms

如果板上拉了 DRDY/ALERT 中断脚,把 HAL_Delay 去掉,改成等待 GPIO 下降沿,时序更精确,也不会受延时余量影响。

4.2 双极性补码怎么转电压

ADS112C04 的输出是 16 位二进制补码,正满量程 0x7FFF,负满量程 0x8000。电压换算公式:

float ads112c04_raw_to_volt(int16_t raw, float vref, float gain) { return ((float)raw * vref) / (32768.0f * gain); }

注意这里除的是 32768.0f,不是 65535。补码的正半轴是 32767 对应正满量程,负半轴到 -32768 对应负满量程。假如 vref=2.048V、gain=1,原始码 0x7FFF 约等于 2.048V,0x8000 约等于 -2.048V。PGA 增益在 CONFIG0 里设置,换算时要除一遍 gain,否则读出来的电压会整体偏大。

4.3 用比例法把 RTD 电阻算出来

测 PT100 时不要直接拿算出的电压除以激励电流,因为流过 Pt100 的电流和参考电压会同时漂。常见做法是让同一个 IDAC 同时流过 PT100 和精密基准电阻 RREF,用 ADC 分别读两个电阻上的电压比,这样参考电压和激励电流在比例项里同时约掉:

#define RREF 4300.0f /* 精密基准电阻,单位欧姆 */ float ads112c04_measure_rtd(void) { int16_t raw_rtd = ads112c04_read_raw_ch(0); /* RTD 通道 */ int16_t raw_ref = ads112c04_read_raw_ch(1); /* RREF 通道 */ float ratio = (float)raw_rtd / (float)raw_ref; return ratio * RREF; }

raw_rtd / raw_ref是比例测量的核心。真实的 PT100 线性化还要查分度表或使用 Callendar-Van Dusen 方程,驱动层只负责提供电阻值,不要把温度公式写死在驱动里,否则换一种传感器就要改驱动。

4.4 数据率与滤波程序

数据率越高,噪声越大。20 SPS 适合工频环境;如果必须在 1000 SPS 下工作,程序里加一个移动平均:

#define FILTER_N 16 static int16_t history[FILTER_N]; static uint8_t idx; float ads112c04_read_filtered(void) { history[idx++] = ads112c04_read_raw(); if (idx >= FILTER_N) idx = 0; int32_t sum = 0; for (uint8_t i = 0; i < FILTER_N; i++) sum += history[i]; return (float)sum / FILTER_N; }

移动平均的代价是延迟变大。16 点平均会把系统响应时间拉长 16 倍,如果场景是阀门缓变,可以接受;如果是快速突变监测,不如把数据率降下来,用硬件本身的过采样能力换稳定性。

5. 用这些手段验证 ADS112C04 驱动和硬件有没有接对

5.1 把内部温度传感器当“环回自检”

上电后先不接传感器,把 CONFIG2 的温度传感器位打开,读出来的电压和裸片温度强相关。如果读到的值稳定在 25°C 到 45°C 范围,说明 I2C 通路、寄存器写入、数据读取三段链路都正常:

float ads112c04_die_temp(void) { ads112c04_write_reg(0x02, 0xC0); /* 打开内部温度传感器 */ int16_t raw = ads112c04_read_raw(); float v = ads112c04_raw_to_volt(raw, 2.048f, 1.0f); return (v - 0.05f) / 0.003f; /* 线性近似,精度要求高请做标定 */ }

这里的 0.05V 偏移和 0.003V/°C 只是通常的典型参数,不同批次会有偏差。实际操作时先记录一个温度点,用手摸芯片看读数是否跟着变,比直接搬手册公式可靠。

5.2 排查表

现象优先检查顺序
I2C 总线无 ACKSDA/SCL 是否开漏、外部上拉有没有、7 位地址左移有没做、AD0 电平
读寄存器全 0xFFDVDD/AVDD 电源是否正常、WREG 命令第二个字节是否写成 0
转换值一直 0x7FFF输入是否超过满量程、参考电压是否太低、PGA 是否过大
转换值跳动很大模拟输入是否悬空、未用通道是否接 AGND、电源去耦电容位置
温度读数一动不动寄存器写入可能没生效,直接读回 CONFIG2 确认

5.3 把 I2C 层抽出来做注入测试

驱动写多了会发现,排错最难的不是功能,而是区分寄存器到底写没写进去。我会在驱动代码里放一个开关宏,把寄存器读写接口单独抽象出来:

#ifdef ADS112C04_MOCK #define i2c_write(reg, val) mock_write(reg, val) #else #define i2c_write(reg, val) ads112c04_write_reg(reg, val) #endif

这个开关在 PC 上配一套 mock_write,用固定的假寄存器表跑初始化代码,就能在没有硬件的情况下验证配置流程对不对。上板后万一还是不对,先用ads112c04_read_reg(0x02)把寄存器读回来和写入值对比;写入值和读回值不一致,问题大概率在 WREG 命令的第二个字节,而不是传感器。I2C 波形、寄存器值、内部温度三点都对上之后,再接外部传感器;接上后读数异常,范围就只剩下模拟前端。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/13 6:16:54

BMI健康计算器Android源码落地:从zip解压到release打包全指南

简介&#xff1a;BMI健康计算器是一款用于评估人体体重与身高比例的安卓应用源码&#xff0c;压缩包内提供完整的Android Studio工程&#xff0c;适合初学安卓开发的读者通过实战项目理解健康类应用的开发要点。资源共26个文件&#xff0c;包括Java核心逻辑、XML界面布局、PNG图…

作者头像 李华
网站建设 2026/9/13 6:14:55

MySQL慢查询排查与优化:执行计划、索引设计与SQL改写实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 6:14:31

OpenClaw多智能体系统开发指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 6:13:01

机械臂选型四步速判法:任务反推工作空间与自由度匹配

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/13 6:12:54

CiA402从站示例解析:对象字典、状态机与SDO/PDO实现

简介&#xff1a;这是一份面向伺服驱动与运动控制开发者的CiA402子协议从站字典示例包&#xff0c;围绕CANopen协议中关键的对象字典概念&#xff0c;演示如何构建符合CIA402规范的CANopen节点&#xff0c;解决设备配置、状态管理与参数读写等实际开发问题&#xff0c;适合正在…

作者头像 李华