1. 项目概述
最近完成了一个基于STM32的温湿度采集控制系统项目,通过Proteus仿真实现了完整的温湿度监测和自动控制功能。这个系统以STM32F103C8T6最小系统板为核心,搭配DHT11温湿度传感器、LCD1602液晶显示屏、按键和继电器模块,构建了一个完整的闭环控制系统。
系统工作时,DHT11传感器实时采集环境温湿度数据,STM32处理后通过LCD1602显示当前数值。用户可以通过按键设置温湿度报警阈值,当检测值超过设定阈值时,系统会自动触发相应的继电器:温度过高时启动散热风扇,湿度过高时点亮洒水指示灯(实际应用中可连接水泵)。
提示:在Proteus仿真环境下,继电器负载用电机和LED灯模拟,既降低了仿真复杂度,又能清晰展示控制效果。
2. 硬件设计与连接
2.1 核心元件选型
STM32最小系统:选用STM32F103C8T6作为主控,这款Cortex-M3内核的MCU具有72MHz主频、64KB Flash和20KB RAM,完全满足本项目的处理需求。其丰富的GPIO口(37个I/O)可以轻松连接各类外设。
温湿度传感器:DHT11数字温湿度传感器,单总线通信协议,测量范围:温度0-50℃(±2℃精度),湿度20-90%RH(±5%RH精度)。虽然精度一般,但价格低廉、接口简单,非常适合教学和入门项目。
显示模块:LCD1602字符型液晶,16x2显示格式,4位或8位并行接口。本项目采用8位模式连接,显示内容更稳定可靠。
2.2 电路连接详解
2.2.1 STM32最小系统连接
- 电源:3.3V供电,在Proteus中直接连接电源符号
- 复位电路:10kΩ上拉电阻+0.1μF电容构成复位电路
- 晶振电路:8MHz晶振+两个22pF负载电容
2.2.2 传感器与显示模块连接
code复制DHT11数据线 → PA0
LCD1602 RS → PD0
LCD1602 RW → PD1
LCD1602 E → PD2
LCD1602 D0-D7 → PC0-PC7
2.2.3 控制输出连接
code复制散热继电器控制 → PA2 → 驱动电机
洒水继电器控制 → PA3 → LED指示灯
按键设置 → PA4(KEY0), PA5(KEY1)
注意:实际硬件连接时,继电器线圈需并联续流二极管(如1N4007),防止感应电动势损坏单片机IO口。
3. 软件开发环境搭建
3.1 工具链配置
Keil MDK-ARM:使用Keil uVision5作为开发环境,需安装STM32F1系列设备支持包。工程配置要点:
- Target选项:选择STM32F103C8设备
- Output选项:勾选Create HEX File
- C/C++选项:定义STM32F10X_MD和USE_STDPERIPH_DRIVER
Proteus 8 Professional:用于电路仿真,需加载STM32F103C8和所有外设元件模型。特别注意:
- 在Proteus中加载Keil生成的HEX文件
- 设置STM32的晶振频率为8MHz
- 配置DHT11的采样间隔为1秒
3.2 关键代码实现
3.2.1 DHT11驱动代码
c复制// DHT11数据读取函数
uint8_t DHT11_Read_Data(float *temperature, float *humidity) {
uint8_t buf[5] = {0};
uint8_t i, j;
// 主机拉低18ms
DHT11_IO_OUT();
DHT11_DQ_OUT(0);
delay_ms(18);
DHT11_DQ_OUT(1);
delay_us(30);
// 等待从机响应
DHT11_IO_IN();
while(DHT11_DQ_IN());
while(!DHT11_DQ_IN());
// 读取40位数据
for(i=0; i<5; i++) {
for(j=0; j<8; j++) {
while(DHT11_DQ_IN());
while(!DHT11_DQ_IN());
delay_us(40);
buf[i] <<= 1;
if(DHT11_DQ_IN()) buf[i] |= 1;
}
}
// 校验数据
if(buf[0] + buf[1] + buf[2] + buf[3] == buf[4]) {
*humidity = buf[0] + buf[1]*0.1;
*temperature = buf[2] + buf[3]*0.1;
return 1;
}
return 0;
}
3.2.2 LCD1602显示驱动
c复制// LCD写命令函数
void LCD_Write_Cmd(uint8_t cmd) {
LCD_RS(0);
LCD_RW(0);
DATA_OUT(cmd);
LCD_EN(1);
delay_us(5);
LCD_EN(0);
delay_us(5);
}
// LCD显示字符串函数
void LCD_Show_String(uint8_t x, uint8_t y, uint8_t *str) {
uint8_t addr;
if(y == 0) addr = 0x80 + x;
else addr = 0xC0 + x;
LCD_Write_Cmd(addr);
while(*str != '\0') {
LCD_Write_Data(*str++);
}
}
4. 系统功能实现细节
4.1 温湿度采集与处理
DHT11传感器采用单总线协议通信,时序要求严格。实际开发中发现几个关键点:
- 主机拉低时间必须大于18ms,但不宜超过30ms
- 从机响应后,数据位的判定时机很关键,40us延时最稳定
- 两次读取间隔建议≥1秒,否则可能读取失败
在代码中增加了CRC校验和超时处理:
c复制#define DHT11_TIMEOUT 100 // 100ms超时
uint8_t DHT11_Wait_State(uint8_t state) {
uint16_t cnt = 0;
while(DHT11_DQ_IN() == state) {
if(++cnt > DHT11_TIMEOUT) return 1;
delay_us(1);
}
return 0;
}
4.2 阈值设置功能实现
通过两个按键实现阈值调整:
- KEY0:进入/退出温度阈值设置模式
- KEY1:增加/减少当前设置值
采用状态机方式处理按键逻辑:
c复制enum {NORMAL_MODE, TEMP_SET_MODE, HUMI_SET_MODE} sys_mode = NORMAL_MODE;
void KEY_Process(void) {
static uint8_t key0_cnt = 0, key1_cnt = 0;
if(KEY0 == 0) {
if(++key0_cnt > 10) { // 长按1s
sys_mode = (sys_mode == NORMAL_MODE) ? TEMP_SET_MODE : NORMAL_MODE;
key0_cnt = 0;
while(KEY0 == 0); // 等待按键释放
}
} else key0_cnt = 0;
if(sys_mode != NORMAL_MODE && KEY1 == 0) {
if(++key1_cnt > 2) { // 消抖后
if(sys_mode == TEMP_SET_MODE) {
temp_threshold = (KEY1_PRESSED) ? temp_threshold+1 : temp_threshold-1;
if(temp_threshold > 50) temp_threshold = 50;
if(temp_threshold < 10) temp_threshold = 10;
} else {
humi_threshold = (KEY1_PRESSED) ? humi_threshold+5 : humi_threshold-5;
if(humi_threshold > 90) humi_threshold = 90;
if(humi_threshold < 30) humi_threshold = 30;
}
key1_cnt = 0;
}
} else key1_cnt = 0;
}
5. 常见问题与解决方案
5.1 Proteus仿真问题排查
问题1:DHT11始终读取失败
- 检查STM32的时钟配置是否正确(HSI/PLL)
- 确认DHT11模型参数设置(默认采样间隔1s)
- 调整时序延时,特别是主机拉低后的等待时间
问题2:LCD1602显示乱码
- 检查初始化序列是否正确(0x38→0x0C→0x06→0x01)
- 确认总线模式(4位/8位)与代码设置一致
- 调整EN使能信号的脉冲宽度(建议5us)
5.2 实际硬件调试经验
继电器误动作:
- 在继电器线圈两端并联1N4007二极管
- 增加光耦隔离(如PC817)
- 确保控制IO口设置为推挽输出模式
DHT11数据不稳定:
- 数据线上拉4.7kΩ电阻
- 电源端并联100nF电容
- 避免长距离布线(建议<20cm)
实测技巧:用逻辑分析仪捕捉DHT11通信波形,可以直观检查时序是否符合规格书要求。发现大部分读取失败都是由于主机拉低时间不足或从机响应超时导致。
6. 系统优化与扩展
6.1 功能增强建议
-
多级报警:设置预警和紧急报警两级阈值,不同级别触发不同动作
c复制if(temp >= temp_emergency) { // 紧急高温 FAN_ON(); BUZZER_ON(); } else if(temp >= temp_warning) { // 预警 FAN_ON(); } -
历史数据记录:利用STM32内部Flash或外接EEPROM存储历史数据
c复制#define FLASH_PAGE_SIZE 1024 #define LOG_ADDR 0x0801FC00 // 最后一页 void Save_To_Flash(float temp, float humi) { FLASH_Unlock(); FLASH_ErasePage(LOG_ADDR); uint16_t temp_data = (uint16_t)(temp * 10); uint16_t humi_data = (uint16_t)(humi * 10); FLASH_ProgramHalfWord(LOG_ADDR, temp_data); FLASH_ProgramHalfWord(LOG_ADDR+2, humi_data); FLASH_Lock(); }
6.2 性能优化措施
-
低功耗设计:
- 使用STM32的睡眠模式,间隔唤醒采样
- 关闭不用的外设时钟(如ADC、TIM等)
- 降低系统时钟频率(当温湿度变化缓慢时)
-
滤波算法改进:
c复制#define FILTER_LEN 5 float temp_buf[FILTER_LEN], humi_buf[FILTER_LEN]; void Moving_Average_Filter(float *temp, float *humi) { static uint8_t index = 0; temp_buf[index] = *temp; humi_buf[index] = *humi; index = (index + 1) % FILTER_LEN; float temp_sum = 0, humi_sum = 0; for(uint8_t i=0; i<FILTER_LEN; i++) { temp_sum += temp_buf[i]; humi_sum += humi_buf[i]; } *temp = temp_sum / FILTER_LEN; *humi = humi_sum / FILTER_LEN; }
这个STM32温湿度控制系统从硬件搭建到软件开发,涵盖了嵌入式开发的完整流程。通过这个项目,不仅可以掌握STM32的基本外设操作,还能学习闭环控制系统的设计思路。在实际应用中,可以根据需求替换更高精度的传感器(如SHT30),或增加无线通信模块(如ESP8266)实现远程监控。
