博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
《51单片机应用开发从入门到精通》——2.11 歌曲演奏实例
阅读量:7236 次
发布时间:2019-06-29

本文共 6534 字,大约阅读时间需要 21 分钟。

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

2.11 歌曲演奏实例

本实例是利用单片机演奏一首生日快乐歌。

2.11.1 编程演奏器原理

1.演奏器原理

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

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

screenshot

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

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

screenshot

screenshot
screenshot

2.歌谱

screenshot

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所示。

screenshot

2.程序

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

01:          ORG           00H                ;主程序起始地址02:          JMP           START              ;跳转至主程序03:          ORG           0BH                ;定时器T0中断入口04:          JMP           EXT0               ;跳转至T0中断子程序     05:  START:  MOV           TMOD,#00000001B    ;设置T0方式106:          MOV           IE,#10000010B      ;允许T0中断      07:          MOV           DPTR, #TABLE       ;存表首地址            08:  LOOP:   CLR           A                  ;清009:          MOVC          A,@A + DPTR        ;查表                10:          MOV           R1, A              ;定时器高8位存入R1     11:          INC           DPTR               ;指针加112:          CLR           A                  ;清013:          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                  ;清027:          INC           DPTR               ;指针加128:          MOVC          A, @A + DPTR       ;查延时常数29:          MOV           R2, A              ;延时常数存入R2       30:  LOOP1:  ACALL         DELAY              ;调用延时子程序31:          DJNZ          R2, LOOP1          ;控制延时次数          32:          INC           DPTR               ;指针加133:          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,04H47:           DB  0FEH,2AH,04H, 0FEH,02H,04H48:           DB  00H,00H,04H                 49:           DB  0FDH,80H,03H, 0FDH,80H,01H50:           DB  0FDH,0C6H,04H, 0FDH,80H,04H51:           DB  0FEH,5CH,04H, 0FEH,2AH,04H  52:           DB  00H,00H,04H                 53:           DB  0FDH,80H,03H, 0FDH,80H,01H54:           DB  0FEH,0C0H,04H, 0FEH,84H,04H55:           DB  0FEH,2AH,04H, 0FEH,02H,04H56:           DB  0FDH,0C6H,04H57:           DB  0FEH,98H,03H, 0FEH,98H,01H58:          DB  0FEH,84H,04H, 0FEH,2AH,04H59:          DB  0FEH,5CH,04H, 0FEH,2AH,04H60:          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,然后进行三次查表。第一、第二次查表完成对音符的控制,第三次查表读取节拍码,完成对节拍的控制。

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

转载地址:http://cvmfm.baihongyu.com/

你可能感兴趣的文章
系统启动之后将/var/log挂载到独立的分区
查看>>
资源池的两个小教训
查看>>
python pip 修改源
查看>>
中国联通与用友签署合作框架协议 多领域展开全面合作
查看>>
RDO CentoS 7.1部署 kilo 问题及处理办法
查看>>
hashlib模块
查看>>
部署Swarm Mode集群
查看>>
Swarm Mode服务管理
查看>>
centos 5.7_64位下FastDFS_client的配置和PHP测试
查看>>
Submodule path checked out in submodule path 错误解决方法
查看>>
SQL调优日记--并行等待的原理和问题排查
查看>>
网站Gzip压缩
查看>>
FISCO-BCOS源码分析(三)
查看>>
df命令、du命令、磁盘分区
查看>>
linux中的远程连接基本操作
查看>>
敏捷开发方法Scrum经验总结
查看>>
对象的创建与调用
查看>>
Centos搭建iSCSI
查看>>
Nginx的location详解
查看>>
环境变量PATH 及cp、mv、文档查看cat、more、less、head、tail等命令
查看>>