(周振安)利用单片机(或单板机)奏乐大概是无线电爱好者感兴趣的问题之一。本文从单片机的基本发音实验出发,谈谈音乐程序的设计原理,并给出具体实例,以供参考。
1.单片机的基本发音实验
我们知道,声音的频谱范围约在几十~几千赫兹,若能利用程序来控制单片机某个口线的“高”电平或“低”电平,则在该口线上就能产生一定频率的矩形波,接上喇叭就能发出一定频率的声音。若再利用延时程序控制“高”“低”电平的持续时间,就能改变输出频率,从而改变音调。

例如,要产生200Hz的音频信号,按图1接入喇叭(若属临时实验,也可将喇叭直接接在P1口线上),实验程序为:
MUSIC2:CPL P1.4
MOV R3,#7DH
LCALL DEL
LJMP M
DEL:NOP
DEL3:MOV R4,#02H
DEL4:NOP
DJNZ R4,DEL4
NOP
DJNZ R3,DEL3
RET
其中子程序DEL为延时子程序,当R3为1时,延时时间约为20μs。R3中存放延时常数,对200Hz音频,其周期为1/200秒,即5ms。这样,当P1.4的高电平或低电平的持续时间为2.5ms,即R3的时间常数取2500/20=125(7DH)时,就能发出200Hz的音调。将上述程序键入学习机,并不断修改R3的常数可以感觉音调的变化
2.音乐程序的设计
乐曲中,每一音符对应着确定的频率。表1给出了C调时各音符频率及其相应的时间常数。

读者可以根据表1中所提供的常数,将其16进制代码送入R3,反复练习体会。根据表1可以奏出音符,仅这还不够,要准确奏出一首曲子,必须准确地控制乐曲节奏,即一音符的持续时间。
音符的节拍我们可以用定时器T0来控制。当T0送入不同的初值,就可产生不同的定时时间。例如某首歌曲的节奏为每分钟94拍,即每一拍为0.64秒。其它节拍与时间的对应关系见表2。

但是,由于T0的最大定时时间只能为131ms,因此不可能直接用改变T0的时间初值来实现不同节拍。我们可以用T0来产生10ms的时间基准,然后设置一个中断计数器,通过判别中断计数器的值来控制节拍时间的长短。表2中也给出了各种节拍所对应的时间常数。例如对1/4拍音符,定时时间为0.16s,相应的时间常数为16(即10H);对3拍音符,定时时间为1.92s,相应时间常数为192(即C0H)。
我们将每一音符的时间常数和其相应的节拍时间常数作为一组,按顺序将乐曲中的所有常数排列成一个表,然后由查表程序依次取出,产生音符并控制节奏,就可实现演奏效果。
此外,结束符和休止符可以分别用代码00H和FFH来表示,若查表结果为00H,则表示曲子终了;若查表结果为FFH,则产生相应的停顿效果。
为了产生手弹的节奏感,在某些音符(例如两个相同音符)间插入一个时间单位的频率略有不同的音符。
程序框图如图2所示。

