ADS1115深度解析:寄存器配置、I2C时序与STM32实战
2026/9/9 5:28:52 网站建设 项目流程

ADS1115是我在项目里用得最多的16位 ADC,也是让不少初学者卡壳最多的地方。芯片本身不复杂,但寄存器配置、工作模式、I2C 通信时序这三件事如果没理顺,很容易出现“设备扫描不到”“读数全 0”或者“数据跳得像心电图”这类问题。这篇文章我从底层把 ADS1115 的寄存器、模式和 I2C 时序完整拆开讲,配合 STM32 的 HAL 库和模拟 I2C 代码,适合正在调嵌入式采集、或者想深入理解 I2C 协议的人参考。没有废话,全部是实操干货。

1. 项目概述与整体设计思路

1.1 为什么选型会落到 ADS1115 这颗芯片上

ADS1115 是 TI 推出的一款 16 位、低功耗、带 I2C 接口的模数转换器,集成了多路输入选择器、可编程增益放大器、内部基准源和振荡器,外围只需要接几颗电阻电容就能工作。在 24 位 ADC 满天飞的今天,它依然大量出现在电压采集、电池电量监测、电流采样、传感器信号读取等场景里,原因很直接:16 位精度对于大多数工业信号足够了,内部 PGA 可以把小信号放大后再采样,I2C 接口只占两根线,而且芯片封装极小。

我做过一个 8 通道电池组监测板,主控是 STM32F407,板子上需要同时采集每一串电芯电压。如果全用 MCU 内置 ADC,输入通道不够,而且 F407 内置 ADC 在 12 位分辨率下做多通道轮流扫,通道间的串扰和采保时间都是麻烦事。后来改成两片 ADS1115,每片 4 个单端输入,I2C 地址不同并联在同一条总线上,代码逻辑瞬间简单了。这就是这颗芯片的核心价值:用简单可靠的方式做多路模拟量采集,不用跟 ADC 的时序较劲。

1.2 硬件连接与总线上拉设计

ADS1115 的引脚不复杂:VDD 是电源,接 2.0V 到 5.5V;GND 接地;SCL 和 SDA 是 I2C 引脚;ADDR 用来配置 I2C 地址;ALERT/RDY 在比较器模式或就绪模式下给主控发信号。很多人第一次画板子只接了 VDD、GND、SCL、SDA,把 ALERT 空着,这没问题,但调试时少了一个非常有用的状态指示手段。我的习惯是预留 ALERT 到 MCU 的任意一个 GPIO,哪怕最终固件里不用,调试时用示波器或者逻辑分析仪挂在上面看转换完成信号,能省很多猜时间的工作。

I2C 总线必须要有上拉电阻,这点几乎人人知道,但阻值选多少经常被忽略。SCL 和 SDA 各接一颗上拉电阻到 VDD,常见取 4.7kΩ,适用于 100kHz 和 400kHz 总线。如果你的总线上挂的设备多、走线长,总线电容会变大,4.7kΩ 时上升沿可能太慢,建议换 2.2kΩ 甚至 1kΩ。反过来,如果追求极低功耗、总线速率只有 100kHz,10kΩ 也可以。我画板子时习惯先按 4.7kΩ 设计,预留 0402 封装的位置,出问题再换,实测下来这个方法最省事。

还有一个容易踩的电平匹配问题。ADS1115 的 VDD 如果接 5V,I2C 上拉也会到 5V,而 MCU 侧是 3.3V 电平,这时候直接连 SDA/SCL 可能超 IO 耐压,或者导致高电平判别异常。稳妥做法是 VDD 和 MCU 同电源,或者用电平转换芯片/模块。ADS1115 本身在 3.3V 供电下完全正常工作,我大多数设计直接统一用 3.3V。

1.3 ADDR 引脚决定设备地址,必须提前规划

