ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

ADS1110驱动开发与I2C地址配置实战:中文手册与避坑指南

ADS1110驱动开发与I2C地址配置实战:中文手册与避坑指南 简介这是一份面向嵌入式开发者的ADS1110高精度12位ADC配套驱动与中文手册资料包适合需要基于I²C接口在Arduino、树莓派等平台上快速集成该芯片的工程师。压缩包共2个文件包含ads1110.c驱动源码与ADS1110.doc中文技术手册整体体积约199KB小巧便携。驱动源码提供初始化、读取转换结果、写入配置寄存器等基础函数并涉及通过A0-A2引脚组合修改设备地址、多设备并联等关键操作中文手册则系统整理了器件规格、增益与采样速率配置、电气特性、应用电路示例及故障排查提示。目前已有866人学习下载对于正在调试I²C时序或为传感器信号采集选型ADC的开发者这份驱动与手册组合能缩短上手周期帮助快速完成原理验证与代码移植。1. 为ADS1110单独存一份驱动和中文手册它值这个待遇ADS1110是一颗低调但不好伺候的12位ADC。说它低调是因为封装小、外围少、价格也便宜说它不好伺候是因为所有关键操作都压在I²C寄存器上尤其是“改地址”这一项很多人第一次把两片ADS1110挂到同一条总线上时连数据都读不回来。这份ADS1110驱动与中文手册.rar里正好是两件硬货一份ads1110.c驱动源码和一份ADS1110.doc中文手册。驱动解决“怎么驱动”的问题手册解决“为什么这么驱动”的问题配合起来能省掉不少翻数据手册的时间。适合正在用Arduino、STM32、树莓派这类平台做模拟量采集的工程师也适合刚接触I²C设备驱动、想找个典型器件练手的入门者。2. ADS1110的硬件特性与I²C通信先搞懂再写驱动2.1 芯片功能框图和关键寄存器增益、数据率、连续转换ADS1110严格来说是一颗“带有PGA的差分输入ADC”不是多通道扫描型芯片。它只有一对差分输入Vin和Vin-但内部集成了可编程增益放大器PGA、12位逐次逼近型ADC、I²C接口以及一个可配置的参考电压源。在写驱动之前最好先记住它的寄存器布局因为ads1110.c里所有Read/Write操作最终都要落到这几个字节上。芯片内部有一个1字节的配置寄存器地址是0x01默认值通常是0x8C。这个字节拆开来看Bit7ST/SC。写1时启动单次转换在连续转换模式下读此位表示转换是否完成。Bit6DR数据率选择。0表示15 SPS默认1表示30 SPS。Bit5-4PGA增益设置。001倍012倍104倍118倍。Bit3MODE0连续转换1单次转换默认。Bit2-0保留位写入0。数据寄存器占用两个字节地址是0x00。ADC的12位结果以16位二进制补码格式存放高12位有效低4位注意是低4位不是低2位是无效的0或者说是“被舍弃的位”。驱动解析时通常把读到的16位值右移4位再符号扩展成带符号整数最后乘以当前PGA下的每LSB电压值。增益和每LSB电压的关系中文手册里有一张表我习惯把它抄在代码头部注释里避免下次翻手册PGA编码增益满量程输入范围V每LSB电压uV001±2.0481000012±1.024500104±0.512250118±0.256125注意满量程范围是相对内部参考电压1.024V来说的并不是说芯片能承受±2.048V的绝对电压。输入绝对电压不能超过电源轨也不能低于GND否则芯片可能烧掉。这一点很多新手会翻车后面避坑章我会再提。2.2 I²C时序与设备地址0x48只是起点ADS1110的I²C从机地址是7位出厂默认是0x48即二进制1001000。这7位地址由两部分组成固定部分1001和可变部分A2/A1/A0。可变部分来自芯片引脚A0、A1、A2的电平每个引脚可接GND0或VCC1所以最多有8种组合对应地址从0x48到0x4F。这里要特别强调ADS1110没有像某些I²C芯片那样提供“软件写寄存器改地址”的功能。所谓的“ADS1110改地址”实际是通过改变A0、A1、A2三个引脚的电平来改变硬件地址。软件里要做的是枚举这些地址并确保驱动里使用与硬件跳线一致的地址去初始化。很多帖子把“改地址”说成“软件配置”这是个误导实战中必须先确认硬件接法。写驱动时最好做一个地址扫描函数把总线上所有存在的ADS1110地址扫出来再结合应用按需选择。这样既验证了硬件也避免了烧错地址导致的“读不到数据”尴尬。下面是一段用于Arduino平台的I²C扫描代码同样适用于STM32模拟I²C或树莓派的smbus#include Wire.h void setup() { Serial.begin(115200); Wire.begin(); Serial.println(Scan ADS1110 address on bus...); for (byte addr 0x48; addr 0x4F; addr) { Wire.beginTransmission(addr); byte error Wire.endTransmission(); if (error 0) { Serial.print(Found ADS1110 at 0x); Serial.println(addr, HEX); } } } void loop() {}这段代码的逻辑很简单I²C协议里从机收到匹配地址后会回ACK主机通过endTransmission()的返回值判断有没有ACK。0代表有设备应答其他值代表无应答或总线错误。参数方面Wire.beginTransmission(addr)里的addr是7位地址左移一位后的8位形式Arduino的Wire库会自动处理不需要手动移位。实际使用时如果你把A0接VCC、A1接GND、A2接GND那么地址就是0x49扫描会返回这个结果。3. ads1110.c 驱动解读从初始化到改地址的完整实现3.1 驱动文件结构和I²C适配层ads1110_init()做了什么ads1110.c这类驱动代码通常面向裸机I²C风格是把I2C底层操作抽象成几个函数指针再由初始化函数接收这些指针。这样做的好处是驱动可以同时适配硬件I²C和GPIO模拟I²C换平台不用改业务流程。常见的结构是先定义几个关键函数typedef uint8_t (*i2c_write_ptr)(uint8_t addr, uint8_t *buf, uint8_t len); typedef uint8_t (*i2c_read_ptr)(uint8_t addr, uint8_t reg, uint8_t *buf, uint8_t len); typedef struct { i2c_write_ptr write; i2c_read_ptr read; uint8_t dev_addr; } ads1110_t;ads1110_init()的工作是把这些函数指针和地址绑定到句柄上顺便把配置寄存器写入默认值。下面是我基于ads1110.c整理出来的初始化函数它在原文件基础上加了地址参数方便多设备场景uint8_t ads1110_init(ads1110_t *dev, uint8_t addr, i2c_write_ptr write_func, i2c_read_ptr read_func) { uint8_t config 0x8C; // 默认单次转换、15SPS、PGA1 uint8_t ret; if (dev NULL || write_func NULL || read_func NULL) { return 1; } dev-dev_addr addr; dev-write write_func; dev-read read_func; // 向0x01寄存器写配置启动单次转换由后续read触发 ret dev-write(dev-dev_addr, config, 1); if (ret ! 0) { return 2; // 写入失败通常是地址不对或上拉缺失 } // 读回校验防止写错 uint8_t rd_buf 0; dev-read(dev-dev_addr, 0x01, rd_buf, 1); if (rd_buf ! config) { return 3; // 读回不一致寄存器被意外修改 } return 0; }逻辑说明初始化有两步写配置和读回校验。写配置时人为把默认配置写进0x01寄存器确保上电乱值被覆盖读回校验则能发现总线干扰或从机地址错误。参数说明addr就是硬件引脚决定的7位地址比如0x48~0x4Fwrite_func和read_func是由移植层提供的底层函数它们的返回值0或非0决定了上层是否继续。如果读回不一致先检查是不是有别的设备也挂在同一地址上。3.2 改地址的实现用软件区分多片ADS1110“ADS1110改地址”在驱动层的核心就是让同一个驱动函数能处理0x48到0x4F任意地址。硬件上把A0/A1/A2接成目标电平软件上把对应地址传进ads1110_init()就行。实际项目中我通常写一个扫描绑定函数让系统在启动时自动发现设备避免手动烧地址uint8_t ads1110_autobind(ads1110_t *devs, uint8_t max_devices, i2c_write_ptr w, i2c_read_ptr r) { uint8_t count 0; for (uint8_t addr 0x48; addr 0x4F; addr) { uint8_t probe 0; if (r(addr, 0x00, probe, 1) 0) { ads1110_init(devs[count], addr, w, r); count; if (count max_devices) break; } } return count; }逻辑说明这个函数从0x48开始逐个测试地址能读到数据寄存器的地址视为存在设备然后初始化它。参数说明devs是驱动句柄数组max_devices限制最大挂载数量防止越界。注意这里读地址0x00不一定100%可靠因为ADS1110的0x00寄存器在未完成转换时会返回上次结果但只要有ACK就说明地址没错。如果想更稳妥改成读0x01配置寄存器后判断返回值是否在预期范围内。改地址最常见的误用是把A0/A1/A2直接悬空。悬空引脚会被内部弱下拉拉到GND地址变成0x48如果多片都悬空所有芯片都挤在0x48I²C总线直接打架表现为读回的数据抖动、错误率极高。正确做法是把每个地址脚硬接到VCC或GND不要依赖内部弱下拉。3.3 读取转换结果与配置校验ads1110_read()的坑读取结果是ADS1110驱动里最容易出错的部分。数据寄存器是16位I²C读操作要先从寄存器地址0x00开始连续读两个字节。由于I²C是大端传输第一个字节是高位第二个字节是低位但ADS1110的12位有效数据分布在这16位的最高12位也就是低4位是无效的。一个常见翻车点是把低4位当作数据位直接用左移8位相加结果数值差16倍。正确的解析是int16_t ads1110_read_raw(ads1110_t *dev) { uint8_t buf[2] {0}; int16_t raw; // 启动单次转换向配置寄存器写入启动位不修改增益和速率 uint8_t cfg 0x8C; dev-write(dev-dev_addr, cfg, 1); // 等待转换完成15SPS下最多等70ms uint32_t timeout 100; do { dev-read(dev-dev_addr, 0x01, cfg, 1); timeout--; } while ((cfg 0x80) 0 timeout 0); // 读取数据 dev-read(dev-dev_addr, 0x00, buf, 2); raw (buf[0] 8) | buf[1]; // 右移4位去掉无效位再符号扩展 return raw 4; }逻辑说明先向配置寄存器写0x8C目的是把ST位置1触发一次单次转换。然后循环读配置寄存器检查ST位bit7是否自动归0归0表示转换完成。最后读两个字节的数据左移拼接后右移4位得到有效12位。参数说明timeout设为100次循环如果硬件I²C快每次循环可能不到1ms100次足够如果用了慢速GPIO模拟建议把timeout加大到500。cfg 0x80判断的是ST位注意MODE位保持1即单次转换模式这样每次读取都会触发一次新转换不会读到旧值。把raw右移4位后得到的是带符号12位整数范围是-2048~2047。结合PGA增益实际电压计算公式为double ads1110_voltage(int16_t raw, uint16_t pga) { double lsb 2.048 / (1 (pga 1)) / 2048.0; return raw * lsb; }增益参数pga对应0、1、2、3分别代表1、2、4、8倍。这个公式里的1 (pga 1)其实就是2、4、8、16的一半对应满量程除以增益后的电平。4. 避坑ADS1110驱动开发中的五个常见问题4.1 地址引脚悬空导致多片设备互相冲突现象总线上只挂了一片ADS1110但同一地址上好像有两个设备在应答读出来的数据一会儿正常一会儿全是0xFF。原因A0/A1/A2三个引脚没有硬接高低电平内部弱下拉让它们表现出默认0状态地址全为0x48。如果此时恰好有两片ADS1110都设成默认状态它们就会同时对主机ACK数据竞争。解决把每一片ADS1110的A0/A1/A2全部用跳线帽或PCB走线硬接到GND或VCC确保每片地址唯一。上电前用万用表测一下地址脚电平不要相信悬空引脚的状态。4.2 SDA/SCL上拉电阻缺失导致通信超时现象初始化时写配置寄存器一直返回错误逻辑分析仪看不到完整波形SCL有时钟但SDA没有应答。原因我遇到过三次这种情况每次都是因为PCB设计时漏了上拉电阻。I²C总线是开漏结构如果不接上拉SDA和SCL的电平会被钳在低电平主机认为总线上没有设备。解决在SDA和SCL上各加一个4.7kΩ上拉电阻到VCC。如果总线上的设备超过两个上拉电阻可以适当减小到2.2kΩ。记住ADS1110内部没有上拉这个电阻必须外部加。4.3 输入电压超过PGA满量程导致转换值钳位现象输入信号明明在缓慢上升但读到的raw值突然跳到2047之后不管电压怎么变数值都停在2047。原因差分输入电压超过了当前PGA增益下的满量程范围。比如PGA8时满量程只有±0.256V如果输入有0.3VADC就饱和了读数锁定在满量程。解决根据实际信号幅度选择PGA。测量热敏电阻分压电压时先把信号范围估算一下。如果信号动态范围小比如只有几十mV用PGA8提高分辨率如果信号可能到1V用PGA1或2。实在不确定先默认PGA1读出值后再逐步增大增益。4.4 数据解析时忘记右移四位导致数值偏大现象读到的电压值比实际电压大16倍或者数字代码出现规律性的间隔。原因ADS1110的16位数据寄存器中只有高12位是有效数据低4位为0。如果直接按16位有符号数处理相当于把数值放大了16倍而且符号扩展会出错。解决按raw 4处理原始数据。注意这里用的是算术右移对于负数可以保持符号。完成后测试验证输入0V时读到的数值应该在0附近而不是一个十六进制的大数。4.5 单次转换模式下不等待ST位完成导致读取旧数据现象连续读取同一个信号源数据偶尔会跳变但变化规律和输入信号对不上有滞后。原因在单次转换模式下读取操作只触发转换转换完成需要时间。如果在ST位未归零之前就去读数据寄存器读到的是上一次转换的旧值从而造成数据滞后。解决按前面给出的代码读完配置寄存器后要轮询ST位直到它为0或者直接延时等待15SPS时至少延时70ms。这两种方式我推荐轮询因为延时在系统调度紧张时不可靠轮询能确保数据真正新鲜。5. 进阶多片ADS1110挂载与传感器信号调理的实测经验多片挂载的实测技巧核心是“地址分配 通道规划”。我做过一块四路温度采集板用了四片ADS1110分别设成0x48、0x49、0x4A、0x4B。由于ADS1110是差分输入每片只能测一路传感器但这反而有个好处每路传感器完全独立增益可以不同。比如第一路接K型热电偶加AD8495放大增益用1第二路接NTC分压增益用8。初始化时分别给每片句柄传不同的PGA配置即可。实际验证时我会用高精度万用表先测传感器输出再对比ADS1110读到的电压误差在满量程的±0.1%以内就认为正常。注意万用表的表笔会引入共模噪声最好用屏蔽线连接差分输入。ADS1110的差分输入对共模干扰有较好抑制但前提是Vin-要可靠接地路径不能悬空。另一个进阶用法是结合I²C地址扫描做上电自检。系统启动后先执行一次地址扫描把实际发现的设备数和预期数对比。少了设备就报硬件错误多了设备就检查地址冲突。这个习惯救过我一次有一批PCB贴片时贴错电容导致其中一片ADS1110电源脚虚焊上电后扫描不到地址直接把问题定位到硬件层省了半小时排查软件。从那以后我每次画板都会强制检查三件事每片ADS1110的A0/A1/A2是否都硬接了明确电平、SDA/SCL是否放了上拉电阻、输入信号是否可能超过当前PGA的满量程。这三件事检查完ADS1110几乎不会再出问题。希望这份驱动和中文手册也能帮你少走这些弯路。本文还有配套的精品资源点击获取
返回列表