下面给出程序清单,可直接在TD-Ⅲ型学习机上演奏,对其它不同型号的学习机,只需相应改变一下地址即可。本程序演奏的是民歌“八月桂花遍地开”,C调,节奏为94拍/分。读者也可自行找出一首歌,按表1和表2给定的常数,将乐曲翻译成码表输入机器,而程序不变。本实验方法简便,即使不懂音乐的人,将一首陌生的曲子翻译成代码也是易事,和着机器的演奏,学唱一首歌曲,其趣味无穷。
m侭m
程序清单:
l:*ORG 800BH
2:INC 20H;中断服务,中断计数器加1
3:MOV TH0,#0EDH;T0送初值
4:MOV TL0,#040H
5:RETI
6:ORG A000H
7:MOV TH0,#0EDH;T0初始化
8:MOV TL0,#040H
9:MOV TMOD,#01H
10:MOV IE,#82H
11:MUSICO:NOP
12:MOV DPTR,#DATA;表头地址→DPTR
13:MOV 20H,#00H;中断计数器清0
14:MOVB,#00H;表序号清0
15:MUSICI:NOP
16:MOVA,B
17:MOVCA,@A+DPTR;查表取代码→A
18:JZ END;是00H,则结束
19:CJNE A,#0FFH,MUSIC5;是FFH则停顿
20:LJMP MUSIC3
21:MUSIC5;NOP;是音符代码→R6
22:MOV R6,A
23:INC B
24:MOV A,B
25:MOVC A,@A+DPTR;取节拍常数代码→R7
26:MOV R7,A
27:SETB TR0;启动T0计数
28:MUSIC2:NOP
29:CPL P1.4;输出音频
30:MOV A,R6
31:MOV R3,A
32:LCALL DEL
33:MOV A,R7
34:CJNE A,20H,MUSIC2;中断计数器(20H)=R7否?不等,继续循环
35:MOV 20H,#00H;等于,则取下一代码
36:INC B
37:LJMP MUSIC1
38:MUSIC3:NOP
39:CLR TR0;休止100ms
40:MOV RZ,#0DH
41:MUSIC4:NOP
42:MOV R3,#0FFH
43:LCALL DEL
44:DJNZ R2,MUSIC4
45:INC B;取下一代码
46:LJMP MUSIC1
47:END:NOP
48: MOV R2,#64H;歌曲结束,延时1S后继续奏乐
49:MUSIC6:MOV R3,#00H
50:LCALL DEL
51:DJNZ R2,MUSIC6
52:LJMP MUSICO
53:DEL:NOP
54:DEL3;MOV R4,#02H;延时子程序
55:DEL4:NOP
56:DJNZ R4,DEL4
57:NOP
58:DJNZ R3,DEL3
59:RET
60:ORG A100H;歌曲代码表首址
61:DATA;DB 18H 30H 1CH 10H
62:DB 20H 40H 1CH 10H
63:DB 18H 10H 20H 10H
64:DB 1CH 10H 18H 40H
65:DB 1CH 20H 20H 20H
66:DB 1CH 20H 18H 20H
67:DB 20H 80H 0FFH 20H
68:DB 30H 1CH 10H 18H
69:DB 20H 15H 20H 1CH
70 20H 20H 20H 26H
71:DB 40H 20H 20H 2BH
72:DB 20H 26H 20H 20H
73:DB 20H 30H 80H 0FFH
74:DB 20H 20H 1CH 10H
75:DB 18H 10H 20H 20H
76:DB 26H 20H 2BH 20H
77:DB 30H 20H 2BH 40H
78:DB 20H 20H 1CH 10H
79:DB 18H 10H 20H 20H
80 26H 20H 2BH 20H
8l:DB 30H 20H 2BH 40H
82:DB 20H 30H 1CH 10H
83:DB 18H 20H 15H 20H
84:DB 1CH 20H 20H 20H
85:DB 26H 40H 20H 20H
86:DB 2BH 20H 26H 20H
87:DB 20H 20H 30H 80H
88:DB 20H 10H 1CH 10H
89:DB 20H 10H 1CH 10H
90 20H 20H 26H 20H
91:DB 2BH 20H 30H 20H
92:DB 2BH 40H 20H 15H
93:DB 1FH 05H 20H 10H
94:DB 1CH 10H 20H 20H
95:DB 26H 20H 2BH 20H
96:DB 30H 20H 2BH 40H
97:DB 20H 30H 1CH 10H
98:DB 18H 20H 15H 20H
99:DB 1CH 20H 20H 20H
100 26H 40H 20H 20H
101:DB 2BH 20H 26H 20H
102:DB 20H 20H 30H 30H
103:DB 20H 30H 1CH 10H
104:DB 18H 40H 1CH 20H
105:DB 20H 20H 26H 40H
106:DB 13H 60H 18H 20H
107:DB 15H 40H 13H 40H
108:DB 18H 80H 00H