51单片机高精度电流测量方案:ADS1110 ADC与串口通信实战

📅 2026/8/12 13:29:50
51单片机高精度电流测量方案:ADS1110 ADC与串口通信实战
如果你手头有 51 单片机想用它来测量电流并且希望测量精度比直接用 ADC 引脚高一些那么用 ADS1110 这类高精度 ADC 芯片再通过串口把数据传到电脑上查看是一个很实际的选择。这个方案的核心价值在于它把 51 单片机有限的 ADC 能力很多型号甚至没有内置 ADC扩展成了高精度、低噪声的电流采集通道并且通过串口实现了数据的实时监控和记录非常适合做实验、调试电源或者搭建简单的数据采集系统。很多人一开始会纠结于代码怎么写但实际上用 51 单片机驱动 ADS1110 并通过串口输出最关键的不是代码本身而是三个环节第一硬件上 ADS1110 的 I2C 通信和电流采样电路能不能稳定工作第二51 单片机的串口初始化、波特率设置和发送逻辑是否正确第三如何把 ADS1110 读到的原始数据转换成有实际意义的电流值。下面我就按实际搭建和调试的顺序把这几个环节拆开讲清楚。1. 先搞清楚 ADS1110 和 51 单片机各自要做什么在动手写代码或连线之前得先明白这个系统里每个部分的分工。这能帮你快速定位问题是单片机没发对命令还是 ADS1110 没响应或者是采样电路本身不对。1.1 ADS1110 的角色高精度“翻译官”ADS1110 是一个 16 位分辨率的 Δ-Σ ADC 芯片通过 I2C 接口通信。它的核心任务就两个模数转换把输入引脚AIN0, AIN1之间的电压差转换成一个 16 位的数字值。协议转换把自己内部的转换结果按照 I2C 的规则等着单片机来读取。对于电流测量我们通常会在被测电流的通路上串联一个采样电阻也叫分流器。电流流过这个电阻会产生一个微小的电压降比如 100mA 电流流过 0.1Ω 电阻产生 10mV 电压。ADS1110 就是去测量这个采样电阻两端的电压。所以ADS1110 测的是电压我们需要在程序里根据欧姆定律I V / R来算出电流。ADS1110 有几个关键特性决定了你的电路和代码怎么写I2C 地址出厂默认是 0x481001000可以通过芯片的 ADDR 引脚接地或接 VCC 来改变这决定了单片机呼叫它的“门牌号”。可编程增益放大器PGA量程可选 ±2.048V, ±1.024V, ±0.512V, ±0.256V。量程越小分辨率越高因为 LSB 更小。选择量程的原则是让采样电阻上的最大压降尽量接近但不超过你选的量程这样可以获得最佳精度。数据速率有 15/30/60/240 SPS每秒采样次数可选。速率越低噪声一般也越低但响应慢。做电流表通常 15 或 30 SPS 就足够了。1.2 51 单片机的角色调度员和传令兵51 单片机在这里要干三件事I2C 主机通过模拟 I2C 时序因为大多数 51 单片机没有硬件 I2C向 ADS1110 发送配置字并读取转换结果。数据处理器把读到的 16 位原始数据根据 PGA 增益和参考电压换算成实际的电压值再除以采样电阻阻值得到电流值。串口发送端将计算好的电流值通常是浮点数格式化成字符串比如 “Current: 123.45 mA”通过串口 UART 发送给电脑。这里最容易出问题的地方是时序。51 单片机的指令周期相对较慢在模拟 I2C 通信时必须严格按照 ADS1110 数据手册里的时序要求来拉高拉低 IO 口并插入足够的延时_nop_()。太快了 ADS1110 可能反应不过来太慢了会影响整体采样率。1.3 串口和电脑的角色观察窗口串口UART是连接单片机和电脑的桥梁。电脑上需要一个串口调试助手如 XCOM, SSCOM来接收和显示数据。你需要确保单片机串口的波特率、数据位、停止位、校验位设置必须和串口调试助手上的设置完全一致。最常用的配置是 9600 波特率8 数据位1 停止位无校验。电脑需要安装正确的 USB 转串口驱动如 CH340、CP2102才能在设备管理器中看到对应的 COM 口。很多新手遇到的问题不是单片机程序不对而是串口线没接好、驱动没装、或者波特率设错了导致电脑上什么都收不到。2. 搭建硬件电路从原理图到面包板理论清楚了就可以动手搭电路了。我建议先在面包板上搭建验证通了再考虑焊接到洞洞板或 PCB 上。2.1 元器件清单和连接图你需要准备以下核心器件51 单片机最小系统板如 STC89C52RC包含单片机、晶振通常 11.0592MHz便于产生标准波特率、复位电路、电源接口。ADS1110 模块或芯片如果是模块通常已带电平转换和滤波电容使用更方便。如果是芯片需要自己搭建周边电路。采样电阻根据待测电流大小选择。例如测 0-1A 电流希望压降在 0.1V 左右则选 0.1Ω/1W 以上的功率电阻。精度要求高可选高精度分流器。USB 转 TTL 串口模块如 CH340G 模块用于连接单片机串口和电脑 USB。杜邦线若干。电源为单片机和 ADS1110 供电通常 5V 或 3.3V。注意 ADS1110 的供电电压VDD必须和 51 单片机的 IO 口电压匹配否则 I2C 通信会失败。如果单片机是 5VADS1110 也应用 5V 供电。连接关系如下以使用模块为例电源连接将 5V 电源正极VCC同时连接到 51 单片机 VCC 引脚、ADS1110 模块的 VCC、以及 USB 转串口模块的 VCC如果它需要外部供电。所有 GND 连接在一起。I2C 连接51 单片机的两个 IO 口例如 P2.0 和 P2.1分别连接到 ADS1110 模块的 SCL时钟和 SDA数据引脚。务必在 SDA 和 SCL 线上各接一个 4.7kΩ - 10kΩ 的上拉电阻到 VCC。这是 I2C 总线正常工作的必要条件很多模块已经集成如果没集成需要自己加。电流采样连接将 ADS1110 模块的 AIN0 和 AIN1 引脚分别连接到采样电阻的两端。注意电压极性通常 AIN0 接电流流入采样电阻的一端AIN1 接流出端这样测出的电压差为正时代表正向电流。串口连接51 单片机的 TXD 引脚P3.1连接到 USB 转串口模块的 RXD。51 单片机的 RXD 引脚P3.0连接到 USB 转串口模块的 TXD。切记是交叉连接单片机的发送TX接模块的接收RX单片机的接收RX接模块的发送TX。这是最常接错的地方。被测电流回路将采样电阻串联到你想测量电流的电路中。例如电源正极 - 你的负载如一个LED加电阻 -采样电阻- 电源负极。ADS1110 就并联在这个采样电阻上。2.2 上电前的检查清单连线看起来简单但乱糟糟的面包板很容易出错。上电前请务必按顺序检查电源短路用万用表蜂鸣档快速点测一下 VCC 和 GND 之间是否短路。这是保命步骤防止烧芯片。I2C 上拉确认 SDA 和 SCL 线是否有上拉电阻连接到 VCC。可以用万用表电压档测量当总线空闲时单片机程序还没启动或处于初始化阶段这两条线电压应该接近 VCC。串口线交叉再次确认单片机 TXD - 模块 RXD单片机 RXD - 模块 TXD。采样电阻连接确认采样电阻是串联在负载回路中的而不是并联。ADS1110 的输入线应直接焊在或紧压在采样电阻的两个焊盘上以减少接触电阻引入的误差。共地确保单片机、ADS1110、USB 转串口模块、被测电路所有的 GND 都是连通的。地线不共地是噪声和读数不准的常见原因。3. 编写和调试单片机程序硬件没问题了软件才能跑起来。程序可以分成几个模块来写这样调试起来思路更清晰。3.1 程序框架设计一个结构清晰的程序通常包含以下部分main.c主循环协调所有任务。uart.c和uart.h串口初始化、发送一个字节、发送字符串的函数。i2c.c和i2c.h模拟 I2C 起始、停止、发送字节、接收字节、应答等基本时序函数。ads1110.c和ads1110.hADS1110 的初始化写配置寄存器、读取转换结果的函数。data_process.c将原始数据转换为电压、电流值的函数。对于初学者可以把所有函数写在一个 main.c 里但逻辑上还是要区分开。3.2 核心代码段解析这里给出最关键的几个函数片段和思路你需要根据自己使用的单片机型号和晶振频率调整延时。1. 串口初始化波特率 960051 单片机串口模式通常设为模式18位UART波特率可变使用定时器1工作在模式2自动重装来产生波特率。晶振用 11.0592MHz 可以非常精确地产生 9600 等标准波特率。void UART_Init(void) // 9600bps 11.0592MHz { SCON 0x50; // 8位数据可变波特率允许接收虽然这里只发不收 PCON 0x7F; // 波特率不倍增 TMOD 0x0F; // 清除定时器1模式位 TMOD | 0x20; // 设定定时器1为8位自动重装方式 TL1 0xFD; // 设定定时初值 TH1 0xFD; // 设定定时器重装值 ET1 0; // 禁止定时器1中断 TR1 1; // 启动定时器1 // EA 1; // 如果要用中断接收才开总中断 // ES 1; // 如果要用串口中断才开串口中断 }2. 模拟 I2C 起始和停止信号这是 I2C 通信的基础。注意I2C_Delay()函数需要根据单片机速度调整确保满足 ADS1110 时序要求的最小时间。sbit I2C_SCL P2^0; // 假设 SCL 接 P2.0 sbit I2C_SDA P2^1; // 假设 SDA 接 P2.1 void I2C_Start(void) { I2C_SDA 1; I2C_Delay(); I2C_SCL 1; I2C_Delay(); I2C_SDA 0; // 在 SCL 高电平期间SDA 产生下降沿 - 起始条件 I2C_Delay(); I2C_SCL 0; // 钳住总线准备发送数据 I2C_Delay(); } void I2C_Stop(void) { I2C_SDA 0; I2C_Delay(); I2C_SCL 1; I2C_Delay(); I2C_SDA 1; // 在 SCL 高电平期间SDA 产生上升沿 - 停止条件 I2C_Delay(); }3. ADS1110 读取数据ADS1110 上电后默认进行连续转换。读取数据时先发送设备地址写再发送寄存器指针00H 指向转换结果寄存器然后发送一个重复起始条件再发送设备地址读最后读取两个字节的数据。#define ADS1110_ADDR_WRITE 0x90 // 默认地址 0x48左移一位后加写标志位 0 #define ADS1110_ADDR_READ 0x91 // 默认地址 0x48左移一位后加读标志位 1 unsigned int ADS1110_ReadData(void) { unsigned int data_val 0; unsigned char high_byte 0, low_byte 0; I2C_Start(); I2C_SendByte(ADS1110_ADDR_WRITE); // 发送写地址 if (I2C_CheckAck() 0) { /* 处理无应答 */ } I2C_SendByte(0x00); // 发送寄存器指针指向转换结果寄存器 if (I2C_CheckAck() 0) { /* 处理无应答 */ } I2C_Start(); // 重复起始条件 I2C_SendByte(ADS1110_ADDR_READ); // 发送读地址 if (I2C_CheckAck() 0) { /* 处理无应答 */ } high_byte I2C_ReadByte(); // 读取高字节 I2C_SendAck(0); // 发送应答 low_byte I2C_ReadByte(); // 读取低字节 I2C_SendAck(1); // 发送非应答表示读取结束 I2C_Stop(); data_val ((unsigned int)high_byte 8) | low_byte; return data_val; }4. 数据转换与电流计算这是把原始数字量变成有意义电流值的关键一步。ADS1110 输出的是二进制补码。// 假设PGA 增益 1 (量程 ±2.048V)参考电压 VREF 2.048V #define PGA_GAIN 1 #define VREF 2.048 #define SAMPLE_RESISTOR 0.1 // 采样电阻 0.1 欧姆 float ConvertToCurrent(unsigned int raw_data) { float voltage, current; int signed_raw_data; // 1. 将16位原始数据转换为有符号整数 if (raw_data 0x8000) { // 判断最高位是否为1负数 signed_raw_data (int)(raw_data - 65536); // 补码转有符号数 } else { signed_raw_data (int)raw_data; } // 2. 计算实际电压 (V) (有符号原始值 / 32768) * VREF / PGA_GAIN // 32768 是因为 16位有符号数范围是 -32768 ~ 32767对应满量程电压 voltage ((float)signed_raw_data / 32768.0) * VREF / PGA_GAIN; // 3. 计算电流 (A) 电压 (V) / 采样电阻 (Ω) current voltage / SAMPLE_RESISTOR; // 4. 通常显示为毫安 (mA) return current * 1000.0; }5. 主循环逻辑主循环里就是不断地读数据、转换、格式化、通过串口发送然后延时一段时间。void main() { unsigned int adc_value; float current_mA; char str_buffer[30]; UART_Init(); // 初始化串口 I2C_Init(); // 初始化I2C引脚设为准双向口等 // ADS1110 默认配置如需修改如PGA、速率需在此调用配置函数 while(1) { adc_value ADS1110_ReadData(); // 读取ADS1110原始值 current_mA ConvertToCurrent(adc_value); // 转换为电流值(mA) // 格式化字符串例如 Current: 123.45 mA\r\n sprintf(str_buffer, Current: %.2f mA\r\n, current_mA); UART_SendString(str_buffer); // 发送字符串到串口 Delay_ms(500); // 延时500ms控制发送间隔 } }3.3 调试顺序从孤立测试到联调不要一下子把全部代码写完再下载测试。应该分步验证第一步测试串口。写一个最简单的程序让单片机每隔一秒发送一个固定的字符串如 “Hello\r\n”到串口。在电脑上用串口调试助手看是否能稳定接收。这一步验证了单片机、串口驱动、连接线、波特率设置全部正确。第二步测试 I2C 基本通信。写一个 I2C 扫描程序让单片机扫描 I2C 总线上的设备地址。如果 ADS1110 地址正确默认 0x48电路连接和上拉电阻没问题应该能扫描到设备。这一步验证了 I2C 硬件连接和基本时序正确。第三步读取 ADS1110 固定值。将 ADS1110 的输入引脚 AIN0 和 AIN1 短接输入电压为 0运行读取程序。理论上读到的值应该在 0 附近小幅波动。如果读到一个巨大的固定值或全 FFFF可能是 I2C 读写时序不对或者芯片没工作。第四步施加已知电压测试。用一个可调电源或分压电阻给 ADS1110 输入一个已知的微小电压比如 100mV。计算理论 ADC 值并与读到的值对比。如果偏差很大检查 PGA 增益设置、参考电压计算、采样电阻精度和连接点接触电阻。第五步接入真实电流回路测试。最后才把采样电阻串联到你的目标电路里观察电流读数是否合理。4. 结果验证、校准与常见问题排查即使程序跑通了读出来的数也可能不准。这时候需要验证和校准。4.1 如何验证读数准确性你需要一个可靠的参照物高精度万用表这是最好的校准工具。用万用表的电流档串联到同一个回路中对比你的单片机电流表和万用表的读数。已知负载使用一个阻值精确的电阻作为负载根据欧姆定律 I V / R用高精度万用表测量电阻两端的电压可以精确算出电流。用这个电流值来校准你的系统。校准方法在代码的ConvertToCurrent函数中你使用了一个理论上的VREF2.048V和SAMPLE_RESISTOR0.1Ω。实际芯片的 VREF 可能有微小偏差采样电阻也有精度误差。你可以在代码中引入一个校准系数CAL_FACTORfloat ConvertToCurrent_Calibrated(unsigned int raw_data) { float voltage, current; int signed_raw_data; const float CAL_FACTOR 1.02; // 校准系数通过对比实测值调整 // ... 前面的转换计算与之前相同 ... current voltage / SAMPLE_RESISTOR; current current * CAL_FACTOR; // 应用校准系数 // ... }通过对比单片机读数I_mcu和标准万用表读数I_std可以计算出CAL_FACTOR I_std / I_mcu。在多个不同的电流值下测量并取平均得到一个更可靠的系数。4.2 常见问题与排查清单当你遇到问题时按照以下顺序排查可以节省大量时间问题1串口调试助手收不到任何数据。查硬件USB转串口模块的驱动是否安装成功设备管理器里是否有对应的COM口且没有感叹号TX/RX线是否接反共地了吗查软件单片机程序里的波特率是否与串口助手设置完全一致单片机晶振是否是 11.0592MHz对于9600波特率串口初始化函数是否被正确调用查单片机程序是否成功下载到单片机单片机是否正常运行看指示灯问题2I2C 扫描不到 ADS1110 地址。查电源和地址用万用表量 ADS1110 的 VCC 和 GND 引脚电压是否正确ADDR 引脚电平是否与预期地址匹配查上拉电阻SDA 和 SCL 线上是否有 4.7kΩ - 10kΩ 的上拉电阻总线空闲时电压是否接近 VCC查时序I2C 的起始、停止、应答时序的延时是否足够可以尝试把所有的I2C_Delay()时间加倍试试。查连接SCL、SDA、VCC、GND 这四根线是否都连接牢固没有虚焊或插错孔问题3读到的 ADC 值不变或者总是 0 或 65535。值始终为 0可能 I2C 读取函数只读了低字节没读高字节或者数据转换部分将高字节丢失了。检查ADS1110_ReadData函数中组合高、低字节的语句。值始终为 655350xFFFF可能是 I2C 通信完全失败读到的全是高电平。重点检查 I2C 通信链路和芯片是否损坏。值不变检查 ADS1110 的配置寄存器是否被意外写入了“单次转换”模式并且没有触发新的转换。或者你的输入电压本身就没变化。问题4读数跳动噪声很大。查电源给单片机和 ADS1110 的电源是否干净可以在 VCC 和 GND 之间就近并联一个 10uF 电解电容和一个 0.1uF 瓷片电容滤波。查地线模拟地ADS1110 的 GND和数字地单片机的 GND的走线是否合理尽量采用星型单点接地或确保地线路径宽而短。查采样信号测量采样电阻两端的电压信号用示波器观察是否有高频噪声。可以在 ADS1110 的 AIN0 和 AIN1 引脚对地各接一个 0.1uF 的电容构成低通滤波器。降低数据速率将 ADS1110 配置为更低的数据速率如 15SPS可以有效抑制噪声。问题5测量值有固定偏差或非线性。校准采样电阻你的采样电阻标称 0.1Ω实际可能是 0.102Ω。用万用表精确测量其阻值并更新代码中的SAMPLE_RESISTOR值。考虑接触电阻如果采样电阻是通过导线连接的导线和焊点的电阻会引入误差。对于小电流测量这个误差不可忽视。尽量让 ADS1110 的输入线直接连接在采样电阻的焊盘上。检查 PGA 设置确保代码中PGA_GAIN和VREF的定义与实际硬件配置一致。如果你通过配置寄存器修改了增益这里的计算也要同步修改。4.3 进阶优化方向当基本功能实现后可以考虑以下优化让这个电流表更实用软件滤波在单片机端对连续采样值进行滑动平均滤波或中值滤波可以显著平滑显示值减少跳动。量程自动切换如果被测电流动态范围大可以编写程序判断当前读数。当接近满量程时自动切换 ADS1110 的 PGA 增益到更大量程当读数很小时切换到更小量程以提高分辨率。数据打包与协议不再发送简单的字符串而是定义一套二进制通信协议。例如先发送帧头再发送电流值的浮点数字节最后发送校验和。这样电脑端程序可以更可靠地解析也便于实现多通道采集。加入电压测量ADS1110 本身是测电压差的。你可以用单片机控制模拟开关轮流测量不同采样电阻上的压降或者直接测量电源电压实现一个简单的万用表功能。本地显示增加一个 LCD 屏如 1602或 OLED 屏将电流值实时显示在本地而不完全依赖电脑串口。这个基于 51 单片机和 ADS1110 的电流表项目从学习角度来说几乎涵盖了单片机开发的所有基础环节IO 口操作、定时器、串口通信、模拟总线协议I2C、传感器数据采集、数据处理以及基本的硬件调试。它不是一个炫技的项目但非常扎实。把它调通的过程就是你理解嵌入式系统如何与真实世界交互的过程。我建议不要只满足于“跑通”多试试改变采样电阻、改变 PGA、添加滤波算法观察这些变化如何影响最终结果你的收获会大得多。