单片机课设----基于红外和超声波的手动/自动调速风扇系统

1、前言

  本系统为基于红外和超声波的手动/自动调速风扇系统,风扇转速的调节模式可分为自动模式与手动模式:在自动模式下,由超声波检测人与风扇的距离,根据距离调节风扇转速;在手动模式下,可经过红外遥控的按键调节风扇转速。相应参数信息经过LCD液晶显示屏显示。本系统的主控芯片采用STC89C52单片机,测距采用HC-SR04超声波模块,风扇电机由L298N电机驱动模块驱动,遥控部分用传统的红外遥控器,显示部分用LCD1602液晶显示屏。电机驱动模块采用12V供电,单片机及其余各部分采用5V供电。ide

  该项目是笔者在大一暑假时完成的,在大三上学期又把代码整理、优化了一次,拿去充当了一次课程设计。正好遇上这两天有空,决定把这个小玩意整理成博客。一来这个东西确实是当时用心作了的,且之后笔者可能也不会再碰单片机相关的东西了,整理出来留做念想;二来但愿能在学弟学妹们作课设的时候提供一些思路,抛砖引玉;仅此而已。这里先附丑图一张:函数

2、思路分析

2.1 系统供电问题

  STC89C52单片机及超声波传感器、红外遥控接收头、液晶显示屏均为+5V标准供电,能够直接使用电脑USB接口引出电压。但考虑到电机用到PWM调速,须要大电压和大电流,所以决定使用电池盒额外供电。优化

2.2 自动/手动模式的切换

  主函数内部用一个while大循环,超声波数据采集及电机驱动等程序均放在循环内部。在while内部有两段程序,一段为手动模式,一段为自动模式,分别放在if…else…的两个分支内。定义全局变量flag,在红外遥控中断内部可改变flag的值,经过flag的值控制if…else…选择结构的走向,进而实现两种模式的切换。ui

2.3 PWM信号的产生

  电机转速调节需用到PWM信号,需由单片机内部产生。有两种可行方案:其一为经过软件延时,不断地改变某一引脚电平的高低,由该引脚向外输出PWM信号;其二为经过中断计时,计满后进入中断服务程序,在中断服务程序中改变某一引脚电平的高低,由该引脚向外输出PWM信号。考虑到系统较为复杂,用方案一在时间上会占用单片机的大量资源,影响到系统的稳定性和实时性,所以采用方案二。编码

2.4 单片机内部资源的分配 

  在本系统中,用到两个定时器和两个中断:超声波测距时等待返回波用到一个定时器,控制PWM信号的发生用到一个定时器;红外遥控的响应用到一个外部中断,PWM信号的发生用到定时器中断。考虑到系统的实时性,给红外遥控分配优先级最高的外部中断0,PWM信号发生使用定时器T0并开中断,超声波测距使用定时器T1,不开中断。spa

3、硬件搭建

  因为硬件部分中的不少模块在仿真软件中都没有,且各模块之间的链接关系比较简单,所以在这里不提供电路图,仅用语言描述各引脚之间的链接关系。设计

3.1 单片机最小系统 

  对51 系列单片机来讲, 最小系统通常应该包括: 单片机、时钟电路、复位电路、输入/ 输出设备等。最小系统的焊接有一套标准的流程,为基本功,这里不作赘述。指针

3.2 电机驱动模块

   本系统电机驱动模块使用常见的L298N电机驱动模块。L298N芯片能够驱动两个二相电机,也能够驱动一个四相电机,输出电压最高可达50V,能够直接经过电源来调节输出电压;能够直接用单片机的IO口提供信号;并且电路简单,使用比较方便。code

  在本系统中,只使能了EnA来驱动一个电机,其中EnA接单片机引脚P20,IN1接P21,IN2接P22。L298N电机驱动模块实物图以下所示:blog

 

