
简介一套完整的芯片驱动工程基于官方固件库可直接在主流集成开发环境中打开使用。它所驱动的模拟数字转换器具有十六位分辨率、四个输入通道内置可编程增益放大器适用于工业测量与传感器信号采集场景也适合医疗设备和科学仪器使用。压缩包内共有一百九十二个文件大小约八点八五兆字节包含芯片数据手册、驱动源码、工程配置文件以及编译过程中生成的中间文件目录按内核、外设库、用户代码等模块划分清晰。目前已有五百六十一人学习下载。借助现成的引脚图表硬件工程师可以快速确认接线方式跟随示例代码开发者可以掌握寄存器初始化、数据读取和错误处理的完整流程大大减少驱动调试时间。1. ADS112C04I2C 接口的 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: PB6SCL, PB7SDA */ 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、参考源0x00CONFIG10x01MODE、DR[2:0]、CM转换模式、数据率、比较器0x04CONFIG20x02TS[1:0]、VREF_INT、LOW_SIDE温度传感器开关、内部参考、低侧开关0x10CONFIG30x03IDAC[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 差分输入PGA1内部参考 */ 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、PGA1、参考源由 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 SPS50ms60ms90 SPS11ms15ms330 SPS3ms5ms1000 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 对应负满量程。假如 vref2.048V、gain1原始码 0x7FFF 约等于 2.048V0x8000 约等于 -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 波形、寄存器值、内部温度三点都对上之后再接外部传感器接上后读数异常范围就只剩下模拟前端。本文还有配套的精品资源点击获取