别再对着手册发愁了!STM32驱动ADS1115的完整配置流程与电压读取代码分享
从零玩转STM32与ADS1115:高精度ADC配置实战指南
1. 硬件连接与基础认知
当你第一次拿到ADS1115模块时,可能会被它小巧的尺寸和密密麻麻的引脚吓到。别担心,让我们先来认识这个16位精度ADC的核心特性。ADS1115采用I2C接口通信,这意味着你只需要连接4根线就能让它工作:
- VDD:3.3V电源输入(注意:绝对不要超过5.5V)
- GND:接地
- SCL:I2C时钟线,连接STM32的PB6(以F103系列为例)
- SDA:I2C数据线,连接STM32的PB7
提示:ADDR引脚接地时,器件地址为0x90(写)和0x91(读),这是最常见的配置方式。
ADS1115的三大核心优势使其在嵌入式领域广受欢迎:
- 16位分辨率:比常见的10位ADC(如STM32内置ADC)精度高出64倍
- 可编程增益:支持±6.144V到±0.256V的输入范围
- 内置基准:无需外接基准电压源
// 基础I2C初始化代码(HAL库示例) I2C_HandleTypeDef hi2c1; void MX_I2C1_Init(void) { hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000; // 100kHz标准模式 hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init(&hi2c1) != HAL_OK) { Error_Handler(); } }2. 配置寄存器深度解析
Config寄存器是ADS1115的大脑,理解它的每一位含义是成功使用的关键。这个16位寄存器分为高字节(MSB)和低字节(LSB),每个bit都有特定功能:
| 位域 | 名称 | 功能描述 | 典型值 |
|---|---|---|---|
| 15 | OS | 单次转换触发 | 1(启动转换) |
| 14-12 | MUX | 输入选择 | 0x4(A0单端) |
| 11-9 | PGA | 增益设置 | 0x1(±4.096V) |
| 8 | MODE | 工作模式 | 0(连续转换) |
| 7-5 | DR | 数据速率 | 0x4(128SPS) |
| 4-0 | 比较器配置 | 本文不使用 | 0x03 |
MUX配置实战:
- 0x0: AIN0 vs AIN1(差分)
- 0x1: AIN0 vs AIN3(差分)
- 0x4: AIN0单端(最常用)
- 0x5: AIN1单端
- 0x6: AIN2单端
- 0x7: AIN3单端
#define CONFIG_MSB 0xC2 // OS=1, MUX=0x4, PGA=0x1, MODE=0 #define CONFIG_LSB 0x83 // DR=0x4, 比较器禁用3. 完整驱动代码实现
现在让我们把这些理论知识转化为可运行的代码。以下是一个经过实战检验的驱动实现:
// ads1115.h #ifndef __ADS1115_H #define __ADS1115_H #include "stm32f1xx_hal.h" #define ADS1115_ADDR 0x90 // 寄存器定义 #define REG_CONVERSION 0x00 #define REG_CONFIG 0x01 // 电压读取函数 float ADS1115_ReadVoltage(I2C_HandleTypeDef *hi2c, uint8_t channel); #endif// ads1115.c #include "ads1115.h" void ADS1115_WriteConfig(I2C_HandleTypeDef *hi2c, uint8_t mux_config) { uint8_t config[3]; config[0] = REG_CONFIG; config[1] = 0xC2 | (mux_config << 4); // 保持其他配置不变,仅修改MUX config[2] = 0x83; HAL_I2C_Master_Transmit(hi2c, ADS1115_ADDR, config, 3, 100); } float ADS1115_ReadVoltage(I2C_HandleTypeDef *hi2c, uint8_t channel) { uint8_t reg = REG_CONVERSION; uint8_t data[2]; int16_t raw_value; float voltage; // 先配置输入通道 ADS1115_WriteConfig(hi2c, channel); // 启动转换(对于单次模式) if((CONFIG_MSB & 0x80) == 0x80) { HAL_Delay(10); // 等待转换完成 } // 读取转换结果 HAL_I2C_Master_Transmit(hi2c, ADS1115_ADDR, ®, 1, 100); HAL_I2C_Master_Receive(hi2c, ADS1115_ADDR, data, 2, 100); // 组合16位数据 raw_value = (data[0] << 8) | data[1]; // 转换为实际电压(±4.096V量程) if(raw_value >= 0x8000) { voltage = (0xFFFF - raw_value) * -4.096 / 32768.0; } else { voltage = raw_value * 4.096 / 32768.0; } return voltage; }4. 实际应用与调试技巧
在实际项目中,你可能会遇到各种意想不到的问题。以下是几个常见场景的解决方案:
场景1:读数不稳定
- 检查电源是否干净(建议增加0.1μF去耦电容)
- 降低I2C时钟速度(尝试100kHz→50kHz)
- 在SCL/SDA线上增加4.7kΩ上拉电阻
场景2:I2C通信失败
- 确认地址正确(0x90或0x48左移一位)
- 用逻辑分析仪抓取I2C波形
- 检查HAL_I2C_Master_Transmit返回值
场景3:电压读数偏差大
- 确认PGA设置与实际输入电压匹配
- 检查参考电压是否稳定
- 使用万用表对比测量结果
注意:当输入电压接近量程上限时,建议选择更大的PGA值(如±2.048V),这样可以获得更好的分辨率。
多通道轮询示例:
void ReadAllChannels(void) { float voltages[4]; const uint8_t channels[] = {0x04, 0x05, 0x06, 0x07}; // A0-A3单端 for(int i=0; i<4; i++) { voltages[i] = ADS1115_ReadVoltage(&hi2c1, channels[i]); printf("AIN%d: %.3fV\r\n", i, voltages[i]); HAL_Delay(100); } }5. 进阶应用:数据采集系统搭建
当你掌握了基础读数后,可以尝试构建更完整的采集系统:
- 定时采样:利用STM32定时器触发定期读取
- DMA传输:减少CPU开销
- 数据滤波:实现简单的移动平均滤波
#define SAMPLE_COUNT 10 float GetFilteredVoltage(uint8_t channel) { static float buffer[SAMPLE_COUNT]; static uint8_t index = 0; float sum = 0; // 采集新数据 buffer[index] = ADS1115_ReadVoltage(&hi2c1, channel); index = (index + 1) % SAMPLE_COUNT; // 计算平均值 for(int i=0; i<SAMPLE_COUNT; i++) { sum += buffer[i]; } return sum / SAMPLE_COUNT; }在电源监测项目中,我发现ADS1115的连续转换模式配合DMA可以构建极低功耗的数据记录仪。通过合理配置DR位(数据速率),可以在精度和功耗之间取得平衡——对于缓慢变化的温度信号,8SPS的速率就足够了,这能显著降低系统功耗。