3.3 超声波测距模块

  在自动调速模式下,需用到超声波模块采集距离信息。本系统采用HC-SR04超声波模块, HC-SR04超声波测距模块可提供2cm-400cm的非接触式距离感测功能,测距精度可达高到3mm;模块包括超声波发射器、接收器与控制电路。基本工做原理:

(1)采用I0口TRIG触发测距,给至少10us的高电平信号;

(2)模块自动发送8个40khz的方波,自动检测是否有信号返回;

(3)有信号返回,经过I0口ECHO输出一个高电平,高电平持续的时间就是超声往返所用的时间。

(4)根据声音在空气中的速度为344米/秒,便可计算出所测的距离。

  在本系统中,超声波模块的Trig脚接单片机引脚P36,Echo脚接单片机引脚P22。HC-SR04工做时序图以下所示:

3.4 红外遥控模块

  根据使用的编码芯片不一样,红外遥控编码的格式也不一样,较广泛的有NEC标准和PHILIPS标准。最经常使用的是NEC标准,本系统采用的也是NEC标准。

  NEC标准:遥控载波的频率为38KHz(占空比1:3)当某个键按下时,发射端首先发射一个完整的全码,若是按键超过108ms仍未松开,接下来发射的代码(连发代码)将由起始码(9ms)和结束码(2.5ms)组成,并每隔108ms重复。

  一个完整的全码由引导码、用户码、用户码、数据码、数据码以及数据反码共同组成。其中,引导码高电平9ms,低电平4.5ms;系统码8位,数据码8位,共32位;其中前16位为用户识别码,能区别不一样的红外遥控设备,以防止不一样的机种遥控码互相干扰。后16位为8位的操做码和8位的操做反码,用于核对数据是否接收准确。收端根据数据码作出应该执行上面动做的判断。连发代码是在持续按键时发送的码。它告知接收端。某键是在被连续的按着。

  NEC标准下的发射码表示:发射数据0时用“0.56ms高电平 + 0.565ms低电平 = 1.125ms”表示;发射数据1用“0.56ms高电平 + 1.69ms低电平 = 2.25ms”表示。 

  在本系统中,红外接收器的INIR脚接单片机引脚P32。NEC标准完整码组成及NEC标准发射码以下所示:

3.5 液晶显示模块

  本系统的液晶显示部分采用LCD1602液晶显示屏。1602液晶也叫1602字符型液晶,它是一种专门用来显示字母、数字、符号等的点阵型液晶模块 它有若干个5X7或者5X11等点阵字符位组成,每一个点阵字符位均可以显示一个字符。每位之间有一个点距的间隔,每行之间也有也有间隔,起到了字符间距和行间距的做用。

  LCD1602是指显示的内容为16X2,便可以显示两行,每行16个字符液晶模块(显示字符和数字)。目前市面上字符液晶绝大多数是基于HD44780液晶芯片的,控制原理是彻底相同的,所以基于HD44780写的控制程序能够很方便地应用于市面上大部分的字符型液晶。

  在本系统中,液晶显示屏接法以下所示:

3.6 供电模块

  为了驱动电机,需采用+12V供电,结合手上现有资源,决定采用4节3.7V的锂电池串联供电。串联后的输出电压在+15V左右,使用LM2596S直流降压模块,将电压降至+12V后提供给电机驱动模块L298N,单片机所需的+5V电可直接从电机驱动模块中引出。

4、代码分享

  代码用C语言编写,在Keil4环境下开发的。给每一个模块都写了驱动,每一个模块的驱动拿出后略加改动,都能单独使用。代码工程结构以下所示:

