51之蜂鸣器

目录

1.1蜂鸣器介绍

1.2驱动电路介绍

1.3基本乐理知识


1.1蜂鸣器介绍

        蜂鸣器在我们生活中很常见,可以说很懂地方都有蜂鸣器的存在,常常用来产生按键音和报警音,它的样子主要是这样的:

        蜂鸣器分为有源蜂鸣器和无源蜂鸣器,有源蜂鸣器内部自带振荡源,将正负极接上直流电压即可持续发声,频率固定。

        这里显然我们单片机上面的蜂鸣器是无源蜂鸣器,需要我们手动编写代码为其配置振荡脉冲的频率,而使其发出不同的音调。

1.2驱动电路介绍

        驱动电路主要是一种以微小的数字信号驱动大电流电压的电路,在其中广泛使用的电路元件就是PNP和NPN型三极管。

        P即Postive,表示正极,N即Negative,表示负极,NPN三极管需要我们在P端输入一个高电平表示1导通电路,PNP三极管需要我们在N端输入一个低电平表示0导通电路,我们使用三极管就可以使用微弱的芯片信号去驱动更大的电流和电压,这样就可以达到“四两拨千斤”的效果。

        在我们的单片机里,蜂鸣器和步进电机共用一个芯片ULN2003

        然后我们看到这里的原理图示意:

        这里的其实就是每个引脚对应的内部逻辑,就是通过一个个取反电路和放大电路之后得到较大的信号,比如左边输入0,右边就可以输出一个高电平并且电压大大增加

        我们的蜂鸣器一端连接VCC,一端连接这个ULN2003芯片的一端输出,对应的输入是单片机的P2_5

        所以,我们要想让蜂鸣器响,就要让蜂鸣器接受到一个震荡脉冲,以先高后低为例,我们BEEP就要输出一个先低后高的脉冲(低才能驱动),那么对应P2_5引脚的输入就是先高后低。但是其实我们在使用的时候,并不需要管太多这些低和高,我们只要控制好蜂鸣器频率,即它的震荡周期即可,控制好了周期,我们就可以使用这个蜂鸣器发出对应的音色的声音了。

1.3基本乐理知识

        这里我们主要是想要使用这个蜂鸣器演奏一首歌曲,所以我们这里就要学习一点的乐理知识:

        蜂鸣器震荡脉冲的频率可以表示不同的音色

        这里我们以钢琴键盘为例,以低音6为基准频率,我们可以计算出相应的音符频率值,低音6的基准频率为440Hz,到下一个中音6对应的是880Hz,到高音6变成1760Hz,并且在每两个音符之间的变化是均等的,这样我们就可以写出下面的图示

        我们要根据这个计算出对应的周期,但是对于我们而言,我们控制蜂鸣器的变化就要控制蜂鸣器每半个周期的变化,所以我们还要算出半个周期的时间,而我们想要实现蜂鸣器演奏音乐,就要让计时和蜂鸣器响在同一个时间运行,所以,我们还要配置定时器,也就是我们要配置定时器的重装载的值,我们要把计算出赋值给计时器的初始值,所以我们要使用us为单位

        这里使用定时器0有这样的表格对应:

        然后还有一些乐理知识补充一下:

        这样的在音符上方标点在下方标点都是表示高一音和低一音,两点就表示高两音和低两音,主要控制音符的主频率和音调。

        这样的表示控制音符的音长,什么是音长?

        这就是音长,大部分的乐谱基准符都是4分、8分音符,较为激烈的就是16分音符了,在已知默认音长的情况下,我们再来讨论音符的音长

        例如表示的就是原来音长的一半,表示的就是原来的音长乘以1.5,而

这样的就是典型的延音线,每多一个横线,就相当于加上同样的音符在这个位,即“1 -”==“1 1” 

        像这种就是我们钢琴键盘上的黑键,#1对应的就是1旁边的黑键,一般对应的就是升半音

        乐谱上还有类似这样的一根曲线连接,表示两个音符其实是连在一起的,分开写纯属是为了美观,我们只要记住,把这两个当成一个长的音符弹就好了。

