当前位置: 移动技术网 > IT编程>开发语言>C/C++ > 基于红外和超声波的手动/自动调速风扇系统

基于红外和超声波的手动/自动调速风扇系统

2019年06月07日  | 移动技术网IT编程  | 我要评论

广州国庆兼职,河北省最低工资标准,家悦e3682

一、前言

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

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

二、思路分析

2.1 系统供电问题

  stc89c52单片机及超声波传感器、红外遥控接收头、液晶显示屏均为+5v标准供电,可以直接使用电脑usb接口引出电压。但考虑到电机用到pwm调速,需要大电压和大电流,因此决定使用电池盒额外供电。

2.2 自动/手动模式的切换

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

2.3 pwm信号的产生

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

2.4 单片机内部资源的分配 

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

三、硬件搭建

  由于硬件部分中的很多模块在仿真软件中都没有,且各模块之间的连接关系比较简单,因此在这里不提供电路图,仅用语言描述各引脚之间的连接关系。

3.1 单片机最小系统 

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

3.2 电机驱动模块

   本系统电机驱动模块使用常见的l298n电机驱动模块。l298n芯片可以驱动两个二相电机,也可以驱动一个四相电机,输出电压最高可达50v,可以直接通过电源来调节输出电压;可以直接用单片机的io口提供信号;而且电路简单,使用比较方便。

  在本系统中,只使能了ena来驱动一个电机,其中ena接单片机引脚p20,in1接p21,in2接p22。l298n电机驱动模块实物图如下所示:

 

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电可直接从电机驱动模块中引出。

四、代码分享

  代码用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

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 }

  电机驱动头文件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

 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 }

  超声波驱动头文件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

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 }

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

1 #ifndef __infrared_driver_c__
2 #define __infrared_driver_c__
3 
4 #endif

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 }

  液晶显示驱动头文件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

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 }

  对应头文件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

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 }

 

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网