《51单片机应用开发从入门到精通》——2.11 歌曲演奏实例

本节书摘来异步社区《51单片机应用开发从入门到精通》一书中的第2章,第2.11节,作者:张华杰 ,更多章节内容可以访问云栖社区“异步社区”公众号查看

2.11 歌曲演奏实例

51单片机应用开发从入门到精通
本实例是利用单片机演奏一首生日快乐歌。

2.11.1 编程演奏器原理

1.演奏器原理
(1)通过控制单片机定时器的定时时间产生不同频率的音频脉冲,经放大后驱动蜂鸣器发出不同音节的声音。

(2)用软件延时来控制发音时间的长短,控制节拍,表2-14是各调1/4节拍的时间表。

《51单片机应用开发从入门到精通》——2.11 歌曲演奏实例_第1张图片

(3)把乐谱中的音符和相应的节拍变换为定时常数和延时常数,作为数据表格存放在存储器中。由程序查表得到定时常数和延时常数,分别用来控制定时器产生的脉冲频率和发出该音频脉冲的持续时间。

(4)表2-15为单片机晶振频率为12MHz时,乐曲中的音符、频率及定时常数之间的对应关系表。

《51单片机应用开发从入门到精通》——2.11 歌曲演奏实例_第2张图片 《51单片机应用开发从入门到精通》——2.11 歌曲演奏实例_第3张图片

2.歌谱

《51单片机应用开发从入门到精通》——2.11 歌曲演奏实例_第4张图片

3.建立步骤
(1)先把乐谱的音符找出,然后根据表2-15给出的定时值按乐谱的音符顺序建立编码表TABLE。

定时值为十六进制4位数,拆开分为两组,如5对应的定时值为FD80H,拆分为FDH和80H两组。前组装入定时器的高位TH0,后组装入定时器的低位TL0。程序中将进行两次查表来完成一个音符对应的定时初值的装入。

(2)在程序中使用定时器T0方式1来产生歌谱中各音符对应频率的音频脉冲,由 P3.4输出,再经三极管将信号放大后驱动蜂鸣器发出不同音节的声音。

(3)程序中节拍的控制是通过调用延时子程序DELAY的次数来实现的,1拍为 748 ms,即需要调用4次DELAY;3/4拍需要调用3次DELAY;2/4拍需要调用2次DELAY。

(4)节拍的控制码在表TABLE中位于音符码的后面。如第1行“DB 0FDH,80H, 03H,…”中,0FDH和80H是音符5的音符码,其后边的03H是节拍码,即3/4拍的时间。

(5)当一个音符的发音时间到时,再查下一个音符的定时常数和延时常数。依此进行下去,就可演奏出悦耳动听的乐曲。

2.11.2 程序设计

1.流程图
程序设计流程如图2-18所示。

《51单片机应用开发从入门到精通》——2.11 歌曲演奏实例_第5张图片

2.程序
汇编语言编写的歌曲演奏源程序FS03.ASM代码如下:

