恭喜大家顺利通过测试一!在测试一中我们学会了如何利用现有模块HC-05/06进行简单的连线来制作一个蓝牙防丢器,同时学习了安卓蓝牙相关的几个API并最终制作了一个自己的蓝牙防丢客户端软件。可能有些专门来看软硬结合的同学会抱怨“什么呀,感觉就是在开发安卓App嘛!“。不错!测试一的目的就是让大家通过了解硬件原理DIY一个简单的硬件,并学习如何充分利用移动端开发的特点设计一款配套的应用。除此之外楼主还悄悄地为测试二埋下了伏笔,因为测试二将会涉及利用移动端和蓝牙模块的通信功能来实现一个遥控小风扇!如果没有前面关于蓝牙软硬件知识的铺垫,直接做这个可能会很吃力。那么现在我们就着手测试二吧![正版请搜索:beautifulzzzz(看楼主博客园官方博客,享高质量生活)嘻嘻!!!]
1 预期效果构思
简单起见我们实现一个可以通过手机App遥控的可调速小风扇。如图1_1左边手机应用部分主要包括1、2、3三个按钮和4用于显示风扇速度的文本框;右边小风扇部分主要包括7风扇模块和8用于显示风扇速度的显示模块;中间的5、6表示双方通过蓝牙进行无线通信实现遥控功能。
图1_1 预期效果构思
2 硬件轮廓勾勒
其实整个硬件部分都是要我们自己DIY的。如图2_1所示1号为51最小系统模块,起总控作用;2号为电源模块,用于向整个系统供电;3号为蓝牙模块,用于单片机和智能手机进行蓝牙通信;4号为电机模块(包括电机驱动电路),用于将电能转换为机械能提供风;5号为数码管显示模块,用于显示小风扇的当前转速。
图 2_1 硬件轮廓勾勒
3 硬件整体电路图设计
既然轮廓已经勾勒出,接下来要看看我们具体需要哪些元件。首先对于51最小系统模块(如图3_1所示)包括晶振电路和89C52单片机(其实为了简单笔者偷偷地将复位电路去掉了,这样带来的直接后果是程序烧不进去。如果大家也一样学着偷懒,不妨把该最小系统的电源引脚和串口引脚用杜邦线连接到你买来的开发板对应的引脚处,同时把开发板上的单片机拿掉。这样就可以利用开发板上的复位电路模块来实现程序的有效烧写。)
图 3_1 51最小系统
对于电源模块,我们可以使用可充电的5V锂电池或者用3节1.5V的普通电池凑合。蓝牙模块是我们上一章中制作蓝牙防丢器的HC-05或HC-06。这里电机模块要特别说明下:如图3_2需要用一个ULN2003做驱动,这样控制信号要从4号引脚输入以实现对马达的控制。另外马达可以选择玩具四驱车上的那种。
图 3_2 电机模块
最后显示模块采用的是四位八段共阴数码管3461AS。如图3_3每个3461AS有4个数码管,每个数码管中有8个LED灯。这样当我们想使某一个数码管显示相应的数字时,只要给4路位选信号和8路段选信号相应的组合电平就能实现功能。需要另外说明的是:3461AS属于共阴数码管,如图3_4其中6、8、9、12为位选引脚,3、5、10、1、2、4、7、11为段选引脚。如果我们想让第二个数码管显示2时,要让9号引脚置低电平其余位选引脚置高电平,同时要让11、7、5、1、2置高电平其余段选置低电平。
图 3_3 3461AS封装图
图 3_4 3461AS内部电路图
因此在实际电路中(如图3_5)将P0口作段选信号引脚,同时用P2.3、P2.4、P2.5、P2.6作为位选信号引脚,通过单片机直接驱动即可。此外R1~R8八个上拉电阻不能忽视,起初笔者没有注意结果烧坏了2个3461AS。
图3_5 显示模块实际驱动电路
最终我们设计的电路图如下,其中RXD和TXD引脚接HC-05或HC-06的TXD和RXD(要交错相连)。因为HC-05/06是蓝牙串口模块,也就是说只要单片机采用串口驱动程序并且相应的引脚连接正确,单片机-蓝牙模块通信完全和单片机-串口设备通信一样。所以图中的串口模块也就相当于我们的蓝牙模块,唯一需要注意的是单片机和蓝牙模块的RXD和TXD是交错相连。
图 3_6 整体电路图
4 四位八段共阴数码管3461AS的驱动程序设计
由上面分析我们知道通过位选信号和段选信号的组合可以实现数码管显示功能。如果采用图3_6所示电路图,上面想让第二个数码管显示2时,则P0等于0x5b(01011101),P2等于0xdf(11011111)。采用同样的分析方法我们可以计算出让八段数码管显示从0~F的所有P0对应的赋值:0x3f 0x06 0x5b 0x4f 0x66 0x6d 0x7d 0x07 0x7f 0x6f 0x77 0x7c 0x39 0x5e 0x79 0x71,以及单独选通第1位到第4位P2的所有赋值:0xbf 0xdf 0xef 0xf7。这样当我们想让第3位显示9只需要给P0、P2分别赋值0x6f和0xef即可。
这时大家可能会有这样的疑惑:“按照上面的说法似乎每次只能让某一位显示一个数字”。其实有这样的疑惑说明大家学的比较认真,其实生活中很多数码管的显示案例中都是每次只显示一位的!之所以我们看到的情况是一次显示多个,就在于数码管驱动程序设计了!而这其中的秘诀则是采用了高频刷新(也即动态扫描)这一技巧。如果大家对动态扫描没有感觉,可以想象一下挥舞荧光棒时的样子——本来只是一根荧光棒,由于挥舞速度比较快而在空中划出一道美丽的弧线。下面结合驱动程序和大家详细介绍:
1 #include"display_4X8.h" 2 3 unsigned char code DuanMa[16]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 4 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};// 显示段码值0~F 5 unsigned char code WeiMa[]={0xbf,0xdf,0xef,0xf7};//分别对应相应的数码管点亮,即位码 6 unsigned char TempData[4]; //存储显示值的全局变量 7 8 //------------------------------------------------ 9 //4位8段共阴数码管显示函数 10 //第一个参数为0表示从第一个数码管开始显示num个数 11 //提前要显示的数要存在TempData中(TempData[0]表示要显示的第一个数) 12 //------------------------------------------------ 13 void Display(unsigned char FirstBit,unsigned char Num) 14 { 15 static unsigned char i=0; 16 17 DataPort=0x00; //清空数据,防止有交替重影 18 DataControl=0x00; 19 20 DataPort=TempData[i]; //取显示数据,段码 21 DataControl=WeiMa[i+FirstBit]; 22 23 i++; 24 if(i==Num) 25 i=0; 26 }
这里的DuanMa[]和WeiMa[]不再说明,TempData[4]用来存储要显示数据。在该驱动中只有一个Display函数,正如第10行提示所述:第一个参数用来表明从哪一个数码管开始显示数据,第二个参数表明一共要显示多少位数据。这样如果要在数码管的后两位显示一个两位数则可以用Display(2,2)。这里要特别说明下TempData数组,该数组用于存放数码管要显示的数据,千万不要把该数组和数码管直接对应。例如同样是在数码管后两位显示一个两位数num可以采用下列两种方案:
① TempData[0]=DuanMa[num/10];
TempData[1]=DuanMa[num%10];
Display(2,2);
② TempData[2]=DuanMa[num/10];
TempData[3]=DuanMa[num%10];
Display(0,4);
其中方案一直接把要显示的两位数据存储在TempData的前两位,然后调用Display函数从第3个数码管开始显示2位来实现功能。方案二其实是把要显示的数据存放在TempData的后两位(前两位默认为0),然后调用Display函数从第1个数码管开始显示4位来实现功能。
对于动态扫描这里用了一个很巧妙的方法:注意到第15行定义了一个静态变量i,其功能在于实现一个周期内实现对需要点亮的数码管顺序点亮。这样如果Display(0,4)显示1234,则数码管的慢动作则为:第一个数码管显示1、接着第二个数码管显示2、然后第三个数码管显示3……由于刷新频率很高,所以人眼看上去就是4个数码管同时显示1234的效果。
5 PWM实现变速小马达
欲实现直流小马达的速度控制这里必须先讲解下PWM。所谓PWM是“Pulse Width Modulation”的缩写,简称脉宽调制。它是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。这里举个通俗的例子来解释PWM:假设你是某公司的老板,手下有个奇葩的员工喜欢周期性的在一个小时内干一会休息一会,如果你想多压榨一下他就会督促让他在一个周期内多干活少休息。同样的利用微处理器在一个比较短的周期内设置某个引脚输出高电平比低电平的持续时间多一点,从宏观上看则呈现出输出功率升高的效果,反之输出功率变低。
图 5_1不同占空比的输出脉冲
6 串口驱动程序设计
上面已经介绍过单片机和蓝牙模块的通信方式是采用串口通信,其重要特别注意的是单片机和HC-05/06的RXD引脚和TXD引脚要交错相连。既然HC-05/06采用的是串口通信方式,所以在给单片机编程时只要按照串口驱动来设计就可以了。
1 #include"uart.h" 2 3 #define Length 8 4 5 unsigned char getByte[Length]; //定义临时变量 6 unsigned char flag; //接收标记 7 unsigned char point; //指针 8 9 //------------------------------------------------ 10 //串口初始化 11 //------------------------------------------------ 12 void InitUART (void) 13 { 14 flag=0; 15 point=0; 16 SCON = 0x50; // SCON: 模式 1, 8-bit UART, 使能接收 17 TMOD |= 0x20; // TMOD: timer 1, mode 2, 8-bit 重装 18 TH1 = 0xFD; // TH1: 重装值 9600 波特率 晶振 11.0592MHz 19 TL1 = 0xFD; 20 TR1 = 1; // TR1: timer 1 打开 21 EA = 1; //打开总中断 22 ES = 1; //打开串口中断 23 } 24 25 //------------------------------------------------ 26 //发送一个字节 27 //------------------------------------------------ 28 void SendByte(unsigned char dat) 29 { 30 SBUF = dat; 31 while(!TI); 32 TI = 0; 33 } 34 35 //------------------------------------------------ 36 //发送一个字符串 37 //------------------------------------------------ 38 void SendStr(unsigned char *s) 39 { 40 while(*s!='