【51单片机】10-蜂鸣器

1.蜂鸣器的原理

这里的"源"不是指电源。而是指震荡源。 也就是说,有源蜂鸣器内部带震荡源,所以只要一通电就会叫。 而无源内部不带震荡源,所以如果用直流信号无法令其鸣叫。必须用2K~5K的方波去驱动它。 有源蜂鸣器往往比无源的贵,就是因为里面含有震荡电路。

1.无源蜂鸣器原理

(1)早期的蜂鸣器都是无源的

(2)内部结构和材料

(3)发生原理:是利用了压电效应的原理

(4)控制信号,是高低电平相间

(5)电路图

(6)音调如何控制:音调受震动频率控制,就等于控制信号的频率。频率越高音调越高,听起来越刺耳。

(7)声音大小如何控制。由硬件决定的。无法写代码取控制声音大小。

2.有源蜂鸣器

1.无源蜂鸣器的缺陷

无源蜂鸣器只能在一定的范围内进行设置,太大或者太小都不行。【外部提供一个方波】

2.内置震荡电路后形成有源蜂鸣器

在内部添加震荡电路,不用使用外部的方波进行控制**【但是内部的震荡电路是不可以改变的】**

3.有源蜂鸣器也可以用频率信号驱动

可以直接加电也可以直接通过方波进行工作**【有源蜂鸣器包含无源蜂鸣器】**

4.三极管驱动

5.蜂鸣器发声音频率

2.让蜂鸣器响起来

1.接线确定

2.使用delay让蜂鸣器响起来

cpp 复制代码
#include<reg51.h>


sbit BUZZER=P0^0;   //buzzer的驱动引脚



void delay(void){
	unsigned char i,j;
	for(i=0;i<100;i++){
		for(j=0;j<10;j++);
	}
	
}


void main(){
	
	while(1){
		BUZZER=1;//将引脚变外高电平
		delay();
		BUZZER=0;
		delay();
	}
}

3.调节delay控制音乐的变化

控制delay中的i和j的大小,可以控制音调的振动频率,从而影响声音的尖锐。

如果i和j越大,则音调越小,声音越不尖锐

如果i和j越小,则音调越大,声音越尖锐

4.时钟周期的计算

3.用定时器控制蜂鸣器音调

1.上节驱动方法的问题

(1)不容易精确控制时间

(2)CPU控制蜂鸣器中不能做其他事

2.定时器控制蜂鸣器响

在一定时间后,通过定时器中的中断处理程序取减低蜂鸣器的电平,从控制蜂鸣器的响应
(1)10KHz=>1/10000s=>100us===>高电平+低电平的时间都为50us。所以要定的时间就是50us

(2)外部晶振12MHz+12T设置==>内部时钟频率1MHz===》1us===》TL0=255-50=205,TH0=255

3.注意点1:TL0和TH0的计算

我们在设置TH0和TL0应该将获得的时间取其补码

比如:我们是TL0=50; TH0=0;

则我们应该写入"TL0=205;TH0=255;"

cpp 复制代码
	TL0=205;
	TH0=0;
cpp 复制代码
	TL0=205 % 256;//低位取余
	TH0=255/ 256;//高位取商

4.注意点2:定时器的计算

(65536-50000)/256

计数器是16位的,由高8位TH0和低8位TL0组成,可以存储2^16=65536个数,例如当设定计算值为65536-50000=15536时,也就是计数器从15536开始计时,到65536溢出,产生中断,对于晶振频率为12MHz的单片机来说,执行一个机器周期时长为1us,所以这里计时50000us,15536(D)转换为16进制是3CB0(H),此时TH0=3C,TL0=B0分别装入定时器即可,为了免除这些计算步骤,很多编程者采用"TH0=(65536-50000)/256;TL0=(65536-50000)%256",那么为什么要介入256呢?我们可以做一下运算,256(D)=0100(H),512(D)=0200(H),512(D)有两个256,所以高8位就是02,那么15536有多少个256?就是15536/256个,就相当于高8位有多少数值,商存入高8位,剩下的不足一个256,存入低8位,15536%256。
直接使用宏定义

【注意点】我们51是加法计数器,所以是65535-US

cpp 复制代码
//宏定义一个时钟频率
#define XKHZ 10  //10*10的三次方Hz   要定多少Khz,就定义在这里
//宏定义us
//这里我们除以2,是因为想要分给TH0和TL0
//1000---》1000ms
#define US (1000/XKHZ)/2
sbit BUZZER=P0^0;   //buzzer的驱动引脚
//【注意点】因为51单片机是加法计数器,所以实际上我们要算的范围
//应该是65535-US,而不是0-US
#define N (65535 -US)