4.1 总头文件

  把经常使用的宏定义和硬件的引脚链接定义到了reg52.h里面,改名为my52.h。因此整个工程代码中每一个文件都#include "my52.h"而不是 #include "reg52.h"

 1 #ifndef __MY52_H__  2 #define __MY52_H__
 3 
 4 /* BYTE Registers */
 5 sfr P0    = 0x80;  6 sfr P1    = 0x90;  7 sfr P2    = 0xA0;  8 sfr P3    = 0xB0;  9 sfr PSW   = 0xD0;  10 sfr ACC   = 0xE0;  11 sfr B     = 0xF0;  12 sfr SP    = 0x81;  13 sfr DPL   = 0x82;  14 sfr DPH   = 0x83;  15 sfr PCON  = 0x87;  16 sfr TCON  = 0x88;  17 sfr TMOD  = 0x89;  18 sfr TL0   = 0x8A;  19 sfr TL1   = 0x8B;  20 sfr TH0   = 0x8C;  21 sfr TH1   = 0x8D;  22 sfr IE    = 0xA8;  23 sfr IP    = 0xB8;  24 sfr SCON  = 0x98;  25 sfr SBUF  = 0x99;  26 
 27 /* 8052 Extensions */
 28 sfr T2CON  = 0xC8;  29 sfr RCAP2L = 0xCA;  30 sfr RCAP2H = 0xCB;  31 sfr TL2    = 0xCC;  32 sfr TH2    = 0xCD;  33 
 34 
 35 /* BIT Registers */
 36 /* PSW */
 37 sbit CY    = PSW^7;  38 sbit AC    = PSW^6;  39 sbit F0    = PSW^5;  40 sbit RS1   = PSW^4;  41 sbit RS0   = PSW^3;  42 sbit OV    = PSW^2;  43 sbit P     = PSW^0; //8052 only
 44 
 45 /* TCON */
 46 sbit TF1   = TCON^7;  47 sbit TR1   = TCON^6;  48 sbit TF0   = TCON^5;  49 sbit TR0   = TCON^4;  50 sbit IE1   = TCON^3;  51 sbit IT1   = TCON^2;  52 sbit IE0   = TCON^1;  53 sbit IT0   = TCON^0;  54 
 55 /* IE */
 56 sbit EA    = IE^7;  57 sbit ET2   = IE^5; //8052 only
 58 sbit ES    = IE^4;  59 sbit ET1   = IE^3;  60 sbit EX1   = IE^2;  61 sbit ET0   = IE^1;  62 sbit EX0   = IE^0;  63 
 64 /* IP */
 65 sbit PT2   = IP^5;  66 sbit PS    = IP^4;  67 sbit PT1   = IP^3;  68 sbit PX1   = IP^2;  69 sbit PT0   = IP^1;  70 sbit PX0   = IP^0;  71 
 72 /* P3 */
 73 sbit RD    = P3^7;  74 sbit WR    = P3^6;  75 sbit T1    = P3^5;  76 sbit T0    = P3^4;  77 sbit INT1  = P3^3;  78 sbit INT0  = P3^2;  79 sbit TXD   = P3^1;  80 sbit RXD   = P3^0;  81 
 82 /* SCON */
 83 sbit SM0   = SCON^7;  84 sbit SM1   = SCON^6;  85 sbit SM2   = SCON^5;  86 sbit REN   = SCON^4;  87 sbit TB8   = SCON^3;  88 sbit RB8   = SCON^2;  89 sbit TI    = SCON^1;  90 sbit RI    = SCON^0;  91 
 92 /* P1 */
 93 sbit T2EX  = P1^1; // 8052 only
 94 sbit T2    = P1^0; // 8052 only
 95              
 96 /* T2CON */
 97 sbit TF2    = T2CON^7;  98 sbit EXF2   = T2CON^6;  99 sbit RCLK   = T2CON^5; 100 sbit TCLK   = T2CON^4; 101 sbit EXEN2  = T2CON^3; 102 sbit TR2    = T2CON^2; 103 sbit C_T2   = T2CON^1; 104 sbit CP_RL2 = T2CON^0; 105 
