关于 51 单片机驱动 ADS1115 进行 AD 模拟信号采集,下面是完整的原理讲解、通信流程、驱动代码示例与注意事项,适用于 C51 系列(如 STC89C52、AT89S52 等) 单片机配合 ADS1115(I²C 接口 16 位 ADC)模块进行高精度电压采集。
🧩 51 驱动 ADS1115 进行 AD 采集全指南
📦 一、ADS1115 模块简介
参数 | 值 |
---|---|
分辨率 | 16位(15位有效 + 符号位) |
通道数 | 4通道单端 / 2通道差分 |
通信接口 | I²C |
内置PGA | 支持 ±6.144V ~ ±0.256V 可编程增益 |
电压范围 | 2.0V ~ 5.5V |
采样率 | 8~860 SPS(Samples Per Second) |
🔌 二、硬件连接方式
假设你使用 ADS1115 模块(默认地址 0x48),连接如下:
ADS1115 引脚 | 51 单片机引脚 |
---|---|
SDA(数据) | P2.0(或任意IO口) |
SCL(时钟) | P2.1 |
VCC | 5V |
GND | GND |
ADDR | 悬空或接地(决定I2C地址) |
🧠 三、I²C 通信基本流程
51 单片机通过软件模拟 I²C 通信(位操作),与 ADS1115 交互:
- 启动条件 → 发送器件地址(0x48)+ 写命令
- 写配置寄存器(设置通道、采样率等)
- 启动转换或读取转换结果寄存器
- 读取两个字节的转换数据
🧾 四、配置说明(关键寄存器)
- 配置寄存器地址:
0x01
- 数据寄存器地址:
0x00
配置寄存器 16 位格式(简略):
15 14-12 11 10-9 8 7-5 4-0
OS | MUX | PGA | MODE | DR | COMP_MODE 等
示例配置值:
- MUX = 100:通道 AIN0 单端输入
- PGA = 010:±2.048V 量程
- MODE = 1:单次转换模式
- DR = 100:128 SPS
组合配置字(16位)示例:0xC283
(高字节 0xC2,低字节 0x83)
🧰 五、驱动代码(C51 示例)
以下为 51 单片机模拟 I²C + 读取 ADS1115 AIN0 的简单示例:
#include <reg52.h>
sbit SDA = P2^0;
sbit SCL = P2^1;
void I2C_Start(void);
void I2C_Stop(void);
bit I2C_WriteByte(unsigned char dat);
unsigned char I2C_ReadByte(bit ack);
// 延时函数略(需10us级)
#define ADS1115_ADDR 0x90 // 0x48 << 1 | Write
void ADS1115_Init(void)
{
I2C_Start();
I2C_WriteByte(ADS1115_ADDR); // 器件地址 + 写
I2C_WriteByte(0x01); // 配置寄存器地址
I2C_WriteByte(0xC2); // 高字节:单端AIN0, ±2.048V, 单次模式
I2C_WriteByte(0x83); // 低字节:128SPS,无比较器
I2C_Stop();
}
int ADS1115_Read(void)
{
unsigned char dataH, dataL;
int result;
// 再次指向寄存器0x00(转换结果)
I2C_Start();
I2C_WriteByte(ADS1115_ADDR);
I2C_WriteByte(0x00);
I2C_Stop();
// 读取数据
I2C_Start();
I2C_WriteByte(ADS1115_ADDR | 0x01); // 读
dataH = I2C_ReadByte(1);
dataL = I2C_ReadByte(0);
I2C_Stop();
result = (dataH << 8) | dataL;
if(result > 0x7FFF) result -= 65536; // 转换为有符号值
return result; // 原始ADC值(范围 -32768~+32767)
}
🧮 六、电压换算
float voltage = adc_val * 2.048 / 32768.0;
假设设置量程为 ±2.048V,则对应:
- 原始值
0x7FFF
≈ +2.048V - 原始值
0x0000
≈ 0V - 原始值
0x8000
≈ -2.048V
⚠️ 七、注意事项
项目 | 说明 |
---|---|
地址 | 默认地址为 0x48(写 0x90,读 0x91),通过 ADDR 引脚可改 |
参考电压 | 与 VCC 一致,设置 PGA 需匹配输入电压范围 |
转换模式 | 支持连续和单次模式,单次需每次触发 |
I²C 总线 | 建议加 4.7kΩ 上拉电阻 |
电平兼容 | 3.3V 与 5V 都支持,注意与 51 单片机电平匹配 |
📚 资料与链接
- 官方数据手册:
https://www.ti.com/product/ADS1115 - 软件I2C协议详解:
https://www.cnblogs.com/embedded2013/p/ads1115.html - Arduino ADS1115库参考:可用于参数验证
如果你使用的是 STC 系列、想扩展多通道读取、或将数据通过串口输出给上位机,我可以继续帮你写完整采集程序和串口调试助手脚本。是否继续?
好的,下面给你一个基于51单片机(STC89C52)驱动ADS1115的多通道采集完整示例程序,并包含通过串口将采集数据发送给上位机的代码。方便你做多路模拟量采集与调试。
51单片机驱动ADS1115多通道采集 + 串口发送完整代码示例
#include <reg52.h>
sbit SDA = P2^0;
sbit SCL = P2^1;
#define ADS1115_ADDR_W 0x90 // 写地址 (0x48 << 1)
#define ADS1115_ADDR_R 0x91 // 读地址
// 延时函数,约10us,视晶振调整
void delay_us(unsigned int us)
{
unsigned int i,j;
for(i=us; i>0; i--)
for(j=10; j>0; j--);
}
// I2C启动条件
void I2C_Start(void)
{
SDA = 1; SCL = 1; delay_us(5);
SDA = 0; delay_us(5);
SCL = 0; delay_us(5);
}
// I2C停止条件
void I2C_Stop(void)
{
SDA = 0; SCL = 1; delay_us(5);
SDA = 1; delay_us(5);
}
// I2C发送字节,返回ACK(0:应答,1:无应答)
bit I2C_WriteByte(unsigned char dat)
{
unsigned char i;
bit ack;
for(i=0; i<8; i++)
{
SDA = (dat & 0x80) ? 1 : 0;
dat <<= 1;
SCL = 1; delay_us(5);
SCL = 0; delay_us(5);
}
SDA = 1; // 释放总线,等待ACK
SCL = 1; delay_us(5);
ack = SDA; // 读取ACK
SCL = 0; delay_us(5);
return ack;
}
// I2C读取字节,参数 ack=1表示发送ACK,0表示NACK
unsigned char I2C_ReadByte(bit ack)
{
unsigned char i, dat=0;
SDA = 1; // 释放数据线
for(i=0; i<8; i++)
{
dat <<= 1;
SCL = 1; delay_us(5);
if(SDA) dat |= 0x01;
SCL = 0; delay_us(5);
}
SDA = (ack) ? 0 : 1; // 发送ACK或NACK
SCL = 1; delay_us(5);
SCL = 0; delay_us(5);
SDA = 1; // 释放数据线
return dat;
}
// 发送配置命令启动ADS1115单次采样(指定通道)
void ADS1115_ConfigChannel(unsigned char channel)
{
unsigned int config = 0;
// 参考TI手册配置:
// OS=1 (启动单次转换)
// MUX: 100 + channel (AIN0:100, AIN1:101, AIN2:110, AIN3:111)
// PGA: 010 (±2.048V)
// MODE: 1 (单次)
// DR: 100 (128SPS)
// COMP: 0000 (关闭比较器)
config = 0x8000 | ((0x04 + channel) << 12) | (0x02 << 9) | (1 << 8) | (0x04 << 5);
I2C_Start();
I2C_WriteByte(ADS1115_ADDR_W);
I2C_WriteByte(0x01); // 配置寄存器地址
I2C_WriteByte((config >> 8) & 0xFF);
I2C_WriteByte(config & 0xFF);
I2C_Stop();
}
// 读取转换结果(16位有符号数)
int ADS1115_ReadResult(void)
{
unsigned char high, low;
int val;
I2C_Start();
I2C_WriteByte(ADS1115_ADDR_W);
I2C_WriteByte(0x00); // 指向转换寄存器
I2C_Stop();
I2C_Start();
I2C_WriteByte(ADS1115_ADDR_R);
high = I2C_ReadByte(1);
low = I2C_ReadByte(0);
I2C_Stop();
val = (high << 8) | low;
if(val & 0x8000) val -= 0x10000; // 转换成带符号数
return val;
}
// 串口初始化 9600bps@11.0592MHz
void UART_Init(void)
{
SCON = 0x50; // 8位可变波特率,允许接收
TMOD = 0x20; // 定时器1模式2,8位自动重装载
TH1 = 0xFD; // 9600bps
TL1 = 0xFD;
TR1 = 1; // 启动定时器1
EA = 1; // 开总中断
ES = 1; // 允许串口中断
}
// 串口发送一个字节
void UART_SendByte(unsigned char dat)
{
SBUF = dat;
while(!TI);
TI = 0;
}
// 发送整型数字(-32768~32767)字符串到串口
void UART_SendInt(int val)
{
char buf[7]; // 负号+5位+结束符足够
unsigned char i = 0;
unsigned char sign = 0;
unsigned int v;
if(val < 0)
{
sign = 1;
v = -val;
}
else
{
v = val;
}
do
{
buf[i++] = v % 10 + '0';
v /= 10;
}while(v > 0);
if(sign) buf[i++] = '-';
// 反转字符串
while(i > 0)
{
UART_SendByte(buf[--i]);
}
}
// 延时ms函数(粗略)
void delay_ms(unsigned int ms)
{
unsigned int i,j;
for(i=ms; i>0; i--)
for(j=120; j>0; j--);
}
void main(void)
{
int adc_val;
unsigned char ch;
UART_Init();
while(1)
{
for(ch=0; ch<4; ch++) // 四通道采集
{
ADS1115_ConfigChannel(ch); // 配置指定通道启动单次转换
delay_ms(10); // 等待采样完成(大于1/128s)
adc_val = ADS1115_ReadResult();
// 发送格式:CHx:数值\n
UART_SendByte('C');
UART_SendByte('H');
UART_SendByte(ch + '0');
UART_SendByte(':');
UART_SendInt(adc_val);
UART_SendByte('\r');
UART_SendByte('\n');
delay_ms(100);
}
}
}
说明
- 通过 软件I2C 实现对 ADS1115 的配置和读取
- 支持 4个单端通道 轮流采样
- 采样后数据通过 串口 9600 波特率 发送到上位机
- 上位机串口助手(如 SecureCRT、PuTTY、或者自行开发的软件)可接收数据,格式为:
CH0:1234
每通道一行 - 采样延时保证转换完成,约10ms(128SPS对应约7.8ms)
上位机接收建议
明白!下面给你一个简单的 Python 上位机串口接收和实时绘图示例,基于 pyserial
和 matplotlib
,适合接收上面51单片机发送的 ADS1115 多通道数据并实时显示:
Python 串口接收 + 实时绘图示例(支持4通道)
import serial
import threading
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import re
from collections import deque
# 串口参数
PORT = 'COM3' # 请根据实际改成你的串口号
BAUDRATE = 9600
# 创建串口对象
ser = serial.Serial(PORT, BAUDRATE, timeout=1)
# 每个通道保存最近100个采样点
data_ch0 = deque(maxlen=100)
data_ch1 = deque(maxlen=100)
data_ch2 = deque(maxlen=100)
data_ch3 = deque(maxlen=100)
# 初始化全部通道数据为0
for _ in range(100):
data_ch0.append(0)
data_ch1.append(0)
data_ch2.append(0)
data_ch3.append(0)
# 正则匹配数据格式,如 CH0:1234
pattern = re.compile(r'CH(\d):(-?\d+)')
def read_serial():
while True:
line = ser.readline().decode('utf-8', errors='ignore').strip()
match = pattern.match(line)
if match:
ch = int(match.group(1))
val = int(match.group(2))
if ch == 0:
data_ch0.append(val)
elif ch == 1:
data_ch1.append(val)
elif ch == 2:
data_ch2.append(val)
elif ch == 3:
data_ch3.append(val)
# 启动串口读取线程
threading.Thread(target=read_serial, daemon=True).start()
# 绘图初始化
fig, ax = plt.subplots()
line0, = ax.plot(range(100), data_ch0, label='CH0')
line1, = ax.plot(range(100), data_ch1, label='CH1')
line2, = ax.plot(range(100), data_ch2, label='CH2')
line3, = ax.plot(range(100), data_ch3, label='CH3')
ax.set_ylim(-35000, 35000) # 根据16位有符号范围调整
ax.set_title("ADS1115 4通道 ADC 实时采样")
ax.set_xlabel("采样点")
ax.set_ylabel("ADC值")
ax.legend()
def update(frame):
line0.set_ydata(data_ch0)
line1.set_ydata(data_ch1)
line2.set_ydata(data_ch2)
line3.set_ydata(data_ch3)
return line0, line1, line2, line3
ani = animation.FuncAnimation(fig, update, interval=100)
plt.show()
使用说明
- 安装依赖
pip install pyserial matplotlib
- 修改串口号
把代码中 PORT = 'COM3'
替换成你实际串口号,比如 Linux/Mac 可能是 /dev/ttyUSB0
。
- 运行脚本
运行该脚本后,会弹出一个窗口,实时显示4个通道的ADC数值曲线。
- 数据格式要求
确保单片机发送数据格式为:
CH0:1234
CH1:-123
CH2:2345
CH3:-3456
每行一个通道数据,数字为带符号的16位ADC值。
发表回复