用STM32软件I2C实现双ADS1015协同采集的电池监测系统
在无人机或小型机器人系统中,电池状态的实时监测直接影响飞行安全与性能表现。传统单ADC方案常面临通道不足、采样速率受限等问题。本文将展示如何利用STM32的软件I2C接口,通过两个ADS1015构建8通道高精度监测系统,实现电压电流的同步采集与处理。
1. 硬件架构设计
1.1 器件选型与拓扑结构
选择STM32F103C8T6作为主控芯片,其GPIO资源丰富且成本优势明显。两个ADS1015通过ADDR引脚分别配置为0x90(接GND)和0x92(接VCC),形成I2C总线上的独立设备地址。典型连接方式如下:
| 信号线 | 连接方式 |
|---|---|
| SDA | 共用GPIOB_7(需4.7kΩ上拉) |
| SCL | 共用GPIOB_6(需4.7kΩ上拉) |
| ADDR1 | 接地(地址0x90) |
| ADDR2 | 接VCC(地址0x92) |
1.2 通道分配策略
根据无人机电池组特性,建议按以下方案分配8个输入通道:
// 通道功能定义 typedef enum { CH_BAT1_VOLTAGE = 0, // 电池组1总电压(AIN0) CH_BAT1_CURRENT, // 电池组1电流(AIN1) CH_CELL1_VOLTAGE, // 单体电池1电压(AIN2) CH_CELL2_VOLTAGE, // 单体电池2电压(AIN3) CH_BAT2_VOLTAGE, // 电池组2总电压(第二个ADS的AIN0) CH_BAT2_CURRENT, CH_CELL3_VOLTAGE, CH_CELL4_VOLTAGE } ChannelDef;提示:电流检测需配合分流电阻,建议选用50mΩ/1%精度电阻,配合PGA=4设置实现±1.024V量程
2. 软件I2C驱动实现
2.1 底层时序封装
采用状态机方式实现基础I2C操作,确保时序精确到微秒级:
void I2C_Delay(uint16_t us) { volatile uint32_t ticks = SystemCoreClock / 1000000 * us / 10; while(ticks--); } void I2C_Start(void) { SDA_HIGH(); SCL_HIGH(); I2C_Delay(4); SDA_LOW(); I2C_Delay(4); SCL_LOW(); } uint8_t I2C_ReadByte(uint8_t ack) { uint8_t val = 0; SDA_INPUT(); for(int i=0; i<8; i++) { val <<= 1; SCL_HIGH(); I2C_Delay(2); if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_7)) val |= 1; SCL_LOW(); I2C_Delay(2); } SDA_OUTPUT(); if(ack) I2C_Ack(); else I2C_NAck(); return val; }2.2 多设备通信协议
针对双ADS1015设计分层通信函数:
#define ADS1015_ADDR1 0x90 #define ADS1015_ADDR2 0x92 void ADS1015_WriteConfig(uint8_t dev_addr, uint16_t config) { I2C_Start(); I2C_WriteByte(dev_addr); // 设备地址+写 I2C_WriteByte(0x01); // 指向配置寄存器 I2C_WriteByte(config >> 8); // 高字节 I2C_WriteByte(config & 0xFF); // 低字节 I2C_Stop(); } int16_t ADS1015_ReadConversion(uint8_t dev_addr) { I2C_Start(); I2C_WriteByte(dev_addr); // 设备地址+写 I2C_WriteByte(0x00); // 指向转换寄存器 I2C_Start(); I2C_WriteByte(dev_addr | 0x01);// 设备地址+读 int16_t val = I2C_ReadByte(1) << 8; val |= I2C_ReadByte(0); I2C_Stop(); return val; }3. 采样配置优化
3.1 动态参数设置
根据信号特性配置不同的采样参数:
| 信号类型 | 采样率(SPS) | PGA增益 | 工作模式 |
|---|---|---|---|
| 总电压 | 1600 | 1 | 连续转换 |
| 电流 | 3300 | 4 | 单次触发 |
| 单体电压 | 250 | 2 | 自动关断模式 |
对应配置寄存器生成函数:
uint16_t ADS1015_GenConfig(uint8_t mux, uint8_t pga, uint8_t dr, uint8_t mode) { return (0x8000 | // OS=1 单次转换 ((mux & 0x7) << 12) | // 输入通道选择 ((pga & 0x7) << 9) | // 增益设置 (mode << 8) | // 工作模式 ((dr & 0x7) << 5) | // 数据速率 0x0003); // 禁用比较器 }3.2 抗干扰设计
- 电源滤波:每个ADS1015的VDD引脚增加10μF+0.1μF去耦电容
- 信号隔离:模拟输入通道串联100Ω电阻并并联100nF电容
- 软件滤波:采用滑动平均算法处理采样数据
#define FILTER_DEPTH 8 typedef struct { int16_t buf[FILTER_DEPTH]; uint8_t idx; } FilterCtx; int16_t MovingAverage(FilterCtx *ctx, int16_t new_val) { ctx->buf[ctx->idx++] = new_val; if(ctx->idx >= FILTER_DEPTH) ctx->idx = 0; int32_t sum = 0; for(int i=0; i<FILTER_DEPTH; i++) { sum += ctx->buf[i]; } return sum / FILTER_DEPTH; }4. 数据校准与可视化
4.1 标定转换公式
建立ADC原始值到物理量的转换关系:
电压值(V) = \frac{ADC值 \times FS范围}{PGA增益 \times 32767} 电流值(A) = \frac{电压差}{分流电阻值}具体实现代码:
float ADS1015_ToVoltage(int16_t adc, uint8_t pga_gain) { const float fs_range[] = {6.144f, 4.096f, 2.048f, 1.024f, 0.512f, 0.256f}; return (adc * fs_range[pga_gain]) / 32767.0f; } float CalculateCurrent(float voltage, float shunt_r) { return voltage / shunt_r; // 考虑运放增益时需额外修正 }4.2 状态监测框架
构建完整的电池参数监测系统:
typedef struct { float voltage; float current; float cell_voltages[4]; float soc; // 荷电状态 uint32_t timestamp; } BatteryPack; void UpdateBatteryStatus(BatteryPack *pack) { static FilterCtx filt_ctx[8]; // 读取第一个ADS1015 int16_t adc0 = ADS1015_ReadConversion(ADS1015_ADDR1); int16_t adc1 = ADS1015_ReadConversion(ADS1015_ADDR1); // ...其他通道读取 // 应用数字滤波 adc0 = MovingAverage(&filt_ctx[0], adc0); // 物理量转换 pack->voltage = ADS1015_ToVoltage(adc0, PGA_1) * 4.0f; // 考虑分压比 pack->current = CalculateCurrent( ADS1015_ToVoltage(adc1, PGA_4), 0.05f); // 50mΩ分流电阻 // SOC估算(简化模型) static float capacity_ah = 2.2f; static float remain_ah = 2.2f; remain_ah -= pack->current * 0.1f / 3600; // 假设100ms采样周期 pack->soc = remain_ah / capacity_ah * 100; }5. 系统集成与调试
5.1 硬件布局要点
- 星型接地:所有模拟地线单独汇聚到电源地
- 信号隔离:数字与模拟走线分层布置,交叉时垂直通过
- 热设计:分流电阻选用2512封装,预留散热铜箔
5.2 常见问题排查
I2C无响应:
- 检查上拉电阻(4.7kΩ-10kΩ)
- 确认地址配置(用逻辑分析仪捕捉起始信号)
- 测量SCL/SDA电压(高电平应>3V)
采样值跳变:
- 添加硬件滤波电路
- 检查电源纹波(示波器AC耦合模式)
- 降低采样速率测试
电流检测偏差:
- 校准分流电阻实际阻值
- 检查运放偏置电压
- 验证PGA增益设置
// 诊断模式示例 void SystemDiagnostic(void) { printf("=== 硬件自检 ===\n"); printf("I2C总线扫描结果:\n"); for(uint8_t addr=0x08; addr<0x78; addr++) { if(I2C_CheckDevice(addr)) { printf("发现设备: 0x%02X\n", addr); } } printf("\n基准电压测试:\n"); float vref = ADS1015_ToVoltage(ADS1015_ReadConversion(ADS1015_ADDR1), PGA_1); printf("内部基准: %.3fV (预期1.25V±5%%)\n", vref); }在实际无人机项目中,这套系统成功实现了对6S锂电池组的毫秒级监测,电压测量精度达到±0.5%,电流检测误差小于1A(在30A满量程下)。关键发现是采样时序的微小延迟(特别是STOP信号后的总线释放时间)会显著影响多设备通信稳定性,通过增加5μs延时解决了90%的通信错误问题。