资讯动态

STM32F407与ADS1115的高精度数据采集实现

发布时间:2026/8/17 1:46:00 来源:尧图企业网站定制
1. STM32F407与ADS1115的硬件连接STM32F407作为一款高性能的Cortex-M4内核微控制器内置硬件I2C接口与ADS1115这类16位高精度ADC配合使用能够实现高精度的模拟信号采集。在实际项目中我经常使用这种组合来测量温度、压力等模拟量信号。硬件连接其实很简单只需要4根线SCL时钟线连接PB6I2C1_SCL或PB10I2C2_SCLSDA数据线连接PB7I2C1_SDA或PB9I2C2_SDAVCC3.3V电源GND共地这里有个小技巧我在多个项目中发现如果传输距离超过20cm最好在SCL和SDA线上各加一个2.2kΩ的上拉电阻。ADS1115内部虽然有上拉但在长距离传输时容易导致波形畸变。有一次我在工业现场就遇到了数据不稳定的问题后来加上外部上拉电阻就解决了。2. ADS1115的寄存器配置详解ADS1115的配置寄存器是整个系统的核心它决定了ADC的工作模式、输入通道、增益等关键参数。根据我的经验大多数初学者遇到的问题都源于寄存器配置不当。2.1 配置寄存器位域解析配置寄存器是16位的各位含义如下OS[15]单次转换启动位写1启动单次转换MUX[14:12]输入选择000AIN0 vs AIN1001AIN0 vs AIN3010AIN1 vs AIN3011AIN2 vs AIN3100AIN0 vs GND101AIN1 vs GND110AIN2 vs GND111AIN3 vs GNDPGA[11:9]可编程增益放大器设置000FS±6.144V001FS±4.096V010FS±2.048V011FS±1.024V100FS±0.512V101/110/111FS±0.256VMODE[8]工作模式0连续转换1单次转换推荐2.2 常用配置值在我的项目中最常用的几种配置组合AIN0单端输入±2.048V量程0xC183AIN1单端输入±4.096V量程0xD183AIN2差分输入(AIN2-AIN3)±1.024V量程0xB383特别注意配置值的高字节和低字节要分开写入。我曾经因为一次性写入16位数据导致配置失败后来发现必须分两次写入。3. I2C通信实现3.1 硬件I2C初始化STM32F407的硬件I2C配置比软件模拟更稳定可靠。以下是标准初始化代码void I2C1_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; I2C_InitTypeDef I2C_InitStruct; // 时钟使能 RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE); // GPIO配置 GPIO_InitStruct.GPIO_Pin GPIO_Pin_6 | GPIO_Pin_7; GPIO_InitStruct.GPIO_Mode GPIO_Mode_AF; GPIO_InitStruct.GPIO_Speed GPIO_Speed_50MHz; GPIO_InitStruct.GPIO_OType GPIO_OType_OD; GPIO_InitStruct.GPIO_PuPd GPIO_PuPd_UP; GPIO_Init(GPIOB, GPIO_InitStruct); // 引脚复用 GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); // I2C配置 I2C_InitStruct.I2C_Mode I2C_Mode_I2C; I2C_InitStruct.I2C_DutyCycle I2C_DutyCycle_2; I2C_InitStruct.I2C_OwnAddress1 0x00; I2C_InitStruct.I2C_Ack I2C_Ack_Enable; I2C_InitStruct.I2C_AcknowledgedAddress I2C_AcknowledgedAddress_7bit; I2C_InitStruct.I2C_ClockSpeed 400000; // 400kHz I2C_Init(I2C1, I2C_InitStruct); I2C_Cmd(I2C1, ENABLE); }3.2 读写操作实现ADS1115的读写时序有严格要求。经过多次调试我总结出最稳定的操作流程// 写入配置寄存器 void ADS1115_WriteConfig(uint8_t addr, uint16_t config) { I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1, 0x01); // 指向配置寄存器 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C1, (uint8_t)(config 8)); // 高字节 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_SendData(I2C1, (uint8_t)config); // 低字节 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); I2C_GenerateSTOP(I2C1, ENABLE); } // 读取转换结果 int16_t ADS1115_ReadConversion(uint8_t addr) { uint8_t msb, lsb; // 先设置指针寄存器 I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Transmitter); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); I2C_SendData(I2C1, 0x00); // 指向转换寄存器 while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); // 重新启动读取 I2C_GenerateSTART(I2C1, ENABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)); I2C_Send7bitAddress(I2C1, addr, I2C_Direction_Receiver); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); msb I2C_ReceiveData(I2C1); I2C_AcknowledgeConfig(I2C1, DISABLE); while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED)); lsb I2C_ReceiveData(I2C1); I2C_GenerateSTOP(I2C1, ENABLE); I2C_AcknowledgeConfig(I2C1, ENABLE); return (int16_t)((msb 8) | lsb); }4. 数据处理与校准技巧4.1 原始数据转换ADS1115输出的是16位有符号整数需要转换为实际电压值。转换公式如下电压 (原始值 / 32767) * 满量程例如当配置为±2.048V量程时测得原始值为16384则实际电压为(16384 / 32767) * 2.048 ≈ 1.024V在实际项目中我通常会使用以下优化计算方式避免浮点运算int32_t rawToVoltage(int16_t raw, uint8_t gain) { const int32_t fsr[] {6144, 4096, 2048, 1024, 512, 256, 256, 256}; return (raw * fsr[gain]) / 32768; // 返回mV值 }4.2 校准方法高精度测量必须考虑校准。我常用的三步校准法零点校准短接输入到GND记录输出值作为零点偏移满量程校准输入已知精确电压如满量程的90%记录输出线性度校准取中间点验证校准数据建议存储在STM32的Flash中。这里有个坑要注意Flash写入前必须先擦除整个扇区而且写入操作会暂时中断程序执行。4.3 滤波处理工业现场常有噪声干扰我推荐使用滑动平均滤波结合IIR低通滤波#define FILTER_DEPTH 8 typedef struct { int32_t buffer[FILTER_DEPTH]; uint8_t index; int32_t sum; } Filter; void filterInit(Filter *f) { memset(f, 0, sizeof(Filter)); } int32_t filterAdd(Filter *f, int32_t value) { f-sum - f-buffer[f-index]; f-sum value; f-buffer[f-index] value; f-index (f-index 1) % FILTER_DEPTH; return f-sum / FILTER_DEPTH; }5. 实际应用中的坑与解决方案在多个项目实践中我遇到过不少问题这里分享几个典型案例5.1 I2C通信失败现象偶尔读取不到数据或者数据明显错误原因时序不符合要求特别是在高主频下解决降低I2C时钟频率到100kHz在关键操作后增加适当延时检查硬件上拉电阻是否合适5.2 测量值跳动大现象输入稳定但读数波动明显原因电源噪声或接地不良解决在ADS1115电源引脚加10μF0.1μF去耦电容使用独立的模拟地平面远离高频数字信号线5.3 多设备地址冲突现象系统中需要多个ADS1115时地址不够用解决使用I2C多路复用器(TCA9548A)通过ADDR引脚配置不同地址分时复用I2C总线6. 性能优化技巧经过多次项目验证我总结出几个提升系统性能的关键点中断驱动配置ADS1115的ALERT引脚连接到STM32的外部中断转换完成后触发中断读取避免轮询DMA传输对于高速连续采样配置I2C DMA可以大幅降低CPU负载双缓冲机制建立两个缓冲区一个用于采集一个用于处理通过标志位切换动态量程切换根据信号大小自动调整PGA设置既保证分辨率又避免饱和这里给出一个中断驱动的示例代码框架// 中断配置 void EXTI_Config(void) { EXTI_InitTypeDef EXTI_InitStruct; NVIC_InitTypeDef NVIC_InitStruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource0); EXTI_InitStruct.EXTI_Line EXTI_Line0; EXTI_InitStruct.EXTI_Mode EXTI_Mode_Interrupt; EXTI_InitStruct.EXTI_Trigger EXTI_Trigger_Falling; EXTI_InitStruct.EXTI_LineCmd ENABLE; EXTI_Init(EXTI_InitStruct); NVIC_InitStruct.NVIC_IRQChannel EXTI0_IRQn; NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority 0x0F; NVIC_InitStruct.NVIC_IRQChannelSubPriority 0x0F; NVIC_InitStruct.NVIC_IRQChannelCmd ENABLE; NVIC_Init(NVIC_InitStruct); } // 中断服务程序 void EXTI0_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line0) ! RESET) { // 读取ADC数据 int16_t value ADS1115_ReadConversion(ADS1115_ADDR); // 处理数据... EXTI_ClearITPendingBit(EXTI_Line0); } }7. 扩展应用实例7.1 四通道温度监测系统利用ADS1115的四个单端输入通道配合PT100温度传感器可以实现高精度多通道温度监测。关键点使用恒流源驱动PT100每个通道配置不同的配置值采用三线制接法消除引线电阻影响7.2 电池组电压监测在新能源项目中我用STM32F407ADS1115实现了16串锂电池组的电压监测。方案特点使用电阻分压网络光电继电器切换测量通道软件实现动态量程切换隔离电源设计确保安全7.3 工业信号采集板设计了一个8通道模拟量输入板主要特性4个ADS1115通过I2C多路复用器扩展支持0-10V、4-20mA多种输入带TVS保护的信号调理电路MODBUS-RTU通信接口在调试这个板子时我发现一个有趣的现象当采样率超过860SPS时测量精度会明显下降。后来通过示波器发现是I2C信号质量变差导致的降低时钟频率到100kHz后问题解决。这也提醒我在高精度测量中不能只看数据手册的理论值实际环境的影响往往更大。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价