void delay(void){
	unsigned char i,j;
	for(i=0;i<100;i++){
		for(j=0;j<10;j++);
	}
	
}


void timer0_isr(void) interrupt 1 using 1{
	//这里再一次赋值,是因为我们想要他循环,所以我们要在他每一次进来的时候重新赋值
	
	TL0=N % 256;//低位取余
	TH0=N / 256;//高位取商
	BUZZER=!BUZZER;
	
}

4.蜂鸣器发出滴滴声音

通过count可以控制有声音和无声音的长短

cpp 复制代码
#include<reg51.h>

/**

	用定时器控制蜂鸣器音调
*/
//宏定义一个时钟频率
#define XKHZ 10  //10*10的三次方Hz   要定多少Khz,就定义在这里
#define US (1000/XKHZ)/2
sbit BUZZER=P0^0;   //buzzer的驱动引脚
#define N (65535 -US)

//计数器
unsigned int count;

//判断此时是从"有声音"到"没声音",还是从"没声音"到"有声音"
unsigned char flag=0;  //flag=0表示有声音,flag=1表示没有声音


void delay(void){
	unsigned char i,j;
	for(i=0;i<100;i++){
		for(j=0;j<10;j++);
	}
	
}


void timer0_isr(void) interrupt 1 using 1{
	//这里再一次赋值,是因为我们想要他循环,所以我们要在他每一次进来的时候重新赋值
	
	TL0=N % 256;//低位取余
	TH0=N / 256;//高位取商
	
	if(count--==0){
		//说明到了翻转的时候了
		//count=5000;//记得重新赋值,要不然就只能响一次
		if(flag==0){
			//说明之前处于有声音的,说明本次是从有声音到无声音的翻转
			flag=1;
            //此时"无声音"比"有声音"的时间还长了3倍
			count=600*3;
		}else{
			//说明之前没声音的,说明本次是没声音到有声音的翻转
			flag=0;
			//下面这句话,加上了,则表示在翻转的时候也会出现声音
			BUZZER=!BUZZER;
			count=600;
		}
	}else{//常规情况,也就是不翻转时候
		if(flag==0){
			BUZZER=!BUZZER; //4999次声音
		}else{
			//空的,因为不进行任何IO操作就是没有声音
		}
		
	}
	
	
}



void main(){
	
	//【第一步】初始化:我们使用的是定时器T0
	TMOD=0x01;  //T0使用16位bit定时器
	//********************************************************
	TL0=N % 256;//低位取余
	TH0=N / 256;//高位取商
	//********************************************************
	
	
	//打开计数器;TCON中的TR0【定时器T0的运行控制位】
	TR0=1;		//T0打开开始计数
	//T0的中断溢出位,表示允许中断
	ET0=1;		//T0中断允许
	EA=1;			//打开中断允许
	
	
	BUZZER=1;
	
	//设置响和不响的周期时间
	count=5000;     //5000*100us=500ms
	//初始化,有声音
	flag=0;
	
}

5.让蜂鸣器唱歌

1.为什么蜂鸣器可以唱歌

(1)发声音频可变---》延迟函数(delay)

(2)发声音长度可变---》定时器

cpp 复制代码
unsigned char i;
for(i=0;i<200;i++){//控制声音响应时间长短
	Sound=~Sound;
	DelayXms(1);//控制声音的不同
}

for(i=0;i<50;i++){
	Sound=~Sound;
	DelayXms(2);
}

2.分析写好的唱歌程序

(1)复制代码过去

(2)修改控制蜂鸣器的IO引脚定义

3."code"关键字的使用

因为我们加入的歌曲的编码是固定不变的,但是51单片机的内存有限制,所以我们只能把歌曲的编码放在常量区中,才使得其不会占据内存。则加上"code"关键字。

cpp 复制代码
unsigned char code music_tab[] = 
{   
	0x18, 0x30, 0x1C , 0x10, //格式为: 频率常数, 节拍常数, 频率常数, 节拍常数,    
	0x20, 0x40, 0x1C , 0x10,   
	0x18, 0x10, 0x20 , 0x10,   
	0x1C, 0x10, 0x18 , 0x40,   
	0x1C, 0x20, 0x20 , 0x20,   
	0x1C, 0x20, 0x18 , 0x20,   
	0x20, 0x80, 0xFF , 0x20,   
	0x30, 0x1C, 0x10 , 0x18,   
	0x20, 0x15, 0x20 , 0x1C,
}

4.音节的构成

0x18, 0x30, 0x1C , 0x10,【2个一组】

(1)音调**【振动频率决定】**:0x18【奇数次】

(2)音长:0x30【偶数次】

注意点:

