免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

【STM32入门项目】DHT11温湿度监测与声光报警系统

【STM32入门项目】DHT11温湿度监测与声光报警系统 【STM32 入门项目】DHT11 温湿度监测与声光报警系统前言一、项目功能二、硬件清单三、引脚连接四、项目实物与接线展示项目整体实物图硬件接线示意图搭建要点五、DHT11 通信原理六、工程模块划分工程按功能进行封装七、DHT11 驱动代码八、蜂鸣器与 LED 控制九、按键读取按键使用内部上拉输入按下时引脚被拉低。九、主程序逻辑主程序中设置了四个阈值十、调试过程中遇到的问题总结前言最近使用 STM32F103C8T6 做了一个温湿度监测与报警小项目。项目使用 DHT11 采集温湿度数据使用 0.96 英寸 OLED 实时显示温度、湿度和报警状态。当温湿度超过设定范围时红灯亮、蜂鸣器间歇报警正常状态下绿灯亮。同时加入了按键调节阈值和错误码显示。这个项目比较适合 STM32 入门练习主要涉及 GPIO 输入输出、DHT11 单总线通信、OLED 显示、蜂鸣器控制、LED 状态指示、按键读取以及模块化编程。一、项目功能本项目实现功能如下OLED 实时显示温度和湿度支持温度上限、温度下限、湿度上限、湿度下限判断超出阈值时红灯亮蜂鸣器间歇报警正常范围内绿灯亮蜂鸣器关闭使用两个按键调节报警阈值DHT11 读取异常时显示不同错误状态按功能封装 DHT11、OLED、Buzzer、LED、Key 等模块二、硬件清单硬件说明STM32F103C8T6主控芯片DHT11温湿度传感器0.96 英寸 OLEDI²C 显示屏有源蜂鸣器低电平触发红色 LED超阈值报警绿色 LED正常状态按键 x2调节阈值ST‑Link程序下载杜邦线、面包板硬件连接三、引脚连接四、项目实物与接线展示项目整体实物图示意图STM32F103C8T6 最小系统板居中放置左侧通过杜邦线连接 DHT11 温湿度传感器右侧连接 0.96 英寸 OLED 显示屏上方为蜂鸣器与红绿 LED下方为两个按键整体通过面包板完成供电与信号连接。硬件接线示意图示意图以 STM32F103C8T6 为核心DHT11 的 DATA 接 PA6OLED 的 SCL/SDA 接 PB8/PB9蜂鸣器接 PB12红灯 LED1 接 PA1绿灯 LED2 接 PA2KEY1 接 PB1KEY2 接 PB11所有模块共地并接入 3.3V/5V 电源。搭建要点先接电源与地线确认 STM32 与面包板共地避免信号电平不一致。DHT11 的 DATA 线尽量短减少干扰上拉电阻可选用 4.7kΩ 至 10kΩ。OLED 使用软件 I²CSCL 与 SDA 不要接反否则无法正常显示。蜂鸣器与 LED 均为低电平触发接线时注意正负极方向。按键使用内部上拉按下时引脚被拉低接线时一端接 GPIO、另一端接地。上电前用万用表检查电源与地之间是否短路再下载程序。模块STM32 引脚说明DHT11 DATAPA6单总线数据引脚OLED SCLPB8软件 I²C 时钟线OLED SDAPB9软件 I²C 数据线蜂鸣器PB12低电平触发红灯 LED1PA1低电平点亮绿灯 LED2PA2低电平点亮KEY1PB1低电平按下KEY2PB11低电平按下五、DHT11 通信原理DHT11 使用单总线通信方式只需要一根 DATA 线完成数据传输。读取一次数据的大致流程如下STM32 将 DATA 拉低至少 18 ms发送起始信号。STM32 拉高 DATA 20~40 µs。STM32 将 DATA 引脚切换为输入模式。DHT11 响应低电平约 80 µs。DHT11 响应高电平约 80 µs。DHT11 连续发送 40 位数据。STM32 接收 5 个字节并进行校验。DHT11 返回 5 个字节字节含义Byte1湿度整数Byte2湿度小数Byte3温度整数Byte4温度小数Byte5校验和校验方式check_sumhumi_inthumi_dectemp_inttemp_decDHT11 每一位数据的判断方式每一位开始先有约 50 µs 低电平高电平约 26~28us 表示 0高电平约 70us 表示 1六、工程模块划分工程按功能进行封装文件功能DHT11.c / DHT11.hDHT11 初始化、响应检测、数据读取OLED.c / OLED.hOLED 显示BUZZER.c / BUZZER.h蜂鸣器控制LED.c / LED.h红绿灯控制KEY.c / KEY.h按键读取Delay.c / Delay.h微秒、毫秒延时main.c主程序逻辑七、DHT11 驱动代码初始化与模式切换PA6 是 DHT11 的 DATA 引脚。由于 DHT11 是单总线通信STM32 需要在输出模式和输入模式之间切换。#includestm32f10x.h#includeDelay.hvoidDHT11_ModeOut(void){GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_ModeGPIO_Mode_Out_OD;GPIO_InitStructure.GPIO_PinGPIO_Pin_6;GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_InitStructure);}voidDHT11_ModeIn(void){GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_ModeGPIO_Mode_IPU;GPIO_InitStructure.GPIO_PinGPIO_Pin_6;GPIO_InitStructure.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_InitStructure);}voidDHT11_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);DHT11_ModeOut();GPIO_SetBits(GPIOA,GPIO_Pin_6);}这里使用开漏输出 GPIO_Mode_Out_OD适合这种一根数据线由双方轮流控制的通信方式。发送起始信号voidDHT11_Start(void){DHT11_ModeOut();GPIO_ResetBits(GPIOA,GPIO_Pin_6);Delay_ms(20);GPIO_SetBits(GPIOA,GPIO_Pin_6);Delay_us(30);}检测 DHT11 响应uint8_tDHT11_Check(void){uint8_tretry0;while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)SET){retry;Delay_us(1);if(retry100)return1;}retry0;while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)RESET){retry;Delay_us(1);if(retry100)return1;}retry0;while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)SET){retry;Delay_us(1);if(retry100)return1;}return0;}返回值含义0响应正常1无响应这个函数依次等待DHT11 拉低DHT11 拉高DHT11 再次拉低进入数据发送阶段读取 1 bit 数据uint8_tDHT11_ReadBit(void){uint8_tbit0;while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)RESET);Delay_us(35);if(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)RESET){bit0;}else{bit1;while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)SET);}returnbit;}这里的核心是延时 35us 后判断电平如果已经变低说明高电平时间短该位为 0如果仍然是高电平说明高电平时间长该位为 1需要注意如果读到 1要等待高电平结束否则下一位可能会错位导致校验失败。读取 1 byte 数据uint8_tDHT11_ReadByte(void){uint8_tByte0x00;uint8_ti;for(i0;i8;i){ByteByte1;Byte|DHT11_ReadBit();}returnByte;}DHT11 是高位先发送所以每读一位就将当前字节左移一位再把新读到的 bit 放到最低位。6. 读取完整温湿度数据uint8_tDHT11_ReadData(uint8_t*temp,uint8_t*humi){uint8_thumi_int;uint8_thumi_dec;uint8_ttemp_int;uint8_ttemp_dec;uint8_tcheck_sum;DHT11_Start();DHT11_ModeIn();if(DHT11_Check()1){return1;}humi_intDHT11_ReadByte();humi_decDHT11_ReadByte();temp_intDHT11_ReadByte();temp_decDHT11_ReadByte();check_sumDHT11_ReadByte();if((humi_inthumi_dectemp_inttemp_dec)!check_sum){return2;}*temptemp_int;*humihumi_int;return0;}返回值含义返回值含义0读取成功1DHT11 无响应2校验失败八、蜂鸣器与 LED 控制蜂鸣器控制蜂鸣器为低电平触发因此拉低响拉高关闭。voidBUZZER_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_INSTRUCTURE;GPIO_INSTRUCTURE.GPIO_ModeGPIO_Mode_Out_PP;GPIO_INSTRUCTURE.GPIO_PinGPIO_Pin_12;GPIO_INSTRUCTURE.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOB,GPIO_INSTRUCTURE);GPIO_SetBits(GPIOB,GPIO_Pin_12);}voidBUZZER_ON(void){GPIO_ResetBits(GPIOB,GPIO_Pin_12);}voidBUZZER_OFF(void){GPIO_SetBits(GPIOB,GPIO_Pin_12);}LED 控制LED 同样采用低电平点亮。voidLED_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);GPIO_InitTypeDef GPIO_INSTRUCTURE;GPIO_INSTRUCTURE.GPIO_ModeGPIO_Mode_Out_PP;GPIO_INSTRUCTURE.GPIO_PinGPIO_Pin_1|GPIO_Pin_2;GPIO_INSTRUCTURE.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOA,GPIO_INSTRUCTURE);GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);}voidLED1_ON(void){GPIO_ResetBits(GPIOA,GPIO_Pin_1);}voidLED1_OFF(void){GPIO_SetBits(GPIOA,GPIO_Pin_1);}voidLED2_ON(void){GPIO_ResetBits(GPIOA,GPIO_Pin_2);}voidLED2_OFF(void){GPIO_SetBits(GPIOA,GPIO_Pin_2);}本项目中LED1 接红灯LED2 接绿灯。九、按键读取按键使用内部上拉输入按下时引脚被拉低。voidKey_Init(void){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);GPIO_InitTypeDef GPIO_INITSTRUCTURE;GPIO_INITSTRUCTURE.GPIO_ModeGPIO_Mode_IPU;GPIO_INITSTRUCTURE.GPIO_PinGPIO_Pin_1|GPIO_Pin_11;GPIO_INITSTRUCTURE.GPIO_SpeedGPIO_Speed_50MHz;GPIO_Init(GPIOB,GPIO_INITSTRUCTURE);}uint8_tKey_GetNum(void){uint8_tKeyNum0;if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)0){Delay_ms(20);while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1)0);Delay_ms(20);KeyNum1;}if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)0){Delay_ms(20);while(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_11)0);Delay_ms(20);KeyNum2;}returnKeyNum;}按键功能设计按键功能KEY1切换当前设置项KEY2当前阈值加 1九、主程序逻辑主程序中设置了四个阈值uint8_ttemp_high40;uint8_ttemp_low20;uint8_thumi_high65;uint8_thumi_low30;分别表示变量含义temp_high温度上限temp_low温度下限humi_high湿度上限humi_low湿度下限主函数如下#includestm32f10x.h#includeDelay.h#includeOLED.h#includeBUZZER.h#includeDHT11.h#includeLED.h#includeKEY.huint8_ttemp,humi,status;uint8_ttemp_high40;uint8_ttemp_low20;uint8_thumi_high65;uint8_thumi_low30;uint8_tKeyNum;uint8_tset_item0;voidShow_SetItem(void){if(set_item0){OLED_ShowString(4,1,Set TH:);OLED_ShowNum(4,8,temp_high,2);OLED_ShowString(4,10, );}elseif(set_item1){OLED_ShowString(4,1,Set TL:);OLED_ShowNum(4,8,temp_low,2);OLED_ShowString(4,10, );}elseif(set_item2){OLED_ShowString(4,1,Set HH:);OLED_ShowNum(4,8,humi_high,2);OLED_ShowString(4,10, );}else{OLED_ShowString(4,1,Set HL:);OLED_ShowNum(4,8,humi_low,2);OLED_ShowString(4,10, );}}intmain(void){OLED_Init();BUZZER_Init();DHT11_Init();LED_Init();Key_Init();while(1){KeyNumKey_GetNum();if(KeyNum1){set_item;if(set_item3){set_item0;}}elseif(KeyNum2){if(set_item0){temp_high;}elseif(set_item1){temp_low;}elseif(set_item2){humi_high;}else{humi_low;}}statusDHT11_ReadData(temp,humi);if(status0){OLED_ShowString(1,1,humi: % );OLED_ShowString(2,1,temp: C );OLED_ShowNum(1,6,humi,2);OLED_ShowNum(2,6,temp,2);if(temptemp_high||temptemp_low||humihumi_high||humihumi_low){LED1_ON();LED2_OFF();OLED_ShowString(3,1,Alarm: ON );BUZZER_ON();Delay_ms(200);BUZZER_OFF();Delay_ms(200);Show_SetItem();}else{LED2_ON();LED1_OFF();BUZZER_OFF();OLED_ShowString(3,1,Alarm: OFF );Show_SetItem();}}elseif(status1){BUZZER_OFF();LED1_OFF();LED2_OFF();OLED_ShowString(1,1,NO RESPONSE );OLED_ShowString(2,1,Code:1 );OLED_ShowString(3,1,Sensor Error );OLED_ShowString(4,1, );}elseif(status2){BUZZER_OFF();LED1_OFF();LED2_OFF();OLED_ShowString(1,1,CHECKSUM ERROR );OLED_ShowString(2,1,Code:2 );OLED_ShowString(3,1,Data Error );OLED_ShowString(4,1, );}else{BUZZER_OFF();LED1_OFF();LED2_OFF();OLED_ShowString(1,1,UNKNOWN ERROR );OLED_ShowString(2,1, );OLED_ShowString(3,1, );OLED_ShowString(4,1, );}Delay_ms(1000);}}程序逻辑说明读取按键判断是否需要切换或修改阈值。读取 DHT11 温湿度。如果读取成功显示温湿度。判断温湿度是否超出上下限。超限时红灯亮、蜂鸣器间歇报警。正常时绿灯亮、蜂鸣器关闭。如果 DHT11 读取失败显示对应错误码。十、调试过程中遇到的问题DHT11 第一次检测成功后面一直失败现象复位后第一次 OLED 显示 correct后面一直 error原因第一次读取后PA6 被切换成输入模式。下一次开始通信时如果没有重新切回输出模式STM32 就无法拉低 DATA 发送起始信号。解决方法在DHT11_Start()函数开头加入DHT11_ModeOut();status 2校验失败现象DHT11 有响应但是校验失败原因读取 bit 时如果该位是 135us 采样时 DATA 仍然处于高电平。如果此时直接返回下一次读取会提前进入导致数据错位。解决方法读到 1 后等待高电平结束while(GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_6)SET);蜂鸣器低于阈值后仍然响原因GPIO 输出状态会保持上一次写入的状态。调用BUZZER_ON()后如果后续分支没有明确调用BUZZER_OFF()蜂鸣器会继续响。解决方法在正常、报警、错误分支中都明确控制蜂鸣器状态。OLED_Clear()放在循环中导致闪烁原因OLED_Clear()会整屏清空如果在主循环中频繁调用会出现明显闪烁。解决方法使用空格覆盖旧字符而不是每次清屏。例如OLED_ShowString(1,1,humi: % );OLED_ShowString(2,1,temp: C );总结通过这个项目我熟悉了 STM32F103C8T6 的 GPIO 输入输出、DHT11 单总线时序、OLED 显示、蜂鸣器控制、LED 状态指示、按键读取以及模块化编程方法。虽然项目整体不算复杂但它完整地包含了硬件连接、外设配置、传感器读取、数据显示、报警控制、按键调参和问题调试等过程非常适合作为 STM32 入门阶段的综合练习项目。后续可以继续扩展1、使用定时器替代阻塞延时2、使用外部中断读取按键3、将阈值保存到 Flash断电不丢失4、通过串口输出温湿度数据5、加入 ADC 采集其他模拟传感器数据
返回列表