本帖最后由 jerry_jn 于 2025-7-18 10:19 编辑
我常听到提问:“入门单片机该从哪个项目练手?” 其实没必要一开始就啃复杂项目,今天分享的环境监测小站就很适合 —— 既能掌握 STM32 的基础外设用法,又能做出有实际价值的成品,关键是成本可控在百元内。
项目定位与核心功能
这个小站的核心需求很明确:实时监测室内温湿度、光照强度,并通过 OLED 屏幕显示数据,当数值超出预设阈值时触发蜂鸣器报警。之所以选这些参数,是因为对应的传感器都属于入门级,接口简单且价格低廉。
为什么用 STM32F103C8T6 而不是 51 单片机?主要考虑两点:一是 STM32 的 ADC 精度更高(12 位 vs 8 位),测光照时数据更稳定;二是内置 I2C 接口,驱动 OLED 屏幕无需占用太多 GPIO。当然如果手头只有 51 板也能改,后面会说适配方案。
硬件选型与电路设计
核心元器件清单
•STM32 最小系统板(45 元,带 USB 下载功能)
•DHT11 温湿度传感器(8 元,单总线通信,精度够用)
•BH1750 光照传感器(12 元,I2C 接口,支持自动量程)
•0.96 寸 OLED 屏幕(15 元,SSD1306 驱动,I2C 通信)
•有源蜂鸣器(3 元,低电平触发)
•10K 电位器(2 元,用于阈值调节)
这些元件在某立创商城都能配齐,记得领新人券能省 10 元。特别提醒:买 DHT11 时选带杜邦线的套餐,新手焊针脚容易出问题。
电路连接要点
电源部分要注意:DHT11 和 BH1750 都能直接用 3.3V 供电,和 STM32 电平兼容。但蜂鸣器如果用 5V 供电,需要加个 NPN 三极管(如 8050)做电平转换,否则 3.3V 可能驱动不起来。
I2C 总线接线技巧:OLED 和 BH1750 可以共用一组 I2C 引脚(PB6=SCL,PB7=SDA),但要注意两者的地址是否冲突(BH1750 默认 0x46,OLED 默认 0x3C,刚好不冲突)。
ADC 接口推荐用 PA0,接电位器中间引脚,两端分别接 3.3V 和 GND,这样转动旋钮就能实时调节报警阈值。
软件架构与关键代码
程序框架设计
采用模块化编程,分为这几个核心文件:
•main.c:主程序逻辑,负责任务调度
•dht11.c:温湿度采集驱动
•bh1750.c:光照强度读取
•oled.c:屏幕显示控制
•beep.c:报警模块控制
这样后期想加功能(比如蓝牙模块),直接新增模块文件即可,不用大改主程序。
核心代码解析
DHT11 读取函数的关键是时序控制,新手容易在这里掉坑:
uint8_t DHT11_ReadData(uint8_t *temp, uint8_t *humi)
{
uint8_t buf[5];
// 起始信号
DHT11_RST();
if(DHT11_Check() == 0)
{
// 读取40位数据
for(uint8_t i=0;i<5;i++)
{
buf = DHT11_ReadByte();
}
// 校验数据
if(buf[0]+buf[1]+buf[2]+buf[3] == buf[4])
{
*humi = buf[0];
*temp = buf[2];
return 0; // 读取成功
}
}
return 1; // 读取失败
}
这段代码里加了数据校验,能过滤掉 80% 的错误读数。实际测试发现,DHT11 在湿度低于 20% 时误差较大,建议在代码里加个补偿算法:当检测值 < 20% 时,实际值 = 检测值 + 5%。
OLED 显示部分推荐用字符库函数,比直接写点阵简单:
void OLED_ShowMonitorData(uint8_t temp, uint8_t humi, uint16_t light)
{
OLED_ShowString(0,0,"Temp:");
OLED_ShowNum(48,0,temp,2,16);
OLED_ShowChar(64,0,'C',16);
OLED_ShowString(0,2,"Humi:");
OLED_ShowNum(48,2,humi,2,16);
OLED_ShowChar(64,2,'%',16);
OLED_ShowString(0,4,"Light:");
OLED_ShowNum(56,4,light,4,16);
OLED_ShowString(88,4,"lux",16);
}
调试过程中的典型问题
1.DHT11 读取不稳定:
现象:偶尔会读到 0xFF 的错误值
解决:在读取前加 10ms 延时,并且连续读取 3 次取平均值
2.OLED 屏幕闪烁:
原因:I2C 通信速率过高(默认 400K)
解决:将 I2C 时钟频率降到 100K,修改这行代码:
hi2c1.Init.ClockSpeed = 100000;
1.光照值跳变剧烈:
处理:在 BH1750 读取函数里加滑动滤波:
uint16_t BH1750_GetLight()
{
static uint16_t light_buf[5];
static uint8_t i=0;
uint32_t sum=0;
light_buf[i++] = BH1750_ReadRaw();
if(i>=5)i=0;
for(uint8_t j=0;j<5;j++)
sum += light_buf[j];
return sum/5;
}
功能扩展思路
基础版做好后,可以尝试这些进阶功能:
1.加个 ESP8266 模块,通过 MQTT 协议上传数据到阿里云
2.改用锂电池供电,加个 TP4056 充电模块和电压检测电路
3.增加按键功能,支持阈值参数保存到 EEPROM
我正在做第一个扩展,遇到的坑是 ESP8266 的复位电路需要和 STM32 共地,否则会出现连接不稳定的情况。解决办法是在两者 GND 之间接一个 100 欧电阻,抑制地噪声。
写给新手的建议
如果是第一次用 STM32,建议先从标准库入手,HAL 库虽然方便但封装太多,不利于理解底层原理。调试时一定要用串口打印中间变量,比如在读取传感器后加一句:
printf("temp=%d, humi=%d\r\n", temp, humi);
通过观察数值变化能快速定位问题。这个项目全部做完大概需要 15 小时,建议分 3 天完成:第一天搭硬件,第二天写驱动,第三天调试和优化。
|