1.4代码

        这里我们就可以开始弹奏钢琴曲子了:

        这里就贴一个代码:

main.c

#include <REGX52.H>
#include "Timer0.h"
#include "Delay.h"


//这里的宏定义是我们做的音调函数的索引
//就是为了我们可以方便的用这些生动形象的字母符号来对我们的函数进行调用
//比如L2表示这个低一音的2,HH3表示高二音的3,M2表示中音2
//M2_下划线表示#2这个高半音的黑键
#define LL1		1
#define LL1_	2
#define LL2		3		
#define LL2_	4
#define LL3		5
#define LL4		6
#define LL4_	7
#define LL5		8
#define LL5_	9
#define LL6		10
#define LL6_	11
#define LL7		12

#define L1		13
#define L1_		14
#define L2		15
#define L2_		16
#define L3		17
#define L4		18
#define L4_		19
#define L5		20
#define L5_		21
#define L6		22
#define L6_		23
#define L7		24

#define M1		25
#define M1_		26
#define M2		27
#define M2_		28
#define M3		29
#define M4		30
#define M4_		31
#define M5		32
#define M5_		33
#define M6		34
#define M6_		35
#define M7		36

#define H1		37
#define H1_		38
#define H2 		39
#define H2_		40
#define H3		41
#define H4		42
#define H4_		43
#define H5 		44
#define H5_		45
#define H6		46
#define H6_		47
#define H7		48

#define HH1		49
#define HH1_	50
#define HH2		51
#define HH2_	52
#define HH3		53
#define HH4		54
#define HH4_	55
#define HH5		56
#define HH5_	57
#define HH6		58
#define HH6_	59
#define HH7		60


sbit BEEP=P2^5;//定义蜂鸣器管脚

//这里我们定义的是我们的乐谱前面的一分钟四分音符的个数
unsigned char SPEED  = 126;
//定义旋律音调的热重载值,就是定时器溢出我们要哦定义的初始值
//这里使用code改变数组的储存位置可以让我们的单片机储存下更多的代码
unsigned int code FreqTable[]={0,
	57844,58290,58687,59126,59438,59789,60101,60434,60728,60991,61262,61471,
	61719,61939,62135,62331,62506,62679,62833,62985,63132,63263,63390,63512,
	63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64524,
	64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
	65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283
};//这里定义的是从低2个八度音到中央音到高2个八度(由低到高)

//定义歌曲,这里我们用《路小雨》钢琴曲来举例
//采用音调索引加音长时间的方式,时长的单位都是一个十六分音符
//如果有停顿,我们用  0,时长 这样的格式表示就好了
unsigned char code Music[]={
				H3,2,H4,2,H5,2,H1,2,H1,4,H2,4,//
				H2,1,M3,1,M5,1,H2,1,
				H1,1,M3,1,M5,1,H1,1,
				M7,1,M3,1,M5,1,M7,1,
				H1,1,M3,1,M5,1,H1,1,//
				H2,1,M3,1,M5,1,H2,1,
				H1,1,M3,1,M5,1,H1,1,
				M7,1,M3,1,M5,1,M7,1,
				H1,1,M3,1,M5,1,H1,1,//
				H3,1,M4_,1,M6,1,H3,1,
				H2,1,M4,1,M6,1,H2,1,
				H1,1,M4,1,M6,1,H1,1,
				H2,1,M4,1,M6,1,H2,1,//
				H3,1,M4_,1,M6,1,H3,1,
				H2,1,M4,1,M6,1,H2,1,
				H1,1,M1,1,M4,1,H1,1,
				M7,1,M2,1,M5,1,M7,1,//
				H2,1,M3,1,M5,1,H2,1,
				H1,1,M3,1,M5,1,H1,1,
				M7,1,M3,1,M5,1,M7,1,
				H1,1,M3,1,M5,1,H1,1,//
				H2,1,M3,1,M5,1,H2,1,
				H1,1,M3,1,M5,1,H1,1,
				M7,1,M3,1,M5,1,M7,1,
				H1,1,M3,1,M5,1,H1,1,//
				H3,1,M4_,1,M6,1,H3,1,
				H2,1,M4,1,M6,1,H2,1,
				H1,1,M4,1,M6,1,H1,1,
				H2,1,M4,1,M6,1,H2,1,//
				H3,1,M4_,1,M6,1,H3,1,
				H2,1,M4,1,M6,1,H2,1,
				H1,1,M1,1,M4,1,H1,1,
				M7,1,M2,1,M5,1,M7,1,0xff		
};