106 /*------------------一下为添加部分---------------------*/
107 
108 #define uint unsigned int 
109 #define uchar unsigned char
110 
111 #define HighGear 4
112 #define MiddleGear 3
113 #define LowGear 2
114 
115 //电机驱动
116 sbit ENA = P2^0; //PWM输入端口
117 sbit IN1 = P2^1; //0
118 sbit IN2 = P2^2; //1 119 
120 //超声波
121 sbit Trig=P3^6; 122 sbit Echo=P3^7; 123 
124 //LCD1602
125 sbit RS=P1^2;  // 数据/命令选择端(H/L)
126 sbit RW=P1^1;  //读写选择端(H/L)
127 sbit E=P1^0;   //使能信号 128 
129 //红外遥控
130 sbit IRIN=P3^2;// 红外接收器端口定义,外部中断0优先级最高
131 
132 
133 #endif
View Code

4.2 电机驱动及头文件

  电机驱动文件motor_driver.c

 1 #include "my52.h"
 2 
 3 uchar motor_gear,pwm_num;  4 
 5 void motor_run(uchar gear)  6 {  7     motor_gear = gear; //挡位设置,分2,3,4档
 8     TMOD = 0x11; //设置定时器1为工做方式1
 9     TH1 = (65536-100)/256; //装初值,每0.1ms中断一次
10     TL1 = (65536-100)%256; 11     ET1 = 1; //开定时器1中断
12     TR1 = 1; //启动定时器1
13 } 14 
15 void T1_PWM() interrupt 3
16 { 17     TH1 = (65536-100)/256; //装初值
18     TL1 = (65536-100)%256; 19     pwm_num++; 20     if(pwm_num == 5) 21         pwm_num = 0; 22     if(pwm_num <= motor_gear) 23         ENA = 1; 24     else
25         ENA = 0; 26 }
View Code

  电机驱动头文件motor_driver.h

1 #ifndef __MOTOR_DRIVER_C__ 2 #define __MOTOR_DRIVER_C__
3 extern motor_run(uchar gear); //可填2,3,4,占空比分别为0.6,0.8,1
4 #endif
View Code

 4.3 超声波驱动及头文件

  超声波驱动文件sr04_driver.c

 1 #include "my52.h"
 2 #include "motor_driver.h"
 3 #include <intrins.h> // _nop_()延时
 4 
 5 extern uchar InfraredGear;  6 
 7 uint distance()     //HC-SR04超声波测距模块工做函数
 8 {  9     uint dis = 0; 10     uint time = 0; 11     uchar i = 10; 12 
13     Trig = 0;//初始化
14     Echo = 0; 15 
16 
17     TMOD = 0x11; 18     TH0=0;//给T0装初值0
19     TL0=0; 20 
21     Trig = 1; 22     while(i--) 23  _nop_(); 24     while(Echo==0); 25     TR0=1;//启动T0
26     while(Echo==1);//等待返回信号的接收完毕
27     time=TH0*256+TL0;//微秒
28     dis=(time*1.7+5)/10;//340米每秒即0.34毫米每微秒,1.7=0.34/2×10,来回除2,四舍五入先乘10
29     TR0=0;//关闭T0
30 
31     return dis; 32 } 33 
34 void sr04_motor(uint dist) 35 { 36     if(dist <= 300) 37         InfraredGear = LowGear; 38     else if(dist > 600) 39         InfraredGear = HighGear; 40     else
41         InfraredGear = MiddleGear; 42 }
View Code

  超声波驱动头文件sr04_driver.h

1 #ifndef __SR04_DRIVER_C__ 2 #define __SR04_DRIVER_C__
3 extern uint distance(); //单位是毫米
4 extern void sr04_motor(uint dist); 5 #endif
View Code

4.4 红外遥控驱动及头文件 

  红外遥控驱动文件infrared_driver.c

 1 #include "my52.h"
 2 #include "delay.h"
 3 
 4 extern uchar InfraredGear;  5 extern uchar Flag ;  6 
 7 uchar IrValue[4];//两位用户码,一位数据码,一位数据反码
 8 uchar num;  9 
