一,前期准备
(一)硬件准备
- 火焰传感器模块
- 烟雾传感器模块(MQ-2)
- 电机驱动(tb6612)
- 电机+风扇叶(PWM驱动)
- 温湿度传感器(DHT11)
- 蜂鸣器
- 人体红外传感器模块(SR501)
- STM32F103C8T6
- ST--LINK
- USB转串口
- OLED四针(I2C通信)
(二)软件准备
keil,TRAW
二,模块讲解
温湿度模块:
温湿度模块有两种通信方式
- 数字通信(DO)
- 模拟通信(AO)
数字通信:当有火焰时火焰传感器传输低电平,当无火焰时传感器传输高电平(传感器敏感度由模块上的电阻器决定)
模拟通信:通过ADC转换,将火焰大小通过ADC转换成数字数据
烟雾模块:
烟雾模块和温湿度模块一样有两种通信方式
- 数字通信(DO)
- 模拟通信(AO)
数字通信:当有烟雾时烟雾传感器传输低电平,当无烟雾时传感器传输高电平(传感器敏感度由模块上的电阻器决定)
模拟通信:通过ADC转换,将烟雾浓度大小通过ADC转换成数字数据
烟雾传感器和火焰传感器通信方式一样,但烟雾模块得上电1`2 分钟后才能正常使用
温湿度模块
温湿度模块通信方式是单总线串行通信方式,总线为上拉电阻,温湿度模块发送一次数据大小为40bit
- 8bit温度整数数据
- 8bit温度小数数据
- 8bit湿度整数数据
- 8bit湿度小数数据
- 8bit校验数据数据
- 8bit校验数据数据==8bit温度整数数据+8bit温度小数数据+8bit湿度整数数据+8bit湿度小数数据
通信流程:主机发送开始信号------>主机释放总线(拉高)------>从机发送响应信号(从机开始采样)------>从机发送40bit数据------>从机拉低总线40us------>上拉电阻,拉高总线
开始信号:主机将总线拉低18~30ms(一般拉低20ms)
主机释放总线:将总线拉高10~30us(一般13us)
从机发送响应信号:从机拉低总线78~88(一般拉低83us),从机拉高总线80~82(一般拉高87)
**从机发送数据:**从机每发送一位前都要拉低总线50us,拉低完50us在拉高总线,拉高26~28表示0,拉高70us表示1
**从机拉低总线40us:**从机发送完40bit数据后,拉低总线40表示传输结束
**上拉电阻,拉高总线:**为下一次传输40bit数据做准备
**注:**温湿度模块收到一次开始信号,发送一次40bit数据,从机接收到一次开始信号后,模块从低功耗模式转为高速模式,发送完响应信号后,模块采样一次,在发送数据,发送完数据,拉低总线50us,再拉高总线
电机驱动模块(TB6612)
|-------------|-------------|
| 参数 | 数值 |
| 逻辑电压(VM) | 4.5~5.5V |
| 电机驱动电压(VCC) | 2.3~13.5 |
| 待机控制(STBY) | 高电平工作,低电平待机 |
| AIN1/AIN2 | 电机方向控制输入 |
| AO1/AO2 | 电机A输出 |
人体红外传感器
通信方式:如果检测到人输出高电平,没人输出低电平
延时控制:如果检测到人持续输出多长时间1
距离控制:能检测到多远有人
H:传感器在检测到运动后,延时时间内不会重复触发
L:传感器在检测到运动后,延时时间内会重复触发
注:这个模块是要检测到有人动才会输出1,如果你不动就算贴着他都不会显示有人
三,代码
温湿度模块
//温湿度.C文件
#include "DHT11.h"
//转换引脚模式:发送模式和接收模式
void DHT11_MODE(uint8_t MODE)
{
//这四行是固定的,改变的是引脚模式
GPIO_InitTypeDef M={0};
M.Pin=DHT11_PIN;
M.Speed=GPIO_SPEED_FREQ_LOW;
M.Pull=GPIO_PULLUP;
if(MODE==DHT11_OUTPUT)
{
M.Mode=GPIO_MODE_OUTPUT_PP;
}
else
{
M.Mode=GPIO_MODE_INPUT;
}
HAL_GPIO_Init(DHT11_GPIO,&M);
}
//发送开始信号
void DHT11_Ret(void)
{
//将引脚模式该成输出模式
DHT11_MODE(DHT11_OUTPUT);
//此时总线为空闲模式(高电平),将总线拉低,并持续20ms
HAL_GPIO_WritePin(DHT11_GPIO,DHT11_PIN,GPIO_PIN_RESET);
HAL_Delay(20);
//再将总线拉高,让总线处于空闲模式,
HAL_GPIO_WritePin(DHT11_GPIO,DHT11_PIN,GPIO_PIN_SET);
DWT_Delay_us(13);
}
//发送应答信号
uint8_t DHT11_Ack(void)
{
uint8_t NUM=0;
//将引脚该为输入模式
DHT11_MODE(DHT11_INPUT);
//此时总线为空闲模式(高电平),等待DHT11将总线拉低,这等待的时间绝不会超过100ms
while (HAL_GPIO_ReadPin(DHT11_GPIO,DHT11_PIN)&&NUM<=100)
{
DWT_Delay_us(1);
NUM++;
}
//如果等待时间超过100ms,表示无此设备,或DHT11没有应答,检测硬件
if(NUM>=100) return 1;
NUM=0;
//等待DHT11将总线拉高,等待时间依然不会超过100ms
while (HAL_GPIO_ReadPin(DHT11_GPIO,DHT11_PIN)==GPIO_PIN_RESET&&NUM<=100)
{
NUM++;
DWT_Delay_us(1);
}
//如果等待时间超过100ms,可能是误触或者硬件坏了
if(NUM>=100) return 1;
return 0;
}
//接收一个bit
uint8_t DHT11_Read_bit(void)
{
//此时DHT11还是输入模式,
uint8_t NUM=0;
//此时总线为空闲模式(高电平),等待DHT11将总线拉低
while(HAL_GPIO_ReadPin(DHT11_GPIO,DHT11_PIN)&&NUM<100)
{
NUM++;
DWT_Delay_us(1);
}
NUM=0;
//此时总线状态为低电平,等待DHT11将总线拉高
while(HAL_GPIO_ReadPin(DHT11_GPIO,DHT11_PIN)==GPIO_PIN_RESET&&NUM<100)
{
NUM++;
DWT_Delay_us(1);
}
//此时总线为高电平,这持续时间代表数据,因为持续时间为26~28us表示0,所以等40us后如果引脚为高电平则传输数据为1
DWT_Delay_us(40);
if(HAL_GPIO_ReadPin(DHT11_GPIO,DHT11_PIN)==GPIO_PIN_SET)
{
while (HAL_GPIO_ReadPin(DHT11_GPIO,DHT11_PIN))
{
DWT_Delay_us(1);
}
return 1;
}
else return 0;
}
//接收一个字节
uint8_t DHT11_Read_Byte(void)
{
uint8_t Byte=0;
for(uint8_t i=0;i<8;i++)
{
Byte<<=1;
Byte|=DHT11_Read_bit();
}
return Byte;
}
//接收一次数据
void DHT11_Read_Data(uint8_t*WD,uint8_t *SD)
{
uint8_t buf[5];
DHT11_Ret();
if(DHT11_Ack()==0)
{
for(uint8_t i=0;i<5;i++)
{
buf[i]=DHT11_Read_Byte();
}
if(buf[0]+buf[1]+buf[2]+buf[3]==buf[4])
{
*WD=buf[0];
*SD=buf[2];
}
}
}
//初始化
void DHT11_Init(void)
{
DWT_Init();
DHT11_MODE(DHT11_OUTPUT);
HAL_GPIO_WritePin(DHT11_GPIO,DHT11_PIN,GPIO_PIN_SET);
}
void DHT11_Update(void)
{
uint8_t WD,SD;
DHT11_Read_Data(&SD,&WD);
OLED_ShowChinese(0,0,"温度:");
OLED_ShowChinese(0,20,"湿度:");
OLED_ShowNum(38,0,WD,2,OLED_8X16);
OLED_ShowNum(38,20,SD,2,OLED_8X16);
OLED_Update();
HAL_Delay(500);
}
//温湿度.h文件
#ifndef __DHT11_H__
#define __DHT11_H__
#include "main.h" // 包含 HAL 库和 GPIO 定义
#include "gpio.h"
#include "Delay.h"
#include "FMQ.h"
#define DHT11_OUTPUT 1
#define DHT11_INPUT 0
#define DHT11_GPIO GPIOB
#define DHT11_PIN GPIO_PIN_10
//void DWT_Delay_us(uint32_t us);
void DHT11_MODE(uint8_t MODE);
void DHT11_Ret(void);
uint8_t DHT11_Ack(void);
uint8_t DHT11_Read_bit(void);
uint8_t DHT11_Read_Byte(void);
void DHT11_Read_Data(uint8_t*WD,uint8_t *SD);
void DHT11_Init(void);
void DHT11_Update(void);
//void DWT_Init(void);
#endif
微秒延时函数
#include "Delay.h"
void DWT_Init(void) {
if (!(CoreDebug->DEMCR & CoreDebug_DEMCR_TRCENA_Msk)) {
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CYCCNT = 0;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
}
// 微秒延时函数
void DWT_Delay_us(uint32_t us) {
uint32_t startTick = DWT->CYCCNT;
uint32_t delayTicks = us * (SystemCoreClock / 1000000);
while ((DWT->CYCCNT - startTick) < delayTicks) {
// 等待
}
}
#ifndef __DELAY_H__
#define __DELAY_H__
#include "main.h"
void DWT_Delay_us(uint32_t us);
void DWT_Init(void);
#endif
烟雾模块
#include "MQ2.h"
extern uint8_t alarm;
void MQ2_Update(void)
{
//在main函数中初始化
// HAL_ADC_Init(&hadc1);
//启动ADC转换
HAL_ADC_Start(&hadc1);
//等待转换完成
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
uint16_t adc = HAL_ADC_GetValue(&hadc1);
float Vol=(float)adc*100/4095;
// OLED_ShowChinese(0,40,"烟雾浓度");
// OLED_ShowNum(70,40,Vol,2,OLED_8X16);
//OLED_Update();
if(Vol>=30)
{
OLED_ShowChinese(0,40,"烟雾浓度过高");
OLED_ShowNum(100,40,Vol,2,OLED_8X16);
alarm|= 0x01;
// FMQ_MODE(FMQ_OK);
//MY_PWM_Update(100);
}
else
{
OLED_ClearArea(64,40,100,40);
OLED_ShowChinese(0,40,"烟雾浓度");
OLED_ShowNum(70,40,Vol,2,OLED_8X16);
alarm &= ~0x01;
//FMQ_MODE(FMQ_NO);
// MY_PWM_Update(0);
}
}
#ifndef __MQ2_H__
#define __MQ2_H__
#include "main.h"
#include "adc.h"
#include "usart.h"
void MQ2_Update(void);
#endif
人体红外传感模块
#include "SR501.h"
//这是中断模式
uint8_t NUM=0;
uint8_t PRO_NUM=0;
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin==GPIO_PIN_0)
{
PRO_NUM=NUM;
NUM=HAL_GPIO_ReadPin(SR501_GPIO,SR501_PIN);
if(PRO_NUM!=NUM)
{
if(NUM==1)
{
OLED_ShowChinese(70,20,"有人");
OLED_Update();
}
else if(NUM==0)
{
OLED_ShowChinese(70,20,"没人");
OLED_Update();
}
}
}
}
//这是轮询模式
/*
uint8_t NUM=0;
uint8_t PRO_NUM=0;
void SR501_UpDate(void)
{
PRO_NUM=NUM;
NUM=HAL_GPIO_ReadPin(SR501_GPIO,SR501_PIN);
if(PRO_NUM==NUM)
{
if(NUM==1)
{
OLED_ShowChinese(70,20,"有人");
OLED_Update();
}
else if(NUM==0)
{
OLED_ShowChinese(70,20,"没人");
OLED_Update();
}
}
}
**/
#ifndef __SR501_H__
#define __SR501_H__
#include "main.h"
#define SR501_GPIO GPIOB
#define SR501_PIN GPIO_PIN_0
//void SR501_UpDate(void);
#endif
蜂鸣器模块
#include "FMQ.h"
void FMQ_MODE(uint8_t MODE)
{ if(MODE==FMQ_OK)
{
HAL_GPIO_WritePin(FMQ_GPIO,FMQ_PIN,GPIO_PIN_RESET);
}
else if(MODE==FMQ_NO)
{
HAL_GPIO_WritePin(FMQ_GPIO,FMQ_PIN,GPIO_PIN_SET);
}
}
#ifndef __FMQ_H__
#define __FMQ_H__
#include "main.h" // 包含 HAL 库和 GPIO 定义
#include "gpio.h"
#include "Delay.h"
#define FMQ_GPIO GPIOB
#define FMQ_PIN GPIO_PIN_11
#define FMQ_OK 1
#define FMQ_NO 0
void FMQ_MODE(uint8_t MODE);
#endif
火焰传感器模块
#include "IR.h"
extern uint8_t alarm;
void IR_MODE(void)
{
uint8_t mode = HAL_GPIO_ReadPin(IR_GPIO,IR_PIN);
if(mode == GPIO_PIN_RESET)
{
// FMQ_MODE(FMQ_OK);
//MY_PWM_Update(100);
alarm |= 0x02;
OLED_ShowChinese(70,0,"火灾");
OLED_Update();
}
else
{
// FMQ_MODE(FMQ_NO);
//MY_PWM_Update(0);
alarm &= ~0x02;
OLED_ShowChinese(70,0,"安全");
OLED_Update();
}
}
#ifndef __IR_H__
#define __IR_H__
#include "main.h" // 包含 HAL 库和 GPIO 定义
#include "gpio.h"
#include "Delay.h"
#define IR_PIN GPIO_PIN_1
#define IR_GPIO GPIOB
void IR_MODE(void);
#endif
电机驱动
#include "PWM.h"
void MY_PWM_Update( uint8_t CCR )
{
if(CCR>0)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_SET);
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,CCR);
}
else if(CCR==0)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_14,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_15,GPIO_PIN_SET);
__HAL_TIM_SET_COMPARE(&htim2,TIM_CHANNEL_1,0);
}
}
#ifndef __PWM_H__
#define __PWM_H__
#include "main.h"
#include "tim.h"
void MY_PWM_Update( uint8_t CCR );
#endif
四,遇到的问题
问题1:
我用stm32f103的adc读取mq2烟雾模块ao数据,我把ao线连接到stm32上后(烟雾模块供电正常5v),得到的数据一直在0-4095之间乱跳,把烟雾模块断电后,但ao角还是连接在stm32上,数据还是在0-4095上乱跳,当把ao角与stm32断开数据就一直在1900-2000徘徊,我又用adc测继电器,得到的数据也是正常的,用万用表测mq2(烟雾模块)的ao引脚,测得电压是0.1v对烟雾模块哈气测得的电压也会升高
答:MQ-2模块用的电池供电,而STM32用的ST-LINK共电,两者没有共地
问题2:
当火焰和空气烟雾浓度超标时蜂鸣器和风扇都是响一下断一下,风扇是转一下停一下
答:那个时候两者报警是分开的,没有引入Freereos系统,报警控制逻辑存在竞态条件,现在两者使用同一套报警装置
源代码和视频
通过网盘分享的文件:智能家居安防系统
链接: https://pan.baidu.com/s/1402AisjKrZdVJ6QfwzuiRg 提取码: hxb2

