🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
【91.1 蜂鸣器的硬件电路简介。】 ![](https://img.kancloud.cn/89/70/8970513a066fe0726b2997dcb0329ce0_194x190.png) 上图91.1.1 PNP三极管驱动有源蜂鸣器 蜂鸣器有两种,一种是有源蜂鸣器,一种是无源蜂鸣器。有源蜂鸣器的驱动最简单,只要通电就一直响,断电就停,跟驱动LED灯一样。无源蜂鸣器则不一样,无源蜂鸣器一直断电不响,奇怪的是一直通电也不响,只有“通,关,通,关...”反复通电关电的状态,才会持续发生稳定的声音,此方式称为脉冲驱动方式,或者PWM驱动方式。本教程用的是有源蜂鸣器。 蜂鸣器的驱动电路也有两种常用的方式,一种是NPN三极管驱动,一种是PNP三极管驱动。NPN三极管驱动电路,单片机输出“1”(高电平)蜂鸣器导通,输出“0”(低电平)蜂鸣器关闭。而PNP三极管驱动电路恰恰相反,单片机输出“0”(低电平)蜂鸣器导通,输出“1”(高电平)蜂鸣器关闭。本教程所用的是PNP三极管驱动电路,如上图。 【91.2 “非阻塞”驱动程序。】 “驱动层”是相对“应用层”而言。“应用层”发号施令,“驱动层”负责执行。一个好的“驱动层”必须给“应用层”提供快捷便利的调用接口,此接口可以是函数或者全局变量。本节驱动蜂鸣器所用的是全局变量vGu16BeepTimerCnt和vGu8BeepTimerFlag。“应用层”只需给vGu16BeepTimerCnt赋值,给vGu8BeepTimerFlag置1,就可以控制蜂鸣器发声,赋值越大,发声越长,500代表发声500ms,1000代表发声1000ms,具体细节实现,则由“驱动层”的驱动函数负责执行,驱动函数放在定时中断函数里定时扫描。为什么不把驱动函数放到main函数的循环里去?因为放在定时中断里,能保证蜂鸣器的声音长度是一致的,如果放在main循环里,声音的长度有可能在某些项目中受到某些必须一气呵成的任务干扰,得不到及时响应,影响声音长度的一致性。下面代码实现的功能是,单片机只要一上电,蜂鸣器就发出一次1000ms长度的“嘀”声音。 \#include "REG52.H" \#define BEEP\_TIME 1000 //控制蜂鸣器发声的长度,此处是1000ms void T0\_time(); void SystemInitial(void) ; void Delay(unsigned long u32DelayTime) ; void PeripheralInitial(void) ; void BeepOpen(void); //蜂鸣器发声 void BeepClose(void); //蜂鸣器关闭 void VoiceScan(void); //蜂鸣器的驱动函数,放在定时中断里 sbit P3\_4=P3^4; //控制蜂鸣器的IO口。0代表发声,1代表关闭。 volatile unsigned char vGu8BeepTimerFlag=0; volatile unsigned int vGu16BeepTimerCnt=0; //控制蜂鸣器发声长度的计时器 void main() { SystemInitial(); Delay(10000); PeripheralInitial(); //此函数内部有“应用层”的赋值操作,控制上电的声音长度。 while(1) { ; } } void T0\_time() interrupt 1 { VoiceScan(); //蜂鸣器的驱动函数 TH0=0xfc; TL0=0x66; } void SystemInitial(void) { TMOD=0x01; TH0=0xfc; TL0=0x66; EA=1; ET0=1; TR0=1; } void Delay(unsigned long u32DelayTime) { for(;u32DelayTime>0;u32DelayTime--); } void PeripheralInitial(void) { vGu8BeepTimerFlag=0; vGu16BeepTimerCnt=BEEP\_TIME; //“应用层”只需赋值,一上电,蜂鸣器发出1000ms长度的声音。 vGu8BeepTimerFlag=1; } //蜂鸣器发声 void BeepOpen(void) { P3\_4=0; //0代表发声 } //蜂鸣器关闭 void BeepClose(void) { P3\_4=1; //1代表关闭 } //蜂鸣器的驱动函数,放在定时中断函数里每定时1ms扫描一次。 void VoiceScan(void) { //Su8Lock的作用是避免BeepOpen()被重复扫描影响效率,发声时只执行一次此函数即可。 //同时,也巧妙借用else结构,实现逻辑顺序分解成“先发声,下一次再开始定时”的两个步骤。 static unsigned char Su8Lock=0; if(1==vGu8BeepTimerFlag&&vGu16BeepTimerCnt>0) { if(0==Su8Lock) { Su8Lock=1; //进入触发声音后就自锁起来 BeepOpen(); //发声,此处封装成函数,为了今后代码的移植性。 } else //巧妙借用else结构,实现先发声,下一次中断再开始计时的逻辑顺序。比如, { //如果赋值1,就能确保有1ms的计时发声。 vGu16BeepTimerCnt--; //定时器自减,控制蜂鸣器发声的时间长度 if(0==vGu16BeepTimerCnt) { Su8Lock=0; //关闭声音后,及时解锁,为下一次触发做准备 BeepClose(); //关闭声音,此处封装成函数,为了今后代码的移植性。 } } } }