基于单片机的智能风扇系统设计与实现

前言

一、基本元器件说明

单片机类型:51单片机(普中)

传感器的使用:

DS18B20温度传感器、LD3320语音模块、JQ8900-16P语音播报、LCD1602显示屏、hc-sr501人体感应模块等

二、具体功能说明

设计有三个模式:

模式1:根据温度变化进行风扇速度的调节

模式2:自主控制温度的风扇变化

模式3:采用人体感应模块,监测风扇前是否有人,进行风扇开启与关闭

三种控制方式:语音控制、按键控制、红外遥控控制

三、设计说明

本设计是根据百家之所长,有很多参考的地方,写本次文章的目的是在于记录与分享在设计中遇见的问题,与自己的一些理解,供大家参考与交流,网上很多模块的使用都有很详细的解释,我这只说一些我使用的理解,描述不准确,多指正。

四、分享说明

这是我第一次写这样的内容,肯定会有很多表达不清楚或者不准确的地方,如果有指正一下,共同进步,我主要是针对主函数的讲解,分享我的设计思路,具体模块的头文件部分并没有展示,因为这些模块很多地方都可以找到。

内容展示

设计流程

我的大体设计流程是:

(1)按键控制风扇的速度调节

(2)LCD1602显示温度的变化

(3)风扇和温度相互结合

(4)模式2部分就是按键控制风扇的旋转,模式1中已经解决

(5)模式3部分就是加上人体感应模块即可

(6)加入JQ9600-16P进行语音播报

(7)操作控制的设计是逐个增加:按键控制—–红外遥控——-语音控制

具体设计

【1】按键控制风扇的速度调节

1. 按键部分

我是采用的51单片机,上面有矩阵按键与独立按键,因为我需要用到的按键比较多,我在设计中使用的是矩阵按键,按键这部分不详细说明。

 2. 对于风扇速度的调节,使用的是PWM调速

对于使用注意几点:

 2.1 采用的是直流电机,51已经有了驱动模块,所以直接对端口赋值1(开启)、0(关闭)即可

 2.2 对于PWM调速,单片机的IO口输出的是数字信号,IO口只能输出高电平和低电平,理论上通过连接和断开,可以输出在范围内的任意模拟电压

2.3 对于PWM,其核心就是占空比:可以理解为高电平在周期中占据多少时间,转换成单片机就是电平1占据的时间,占空比50%,那就是高低电平各占一半

2.4 为什么采用定时器产生PWM,我的理解是,快、反复,只要开启定时器,就可以一直反复快速操作

3.相关代码

3.1定时器代码(采用的是定时器2,中断号别搞错了)
void Timer2_Routine() interrupt 5
{
	RCAP2L = 0x66;		//设置定时重载值
	RCAP2H = 0xFC;		//设置定时重载值
	TF2=0;
	Counter++;
	Counter%=100;	//计数值变化范围限制在0~99
	if(Counter<Compare)	//计数值小于比较值
	{
		Motor=1;		//输出1
	}
	else				//计数值大于比较值
	{
		Motor=0;		//输出0
	}
}

比如我们现在设置Compare  =  50,定时器开启后,Counter会迅速的自加,在小于50的部分都是高电平(1),超过50的部分都是低电平(0),这样就产生了占空比50%,那就是高低电平各占一半,高电平占的越多,风扇转的越快,所以我们想改变风扇的档位,将Compare设置的大一点就可以了,这就是调速的方法。因为在定时器中Counter%=100;这样就可以实现反复的进行操作,这就是用定时器的原因

3.2 按键控制代码,以模式2代码为例:

主要看KeyNum 、Compare 、M 这三个值就行

KeyNum就是按键值的获取,Compare改变值,从而来控制速度,M就是档位显示(0-3档)