5.音频 VS 音调

(1)音节:定时器T0控制的是音乐的节拍(某一个音节持续时间)而不管音调(频率)

(2)音调【音频】:是直接使用delay做出来的,控制发出什么样子的声音

cpp 复制代码
/************************************************************************  
[文件名]  C51音乐程序(八月桂花)  
[功能]    通过单片机演奏音乐  
  
/**********************************************************************/  

#include <REG51.H> 
//提供移位函数,可以省略
//#include <INTRINS.H>    
//本例采用89C52, 晶振为11.0592MHZ    
//关于如何编制音乐代码, 其实十分简单,各位可以看以下代码.    
//频率常数即音乐术语中的音调,而节拍常数即音乐术语中的多少拍;    
//所以拿出谱子, 试探编吧!    

sbit Beep =  P0^0 ; 			// 要根据实际的接线来修改
   
unsigned char n = 0;  //n为节拍常数变量    
unsigned char code music_tab[] = 
{   
	0x18, 0x30, 0x1C , 0x10, //格式为: 频率常数, 节拍常数, 频率常数, 节拍常数,    
	0x20, 0x40, 0x1C , 0x10,   
	0x18, 0x10, 0x20 , 0x10,   
	0x1C, 0x10, 0x18 , 0x40,   
	0x1C, 0x20, 0x20 , 0x20,   
	0x1C, 0x20, 0x18 , 0x20,   
	0x20, 0x80, 0xFF , 0x20,   
	0x30, 0x1C, 0x10 , 0x18,   
	0x20, 0x15, 0x20 , 0x1C,   
	0x20, 0x20, 0x20 , 0x26,   
	0x40, 0x20, 0x20 , 0x2B,   
	0x20, 0x26, 0x20 , 0x20,   
	0x20, 0x30, 0x80 , 0xFF,   
	0x20, 0x20, 0x1C , 0x10,   
	0x18, 0x10, 0x20 , 0x20,   
	0x26, 0x20, 0x2B , 0x20,   
	0x30, 0x20, 0x2B , 0x40,   
	0x20, 0x20, 0x1C , 0x10,   
	0x18, 0x10, 0x20 , 0x20,   
	0x26, 0x20, 0x2B , 0x20,   
	0x30, 0x20, 0x2B , 0x40,   
	0x20, 0x30, 0x1C , 0x10,   
	0x18, 0x20, 0x15 , 0x20,   
	0x1C, 0x20, 0x20 , 0x20,   
	0x26, 0x40, 0x20 , 0x20,   
	0x2B, 0x20, 0x26 , 0x20,   
	0x20, 0x20, 0x30 , 0x80,   
	0x20, 0x30, 0x1C , 0x10,   
	0x20, 0x10, 0x1C , 0x10,   
	0x20, 0x20, 0x26 , 0x20,   
	0x2B, 0x20, 0x30 , 0x20,   
	0x2B, 0x40, 0x20 , 0x15,   
	0x1F, 0x05, 0x20 , 0x10,   
	0x1C, 0x10, 0x20 , 0x20,   
	0x26, 0x20, 0x2B , 0x20,   
	0x30, 0x20, 0x2B , 0x40,   
	0x20, 0x30, 0x1C , 0x10,   
	0x18, 0x20, 0x15 , 0x20,   
	0x1C, 0x20, 0x20 , 0x20,   
	0x26, 0x40, 0x20 , 0x20,   
	0x2B, 0x20, 0x26 , 0x20,   
	0x20, 0x20, 0x30 , 0x30,   
	0x20, 0x30, 0x1C , 0x10,   
	0x18, 0x40, 0x1C , 0x20,   
	0x20, 0x20, 0x26 , 0x40,   
	0x13, 0x60, 0x18 , 0x20,   
	0x15, 0x40, 0x13 , 0x40,   
	0x18, 0x80, 0x00   
};   

 // T0定时控制的是音乐的节拍(某一个音节持续的时间)而不管音调(频率)
 // 音调是直接使用delay做出来的。
void int0()  interrupt 1   //采用中断0 控制节拍    
{  
		TH0 = 0xd8;   
   	TL0 = 0xef;   
   	n--;   
}   
   
void delay (unsigned char m)   //控制频率延时    
{   
		unsigned i = 3 * m;   
		while (--i);   
}   
   
void delayms(unsigned char a)  //豪秒延时子程序    
{   
  	while (--a);                  //采用while(--a) 不要采用while(a--); 各位可编译一下看看汇编结果就知道了!    
}   
   