ADS1115 的 I2C 7 位地址由 ADDR 引脚的电平状态决定,接法有四种:接地、接 VDD、接 SDA、接 SCL。对应地址如下:

ADDR 接法7位地址8位写地址8位读地址
GND0x480x900x91
VDD0x490x920x93
SDA0x4A0x940x95
SCL0x4B0x960x97

这块有一个常见坑:很多例程里写的是 0x90 或 0x91,有人会问为什么 I2C 地址不是 0x48。原因很简单,I2C 总线传输地址时是 7 位地址左移一位,最低位表示读写方向,所以 7 位地址 0x48 对应写地址 0x90、读地址 0x91。如果你用 STM32 HAL 库的HAL_I2C_Mem_Write这类接口,传入的 DevAddress 参数填 7 位地址 0x48 即可,库内部会做移位处理;如果是自己模拟 I2C,就必须在发送地址字节时手动拼上读写位。

多片 ADS1115 并联时,四个地址不够用还可以考虑 TCA9548A 这类 I2C 多路开关,但绝大多数项目两片并联四个地址足够,先把 ADDR 引脚规划好,别等到画完板子发现地址冲突。

2. 寄存器配置逐位拆解

2.1 四个寄存器的地图与访问方式

ADS1115 内部有四个寄存器,都是 16 位宽,通过 I2C 指针寄存器寻址:

指针地址寄存器名称功能
00hConversion存储最新的转换结果
01hConfig配置寄存器,控制所有工作参数
02hLo_thresh低阈值比较寄存器
03hHi_thresh高阈值比较寄存器

对 ADS1115 做任何操作,第一步往往是先写指针寄存器,告诉芯片接下来要访问哪个寄存器。比如读转换结果,必须先通过 I2C 发送指针地址 00h,然后再发起数据读取,否则芯片不知道你要读哪里。这个“先指后读”的机制是多数 I2C 从设备的标准做法,理解它之后,操作任何带寄存器映射的传感器(比如 BH1750、BMP280)都能举一反三。

我在调试时认识的一个典型错误是:有人直接把读地址发出去然后开始读数据,忘发指针地址,结果读回来的是芯片内部默认指针指向的内容,通常是上一次操作遗留的寄存器,数据自然不对。所以“先写指针再读写数据”这两步一定不能省。

2.2 Config 寄存器每一位到底控制什么

Config 寄存器是 ADS1115 最核心的部分,16 位里每一位都有明确含义。下面按位段拆开看。

  • 第 15 位 OS:在写模式下,写入 1 表示启动一次单次转换;在单次模式读回时,该位为 1 表示转换已经完成。连续模式下读回通常为 0。这个位是单次触发模式的开关,也是轮询转换状态的标志位。

  • 第 14~12 位 MUX[2:0]:输入多路选择器配置。000~011 是四种差分输入组合,100~111 分别对应 AIN0 到 AIN3 对 GND 的单端输入。单端模式下输入电压不能为负,否则读数为负值,配置时要留意。

MUX输入组合
000AINP = AIN0,AINN = AIN1(差分)
001AINP = AIN0,AINN = AIN3(差分)
010AINP = AIN1,AINN = AIN3(差分)
011AINP = AIN2,AINN = AIN3(差分)
100AINP = AIN0,AINN = GND(单端)
101AINP = AIN1,AINN = GND(单端)
110AINP = AIN2,AINN = GND(单端)
111AINP = AIN3,AINN = GND(单端)
  • 第 11~9 位 PGA[2:0]:可编程增益放大器的满量程范围。增益越大,能测量的最大输入电压越小,但对应的 LSB 精度越高。满量程 ±4.096V 时 LSB 约 125μV,±0.256V 时 LSB 约 7.8μV。选 PGA 的原则是让信号尽量占满量程,同时又不能超量程,否则读数饱和在 0x7FFF 或 0x8000 附近。
