STM32传感器数据采集实战:I2C与ADC应用详解

📅 2026/7/19 2:38:46
STM32传感器数据采集实战:I2C与ADC应用详解
1. STM32与传感器数据采集概述在嵌入式系统开发中传感器数据采集是最基础也是最关键的功能之一。STM32作为一款广泛使用的32位微控制器凭借其丰富的外设资源和强大的处理能力成为传感器数据采集应用的理想选择。本实战教程将深入讲解如何使用C语言在STM32平台上实现传感器数据采集重点涵盖I2C总线通信和ADC模拟信号采集两大核心技术。传感器数据采集系统通常由三部分组成传感器模块、信号处理单元和数据输出接口。以常见的环境监测系统为例温湿度传感器通过I2C接口与STM32通信光敏电阻等模拟传感器则通过ADC接口连接。STM32负责采集这些传感器的数据经过处理后通过串口输出到上位机或存储在本地Flash中。选择STM32进行传感器数据采集有以下几个优势丰富的外设接口内置I2C、SPI、UART等多种通信接口高性能ADC12位精度多通道输入低功耗特性适合电池供电的便携式设备完善的开发工具链STM32CubeMX、Keil MDK等2. 开发环境搭建与基础配置2.1 硬件准备进行STM32传感器数据采集开发需要准备以下硬件设备STM32开发板推荐STM32F103C8T6最小系统板传感器模块如AHT20温湿度传感器、光敏电阻模块ST-Link调试器杜邦线若干面包板可选硬件连接时需特别注意I2C设备连接要正确区分SDA和SCL线ADC输入电压不得超过STM32的工作电压(3.3V)确保所有设备共地2.2 软件工具安装开发STM32需要以下软件工具STM32CubeMX用于外设配置和代码生成Keil MDK-ARM集成开发环境ST-Link驱动用于程序下载和调试串口调试助手如Putty、SecureCRT等安装步骤从ST官网下载STM32CubeMX并安装安装Keil MDK-ARM注意选择正确的设备支持包连接ST-Link到电脑安装对应驱动程序测试开发板与电脑的连接是否正常2.3 工程创建与基础配置使用STM32CubeMX创建新工程的步骤打开STM32CubeMX点击New Project在芯片选择界面输入STM32F103C8选择对应型号在Pinout Configuration界面配置系统时钟选择HSE外部高速时钟作为时钟源配置PLL使系统时钟达到72MHz配置调试接口通常选择SWD生成基础工程代码提示初次使用STM32CubeMX时建议保存.ioc配置文件方便后续修改和版本管理。3. I2C总线通信与传感器驱动3.1 I2C总线原理详解I2C(Inter-Integrated Circuit)是一种同步、多主从结构的串行通信总线由Philips公司开发具有以下特点仅需两根信号线SCL(时钟线)和SDA(数据线)支持多主多从设备连接标准模式速度100kHz快速模式400kHz7位或10位设备地址寻址I2C通信的基本时序包括起始条件SCL高电平时SDA由高变低设备地址传输7位地址1位读写标志数据传送每个字节后跟随应答位停止条件SCL高电平时SDA由低变高以AHT20温湿度传感器为例其I2C通信流程为STM32发送起始条件发送AHT20设备地址(0x38) 写标志发送测量命令(0xAC)等待测量完成发送设备地址 读标志读取6字节数据(温湿度原始值)发送停止条件3.2 STM32CubeMX配置I2C外设在STM32CubeMX中配置I2C外设的详细步骤在Pinout Configuration界面找到I2C1或I2C2选择I2C模式配置参数Timing标准模式选择0x2000090EClock Speed100kHzDuty Cycle2:1检查自动分配的GPIO引脚是否合适生成代码配置完成后STM32CubeMX会自动生成I2C初始化代码hi2c1.Instance I2C1; hi2c1.Init.ClockSpeed 100000; 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(); }3.3 AHT20温湿度传感器驱动实现AHT20传感器驱动的核心函数实现传感器初始化函数uint8_t AHT20_Init(void) { uint8_t cmd[3] {0xBE, 0x08, 0x00}; if(HAL_I2C_Master_Transmit(hi2c1, AHT20_ADDRESS, cmd, 3, 100) ! HAL_OK) return 1; HAL_Delay(10); return 0; }触发测量函数uint8_t AHT20_StartMeasurement(void) { uint8_t cmd[3] {0xAC, 0x33, 0x00}; if(HAL_I2C_Master_Transmit(hi2c1, AHT20_ADDRESS, cmd, 3, 100) ! HAL_OK) return 1; return 0; }读取温湿度数据函数uint8_t AHT20_ReadData(float *temperature, float *humidity) { uint8_t data[6]; uint32_t temp_raw, humi_raw; // 等待测量完成 uint8_t status 0; do { if(HAL_I2C_Master_Receive(hi2c1, AHT20_ADDRESS, status, 1, 100) ! HAL_OK) return 1; HAL_Delay(5); } while((status 0x80) 0x80); // 读取6字节数据 if(HAL_I2C_Master_Receive(hi2c1, AHT20_ADDRESS, data, 6, 100) ! HAL_OK) return 1; // 数据解析 humi_raw ((uint32_t)data[1] 12) | ((uint32_t)data[2] 4) | (data[3] 4); temp_raw ((uint32_t)(data[3] 0x0F) 16) | ((uint32_t)data[4] 8) | data[5]; *humidity (float)humi_raw * 100 / 0x100000; *temperature (float)temp_raw * 200 / 0x100000 - 50; return 0; }注意AHT20传感器需要定期校准建议每24小时执行一次初始化命令。4. ADC模拟信号采集技术4.1 ADC工作原理与配置STM32内置的12位逐次逼近型(SAR)ADC工作原理采样保持电路捕获输入电压逐次逼近寄存器从最高位开始比较经过12次比较后得到最终数字值结果存储在数据寄存器中在STM32CubeMX中配置ADC的步骤在Analog下选择ADC1配置参数Resolution12位Data Alignment右对齐Scan Conversion Mode禁用Continuous Conversion Mode启用DMA Continuous Requests根据需要启用End Of Conversion SelectionEOC标志在每个转换后置位在Channel Settings中添加使用的通道设置采样时间通常选择239.5 cycles生成代码生成的ADC初始化代码示例hadc1.Instance ADC1; hadc1.Init.ScanConvMode ADC_SCAN_DISABLE; hadc1.Init.ContinuousConvMode ENABLE; hadc1.Init.DiscontinuousConvMode DISABLE; hadc1.Init.ExternalTrigConv ADC_SOFTWARE_START; hadc1.Init.DataAlign ADC_DATAALIGN_RIGHT; hadc1.Init.NbrOfConversion 1; if (HAL_ADC_Init(hadc1) ! HAL_OK) { Error_Handler(); } ADC_ChannelConfTypeDef sConfig {0}; sConfig.Channel ADC_CHANNEL_0; sConfig.Rank ADC_REGULAR_RANK_1; sConfig.SamplingTime ADC_SAMPLETIME_239CYCLES_5; if (HAL_ADC_ConfigChannel(hadc1, sConfig) ! HAL_OK) { Error_Handler(); }4.2 光敏电阻数据采集实现光敏电阻采集电路通常采用分压电路设计光敏电阻与固定电阻串联连接点接入ADC输入引脚光强变化导致电阻值变化从而改变分压值ADC数据采集函数实现uint16_t Read_ADC_Value(ADC_HandleTypeDef *hadc) { HAL_ADC_Start(hadc); HAL_ADC_PollForConversion(hadc, 10); return HAL_ADC_GetValue(hadc); } float Get_Light_Sensor_Voltage(void) { uint16_t adc_value Read_ADC_Value(hadc1); return (float)adc_value * 3.3f / 4095.0f; }根据ADC值控制LED亮度的PWM实现void PWM_Control_LED(uint16_t adc_value) { // 将ADC值映射到PWM占空比(0-100%) uint16_t duty_cycle 100 - (adc_value * 100 / 4095); // 设置PWM占空比 __HAL_TIM_SET_COMPARE(htim3, TIM_CHANNEL_1, (htim3.Init.Period * duty_cycle) / 100); }4.3 多通道ADC采集与滤波处理在实际应用中通常需要对多个模拟信号进行采集并采用数字滤波提高数据稳定性。多通道ADC配置要点在STM32CubeMX中启用扫描模式(Scan Conversion Mode)设置不连续转换模式(Discontinuous Conversion Mode)可选为每个通道配置采样时间和转换顺序启用DMA传输提高效率数字滤波算法实现移动平均滤波示例#define FILTER_SIZE 10 typedef struct { float buffer[FILTER_SIZE]; uint8_t index; float sum; } Filter_t; float Moving_Average_Filter(Filter_t *filter, float new_value) { filter-sum - filter-buffer[filter-index]; filter-buffer[filter-index] new_value; filter-sum new_value; filter-index (filter-index 1) % FILTER_SIZE; return filter-sum / FILTER_SIZE; }5. 数据存储与系统集成5.1 Flash存储传感器数据STM32内部Flash存储数据的基本流程解锁Flash写保护擦除目标扇区编程写入数据重新锁定FlashFlash操作函数实现#define FLASH_USER_START_ADDR 0x08010000 // 从第64KB开始 uint8_t Flash_Write_Data(uint32_t address, uint32_t *data, uint16_t length) { HAL_FLASH_Unlock(); FLASH_EraseInitTypeDef erase; erase.TypeErase FLASH_TYPEERASE_PAGES; erase.PageAddress FLASH_USER_START_ADDR; erase.NbPages 1; uint32_t page_error; if(HAL_FLASHEx_Erase(erase, page_error) ! HAL_OK) { HAL_FLASH_Lock(); return 1; } for(uint16_t i 0; i length; i) { if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, address i*4, data[i]) ! HAL_OK) { HAL_FLASH_Lock(); return 1; } } HAL_FLASH_Lock(); return 0; } void Flash_Read_Data(uint32_t address, uint32_t *data, uint16_t length) { for(uint16_t i 0; i length; i) { data[i] *(__IO uint32_t*)(address i*4); } }5.2 系统集成与主程序实现完整的主程序流程示例int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_I2C1_Init(); MX_ADC1_Init(); MX_USART1_UART_Init(); MX_TIM3_Init(); AHT20_Init(); HAL_TIM_PWM_Start(htim3, TIM_CHANNEL_1); Filter_t temp_filter {0}; Filter_t humi_filter {0}; Filter_t light_filter {0}; while(1) { float temp, humi; AHT20_StartMeasurement(); HAL_Delay(100); AHT20_ReadData(temp, humi); float light_voltage Get_Light_Sensor_Voltage(); // 滤波处理 float filtered_temp Moving_Average_Filter(temp_filter, temp); float filtered_humi Moving_Average_Filter(humi_filter, humi); float filtered_light Moving_Average_Filter(light_filter, light_voltage); // 控制LED亮度 PWM_Control_LED(filtered_light * 4095 / 3.3f); // 串口输出 printf(Temp: %.1fC, Humi: %.1f%%, Light: %.2fV\r\n, filtered_temp, filtered_humi, filtered_light); // 每小时存储一次数据 static uint32_t last_save 0; if(HAL_GetTick() - last_save 3600000) { uint32_t save_data[3] { *(uint32_t*)filtered_temp, *(uint32_t*)filtered_humi, *(uint32_t*)filtered_light }; Flash_Write_Data(FLASH_USER_START_ADDR, save_data, 3); last_save HAL_GetTick(); } HAL_Delay(1000); } }5.3 常见问题与调试技巧I2C通信失败排查步骤检查物理连接SCL、SDA线是否接反上拉电阻是否合适(通常4.7kΩ)用逻辑分析仪抓取I2C波形检查时序是否符合规范确认设备地址是否正确注意7位地址和8位地址的区别检查I2C时钟速度是否超过传感器支持的最大速率ADC采集数据不稳定的解决方法在ADC输入引脚添加0.1μF滤波电容适当增加采样时间软件端实现数字滤波算法确保参考电压稳定必要时使用外部参考电压Flash写入失败的常见原因未先擦除就直接写入写入地址未对齐STM32通常要求32位对齐写入区域被保护或写保护未解除写入过程中发生中断调试建议使用STM32的硬件断点功能定位问题合理利用串口打印调试信息对于时序敏感的问题使用逻辑分析仪或示波器观察信号分模块测试确保每个功能单元正常工作后再进行系统集成