01:          ORG           00H                ;主程序起始地址
02:          JMP           START              ;跳转至主程序
03:          ORG           0BH                ;定时器T0中断入口
04:          JMP           EXT0               ;跳转至T0中断子程序     
05:  START:  MOV           TMOD,#00000001B    ;设置T0方式1
06:          MOV           IE,#10000010B      ;允许T0中断      
07:          MOV           DPTR, #TABLE       ;存表首地址            
08:  LOOP:   CLR           A                  ;清0
09:          MOVC          A,@A + DPTR        ;查表                
10:          MOV           R1, A              ;定时器高8位存入R1     
11:          INC           DPTR               ;指针加1
12:          CLR           A                  ;清0
13:          MOVC          A,@A + DPTR        ;查表             
14:          MOV           R0, A              ;定时器低8位存入R0     
15:          ORL           A, R1              ;进行或运算
16:          JZ            NEXT0              ;全0为休止符
17:          MOV           A, R0        
18:          ANL           A, R1              ;进行与运算
19:          CJNE          A, #0FFH, NEXT     ;全1表示乐曲结束
20:           JMP           START              ;从头开始循环演奏      
21:  NEXT:    MOV           TH0, R1            ;装入高位定时值
22:           MOV           TL0, R0            ;装入低位定时值       
23:           SETB          TR0                ;启动定时器T0        
24:           JMP           NEXT1              ;跳转到NEXT1处
25:   NEXT0:  CLR           TR0                ;关闭定时器,停止发音    
26:   NEXT1:  CLR           A                  ;清0
27:          INC           DPTR               ;指针加1
28:          MOVC          A, @A + DPTR       ;查延时常数
29:          MOV           R2, A              ;延时常数存入R2       
30:  LOOP1:  ACALL         DELAY              ;调用延时子程序
31:          DJNZ          R2, LOOP1          ;控制延时次数          
32:          INC           DPTR               ;指针加1
33:          JMP           LOOP               ;跳转到LOOP处          
34:  EXT0:   MOV           TH0, R1            ;重装定时值
35:          MOV           TL0, R0     
36:          CPL           P3.4               ;反相输出
37:          RETI                             ;中断子程序返回
38:  DELAY:  MOV           R7, #02            ;延时187ms子程序
39:  D2:     MOV           R6, #187     
40:  D3:     MOV           R5, #248    
41:          DJNZ          R5,$       
42:           DJNZ          R6, D3      
43:           DJNZ          R7, D2      
44:           RET                               ;延时子程序返回                       
45:   TABLE:  DB  0FDH,80H,03H, 0FDH,80H,01H     ;编码表
46:           DB  0FDH,0C6H,04H, 0FDH,80H,04H
47:           DB  0FEH,2AH,04H, 0FEH,02H,04H
48:           DB  00H,00H,04H                 
49:           DB  0FDH,80H,03H, 0FDH,80H,01H
50:           DB  0FDH,0C6H,04H, 0FDH,80H,04H
51:           DB  0FEH,5CH,04H, 0FEH,2AH,04H  
52:           DB  00H,00H,04H                 
53:           DB  0FDH,80H,03H, 0FDH,80H,01H
54:           DB  0FEH,0C0H,04H, 0FEH,84H,04H
55:           DB  0FEH,2AH,04H, 0FEH,02H,04H
56:           DB  0FDH,0C6H,04H
57:           DB  0FEH,98H,03H, 0FEH,98H,01H
58:          DB  0FEH,84H,04H, 0FEH,2AH,04H
59:          DB  0FEH,5CH,04H, 0FEH,2AH,04H
60:          DB  00H,00H,04H                 
61:          DB  0FFH,0FFH                   ;结束码        
62:          END                             ;程序结束

2.11.3 代码详解

1.标号说明
START:程序开始的进入点。

LOOP:处理下一个音符的进入点。

NEXT:装入定时初值的进入点。

NEXT0:关闭定时器、停止发音的进入点。

NEXT1:查找延时常数的进入点。

LOOP1:处理节拍时间的进入点。

EXT0:中断子程序的进入点。

DELAY:延时180ms子程序的进入点。

2.寄存器使用分配情况
R0、R1、R2、R5、R6、R7:普通寄存器。其中R0存放低位定时器初值;R1存放高位定时器初值;R2存放延时常数值;R5、R6和R7在延时子程序中作计数器用。

A和DPTR:特殊功能寄存器,其中A又称累加器,DPTR为数据指针。在程序中把表TABLE的首地址存入DPTR作基础地址,A作为变址寄存器。将基址寄存器和变址寄存器的内容相加(@A + DPTR)形成操作数的地址。

TMOD:定时器工作模式控制寄存器。

TL0和TH0:定时器0的计数器,TL0为低8位,TH0为高8位。

TR0:定时器0控制寄存器TCON的一个控制位。

IE:中断允许控制寄存器。

P3.4:对内是P3寄存器的一个位,对外是输入/输出端口的一个引脚,作音频信号输出端口。

3.程序分析解释
01~04:设定入口地址。其中定时器中断是从标号EXT0处进入中断子程序。

05~07:设置定时器T0为方式1,并允许T0中断。其中07行语句将表TABLE的首地址存入数据指针寄存器DPTR中。

08~10:第一次查表,取出表中的第一个码,即乐谱中的第一个音符5的定时常数(定时初值)FD80H中的高位部分FDH,并存入寄存器R1中。

11~14:第二次查表,取出表中的第二个码,即乐谱中的第一个音符5的定时常数FD80H中的低位部分(即80H),并存入寄存器R0中。