PGA满量程范围LSB 大小
000±6.144V187.5μV
001±4.096V125μV
010±2.048V62.5μV
011±1.024V31.25μV
100±0.512V15.625μV
101±0.256V7.8μV
110±0.256V7.8μV
111±0.256V7.8μV
  • 第 8 位 MODE:0 表示连续转换模式,1 表示单次模式。我下面会单独讲两者的行为差异和选型。

  • 第 7~5 位 DR[2:0]:数据速率,从 8SPS 到 860SPS 共 8 档。速率越低,噪声越低,精度越高。采样电池电压这种缓变信号,128SPS 已经非常充裕,我一般直接拉到 128SPS 或更低,既稳定又省电。

  • 第 4 位 COMP_MODE:比较器模式,0 表示传统比较器(窗口比较或阈值比较),1 表示窗口比较器。不用的项目通常忽略。

  • 第 3 位 COMP_POL:比较器输出极性,0 低电平有效,1 高电平有效。这个会影响 ALERT 引脚的判读逻辑。

  • 第 2 位 COMP_LAT:比较器锁存使能,0 表示非锁存,1 表示锁存。锁存模式下需要读 Config 寄存器才能清除。

  • 第 1~0 位 COMP_QUE[1:0]:比较器触发后需要的连续转换次数,11 表示禁用比较器并让 ALERT 引脚处于高阻状态。如果不使用 ALERT 功能,这两位置 11 最省心。

我常用的配置举例:采集 AIN0 对 GND 的电压,满量程 ±4.096V,连续转换,128SPS,禁用比较器。MUX = 100,PGA = 001,MODE = 0,DR = 100,COMP_QUE = 11,组合起来就是二进制 0100 0010 0100 0011,对应十六进制 0x4243。绝大多数 ADS1115 例程里都能看到这个值,现在你明白它的每一位是怎么来的了。

2.3 阈值寄存器的默认值陷阱

Lo_thresh 和 Hi_thresh 在比较器模式下才会用到。上电默认值时 Lo_thresh = 0x8000,Hi_thresh = 0x7FFF,如果开启了比较器功能但不好好设置阈值,ALERT 引脚可能一上电就保持触发状态,导致主控误判。我的建议是凡是不用比较器的项目,Config 寄存器里直接关闭比较器(COMP_QUE = 11),阈值寄存器保持默认值不用管。

但如果你用到了 ALERT/RDY 的转换就绪功能,注意它的配置方式和比较器是同一个引脚,要通过 Config 里的比较器相关位把引脚行为配成“转换完成后拉低”而不是“超过阈值拉低”。这个功能我建议初期先不用,等基本采集逻辑跑通了再开,否则排查问题时容易把两个信号混在一起,分不清到底是数据就绪还是报警。

3. 工作模式深度对比与选型

3.1 连续转换模式的行为逻辑与潜在风险

连续转换模式下,ADS1115 上电后或者写入 Config 后,会按照 DR 设定的速率不间断地执行模数转换,每次转换完成后自动把结果写入 Conversion 寄存器,接着继续下一次转换,不需要主控干预。

连续模式看起来省事,但有一个隐患:如果主控读取频率低于转换频率,Conversion 寄存器的旧数据会被新数据覆盖,读出来的永远是“最近一次”转换结果,你无法判断两次读数之间到底隔了几次转换。在某些实时性要求高的场景这不算问题,但如果你想做多通道同步采集或者需要判断数据有效性,就会心里没底。ADC 的转换结果就绪本身不会通过 I2C 线报告,除非你用了 ALERT 引脚。

从 I2C 时序角度看,连续模式下主控可以在任意时刻发起读数请求,芯片会立刻返回当前 Conversion 寄存器的内容。这里要注意的是,如果读高字节和低字节之间恰好奇偶发生了一次新转换,理论上存在读到的新旧数据拼接风险。ADS1115 的数据手册其实没有明确给出多字节读的锁存机制,我实测时极少遇到这个问题,但在高数据速率(475SPS、860SPS)下如果发现数值偶发跳变,可以考虑把速率降下来,或者改用下面说的单次模式。