unsigned char FreqSelect;//由于FreqTable数组只有61个元素,所以FreqSelect就可以使用unsigned char定义
unsigned int MusicSelect;//这里由于我们的Music数组里面存储的数据过多,超过了unsigned char的最大值,所以我们不得不使用unsigned int定义它
void main()
{
	unsigned int speed = 15000/SPEED;//计算的是一个十六分音符的时间,单位是ms
	Timer0Init();//初始化定时器
	while(1)
	{
		//到了这里每次循环开始之前都会跳转到下面的中断程序里面,然后main函数和Timer0_Routine() interrupt 1同时进行
		if (Music[MusicSelect])//判断Music的这个是不是0,不是0就可以进行
		{
				if (Music[MusicSelect]==0xff)MusicSelect=0;	
				FreqSelect = Music [MusicSelect];//先把压力给到FreqSelect
			  //因为我们的Music数组是先索引后时长,所以我们先来个使用调出音调对应值,然后演奏,所以我们每个while只演奏一个音符
				TR0=0;
				Delay(5);			
				MusicSelect++;//Music数组里面从上一个跳到下一个,接下来就是音符长度
				TR0=1;
				Delay(speed*Music [MusicSelect]);//这里就是这个函数延时的时长,也是我们定时器演奏音乐的时长
				//在这段Delay里面我们的定时器在不断翻转演奏音乐直到我们关闭定时器或者演奏下一个音符
				//演奏完毕,关闭一下定时器,再停顿5ms左右模拟抬手动作
				TR0=0;//关闭定时器
				Delay(5);//模拟停顿
				MusicSelect++;//加一,下一次while循环又是一个音符演奏
				TR0=1;//重新打开定时器
		}
		else 
		{
			FreqSelect=Music[MusicSelect];
			MusicSelect++;
			TR0=0;
			Delay(speed*Music [MusicSelect]);
			TR0=1;
			MusicSelect++;
		}
}
}
void Timer0_Routine() interrupt 1
{
	TL0=FreqTable[FreqSelect]%256;//后八位
	TH0=FreqTable[FreqSelect]/256;//定时器初始值前八位
	BEEP=!BEEP;//翻转IO让蜂鸣器以特定周期响
}

Delay.c

void Delay(unsigned int time)
{
	while(time--)
	{
				unsigned char i, j;

		_nop_();
		i = 2;
		j = 199;
		do
		{
			while (--j);
		} while (--i);
	}
}

Timer0.c

#include <REGX52.H>

void Timer0Init(void)
{
	TMOD &= 0xf0;//设置定时器模式
	TMOD |= 0x01;//设置定时器模式
	TF0=0;			//	消除TF0标志
	TR0=1;			//定时器0开始计时
	TL0=0X18;    //设置定时器初始值
	TH0=0XFC;		//设置定时器初始值
	ET0=1;
	EA=1;
	PT0=0;
}
/*定时器中断函数模板
void Timer0_Routine() interrupt 1
{
	TL0=0X18;
	TH0=0XFC;
	//加上条件以及改计时器值
}
//复制到主函数后面
*/

  • 23
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值