基于51单片机的乐曲播放
我们可以利用单片机产生乐曲的音符,并通过蜂鸣器或喇叭播放出音乐。
Ⅰ单片机的发音
⑴ 音调
音调是表示一个音符唱多高的频率,和平时说的“音高”十分类似。
音乐的产生主要是通过单片机的I/O口输出高低不同的脉冲信号来控制蜂鸣器发音。要想产生音频脉冲信号,需要算出某一音频的周期(1/频率),然后将此周期除以2,即为半周期的时间。利用单片机定时器计时这个半个周期时间,每当计时到后就将输出脉冲的I/O口反相,然后重复计时此半周期时间再对I/O口反相,这样就能在此I/O口上得到此频率的脉冲。
通常,利用AT89C5X单片机的内部定时器O,工作在方式1下,改变计数初值TH0和TL0来产生不同的频率(当然也可以利用延时的方法来获得)。
C调部分音符的频率与定时器TH/TL的预置初值(51单片机晶振频率为12MHz,定时器工作于方式1),如下表所示。
|
低音 |
f/Hz |
THx |
TLx |
中音 |
f/Hz |
THx |
TLx |
高音 |
f/Hz |
THx |
TLx |
|
1 |
262 |
248 |
140 |
1 |
523 |
252 |
68 |
1 |
1045 |
254 |
34 |
|
2 |
293 |
249 |
92 |
2 |
596 |
252 |
173 |
2 |
1171 |
254 |
87 |
|
3 |
329 |
250 |
21 |
3 |
659 |
253 |
10 |
3 |
1316 |
254 |
133 |
|
4 |
349 |
250 |
140 |
4 |
697 |
253 |
52 |
4 |
1393 |
254 |
155 |
|
5 |
392 |
251 |
5 |
5 |
783 |
253 |
131 |
5 |
1563 |
254 |
194 |
|
6 |
440 |
251 |
144 |
6 |
879 |
253 |
200 |
6 |
1755 |
254 |
228 |
|
7 |
494 |
252 |
12 |
7 |
987 |
254 |
6 |
7 |
1971 |
255 |
3 |
⑵ 节拍
节拍表示一个音符唱多长的时间,在一张完整乐谱的开头,都有如1=C 4/4、1=G 3/4;……等的标识。这里的4/4、3/4用来表示节拍,而1=C、1=G表示一个乐谱的曲调,简单地说就是跟音调有关系。对于音符的节拍,这里以下图所示为例加以说明。
它表示乐谱中以四分音符为节拍,每一小节有三拍。其中,1、2为一拍,3、4、5为一拍,6为一拍。
从发音的时长角度看,1、2的时长为四分音符的一半,即为八分音符长;3、4的时长为八分音符的一半,即为十六分音符长;5的时长为四分音符的一半,即为八分音符长;6的时长为四分音符长。一般说来,如果乐曲没有特殊说明,则对于一拍的发音时间大约为400~500ms。
在 单片机上可以采用循环延时的方法来实现控制一个音符唱多长时间,从而实现节拍。一般来说,首先需要编写一个精确地基本时长的延时程序,比如说以最短的十六 分音符的时长为基本延时时间。对于一个音符,如果它为十六分音符,则只需调用一次延时程序,如果它为四分音符,则只需调用四次延时程序,如果它为二分音 符,则只需调用八次延时程序,依次类推即可。
节拍也可以利用定时器中断产生。同样是找出整首乐曲中,最短的拍子,例如整首乐曲中,最短是1/4拍,若1/4拍的时间为125ms,则以1/4拍为基准,然后设定每125ms产生一次中断,其定时器初值为125000(晶振12MHz),它超过任何一个定时器模式的定时器值。若采用模式1,而定时器值设为62500,则只要执行2次中断,即可产生1/4拍的时间长度。同样,若要产生1/2拍的长度,则执行4次中断、若要产生3/4拍的长度,则执行6次中断……依此类推,节拍与定时器中断次数如下表所示。
|
拍数 |
中断次数 |
|
拍数 |
中断次数 |
|
拍数 |
中断次数 |
|
1/8 |
1 |
1/2 |
4 |
1 1/4 |
10 |
||
|
1/4 |
2 |
3/4 |
6 |
1 1/2 |
12 |
||
|
3/8 |
3 |
1 |
8 |
2 |
16 |
本例即是利用T0定时器方式1的中断产生音调,利用T1定时器方式1的中断产生节拍。
⑶ 移调
一首的歌曲如果是C调,则音名C唱Do,音名D唱Re,音名E唱Mi,音名F唱Fa,音名G唱So,音名A唱La,音名B唱Ti等。但是,并不是所有的歌曲都是在C调下演奏的,还有D调、E调、F调、G调等。D调是将C调各音符上升一个频率实现的,即C调下的音名D在D调下唱Do,C调下的音名E在D调下唱Re,C大调的音名F在D调下升高半音符F#唱Mi,C调下的音名G在D调下唱Fa,C调下的音名A在D调下唱So,C调下的音名B在D调下唱La,C调下的音名C在D调下升高半音C#符唱Ti。这种改变唱法称为移调。
E调是在D调的基础上进行移调的,而F调是在E调的基础上进行移调的……下表为各调音符与音名的关系。
|
音名 调 |
Do |
Re |
Mi |
Fa |
So |
La |
Ti |
|
C调 |
C |
D |
E |
F |
G |
A |
B |
|
D调 |
D |
E |
F# |
G |
A |
B |
C |
|
E调 |
E |
F# |
G# |
A |
B |
C |
D |
|
F调 |
F |
G |
A |
B |
C |
D |
E |
|
G调 |
G |
A |
B |
C |
D |
E |
F# |
|
A调 |
A |
B |
C# |
D |
E |
F# |
G# |
|
B调 |
B |
C |
D |
E |
F |
G |
A |
⒉ 硬件设计
采用51单片机的乐曲播放硬件电路如下图所示。
在桌面上双击图标,打开ISIS 7 Professional窗口(本人使用的是v7.4 SP3中文版)。单击菜单命令“文件”→“新建设计”,选择DEFAULT模板,保存文件名为“YQ.DSN”。在器件选择按钮中单击“P”按钮,或执行菜单命令“库”→“拾取元件/符号”,添加如下表所示的元件。
|
51单片机AT89C51 一片 |
晶体CRYSTAL 12MHz 一只 |
|
瓷片电容CAP 22pF 二只 |
电解电容CAP-ELEC 10uF 一只 |
|
电阻RES 10K 一只 |
电阻RES 3K 一只 |
|
三极管 PNP (9015) 一只 |
喇叭 SPEAKER 一只 |
若用Proteus软件进行仿真,则上图中的晶振和复位电路以及U1的31脚,都可以不画,它们都是默认的。要注意CPU属性中晶振频率的选择。
在ISIS原理图编辑窗口中放置元件,再单击工具箱中元件终端图标,在对象选择器中单击POWER和GROUND放置电源和地。放置好元件后,布好线。左键双击各元件,设置相应元件参数,完成电路图的设计。
⒊软件设计
本例利用T0定时器方式1的中断产生音调,利用T1定时器方式1的中断产生节拍。例如,歌曲“送别”中一段乐曲的T0初值和T1中断次数如下所示。
本例的流程如下图所示。
该乐曲播放的详细C51程序如下。
/************************
乐曲播放 12MHz
**********************/
#include "reg51.h"
#define uchar unsigned char
uchar rr7,rr6,rr5=0,ct0h,ct0l,baseh=0x10,basel=0xdc;
sbit speaker="P1"^0;
uchar code tone[]={ //送别的音调TH0/TL0
253,131,253,10,253,131,254,34,253,200,254,34,253,200,253,131,253,131,252,68,252,173,253,10,252,173,252,68,252,173,
253,131,253,10,253,131,254,34,254,6,253,200,254,34,253,131,253,131,252,173,253,10,253,52,252,12,252,68,
253,200,254,34,254,34,254,6,253,200,254,6,254,34,253,200,254,6,254,34,253,200,253,200,253,131,253,10,252,68,252,173,
253,131,253,10,253,131,254,34,254,6,253,200,254,34,253,131,253,131,252,173,253,10,253,52,252,12,252,68,0
};
uchar code beat[]={ //送别节拍--T1中断次数
8,4,4,16,8,4,4,16,8,4,4,8,4,4,24,
8,4,4,12,4,8,8,16,8,4,4,12,4,24,
8,8,16,8,4,4,16,4,4,4,4,4,4,4,4,32,
8,4,4,8,4,8,12,16,8,4,4,12,4,24,0
};
/******载入音调*****************/
load_t()
{
uchar k;
loop:k=tone[rr7];
if(k==0)
{
rr6=0;rr7=0;
goto loop;
}
else
{
ct0h=TH0=k;rr7++;
k=tone[rr7];
ct0l=TL0=k;
rr7++;TR0=1;
}
}
/****************载入节拍*******/
load_b()
{
uchar k;
k=rr5=beat[rr6];
rr6++;TH1=baseh;TL1=basel;TR1=1;
}
main()
{
//uchar i;
TMOD=0X11;IE=0X8A;F0=0;
while(1)
{
load_t(); //载入音阶
load_b(); //载入节拍
while(F0==0)
;
F0=0;
}
}
/*******音调中断子函数************/
void int_t0() interrupt 1
{
TR0=0;speaker=!speaker;
TH0=ct0h;TL0=ct0l;TR0=1;
}
/*******节拍中断子函数************/
void int_t1() interrupt 3
{
TR1=0;rr5--;
if(rr5==0)
{
TR0=0;F0=1;
}
else
{
TH1=baseh;TL1=basel;TR1=1;
}
}
打开Keil的μVision3程序(本人使用的是Keil8.05中文版),执行菜单命令“工程”→“新建工程”创建“YQ”项目,并选择单片机型号为AT89C51(也可以使用原有项目)。执行菜单命令“文件”→“新建”创建文件,输入C语言源程序,保存为“YQ.C”。在Project Workspace窗口中右击源代码组1,选择“添加文件到组‘源代码组 l’”将源程序“YQ.C”添加到项目中。
在Keil的μVision3中执行执行菜单命令“工程”→“创建目标”(或点击“创建目标”快捷按钮),编译源程序。如果编译成功,则在“Output Window”的“创建”窗口中显示没有错误,并创建了“YQ.HEX”文件。
⒋ 仿真与调试
关于Proteus与Keil的联合仿真调试,可参见我以前所写的博文或其它参考资料。需注意Proteus的ISIS与Keil的μVision3中“YQ.HEX”的路径要一致(或Proteus的ISIS中CPU属性Program File为空)。
启动Proteus的ISIS,并将其放在屏幕的右上角(可将原理图放大到合适大小);再启动Keil的μVision3,并将其放在屏幕的左下角。
在Keil的μVision3中执行菜单命令“调试”→“启动/停止调试”,或直接单击图标,进入Keil的μVision3调试环境。同时,在Proteus ISIS的窗口中可看出Proteus也进入了程序调试状态。
在Keil的μVision3代码编辑窗口中设置相应断点,断点的设置方法:在需要设置断点语句前双击鼠标左键,可设置断点;再次双击,可取消该断点。
在Keil的μVision3中按F5键(或点击“运行”快捷按钮)运行程序,就会循环播放“送别”的乐曲。或可以点击单步、运行到光标处、全速运行等快捷按钮,以及同时观察工程窗口寄存器页面、存储器窗口等,来进行仿真调试。
本人邮箱:txxyc104@163.com,欢迎来信讨论.