3.2 单次触发模式的流程与省电优势

单次模式的触发方式很简单:写 Config 寄存器时把 OS 位置 1,芯片执行一次转换,完成后自动进入断电状态,功耗几乎可以忽略。读取时用轮询 OS 位或者延时等待,然后读 Conversion 寄存器即可。

我做一个两节 18650 电池电压监测模块时,主控每秒只读一次电压,如果全程用连续模式,ADS1115 一直以 128SPS 跑着,光 ADC 的电流就白白耗掉不少。改成单次模式后,每次测量从触发到出结果不到 10ms,其余时间芯片都在亚微安级的断电状态,整机待机电流降到非常低。对于电池供电的采集设备,单次模式几乎是必须的选择。

单次模式下的时间预算要算清楚。比如 DR 设置为 128SPS,对应转换时间约 1/128 ≈ 7.8ms;如果 DR 设置为 8SPS,一次转换约 125ms。读数据前等待时间最好取转换周期再加一点余量,别刚发完触发就读数据,这时候转换还没完成,读到的可能是上一次残留值。

3.3 数据速率怎么选才合适

数据速率的选择本质上是精度和响应速度的权衡。ADS1115 的噪声指标随数据速率升高而变差,低速下有效分辨率更接近 16 位,高速下有效位数会掉。我的经验是:采集电池电压、温度这类缓变量,8SPS 到 64SPS 足够;如果需要监测 50Hz 市电波形,250SPS 以上才有意义;如果是音频或者振动信号这种高频动态,ADS1115 本身不太适合,860SPS 也偏慢,应该换内置 ADC 或者 SPI 接口的高速 ADC。

还有一个细节:单次模式下实际等待时间不只是 1/DR。启动转换后,芯片内部还有一小段初始化时间,实测下来总延迟会比理论值略高一点。所以我写等待逻辑时习惯在 1/DR 基础上多加 20%~30% 的余量,比如 128SPS 我就延时 10ms,确保万无一失。

4. I2C 通信时序逐字节拆解

4.1 I2C 协议基础:起始、地址、应答、数据、停止

如果不把 I2C 底层时序搞懂,就很难真正定位为什么 ADS1115 通信失败。I2C 总线核心概念就五个:起始条件、地址字节、应答位、数据字节、停止条件。

起始条件(START)的定义是:在 SCL 保持高电平期间,SDA 从高电平跳变到低电平。停止条件(STOP)反之,在 SCL 高电平期间,SDA 从低电平跳变到高电平。这个时序是必须严格满足的,很多自己模拟 I2C 的人写代码时在电平切换顺序上出错,最常见的错误就是 SDA 还没拉低就先拉高了 SCL,或者结束前 SCL 已经拉低。用软件模拟时,每次电平切换之间稍微加一点延时,时序反而更宽容。

地址字节由 7 位设备地址加 1 位读写标志组成,一共 8 位。发送完地址字节后,从设备会在第 9 个时钟周期拉低 SDA 表示应答(ACK),否则保持高电平就是非应答(NACK)。读数据阶段,主控每读完一个字节需要主动发送应答或非应答:还想继续读就回 ACK,读完最后一个字节必须回 NACK,然后发 STOP,否则从设备会一直输出数据。这个主控回 NACK 的动作是模拟 I2C 里最容易漏的,漏了会导致总线时序错乱。

4.2 写配置寄存器的完整时序

写 Config 寄存器(指针地址 01h)的完整时序如下:

  1. 主控发 START。
  2. 主控发送写地址字节 0x90(7 位地址 0x48 左移一位,最低位为 0 表示写)。
  3. 等待 ADS1115 返回 ACK。
  4. 主控发送指针地址 0x01,指定要访问 Config 寄存器。
  5. 等待 ACK。
  6. 主控发送 Config 寄存器高字节(例如 0x42)。
  7. 等待 ACK。
  8. 主控发送 Config 寄存器低字节(例如 0x43)。
  9. 等待 ACK。
  10. 主控发 STOP。