if(KeyNum==5 || Command==IR_0 || Flag==8)
				{
					Flag=0;
					Delay(200);
					play_song_num(0x04);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=0;
					M=0;
				}					//设置比较值,改变PWM占空比
				if(KeyNum==6 || Command==IR_1 || Flag==9)
				{
					Flag=0;
					Delay(200);
					play_song_num(0x05);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=50;
					M=1;
				}
				if(KeyNum==7 || Command==IR_2 || Flag==10)				
				{
					Flag=0;
					Delay(200);
					play_song_num(0x06);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=75;
					M=2;
				}
				if(KeyNum==8 || Command==IR_3 || Flag==11)//3挡
				{
					Flag=0;
					Delay(200);
					play_song_num(0x07);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=100;
					M=3;
				}
				LCD_ShowNum(2,16,M,1);

【2】DS18b20与LCD1602

这两个资料太多了,其实直接调用就行,这里主要补充一下,对于数据的保存,采用了AT24c02。

1.为什么使用AT24c02  ?

在我的设计中,我希望我设置的温度上下限,并不会因为关机重启而导致我还需要再设置一遍,所以我希望保存我设置的数据,那么就需要掉电不丢失,这就是为什么使用AT24c02

2.相关说明

(1)对于AT24c02,51单片中有这个模块

(2)这部分是针对的模式1,所以将风扇和温度结合

3.模式1相关代码

3.1 读取AT24C02的上次存储的值
	/*****温度初始化***/
	DS18B20_ConvertT();		//上电先转换一次温度,防止第一次读数据错误
	Delay(1000);			//等待转换完成
	THigh=AT24C02_ReadByte(0);	//读取温度阈值数据
	TLow=AT24C02_ReadByte(1);   //读取事先存储的温度
	if(THigh>125 || TLow<-55 || THigh<=TLow)//判断AT24C02数据是否非法
	{
		THigh=20;			//如果阈值非法,则设为默认值
		TLow=15;
	}
3.2温度数据在LCD1602上的显示
        DS18B20_ConvertT();	//转换温度
		T=DS18B20_ReadT();	//读取温度
		if(T<0)				//如果温度小于0
		{
			LCD_ShowChar(1,7,'-');	//显示负号
			TShow=-T;		//将温度变为正数
		}
		else				//如果温度大于等于0
		{
			LCD_ShowChar(1,7,'+');	//显示正号
			TShow=T;
		}
		LCD_ShowNum(1,8,TShow,2);		//显示温度整数部分
		LCD_ShowChar(1,10,'.');		//显示小数点
		LCD_ShowNum(1,11,(unsigned long)(TShow*100)%100,2);//显示温度小数部分
3.3根据温度的变化显示数据以及调整风扇速度

具体说明:这部分代码主要分为三个部分:   /********分割线********/作为区分

3.3.1温度上下限调节与相关显示

 这部分就是调节设置温度上下限

显示部分:如果温度在我们所设定的范围内:

LCD_ShowString(1,13,"  N ");

超过我们所设定的温度范围显示:

LCD_ShowString(1,13,"  H");
3.3.2温度超过上下限风扇转速的变化

        Temp1=THigh+2;//超过上限2度
        Temp2=THigh+4;//超过上限4度

这就是为根据温度变化调整转速的根据,达到Temp1就是2档(Compare = 75),达到Temp2就是3档(compare = 100)