15~20:判断、转移语句。对二次查取到的码,检查是否有休止符码,该判断是通过第15行语句“ORL A,R1”对A和R1寄存器的内容进行或运算实现的。

如果累加器A中的值是00H,R1寄存器的值也是00H,进行或运算后再存入累加器A中,此时累加器A中的值是全0,说明取到的是休止符码。通过第16行语句“JZ NEXT0”,跳转至标号NEXT0处,关闭定时器,停止发音,完成乐谱中休止符的作用。

取码时是否取到了结束码是通过第18行语句指令ANL对A和R1进行与运算来判断的。如果累加器A中的值是FFH,R1寄存器的值也是FFH,进行与运算后再存入累加器A中,此时累加器A中的值是全1,说明取到的是结束码。

第19行语句“CJNE A,#0FFH,NEXT”,如果累加器A中的值与#0FFH不相等,则跳转移到NEXT处,开始向定时器装入定时常数;如果A与#0FFH相等,说明取到的码是结束码,程序向下执行,即从头开始循环演奏。

21~23:开始向定时器装入定时常数并启动定时器工作。

24~25:处理拍节。

26~29:查取延时码并存入R2。

30~31:决定拍节时间。如果取来的码是02H,程序调用两次DELAY延时,即为2/4拍节时间;如果取来的码是04H,程序调用4次DRLAY延时,即为1拍节时间。

32~33:每次查表取码后,数据指针都要加1,以便指向下一个待查的码。第33行语句将程序运行跳转到标号LOOP处,处理下一个音符。

34~37:中断子程序。当定时器计时满后将产生中断,由标号EXT0处进入中断子程序。在中断子程序中重装定时值,并通过第36行语句在P3.4端口输出音频信号。

38~44:延时187ms子程序。

45~61:编码表TABLE。在表中每个音节由3个码组成,前两个为音符码,后一个为节拍码。如表中第1行“0FDH,80H,03H”,其中0FDH和80H即为FD80H,是音符5的发音编码;03H是节拍码,3/4节拍。

表中的最后一行“0FFH,0FFH”是结束码,表示乐曲演奏结束。

4.边用边学指令
本程序用到新的指令有ORL和JZ。

 ORL:逻辑运算及位移类指令中的按位或操作指令。该指令将累加器A中的内容与源操作数所指出的内容按位进行逻辑或运算,结果存入A中。
 JZ:控制转移类指令,功能是当累加器A为0时跳转。

2.11.4 模拟仿真

1.模拟仿真前注意事项
在23与24行语句之间,加一条“SETB TF0”语句,该语句使定时器T0的溢出标志位TF0为1,模拟产生中断,使程序能进入中断子程序中运行。

在39~43行语句的前边加分号,使延时子程序的延时时间缩短为2ms,缩短模拟仿真时间。

2.模拟仿真中注意事项
观察程序主要运行路线:第一次查表→第二次查表→中断子程序及返回→第三次查 表→进入延时子程序及返回→处理下一个音符(跳转到LOOP处)。

观察累加器A中值的变化。第一次查表执行第09行语句“MOVC A,@A + DPTR”后,A值为FDH。第二次查表,执行第13行语句“MOVC A,@A + DPTR”后,A值为80H。两次查表取回音符5的对应码FD80H。第三次查表,执行第28行语句“MOVC A,@A + DPTR”后,A值为03H,取回的是音符5的对应拍节码。

2.11.5 实例测试

将写入歌曲演奏程序的单片机插入实验板插座内,并检查实验板上蜂鸣器接口是否与程序中声音输出端口一致,当检查无误后接通电源,就会听到生日快乐歌。

2.11.6 经验总结

在用单片机作可编程乐曲演奏器的程序里,一般用定时器T0方式1来控制音符的频率,调用延时子程序DELAY来控制节拍。

程序采用查表的方法,将乐谱转换成控制码并制成表TABLE,然后进行三次查表。第一、第二次查表完成对音符的控制,第三次查表读取节拍码,完成对节拍的控制。

在完成第一、第二次查取音符的控制码后,程序中还设有判断语句,如果判断出取来的是休止符码,就将定时器关闭一段时间;如果判断出取来的是结束码,程序就从头开始循环。

你可能感兴趣的:(《51单片机应用开发从入门到精通》——2.11 歌曲演奏实例)