这里有个细节:ADS1115 的 16 位寄存器通过 I2C 传输时永远是高字节在前。有人会习惯性地先发低字节,结果寄存器配置完全错位,读回来的数据也是乱的。

用 STM32 HAL 库写这个配置过程非常简单,HAL_I2C_Mem_Write会把“发指针地址 + 发数据”两步合并成一个函数调用:

uint8_t config[2] = {0x42, 0x43}; // 高字节在前 HAL_StatusTypeDef status; status = HAL_I2C_Mem_Write(&hi2c1, 0x48, 0x01, I2C_MEMADD_SIZE_8BIT, config, 2, 100); if (status != HAL_OK) { // 处理通信错误 }

如果你用的是模拟 I2C,时序就要自己抠了。下面这段是我常用的模拟写时序,GPIO 配置成开漏输出,确保能正确释放总线:

void i2c_start(void) { SDA_HIGH(); SCL_HIGH(); delay_us(5); SDA_LOW(); delay_us(5); SCL_LOW(); } void i2c_stop(void) { SCL_LOW(); SDA_LOW(); delay_us(5); SCL_HIGH(); delay_us(5); SDA_HIGH(); delay_us(5); } void i2c_write_byte(uint8_t dat) { for (int i = 7; i >= 0; i--) { if (dat & (1 << i)) SDA_HIGH(); else SDA_LOW(); delay_us(2); SCL_HIGH(); delay_us(5); SCL_LOW(); } SDA_HIGH(); // 释放 SDA,等待从设备 ACK delay_us(2); SCL_HIGH(); delay_us(5); SCL_LOW(); }

配置过程就是把上面的函数按顺序串起来。特别注意写指针地址和写数据之间不要多插 STOP,除非你想用“先写指针后停止,再启动写数据”的分离式操作,那种写法在部分从设备上不认,统一用一次连续写最稳。

4.3 读转换结果的完整时序

读转换结果(指针地址 00h)的标准做法分两步:先写指针地址 00h,然后重新发起读操作。完整时序如下:

  1. 主控发 START。
  2. 主控发送写地址 0x90,等待 ACK。
  3. 主控发送指针地址 0x00,等待 ACK。
  4. 主控发重复起始条件(REPEATED START)。
  5. 主控发送读地址 0x91,等待 ACK。
  6. 主控读取高字节,并向从设备回 ACK。
  7. 主控读取低字节,并向从设备回 NACK。
  8. 主控发 STOP。

为什么中间要一个重复起始条件?因为一次 I2C 事务里方向不能直接反转,主机要先让从机进入“读模式”,必须重新发一次带读标志的地址。重复起始和 STOP 的区别是总线没有被释放,中间不会有其他主机插进来,读操作是原子的。

HAL 库的读法依然简单:

uint8_t buf[2]; HAL_StatusTypeDef status; status = HAL_I2C_Mem_Read(&hi2c1, 0x48, 0x00, I2C_MEMADD_SIZE_8BIT, buf, 2, 100); if (status == HAL_OK) { int16_t raw = (buf[0] << 8) | buf[1]; }

注意buf[0]是高字节。转换成实际电压时,先把两个字节拼成 int16_t,然后乘上 LSB 电压。比如 PGA 是 ±4.096V,LSB 是 125μV,实际电压就是 raw × 0.000125,单位 V。

如果是模拟 I2C 读多字节,重点在于读完高字节后主控要回 ACK,读完低字节后必须回 NACK 再 STOP。看下面这段:

uint8_t i2c_read_byte(uint8_t ack) { uint8_t dat = 0; for (int i = 7; i >= 0; i--) { SCL_HIGH(); delay_us(5); dat <<= 1; if (SDA_READ()) dat |= 1; SCL_LOW(); delay_us(5); } if (ack) SDA_LOW(); // ACK: 拉低 SDA else SDA_HIGH(); // NACK: 释放 SDA delay_us(2); SCL_HIGH(); delay_us(5); SCL_LOW(); SDA_HIGH(); return dat; }

读两个字节时:

i2c_start(); i2c_write_byte(0x90); // 写地址 i2c_write_byte(0x00); // 指针指向 Conversion i2c_start(); // REPEATED START i2c_write_byte(0x91); // 读地址 uint8_t msb = i2c_read_byte(1); // 读高字节,回 ACK uint8_t lsb = i2c_read_byte(0); // 读低字节,回 NACK i2c_stop();

这个流程我建议你在逻辑分析仪上完整抓一遍,亲眼看看时序和手册是否一致。我在自己项目里就靠这个操作找回了至少两个“看起来没问题但就是不出数据”的 bug。

4.4 SCL 速率对通信稳定性的影响

ADS1115 的数据手册标称支持最高 3.4Mbps 的高速模式,但实际工程里我几乎不会跑到这么高。MCU 的硬件 I2C 外设在 400kHz 快速模式下工作良好,100kHz 标准模式兼容性最好,模拟 I2C 则多用在低速场合,延时时长决定实际频率。

在 STM32F407 上用硬件 I2C 时,我曾经把速度配到 400kHz,配合 4.7kΩ 上拉电阻和短走线,通信稳定。但如果板子上有其他容性负载,或者用了长飞线连接外部模块,总线边沿会变缓,400kHz 下可能出现数据错位。飞到 100kHz 基本能解决 90% 的偶发通信问题。ADS1115 这种低速传感器根本不需要高带宽,100kHz 足够满足任何实际采样需求,我现在的习惯是能用 100kHz 就不用 400kHz,稳定压倒一切。

另外一点,ADS1115 本身不会做时钟拉伸,也就是说它在 I2C 通信过程中不会主动拉低 SCL 让主机等待。它的转换过程和 I2C 通信过程是独立的,转换没完成时你去读寄存器,只会读到旧值,而不是让总线卡住。这一点和某些传感器不同,理解后你就知道“读数据前先判断转换完成”是主控的责任,而不是从设备会帮你阻塞。

5. 实操中的常见问题与排查技巧

5.1 I2C 地址扫描:调试第一步就该做

凡是 I2C 设备、ADS1115 也好、其他芯片也好,第一步一定是扫描总线,确认设备地址能应答。很多“为什么读不到数据”的问题,在地址扫描阶段就能暴露出来。

地址扫描的原理很朴素:对总线上的每个可能 7 位地址发送一个写地址字节,看有没有设备回 ACK。收到 ACK 就说明该地址有设备在应答。我调试 STM32 时写过一个非常简单的扫描函数,用 HAL 库的HAL_I2C_Master_Transmit逐个探测地址:

for (uint8_t addr = 0x03; addr < 0x78; addr++) { HAL_StatusTypeDef status; status = HAL_I2C_Master_Transmit(&hi2c1, addr, NULL, 0, 50); if (status == HAL_OK) { printf("Found device at 0x%02X\r\n", addr); } }

扫描不到设备,按优先级排查:ADDR 引脚是否接对;VDD 是否上电;SDA/SCL 是否接反;上拉电阻是否虚焊;总线是否被拉死。总线被拉死是常见现象,表现是 SDA 一直为低,原因可能是有设备把总线占用未释放。这时候可以对 SCL 手动敲几个脉冲,然后再发 STOP,很多时候总线就恢复了。

5.2 读回数据全 0xFFFF 或 0x0000 的原因

读回高字节和低字节都是 0xFF,通常是总线读到了无应答或者悬空电平,常见于模拟 I2C 读时序里没有正确释放 SDA,或者上拉电阻没接好。读回全是 0x00,可能是地址不对、读到了不存在的寄存器,或者上拉电阻短路到地。

还有一种情况是输入电压超过 PGA 设定范围,导致转换结果饱和。如果配置的是 ±2.048V 量程却输入了 3.3V,读数会固定在 0x7FFF(正饱和)附近,并非通信问题。排查时先拿万用表量一下 AIN 输入电压,再对照 PGA 量程判断是不是输入超范围。

5.3 读数跳动严重时如何降噪

ADS1115 标称 16 位,但实际跳动程度很大程度取决于硬件设计和数据速率。如果读数不稳定,先检查电源。我给 ADS1115 供电时习惯在 VDD 引脚旁边放一颗 0.1μF 陶瓷电容加一颗 1μF~10μF 的钽电容或铝电解电容,模拟输入走线尽量短粗,模拟地和数字地在 ADC 附近单点汇接。这些是模拟电路的常规操作,但能解决大量“软件滤波压不住”的噪声问题。

数据速率越低,噪声越小。128SPS 下我读固定电压时,跳动范围大约在 ±2~±4 个 LSB,8SPS 下可以做到 ±1 LSB。如果 128SPS 的跳动在可接受范围,没必要强求更低速率。软件上再做一次滑动平均或者中值滤波,基本能达到稳定读数。

我实测发现一个有意思的现象:用单次模式逐次读取时,相邻两次读数之间的跳动反而比连续模式略大,因为每次转换的采样时刻是独立的,混入的噪声不相关。但单次模式在低功耗场景下收益太大,数据跳动就靠软件平均处理,两者并不冲突。

5.4 用逻辑分析仪抓取时序的实操心得

逻辑分析仪是调 I2C 的神器,几十块钱的 8 通道 24MHz 采样率设备就够用。把通道 0 接 SDA,通道 1 接 SCL,触发电平设成 1.65V 左右,触发条件选“下降沿”,然后跑一次读操作,波形里就能看到完整的 START、地址、ACK、数据、STOP。

抓波形时关注三点:一是地址字节是不是 0x90/0x91 而不是 0x48/0x49;二是在读数据阶段最后一个字节后主控有没有回 NACK;三是 STOP 条件里 SDA 和 SCL 的先后顺序是否正确。这三处是我见过最容易出错的地方。软件模拟 I2C 时,输出电平顺序和时间可以在波形里非常清楚地看到,比看代码猜靠谱得多。

我印象最深的一次调试经历就是模拟 I2C 读 ADS1115 死活读不对,代码翻来覆去检查都没问题,最后接上逻辑分析仪,发现读高字节和低字节之间少了一次 ACK 时钟,从设备输出的低字节根本没被正确采样。这个错误用眼睛盯代码很难发现,但在波形里一眼就能看出来。

6. 基于个人经验的几条补充建议

我用 ADS1115 做过好几轮产品,从最初的手工飞线调试到后来的批量贴片,积累了一些文档里不太会写的体会,在这里一并分享。

第一,先把地址扫描跑通再谈配置。我见过太多人上来就写配置、读数据,一旦失败就开始怀疑芯片坏了,其实大概率是地址没对上。先扫描、再写配置、再读转换结果,这个顺序能帮你把问题范围切得很干净。第二,所有寄存器操作都用“先写指针再读写数据”的方式,不要依赖芯片上电后的默认指针状态,因为我遇到过不同批次芯片默认指针行为不完全一致的情况。第三,ALERT/RDY 引脚从硬件上预留出来,即使初期不用,后续如果要判断转换完成状态,起码不用重新改板。

ADS1115 真正难的不是寄存器配置本身,而是 I2C 通信底层的那点“手感”。只要把时序图彻底看懂,把模拟 I2C 的 ACK、NACK、START、STOP 都亲手调一遍,以后再碰任何 I2C 设备都会顺很多。希望这篇拆解能帮你少走一些弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询