3.3.3数据存储使用AT24C02
		if(KeyNum==1 || Command==IR_Mode1 || Flag==1)//Mode1,温度模式
			{
				Mode=1;
				Flag=0;
				Delay(200);
				play_song_num(0x01);  //先不看这句
				LCD_ShowString(1,13,"    ");
				LCD_ShowString(1,1,"Tep ");//温度模式
				LCD_ShowString(1,5,"T:");//显示采集温度
				LCD_ShowString(2,1,"TH:");//显示温度上限
				LCD_ShowString(2,8,"TL:");//显示温度下限
				LCD_ShowString(2,7," ");
				LCD_ShowSignedNum(2,4,THigh,2);//显示上下限值
				LCD_ShowSignedNum(2,11,TLow,2);
				LCD_ShowString(2,14,"M:");
			}
			if(Mode==1)
			{	
				/***温度高位+***/
				if(KeyNum==5 || Command==IR_THADD || Flag==4)//+
				{
					Flag=0;
					THigh++;
					if(THigh>125){THigh=125;}//超过最高范围
				}
				/***温度高位-***/
				if(KeyNum==6 || Command==IR_THMINU || Flag==5)//-
				{
					Flag=0;
					THigh--;
					if(THigh<=TLow){THigh++;}
				}
				/***温度低位+***/
				if(KeyNum==7 || Command==IR_TLADD || Flag==6)//+
				{
					Flag=0;
					TLow++;
					if(TLow>=THigh){TLow--;}//超过最低温度
				}
				/***温度低位-***/
				if(KeyNum==8 || Command==IR_TLMINU || Flag==7)//-
				{
					Flag=0;
					TLow--;
					if(TLow<-55){TLow=-55;}
				}
				/********分割线********/
				Temp1=THigh+2;//超过上限2度
				Temp2=THigh+4;//超过上限4度
				LCD_ShowNum(2,16,0,1);
				if(T>THigh && T<Temp1)			//越界判断
				{
					LCD_ShowString(1,13,"  H");
					LCD_ShowNum(2,16,1,1);
					Compare=50;
				}
				else if(T>Temp1 && T<Temp2)
				{
					LCD_ShowString(1,13,"  H");
					LCD_ShowNum(2,16,2,1);
					Compare=75;
				}
				else if(T>Temp2)
				{
					LCD_ShowString(1,13,"  H");
					LCD_ShowNum(2,16,3,1);
					Compare=100;
				}
				/*else if(T<THigh)			//越界判断
				{
					LCD_ShowString(1,13,"    ");
					Compare=0;
				}*/
				else if(T<TLow)
				{
					LCD_ShowString(1,13,"  L");
					LCD_ShowNum(2,16,0,1);
					Compare=0;
				}
				else 
				{
					LCD_ShowString(1,15," ");
				}
				
				if(T>TLow && T<THigh)
				{
					LCD_ShowString(1,13,"  N ");
					LCD_ShowNum(2,16,1,1);
					Compare=50;
				}
				LCD_ShowSignedNum(2,4,THigh,2);	//显示阈值数据
				LCD_ShowSignedNum(2,11,TLow,2);
				/********分割线********/
				AT24C02_WriteByte(0,THigh);		//写入到At24C02中保存
				Delay(5);
				AT24C02_WriteByte(1,TLow);
				Delay(5);

【3】模式3的设计

模式3就是一个人体感应模块,有人在输出1,没人输出0(吐槽:不知道是我使用的问题,还是这个模块的问题,有时候很不灵敏)

            if(Pep==1 && Mode==3)
			{
				LCD_ShowString(1,1,"ECO T:");
				LCD_ShowString(1,14,"YES");
				LCD_ShowString(2,1,"People Detection");
				Compare=75;
			}
			if(Pep==0 && Mode==3)
			{
				LCD_ShowString(1,1,"ECO T:");
				LCD_ShowString(1,14,"NO ");
				LCD_ShowString(2,1,"People Detection");
				Compare=0;
			}

以上就是三个模块的具体设计

【4】JQ8900-16的介绍

1.JQ8900-16P有三种处理方式:IO口触发播放、双线串口、单线串口

我使用的是IO口触发播放(这个简单)

端口:

sbit sda=P2^7;

主函数代码:

就是主函数中这样的数据

play_song_num(0x01);  

具体使用:

1.生成我们所需要的音频(mp3格式),好像IO口触发最多只支持9条(不太清楚),命名格式为00001.mp3(玄学,按照顺序排下来00001.mp3、00002.mp3这样按顺序排),这里用我画圈那个地方写入,像u盘一样,将文件放进去就行

2.主函数中play_song_num(0x01);  这样调用即可

【5】语音控制模块

购买LD3320后,淘宝店家会给很详细的资料,按照步骤操作就行

1.接收

void UART_Routine() interrupt 4
{
	if(RI==1)					//如果接收标志位为1,接收到了数据
	{  
		RI=0; 
    receive_data = SBUF;  
	}
}

2.接收后

switch(receive_data)
			{
				case 'A':receive_data=0;Delay(300);Flag=1;break;//温度模式语音模块
				case 'B':receive_data=0;Delay(300);Flag=2;break;//正常模式语音模块
				case 'C':receive_data=0;Delay(300);Flag=3;break;//节能模式语音模块
				case 'D':receive_data=0;Delay(300);Flag=4;break;//温度上限+语音模块
				case 'E':receive_data=0;Delay(300);Flag=5;break;//温度上限-语音模块
				case 'F':receive_data=0;Delay(300);Flag=6;break;//温度下限+语音模块
				case 'G':receive_data=0;Delay(300);Flag=7;break;//温度下限-语音模块
				case 'H':receive_data=0;Delay(300);Flag=8;break;//风扇关闭语音模块
				case 'I':receive_data=0;Delay(300);Flag=9;break;//风扇1挡语音模块
				case 'J':receive_data=0;Delay(300);Flag=10;break;//风扇2挡语音模块
				case 'K':receive_data=0;Delay(300);Flag=11;break;//风扇3挡语音模块
			}

【6】按键和红外操作我直接放主函数


#include <REGX52.H>
#include "AT24C02.h"
#include "Delay.h"
#include "DS18B20.h"
#include "I2C.h"
#include "LCD1602.h"
#include "OneWire.h"
#include "Timer0.h"
#include "Timer2.h"
#include "IR.h"
#include "Int0.h"
#include "MatrixKey.h"
#include "UART.h"
#include "jq8900.h"

sbit Motor=P1^0;//风扇驱动
sbit Pep=P3^4;//人体检测
sbit sda=P2^7;


unsigned int RunNum=0;//步进电机摇头
float T,TShow;//T是实际温度,Tshow是显示温度,可以变号
char TLow,THigh;//-127--+127
unsigned char KeyNum;//用于读取按键值
unsigned char Counter,Compare;	//计数值和比较值,用于输出PWM
unsigned char Speed,M,Mode,Temp1,Temp2,Command;
unsigned char receive_data;	//接收串口标志位
unsigned int Flag;//语言标识符
//KeyNum读取按下按键值,Speed风扇模式切换,M档位显示,Mode模式
//Temp1,Temp2高温值设置

void main()
{
	/*****温度初始化***/
	DS18B20_ConvertT();		//上电先转换一次温度,防止第一次读数据错误
	Delay(1000);			//等待转换完成
	THigh=AT24C02_ReadByte(0);	//读取温度阈值数据
	TLow=AT24C02_ReadByte(1);   //读取事先存储的温度
	if(THigh>125 || TLow<-55 || THigh<=TLow)//判断AT24C02数据是否非法
	{
		THigh=20;			//如果阈值非法,则设为默认值
		TLow=15;
	}
	/***显示初始化***/
	LCD_Init(); 
	LCD_ShowString(1,1,"NowT:       ");//初步显示模块
	LCD_ShowString(2,1,"K1:T K2:N K3:E"); 
	Timer0_Init();//定时器初始化,红外线部分
	Timer2_Init();//控制直流电机PWM
	IR_Init();//红外线遥控
	UART_Init();//串口初始化,语音模块
	while(1)
	{
		/****显示模块*****/
		KeyNum=MatrixKey();//矩阵键盘控制
		DS18B20_ConvertT();	//转换温度
		T=DS18B20_ReadT();	//读取温度
		if(T<0)				//如果温度小于0
		{
			LCD_ShowChar(1,7,'-');	//显示负号
			TShow=-T;		//将温度变为正数
		}
		else				//如果温度大于等于0
		{
			LCD_ShowChar(1,7,'+');	//显示正号
			TShow=T;
		}
		LCD_ShowNum(1,8,TShow,2);		//显示温度整数部分
		LCD_ShowChar(1,10,'.');		//显示小数点
		LCD_ShowNum(1,11,(unsigned long)(TShow*100)%100,2);//显示温度小数部分
		/*******语音控制模块*******/
		switch(receive_data)
			{
				case 'A':receive_data=0;Delay(300);Flag=1;break;//温度模式语音模块
				case 'B':receive_data=0;Delay(300);Flag=2;break;//正常模式语音模块
				case 'C':receive_data=0;Delay(300);Flag=3;break;//节能模式语音模块
				case 'D':receive_data=0;Delay(300);Flag=4;break;//温度上限+语音模块
				case 'E':receive_data=0;Delay(300);Flag=5;break;//温度上限-语音模块
				case 'F':receive_data=0;Delay(300);Flag=6;break;//温度下限+语音模块
				case 'G':receive_data=0;Delay(300);Flag=7;break;//温度下限-语音模块
				case 'H':receive_data=0;Delay(300);Flag=8;break;//风扇关闭语音模块
				case 'I':receive_data=0;Delay(300);Flag=9;break;//风扇1挡语音模块
				case 'J':receive_data=0;Delay(300);Flag=10;break;//风扇2挡语音模块
				case 'K':receive_data=0;Delay(300);Flag=11;break;//风扇3挡语音模块
			}
			/****红外线初始化******/
			if(IR_GetDataFlag() || KeyNum || Flag!=0)
			{
			Command=IR_GetCommand();		//获取遥控器命令码
			/******Mode1温度模式******/
			if(KeyNum==1 || Command==IR_Mode1 || Flag==1)//Mode1,温度模式
			{
				Mode=1;
				Flag=0;
				Delay(200);
				play_song_num(0x01);  
				LCD_ShowString(1,13,"    ");
				LCD_ShowString(1,1,"Tep ");//温度模式
				LCD_ShowString(1,5,"T:");//显示采集温度
				LCD_ShowString(2,1,"TH:");//显示温度上限
				LCD_ShowString(2,8,"TL:");//显示温度下限
				LCD_ShowString(2,7," ");
				LCD_ShowSignedNum(2,4,THigh,2);//显示上下限值
				LCD_ShowSignedNum(2,11,TLow,2);
				LCD_ShowString(2,14,"M:");
			}
			if(Mode==1)
			{	
				/***温度高位+***/
				if(KeyNum==5 || Command==IR_THADD || Flag==4)//+
				{
					Flag=0;
					THigh++;
					if(THigh>125){THigh=125;}//超过最高范围
				}
				/***温度高位-***/
				if(KeyNum==6 || Command==IR_THMINU || Flag==5)//-
				{
					Flag=0;
					THigh--;
					if(THigh<=TLow){THigh++;}
				}
				/***温度低位+***/
				if(KeyNum==7 || Command==IR_TLADD || Flag==6)//+
				{
					Flag=0;
					TLow++;
					if(TLow>=THigh){TLow--;}//超过最低温度
				}
				/***温度低位-***/
				if(KeyNum==8 || Command==IR_TLMINU || Flag==7)//-
				{
					Flag=0;
					TLow--;
					if(TLow<-55){TLow=-55;}
				}

				Temp1=THigh+2;//超过上限2度
				Temp2=THigh+4;//超过上限4度
				LCD_ShowNum(2,16,0,1);
				if(T>THigh && T<Temp1)			//越界判断
				{
					LCD_ShowString(1,13,"  H");
					LCD_ShowNum(2,16,1,1);
					Compare=50;
				}
				else if(T>Temp1 && T<Temp2)
				{
					LCD_ShowString(1,13,"  H");
					LCD_ShowNum(2,16,2,1);
					Compare=75;
				}
				else if(T>Temp2)
				{
					LCD_ShowString(1,13,"  H");
					LCD_ShowNum(2,16,3,1);
					Compare=100;
				}
				/*else if(T<THigh)			//越界判断
				{
					LCD_ShowString(1,13,"    ");
					Compare=0;
				}*/
				else if(T<TLow)
				{
					LCD_ShowString(1,13,"  L");
					LCD_ShowNum(2,16,0,1);
					Compare=0;
				}
				else 
				{
					LCD_ShowString(1,15," ");
				}
				
				if(T>TLow && T<THigh)
				{
					LCD_ShowString(1,13,"  N ");
					LCD_ShowNum(2,16,1,1);
					Compare=50;
				}
				LCD_ShowSignedNum(2,4,THigh,2);	//显示阈值数据
				LCD_ShowSignedNum(2,11,TLow,2);
				AT24C02_WriteByte(0,THigh);		//写入到At24C02中保存
				Delay(5);
				AT24C02_WriteByte(1,TLow);
				Delay(5);
			}
			/*****正常模式Mode2*****/
			if(KeyNum==2 || Command==IR_Mode2 || Flag==2)//Mode2,正常模式
			{
				Compare=0;
				Flag=0;
				Mode=2;
				Delay(200);
				play_song_num(0x02);
				LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
				LCD_ShowString(2,1,"Exchange Mode   ");//
				LCD_ShowString(2,15,"  ");//
			}
			if(Mode==2)
			{
				if(KeyNum==5 || Command==IR_0 || Flag==8)
				{
					Flag=0;
					Delay(200);
					play_song_num(0x04);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=0;
					M=0;
				}					//设置比较值,改变PWM占空比
				if(KeyNum==6 || Command==IR_1 || Flag==9)
				{
					Flag=0;
					Delay(200);
					play_song_num(0x05);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=50;
					M=1;
				}
				if(KeyNum==7 || Command==IR_2 || Flag==10)				
				{
					Flag=0;
					Delay(200);
					play_song_num(0x06);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=75;
					M=2;
				}
				if(KeyNum==8 || Command==IR_3 || Flag==11)//3挡
				{
					Flag=0;
					Delay(200);
					play_song_num(0x07);
					LCD_ShowString(1,1,"Nat T:          ");//正常模式显示
					LCD_ShowString(2,1,"Now File     M:M");
					Compare=100;
					M=3;
				}
				LCD_ShowNum(2,16,M,1);
			}
				if(KeyNum==3 || Command==IR_Mode3 || Flag==3)//Mode3,节能模式
			{
				Flag=0;
				Mode=3;
				Delay(200);
				play_song_num(0x03);
				LCD_ShowString(1,1,"ECO T:          ");
				LCD_ShowString(2,1,"People Detection");
			}
			
		}
			/****节能模式模式3****/
		
			if(Pep==1 && Mode==3)
			{
				LCD_ShowString(1,1,"ECO T:");
				LCD_ShowString(1,14,"YES");
				LCD_ShowString(2,1,"People Detection");
				Compare=75;
			}
			if(Pep==0 && Mode==3)
			{
				LCD_ShowString(1,1,"ECO T:");
				LCD_ShowString(1,14,"NO ");
				LCD_ShowString(2,1,"People Detection");
				Compare=0;
			}
		}
}

void UART_Routine() interrupt 4
{
	if(RI==1)					//如果接收标志位为1,接收到了数据
	{  
		RI=0; 
    receive_data = SBUF;  
	}
}

void Timer2_Routine() interrupt 5
{
	RCAP2L = 0x66;		//设置定时重载值
	RCAP2H = 0xFC;		//设置定时重载值
	TF2=0;
	Counter++;
	Counter%=100;	//计数值变化范围限制在0~99
	if(Counter<Compare)	//计数值小于比较值
	{
		Motor=1;		//输出1
	}
	else				//计数值大于比较值
	{
		Motor=0;		//输出0
	}
}

物联沃分享整理
物联沃-IOTWORD物联网 » 基于单片机的智能风扇系统设计与实现

发表评论