最近笔者在学习51单片机的使用,在学到蜂鸣器的时候突发奇想,想要演奏Beyond的《海阔天空》,但在网上找不到相关的源码,故便自己参考B站up主@江科大自化协的视频即源码整理了一下实现思路,文中如果出现了什么问题还望大家指出。另外我会把源码,excel文件以及运行出的hex文件放到Github上,欢迎大家前来star
源码及excel地址:https://github.com/udbbdh/51-beyond-.git
演示视频地址:用51单片机演奏《海阔天空》_哔哩哔哩_bilibili
前言
最近笔者在学习51单片机的使用,在学到蜂鸣器的时候突发奇想,想要演奏Beyond的《海阔天空》,但在网上找不到相关的源码,故便自己整理了一下实现思路,连带着源码一起在此分享给大家,欢迎大家多多点赞收藏。
一、51单片机的蜂鸣器原理的简要介绍
蜂鸣器大致可以分为有源蜂鸣器和无源蜂鸣器两大类(其中的“源”指的是震荡源而非电源),而蜂鸣器正是通过震荡源的震荡来发声的。对于有源蜂鸣器来说,由于它自带震荡源,其振荡频率是固定的,所以只能发出特定频率的声音(即发出的声音频率无法改变)。对于无源蜂鸣器来说,它内部没有自带震荡源,可以利用定时器来控制震荡的周期,进而改变声音的频率,使其发出不同音调的声音。我所用的单片机为普中STC89C52R,集成的蜂鸣器为无源蜂鸣器,因此可以发出不同音调的声音,具有演奏音乐的能力。
如果还想要对51单片机蜂鸣器进行更细致的学习,大家可以参考官方给的资料。
二、思路步骤
1.根据晶振频率计算定时器的重载值
晶振频率可以从单片机上读出,是单片机内部用于产生定时的设备,但由于51单片机的定时器所产生的频率只能够是晶振频率的1/12,故定时器的实际频率为11.0592/12MHz。
如果我们想要发出一个音调的声音,只需要查到那个声音的频率f,再控制定时器的重载值即可。
举个例子,假如要发出f=440Hz的声音,则该音调的声音的周期T=1/f=2272.73us,对于方型波来说,也就是每半个周期波形变化一次,因此定时器需要定时0.5T=1136.36us。定时器是通过计数进行定时的,计数范围为0~65535,上面我们提到,计时器的是实际频率为(11.0592/12)MHz,所以定时器每次+1所需要的时间为1s/(11.0592/12MHz)=1.085us,所以要记时0.5T就需要计数0.5T/1.085us,即1136.36/1.085=1047.33次,取整之后即1047次。又由于定时器是溢出才知道到时间了的,所以我们需要从(65536-1047=64489)开始计时,所以定时器TH0=int(64489/256)=251,TL0=int(64489%256)=232,这样定时器就配置好了。
如果是一个一个进行计算的话会十分的麻烦,推荐使用EXCEL进行计算,如下图所示
2.Keil5代码思路
首先我们要看开发板的原理图里面给出的蜂鸣器的端口
我这里是p2_5端口,所以下面就应该是p2^5。
sbit Buzzer=p2^5;//对p2^5端口进行命名,类似于define
之后建立一个数组,用于储存EXCEL中计算得到的定时器的重载值。
unsigned int FreqTable[]={
0,
63776,63871,63968,64053,64139,64215,64290,64359,64425,64488,64546,64602,
64654,64703,64750,64794,64836,64875,64912,64947,64980,65011,65041,65069,
65095,65119,65143,65165,65186,65205,65224,65241,65258,65273,65288,65302,
};//从63776~65302分别表示从低音1到高音7
之后就是根据乐谱再定义一个数组,把每一个音调都用它在数组FreqTable数组中的索引来表示,但一个个去对应会十分麻烦,所以我们用define来简化对应关系的寻找
#define P 0
#define L1 1
#define L1_ 2
#define L2 3
#define L2_ 4
#define L3 5
#define L4 6
#define L4_ 7
#define L5 8
#define L5_ 9
#define L6 10
#define L6_ 11
#define L7 12
#define M1 13
#define M1_ 14
#define M2 15
#define M2_ 16
#define M3 17
#define M4 18
#define M4_ 19
#define M5 20
#define M5_ 21
#define M6 22
#define M6_ 23
#define M7 24
#define H1 25
#define H1_ 26
#define H2 27
#define H2_ 28
#define H3 29
#define H4 30
#define H4_ 31
#define H5 32
#define H5_ 33
#define H6 34
#define H6_ 35
#define H7 36
之后就开始定义谱子
unsigned char code Music[]={
//音符,时值,
M7,4,
H4,4,
H3,8,
H2,2,
H1,2,
M7,2,
M6,4,
M4,2,
M5,2,
H1,2,
M4,2,
P,2,
M3,4,
P,2,
M4,2,
M5,2,
H1,2,
M4,2,
P,2,
M3,4,
//
P,2,
M4,2,
M5,2,
H2,2,
H1,6,
M4,6,
M7,4,
M6,6,
P,3,
//今天我
M3,2,
M2,2,
M1,8,
P,4,
M2,1,
M3,1,
M5,2,
M5,1,
M5,1+2,
M6,2,
M5,4,
P,2,
M6,1,
M7,1,
H1,2,
H1,1,
H1,1+2,
H1,2,
H1,2,
M7,2,
M6,1,
M5,2,
M6,9,
P,4,
M6,2,
M5,2,
M5,6,
M5,2,
M3,1,
M2,1,
M1,2,
M3,2,
M4,2,
M3,2,
M2,2,
M2,2,
M3,1,
M2,1+4,
M3,2,
M2,1,
M2,3,
M1,2,
M1,2,
M1,1,
M1,5,
M2,2,
M1,2,
M1,6,
P,6,
M6,1,
M7,1,
H1,2,
H1,1,
H1,3,
H1,2,
M7,2,
M6,2,
M5,1,
M5,3,
M5,4,
M3,2,
M2,2,
M1,4,
//也会怕有一天会跌倒
P,2,
H1,1,
H1,1,
H1,2,
H1,1,
H2,1,
H2,2,
H2,2,
H1,1,
H2,2,
H3,6,
P,2,
H3,3,
H2,2,
H1,1,
P,6,
H1,1,
H1,1,
H1,2,
H1,1,
H2,3,
P,2,
M5,1,
M5,1,
H3,4,
H2,4,
H1,8,
P,2,
H1,1,
H1,1,
H1,2,
H1,1,
H2,3,
H2,1,
H2,1,
H1,1,
M7,2,
H1,9,
//仍然自由自我
P,4,
M6,2,
M6,2,
M7,2,
M6,2,
M7,2,
H1,8,
//永远高唱我歌
H1,1,
H1,1,
H2,2,
H1,2,
H2,2,
H3,8,
//走遍千里
H3,2,
H2,6,
H3,2,
H1,8,
//原谅我
P,2,
M6,1,
M7,1,
H1,2,
H1,1,
H1,3,
H1,2,
M7,2,
M6,2,
M5,1,
M5,3,
M5,4,
M3,2,
M2,2,
M1,4,
P,4,
H1,1,
H1,1,
H1,2,
H1,1,
H2,1,
H2,2,
H2,2,
H1,1,
H2,2,
H3,9,
H3,3,
H2,2,
H1,1,
P,2,
H1,1,
H1,1,
H1,2,
H1,1,
H2,3,
P,2,
M5,1,
M5,1,
H3,4,
H2,4,
H1,8,
P,2,
H1,1,
H1,1,
H1,2,
H1,1,
H2,3,
H2,1,
H2,1,
H1,1,
M7,2,
H1,9,
0xFF //终止标志
};
然后定义计时器的主函数和中断
#define SPEED 700
unsigned int FreqSelect,MusicSelect;
void main()
{
Timer0Init();
while(1)
{
if(Music[MusicSelect]!=0xFF) //如果不是停止标志位
{
FreqSelect=Music[MusicSelect]; //选择音符对应的频率
MusicSelect++;
Delay(SPEED/4*Music[MusicSelect]); //选择音符对应的时值
MusicSelect++;
TR0=0;//TR0=0表示不允许计数
Delay(5); //音符间短暂停顿
TR0=1;//TR0=0表示允许计数
}
else //如果是停止标志位
{
TR0=0;
while(1);
}
}
}
void Timer0_Routine() interrupt 1
{
if(FreqTable[FreqSelect]) //如果不是休止符
{
/*取对应频率值的重装载值到定时器*/
TL0 = FreqTable[FreqSelect]%256; //设置定时初值
TH0 = FreqTable[FreqSelect]/256; //设置定时初值
Buzzer=!Buzzer; //翻转蜂鸣器IO口
}
}
总结
以上就是今天要讲的内容,本文仅仅简单介绍了如何利用蜂鸣器演奏《海阔天空》,最艰难的工作就在于自己一个一个根据谱子找音符了,网上很多谱子听起来怪怪的,所以我只能根据原曲来一点点调,用这首歌纪念家驹!