1. 项目背景与硬件选型
最近在做一个智能农业的小项目,需要实时监测土壤湿度。经过对比多种方案,最终选择了STM32F103C8T6作为主控芯片,搭配常见的土壤湿度传感器和0.96寸OLED显示屏。这个组合性价比高,而且STM32的ADC性能完全能满足土壤湿度检测的需求。
STM32F103系列是意法半导体推出的经典Cortex-M3内核微控制器,价格亲民但性能不俗。我用的这款"蓝莓板"核心板在某宝上只要十几块钱,自带SWD调试接口,特别适合学生和爱好者使用。土壤湿度传感器就更便宜了,模拟量输出的版本不到5块钱,通过AO引脚输出0-3.3V的电压信号,湿度越大输出电压越低。
OLED我选了SSD1306驱动的I2C接口版本,分辨率128x64,显示效果清晰而且省电。这里有个小经验:市面上有些OLED模块的I2C地址可能是0x78而不是常见的0x7A,购买时最好跟卖家确认清楚。
2. 硬件连接与电路设计
2.1 接线示意图
先来看硬件连接,这是最容易出错的部分。我的接线方案是:
- 土壤湿度传感器:VCC接3.3V,GND接地,AO接PA5(ADC1通道5)
- OLED显示屏:SCL接PB6,SDA接PB7,VCC接3.3V,GND接地
这里有个关键点:STM32的ADC输入电压不能超过VDDA(通常是3.3V),而有些土壤湿度传感器的工作电压是5V。如果传感器输出可能超过3.3V,一定要在AO引脚加个分压电路,最简单的办法就是用两个电阻组成电压分压器。
2.2 电源设计建议
实际部署时我发现,如果直接用USB给开发板供电,当连接线较长时,电源噪声会影响ADC精度。后来我改用了锂电池供电,并在ADC参考电压引脚加了个0.1uF的滤波电容,效果明显改善。如果项目对精度要求高,建议使用独立的基准电压源芯片,比如TL431。
3. 软件实现详解
3.1 ADC初始化配置
ADC配置是核心部分,我把它封装成了独立的soilMoisture.c文件。初始化代码主要做这几件事:
void soilMoisture_Init(void){ // 1. 使能GPIO时钟和ADC时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE); // 2. 配置GPIO为模拟输入 GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; GPIO_Init(GPIOA, &GPIO_InitStructure); // 3. ADC参数配置 ADC_InitTypeDef ADC_InitStructure; ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; ADC_InitStructure.ADC_ScanConvMode = DISABLE; ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; ADC_InitStructure.ADC_NbrOfChannel = 1; ADC_Init(ADC1, &ADC_InitStructure); // 4. 校准ADC ADC_Cmd(ADC1, ENABLE); ADC_ResetCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); ADC_StartCalibration(ADC1); while(ADC_GetCalibrationStatus(ADC1)); }这里有几个经验分享:
- ADC时钟不要超过14MHz,我设置的是72MHz主频6分频得到12MHz
- 采样时间设置为239.5个周期,适合土壤湿度这种变化缓慢的信号
- 每次上电后必须执行校准,否则前几次读数可能不准
3.2 数据采集与滤波处理
土壤湿度信号容易受到干扰,我采用了平均值滤波算法。具体实现是这样的:
int soilMoisture_ReadData(int channel, int count){ int sum_val = 0; for(uint8_t i=0; i<count; i++){ sum_val += soilMoisture_ADC_GET(channel); Delay_ms(10); // 适当延时 } return sum_val/count; }实测发现采样20次取平均效果最好。注意每次采样后要加个小延时,让ADC电路稳定。转换后的电压值计算公式是:
电压值 = ADC读数 × (3.3V / 4096)因为STM32F103的ADC是12位的,最大值为4095。
4. OLED显示实现
4.1 显示驱动配置
我用的OLED驱动库是经过优化的,支持显示中文和浮点数。初始化部分很简单:
OLED_Init(); // 初始化I2C和OLED OLED_Clear(); // 清屏4.2 湿度值实时显示
在主循环中,我每500ms更新一次显示:
while(1){ float moisture = (float)soilMoisture_ReadData(5,20)*(3.3/4096); OLED_ShowChinese(0,16, "土壤湿度:"); OLED_ShowFloatNum(75,16, moisture,2,2,OLED_8X16); OLED_Update(); Delay_ms(500); }这里有几个实用技巧:
- 使用浮点数显示时,最后两个参数分别表示整数位和小数位长度
- OLED_Update()必须调用才会真正刷新显示
- 显示位置坐标单位是像素,第一个参数是列(0-127),第二个是行(0-7),每行8个像素
5. 实际应用中的优化建议
5.1 湿度标定方法
出厂时传感器需要标定才能得到准确的百分比湿度。我的做法是:
- 将传感器完全浸入水中,记录此时的ADC值作为100%湿度基准
- 将传感器放在完全干燥的土壤中,记录ADC值作为0%湿度
- 实际湿度 = (当前值 - 干燥值) / (湿润值 - 干燥值) × 100%
5.2 低功耗优化
如果用在电池供电的场景,可以这样优化:
- 将采样间隔从500ms改为10s
- 采样后立即进入STOP模式
- 用RTC定时唤醒
- 关闭不用的外设时钟
5.3 抗干扰措施
在工业环境中,建议:
- 在传感器信号线上加磁珠
- 使用屏蔽线连接传感器
- 在代码中加入软件滤波算法,比如中值滤波+滑动平均
6. 常见问题排查
调试时遇到过几个典型问题:
- ADC读数一直为0:检查GPIO是否配置为模拟输入模式,ADC时钟是否使能
- 数值跳动大:尝试增加采样次数,检查电源是否稳定
- OLED不显示:检查I2C地址是否正确,上拉电阻是否接好
- 湿度值反向变化:检查传感器接线,AO和DO不要接反
7. 项目扩展思路
这个基础框架可以扩展很多实用功能:
- 增加蓝牙模块,用手机查看实时数据
- 添加阈值报警功能,当湿度过低时触发继电器浇水
- 结合RTC芯片,实现湿度变化曲线记录
- 使用多个传感器,实现区域湿度监测
我在实际项目中加入了ESP8266 WiFi模块,将数据上传到云平台,这样就可以远程监控大棚的土壤状况。STM32F103的USART接口与ESP8266通信非常方便,只需要简单的AT指令就能实现网络连接。