10 void read() interrupt 0{//红外中断读取档位数据
11  uchar j,k,t; 12     uint i; 13     num=0; 14     t=IrValue[2]; 15     delay_ms(7);//起始码前9ms为低电平,在这里等待7ms
16     if(IRIN==0){//确认真的收到信号后执行如下程序
17         i=1000;    //若是出错利用i跳出如下等待,以避免程序在这里死循环
18         while((IRIN==0)&&(i>0)){ //等待前9ms结束
19             delay_10us(1); 20             i--; 21  } 22         if(IRIN==1){//起始码前9ms结束,后4.5ms为高电平
23             i=500; //用i防止死循环
24             while((IRIN==1)&&(i>0)){//等待起始码的后4.5ms高电平
25                 delay_10us(1); 26                 i--; 27  } 28             for(k=0;k<4;k++){//2个用户码,1个数据码,1个数据反码,共4个字节
29                 for(j=0;j<8;j++){ //每一个字节8位,如下程序用于肯定每位电平的高低
30                     i=60; 31                     while((IRIN==0)&&(i>0)){//等待0.56ms的低电平,每位前面都有0.56ms的低电平
32                         delay_10us(1);            //后面高电平0.565ms(565us)为0, 1.69ms(1690us)为1
33                         i--; 34  } 35                     i=500; 36                     while((IRIN==1)&&(i>0)){//低电平结束,高电平到来后进入,用于计算高电平持续时间
37                         delay_10us(10);//延时100us
38                         num++; 39                         i--; 40                         if(num>30){//超出3000us(3ms),本程序出错(最大不能超过2.25ms),返回主调函数
41                             return; 42  } 43  } 44                     IrValue[k]>>=1;//腾出最高位用于接收本位数据
45                     if(num>=8){//高电平持续时间大于800us,该位为1
46                         IrValue[k]|=0x80; //给最高位写1
47  } 48                     num=0;    //计数变量清零
49  } 50  } 51  } 52         if(IrValue[2]!=~IrValue[3]){//数据位校验
53             IrValue[2]=t; 54             return; 55  } 56  } 57     if(IrValue[2]==69) 58         InfraredGear=LowGear; 59     else if(IrValue[2]==70) 60         InfraredGear=MiddleGear; 61     else if(IrValue[2]==71) 62         InfraredGear=HighGear; 63     else if(IrValue[2]==68)      //切换自动模式
64         Flag = 0; 65     else if(IrValue[2]==67)      //切换手动模式
66         Flag = 1; 67     else if(IrValue[2]==64)      //急停
68         IN1 = 1; 69     else if(IrValue[2]==21) 70         IN1 = 0; 71 }
View Code

  红外遥控驱动头文件infrared_driver.h

1 #ifndef __INFRARED_DRIVER_C__ 2 #define __INFRARED_DRIVER_C__
3 
4 #endif
View Code

