☰
基于单片机的智能风扇设计
2026/10/25 12:55:05 网站建设 项目流程

前言

一、基本元器件说明

单片机类型: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 } }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询