1. STM32软件IIC驱动ADS1115实战指南
在嵌入式开发中,ADC(模数转换器)是连接模拟世界与数字系统的关键桥梁。当STM32内置ADC的精度或通道数无法满足需求时,ADS1115这类外部高精度ADC模块就成了工程师们的首选方案。我最近在一个工业传感器项目中就遇到了这种情况——需要以0.1mV的分辨率测量多路热电偶信号,STM32F103自带的12位ADC显然力不从心。经过对比选型,最终选择了TI的ADS1115,并通过软件I2C成功实现了稳定采集。下面就把这套经过实战检验的方案完整分享给大家。
ADS1115作为16位精度的ADC,其最小可检测电压变化量达到0.1875mV(当PGA=±6.144V时),比STM32内置ADC的精度高出16倍。更难得的是,它内置可编程增益放大器(PGA)、电压比较器和稳定的基准电压源,单芯片就实现了信号调理+数字化的一站式解决方案。在实际项目中,我特别看重它的两个特性:一是支持连续转换和单次转换两种模式,可根据功耗需求灵活选择;二是带有可配置的数据速率(8SPS到860SPS),能平衡速度与噪声的关系。
2. 硬件设计与连接要点
2.1 硬件选型与电路设计
我使用的硬件组合是STM32F103C8T6最小系统板+ADS1115模块。这里特别提醒:市面上常见的蓝色ADS1115模块已经集成了必要的上拉电阻(通常4.7kΩ)和电源滤波电容,若使用裸芯片则需要自行添加。以下是关键连接方式:
- VDD:接3.3V(注意:ADS1115的绝对最大电压是7V,但推荐工作电压2.0-5.5V)
- GND:与STM32共地
- SCL/SDA:分别接STM32任意GPIO(我用的PB6/PB7)
- ADDR:接地(设置I2C地址为0x48)
- A0-A3:接四路模拟输入(注意电压不得超过PGA设置范围)
重要提示:若测量差分信号,需确保共模电压在(GND+0.3V)到(VDD-0.3V)之间。我曾因忽略这点导致前三次读数全部异常。
2.2 抗干扰设计经验
在高精度测量中,噪声控制至关重要。通过多次实测,总结出以下有效方法:
- 电源滤波:在ADS1115的VDD引脚就近放置10μF钽电容+0.1μF陶瓷电容组合
- 信号走线:模拟输入线采用双绞线,长度控制在15cm以内
- 接地策略:模拟地与数字地单点连接,在电源入口处用0Ω电阻或磁珠连接
- 屏蔽措施:对微弱信号(如热电偶)使用屏蔽线,屏蔽层单端接地
3. 软件I2C驱动实现
3.1 软件I2C底层配置
由于硬件I2C可能因项目需要被其他设备占用,软件模拟I2C提供了更大的灵活性。以下是基于STM32标准外设库的初始化代码:
c复制// 宏定义IO口操作
#define IIC_SCL_H() GPIOB->BSRR = GPIO_Pin_6
#define IIC_SCL_L() GPIOB->BRR = GPIO_Pin_6
#define IIC_SDA_H() GPIOB->BSRR = GPIO_Pin_7
#define IIC_SDA_L() GPIOB->BRR = GPIO_Pin_7
#define IIC_SDA_READ() (GPIOB->IDR & GPIO_Pin_7)
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
IIC_SCL_H();
IIC_SDA_H();
}
3.2 关键时序实现
软件I2C最核心的是严格满足时序要求。ADS1115的I2C时钟频率最高400kHz,实测在STM32F103上使用以下延时函数可稳定工作:
c复制// 微秒级延时(72MHz主频)
void IIC_Delay_us(uint32_t n)
{
n *= 8;
while(n--);
}
// 起始信号
void IIC_Start(void)
{
IIC_SDA_H();
IIC_SCL_H();
IIC_Delay_us(5);
IIC_SDA_L();
IIC_Delay_us(5);
IIC_SCL_L();
}
// 停止信号
void IIC_Stop(void)
{
IIC_SDA_L();
IIC_SCL_H();
IIC_Delay_us(5);
IIC_SDA_H();
IIC_Delay_us(5);
}
调试心得:时序中的延时参数需要根据主频调整。我曾因延时不足导致从机无应答,用逻辑分析仪抓取波形后发现SCL高电平时间仅1.2μs(要求>2.5μs),调整后问题解决。
4. ADS1115寄存器配置详解
4.1 配置寄存器解析
ADS1115通过配置寄存器(地址0x01)控制工作模式,其各位定义如下:
| BIT15-12 | BIT11-9 | BIT8 | BIT7-5 | BIT4-0 |
|---|---|---|---|---|
| OS | MUX | PGA | MODE | DR |
常用配置示例:
- 单次转换模式:0x8543(AIN0->AIN1差分,±2.048V,128SPS)
- 连续转换模式:0x0543(同上,但为连续模式)
配置函数实现:
c复制void ADS1115_WriteConfig(uint16_t config)
{
IIC_Start();
IIC_Send_Byte(0x90); // 地址+写
IIC_Wait_Ack();
IIC_Send_Byte(0x01); // 指向配置寄存器
IIC_Wait_Ack();
IIC_Send_Byte(config >> 8);
IIC_Wait_Ack();
IIC_Send_Byte(config & 0xFF);
IIC_Wait_Ack();
IIC_Stop();
}
4.2 数据读取流程优化
ADS1115的转换数据存储在0x00寄存器(16位,二进制补码格式)。为提高效率,建议采用以下读取策略:
- 单次转换模式:触发转换后延时等待(根据DR设置计算最坏情况时间)
- 连续转换模式:直接读取,但需注意数据更新时机
实测代码:
c复制int16_t ADS1115_ReadData(void)
{
uint8_t buf[2];
int16_t result;
IIC_Start();
IIC_Send_Byte(0x90); // 地址+写
IIC_Wait_Ack();
IIC_Send_Byte(0x00); // 指向转换寄存器
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0x91); // 地址+读
IIC_Wait_Ack();
buf[0] = IIC_Read_Byte(1); // 发送ACK
buf[1] = IIC_Read_Byte(0); // 发送NACK
IIC_Stop();
result = (buf[0] << 8) | buf[1];
return result;
}
5. 实际应用中的问题排查
5.1 典型故障现象与解决
-
读取值始终为0x7FFF或0x8000
- 检查输入电压是否超限
- 确认PGA设置是否匹配输入范围
- 测量VDD电压是否正常(我曾因LDO故障导致3.3V实际只有2.8V)
-
I2C无应答(NACK)
- 用示波器检查SCL/SDA波形
- 确认上拉电阻值(4.7kΩ对3.3V系统较合适)
- 检查地址设置(ADDR引脚电平决定地址末位)
-
数据跳变过大
- 尝试降低数据速率(DR)
- 检查电源纹波(建议用示波器AC耦合观察)
- 对输入信号添加RC滤波(如1kΩ+0.1μF)
5.2 精度优化技巧
通过三个月的实际项目积累,总结出以下提升测量精度的有效方法:
-
基准源处理
- 使用外部精密基准(如REF5025)替代内部基准
- 对基准电压进行软件校准(记录10次测量取平均)
-
温度补偿
- 定期读取芯片温度(通过TMP117等)
- 建立温度-误差查找表进行补偿
-
数字滤波
- 滑动平均滤波(窗口大小根据信号特性调整)
c复制#define FILTER_SIZE 8 int16_t filter_buf[FILTER_SIZE]; uint8_t filter_index = 0; int16_t MovingAverage(int16_t new_val) { filter_buf[filter_index++] = new_val; if(filter_index >= FILTER_SIZE) filter_index = 0; int32_t sum = 0; for(uint8_t i=0; i<FILTER_SIZE; i++){ sum += filter_buf[i]; } return (int16_t)(sum/FILTER_SIZE); }
6. 进阶应用:多片级联与自动量程
6.1 多片ADS1115管理
当需要更多通道时,可通过ADDR引脚设置不同地址(0x48-0x4B),实现最多4片ADS1115共用I2C总线。关键点:
- 为每片分配唯一地址(通过ADDR引脚接GND/VDD/SCL/SDA)
- 总线负载管理:多片上拉电阻并联值需满足:
$$ R_{parallel} = \frac{V_{DD}-0.4}{3mA} $$
例如3.3V系统:最小上拉约967Ω
6.2 自动量程实现方案
对于动态范围大的信号,可编程PGA实现自动量程切换。我的实现逻辑:
c复制uint16_t AutoRangeConfig(uint8_t channel)
{
uint16_t configs[] = {0x8583, 0x8583, 0x8582, 0x8581, 0x8580}; // ±6.144V到±0.256V
int16_t raw_val;
for(uint8_t i=0; i<5; i++){
ADS1115_WriteConfig(configs[i] | (channel<<12));
Delay_ms(10);
raw_val = ADS1115_ReadData();
if(abs(raw_val) < 30000){ // 留10%余量
return configs[i] | (channel<<12);
}
}
return configs[0] | (channel<<12); // 默认最大量程
}
这个方案在电池电压监测项目中表现优异,能自动适应2V-60V的宽范围输入(配合分压电路)。
