前言
一、基本元器件说明
单片机类型: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); 这样调用即可文章来源:https://www.toymoban.com/news/detail-801190.html
【5】语音控制模块
购买LD3320后,淘宝店家会给很详细的资料,按照步骤操作就行文章来源地址https://www.toymoban.com/news/detail-801190.html
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
}
}
到了这里,关于基于单片机的智能风扇设计的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!