4.5 液晶显示驱动及头文件

  液晶显示驱动文件lcd1602_driver.c

 1 #include "my52.h"
 2 
 3 extern uchar InfraredGear;  4 
 5 void delays(uint i)  6 {  7  uchar j;  8     while(i--)  9         for(j=50;j>0;j--); 10 } 11 void write_com(uchar com) 12 { 13     RS=0; 14     P0=com; 15     delays(1); 16     E=1; 17     delays(1); 18     E=0; 19 } 20 void write_date(uchar date) 21 { 22     RS=1; 23     P0=date; 24     delays(1); 25     E=1; 26     delays(1); 27     E=0; 28 } 29 void lcdinit() 30 { 31     RW=0; 32     E=0; 33     write_com(0x38); //设置16X2显示,5X7点阵,8位数据接口
34     write_com(0x0c); //设置开显示,不显示光标
35     write_com(0x06); //写一个字符后地址指针加1 
36     write_com(0x01); //显示清零,数据指针清零
37 } 38 
39 void display0(uint dis) //手动调速下1602显示
40 { 41  uchar g1,g2,g3,g4; 42     g1=dis%10; 43     g2=(dis/10)%10; 44     g3=(dis/100)%10; 45     g4=dis/1000; 46  lcdinit(); 47     write_com(0x80); 48     write_date('M'); 49     write_date('o'); 50     write_date('d'); 51     write_date('e'); 52     write_date('l'); 53     write_date(':'); 54     write_date('A'); 55     write_date('U'); 56     write_date('T'); 57     write_date('O'); 58 
59     write_date(' ');//挡位显示
60     write_date(0x30+InfraredGear-1); 61     
62     write_com(0x80+0x40);//换行显示
63 
64     write_date('D');//距离显示
65     write_date('i'); 66     write_date('s'); 67     write_date(':'); 68     write_date(0x30+g4); 69     write_date(0x30+g3); 70     write_date(0x30+g2); 71     write_date(0x30+g1); 72     write_date('m'); 73     write_date('m'); 74 } 75 
76 void display1() 77 { 78  lcdinit(); 79     write_com(0x80); 80 
81     write_date('M'); 82     write_date('o'); 83     write_date('d'); 84     write_date('e'); 85     write_date('l'); 86     write_date(':'); 87     write_date('M'); 88     write_date('A'); 89     write_date('N'); 90     write_date('U'); 91 
92     write_date(' ');//挡位显示
93     write_date(0x30+InfraredGear-1); 94 }
View Code

  液晶显示驱动头文件lcd1602_driver.h

1 #ifndef __LCD1602_DRIVER_C__ 2 #define __LCD1602_DRIVER_C__
3 extern void delays(uint i); 4 extern void write_com(uchar com); 5 extern void write_date(uchar date); 6 extern void lcdinit(); 7 extern void display0(uint dis); 8 extern void display1(); 9 #endif
View Code

4.6 延时函数及头文件

  延时函数所在文件delay.c:

 1 #include "my52.h"
 2 
 3 void delay_ms(uint t)  4 {  5     uint i,j;  6     for(i=0;i<t;i++)  7         for(j=0;j<114;j++);  8 }  9 
10 void delay_10us(uint t)  //延时函数,t=1延时10us
11 { 12     while(t--); 13 }
View Code

  对应头文件delay.h:

1 #ifndef __DELAY_C__ 2 #define __DELAY_C__
3 extern void delay_ms(uint t); 4 extern void delay_10us(uint t); 5 #endif
View Code

4.7 主函数

 1 #include "my52.h"
 2 #include "motor_driver.h"
 3 #include "sr04_driver.h"
 4 #include "delay.h"
 5 #include "lcd1602_driver.h"
 6 #include "infrared_driver.h"
 7 
 8 uchar InfraredGear = LowGear;//手动调节挡位,红外驱动文件中改变该值
 9 uchar Flag = 0;//自动0/手动1模式
10 
11 void main() 12 { 13     uint dis; 14     ENA = 1; 15     IN1 = 0; 16     IN2 = 1; 17     EA = 1;//开总中断
18     EX0 = 1; //开外部中断0,接收红外信号
19     while(1) 20  { 21         if(Flag == 0) //自动
22  { 23             dis = distance(); //超声波测距
24             sr04_motor(dis); //根据距离调节挡位
25  motor_run(InfraredGear); 26             display0(dis);    //液晶显示
27             delay_ms(100);    //延时,每100ms更新一次数据
28  } 29         else
30  { 31             motor_run(InfraredGear); //手动调节转速
32             display1();     //液晶显示
33             delay_ms(100); //延时,每100ms更新一次液晶内容
34  } 35  } 36 }
View Code
相关文章
相关标签/搜索