void main()   
{ 
		unsigned char p, m;   // m为频率常数变量    
  	unsigned char i = 0;   

	//此处表示将:低4位留下了,高4位去除
  	TMOD &= 0x0f;   
  	TMOD |= 0x01;  		// timer0 工作在模式1,16位定时器下 
  	TH0 = 0xd8;
		TL0 = 0xef;   		// 这个TH和TL的值合起来定了1个10ms左右的一个时间
  	IE = 0x82;   
play:   
   	while (1)   
    {   
a: 		p = music_tab[i];   //表示指向第一个
			if (p == 0x00)   		// 一遍播放完了,延时1s后自动开始下一遍    
			{ 
				i=0;
				delayms(1000);  //表示播放完一次,延迟1s,接着下一次播放
				//如果想要播放一次,则下面goto注释
				goto play;  //跳转接着播放
			}     //如果碰到结束符,延时1秒,回到开始再来一遍    
     	else if (p == 0xff)  //0xff:休止符
			{ 
				i = i + 1;//跳过这一组数据
				delayms(100);
				TR0 = 0; //TR0:关闭定时器
				goto a;
			}  //若碰到休止符,延时100ms,继续取下一音符    
			else     //常规情况p==正常情况      
			{
				m = music_tab[i++];
				n = music_tab[i++];
			}  // m取频率常数【A,B,C】 和 n取节拍常数 【1/2,1/3,1/4】   
			
			//打开开定时器1   
				TR0 = 1;                               
				while (n != 0) //节拍不等于0
				{ 
					Beep = ~Beep;   //修改蜂鸣器电平 【~】与【!】一样
					delay(m);        	//等待节拍完成, 通过P1口输出音频(可多声道哦!)    
				}
					TR0 = 0;                         	//关定时器1    
			}   
}
  

6.切歌,暂停功能

单片机应用番外篇------蜂鸣器的应用之可实现切歌、暂停功能的简单音乐盒_哔哩哔哩_bilibili

cpp 复制代码
/**

使用中断处理程序控制音乐的暂停和播放,切换


*/

//外部中断初始化

void EX_init(){
	
	IT0=1;//下降沿触发方式  INT0
	IT1=1;//下降沿触发方式    INT1
	EX0=1;  //外部中断0中断允许位
	EX1=1;   //打开中断开关
	EA=1;//打开总的中断开关
	
}

//暂停功能
void EX0_isr() interrupt 0
{
	DelayXms(10);//消除抖动
	if(INT0==0)//这里是INT1已经定义了,对应P3.2这个IO口,可以直接使用
	{
		//暂停通过定时计数器,则将计数器进行取反
		TR0=~TR0;
		TR1=~TR1;
	}
}

//切歌功能
void EX1_isr() interrupt 2
{
		DelayXms(10);//消除抖动
	if(INT1==0)//这里是INT1已经定义了,对应P3.2这个IO口,可以直接使用
	{
		state++;
		if(state==3){  //因为此时我们只有3曲歌
			state=0;
		}
	}
}

unsigned char state=0;//控制切歌

void main(){
	
	EX_init();
	InitialSound();
	while(1){
		switch(state){ //这个切歌,通过外部中断1,则进行转换
			case 0:
				Play(Music_Girl,0,2,345);break;
			case 1:
				Play(Music_haw,0,23,543);break;
			default:break;
		}
	}
	
}
相关推荐
智商偏低2 小时前
单片机之helloworld
单片机·嵌入式硬件
青牛科技-Allen4 小时前
GC3910S:一款高性能双通道直流电机驱动芯片
stm32·单片机·嵌入式硬件·机器人·医疗器械·水泵、
森焱森5 小时前
无人机三轴稳定控制(2)____根据目标俯仰角,实现俯仰稳定化控制,计算出升降舵输出
c语言·单片机·算法·架构·无人机
白鱼不小白6 小时前
stm32 USART串口协议与外设(程序)——江协教程踩坑经验分享
stm32·单片机·嵌入式硬件
S,D6 小时前
MCU引脚的漏电流、灌电流、拉电流区别是什么
驱动开发·stm32·单片机·嵌入式硬件·mcu·物联网·硬件工程
芯岭技术9 小时前
PY32F002A单片机 低成本控制器解决方案,提供多种封装
单片机·嵌入式硬件
youmdt10 小时前
Arduino IDE ESP8266连接0.96寸SSD1306 IIC单色屏显示北京时间
单片机·嵌入式硬件
嘿·嘘10 小时前
第七章 STM32内部FLASH读写
stm32·单片机·嵌入式硬件
Meraki.Zhang10 小时前
【STM32实践篇】:I2C驱动编写
stm32·单片机·iic·驱动·i2c
几个几个n12 小时前
STM32-第二节-GPIO输入(按键,传感器)
单片机·嵌入式硬件