本节书摘来异步社区《51单片机应用开发从入门到精通》一书中的第2章,第2.12节,作者:张华杰 ,更多章节内容可以访问云栖社区“异步社区”公众号查看
2.12 电子琴
51单片机应用开发从入门到精通
本实例是采用44矩阵式键盘设计出16个音符,随意弹奏。
2.12.1 硬件设计
电子琴电路设计如图2-19所示。
单片机P3为输入端口,接有44矩阵式键盘,键盘上标出16个音符。键盘的4条行线的一端与单片机P3端口的P3.0、P3.1、P3.2和P3.3口相接,另一端通过上拉电阻接到 + 5V上;4条列线的一端与P3端口的P3.4、P3.5、P3.6和P3.7口相接。单片机的P2.4端口为输出端,通过限流电阻R1与三极管基极相接,三极管的集电极接有蜂鸣器。
2.12.2 程序设计
电子琴程序设计的思路是:设计16个音符,与44矩阵式键盘16个按键一一对应。音符通过定时器T0产生,然后通过键盘扫描法,将按下的键转换成相对应的音符。
1.流程图
程序设计流程如图2-20所示。
2.程序
汇编语言编写的电子琴源程序JP04.ASM代码如下:
01: ORG 00H ;主程序起始地址
02: JMP START ;跳转至主程序
03: ORG 0BH ;T0中断起始地址
04: JMP TIM0 ;跳转至T0中断子程序
05:START: MOV TMOD, #00000001B ;设T0在MODE1
06: MOV IE, #10000010B ;开通中断
07: SETB TR0 ;启动定时器T0
08:L1: ACALL KEY ;调用KEY,判断是否有键按下
09: CLR EA ;中断屏蔽
10: JB F0, L1 ;没按下则F0 = 1,否则F0 = 0
11: MOV A,22H ;将取码指针暂存地址载入A
12: RL A ;左移1位
13: MOV DPTR, #TABLE ;存表
14: MOVC A, @A + DPTR ;到至TABLE取码,取T的值
15: MOV TH0, A ;取到的高位字节存入TH0
16: MOV 21H, A ;取到的高位字节存入21H
17: MOV A, 22H ;载入取码指针值
18: RL A ;向左移1位
19: INC A ;加1
20: MOVC A, @A + DPTR ;到表TABLE取低位字节计数值
21: MOV TL0, A ;取到的低位字节存入TL0
22: MOV 20H, A ;取低位字节存入20H
23:L2: ACALL KEY ;调用KEY,判断有无键按下
24: SETB EA ;开通中断
25: JB F0, L1 ;是否有键按下
26: JMP L2 ;有则跳转至L2
27:KEY: SETB F0 ;设F0 = 1
28: MOV R3, #0F7H ;扫描初值(P3.3 = 0)
29: MOV R1, #00H ;取码指针初值
30:L3: MOV A, R3 ;载入扫描指针
31: MOV P3, A ;输出至P3,开始扫描
32: MOV A, P3 ;读入P3
33: SETB C ;令C = 1
34: MOV R5, #04H ;检测P3.7~P3.4
35: L4: RLC A ;左移1位
36: JNC KEYIN ;检测行C = 0,表示被按下
37: INC R1 ;无键按下则取码指针加1
38: DJNZ R5, L4 ;4列检测完毕?
39: MOV A, R3 ;载入扫描指针
40: SETB C ;令C = 1
41: RRC A ;扫描下一行
42: MOV R3, A ;存回R3扫描指针寄存器
43: JC L3 ;C = 0,表示行扫描完毕
44: RET ;子程序返回
45: KEYIN: MOV 22H, R1 ;取码指针存入地址22H
46: CLR F0 ;令F0 = 0
47: RET ;子程序返回
48: TIM0: PUSH ACC ;将A的值暂存于堆栈
49: PUSH PSW ;将PSW的值暂存于堆栈
50: MOV TL0, 20H ;重设计数值
51: MOV TH0, 21H
52: CPL P2.4 ;将P2.4位反相
53: POP PSW ;从堆栈取回PSW的值
54: POP ACC ;从堆栈取回A的值
55: RETI ;返回到主程序
56: TABLE: DW 64021,64103,64260,64400 ;编码表
57: DW 64524,64580,64684,64777
58: DW 64820,64898,64968,65030
59: DW 65058,65110,65157,65178
60: END ;程序结束
2.12.3 代码详解
1.程序分析解释
01~04:设置程序开始地址和中断T0起始地址。
05~07:设置T0工作在模式1下;开通中断并启动T0工作。
08:调用扫描子程序KEY开始扫描,判断是否有键按下。
09~22:存表(第13行语句),取码(第14和第20行语句),向定时器装初始值(第15和第21行语句)。与此同时,还将取到的高位字节和低位字节分别存入地址21H(第16行语句)和20H(第22行语句)处,以便在中断子程序中对定时器重装初始值用。地址22H用于暂存取码指针。整个过程由于已将中断关闭,所以定时器不能产生中断信号,输出端P2.4没有输出。
第10行语句是判断F0是0还是1。F0是状态字寄存器PSW的一个自由使用的标志位。程序规定F0 = 1表示没有键按下,F0 = 0表示有键按下。
23~26:再调用扫描程序,确认是否有键按下。当有键按下时,由于此时已经开通了中断,定时器会产生中断信号,使程序进入到中断服务子程序运行,于是输出端P2.4便有输出。
27~44:扫描子程序。
45~47:有键按下后,将取码指针存入地址22H,并令F0 = 0,用来标志有键按下。
48~55:中断服务子程序。其中第48~49行为进栈保护现场,第53~54行为出栈恢复现场。50~51行重装定时器初始值。第52行语句是输出语句。第55行语句是中断服务子程序返回主程序的指令。
56~59:编码表。表中的16个数是定时器发出16个音的定时器初始值。
60:程序结束。
2.至本章用过的指令归类
数据传送类指令:MOV、MOVC、PUSH、POP。
算术运算类指令:INC、DIV。
逻辑运算及位移类指令:RL、RR、RLC、RRC、CLR、ANL、ORL、XRL。
控制转移类指令:JMP、DJNZ、ACALL、RET、CJNE、RETI、JZ。
位操作类指令:CPL、CLR、SETB、JB、JNB、JBC、JNC、JC。
伪指令:END、DB、ORG。
2.12.4 模拟仿真
1.模拟仿真前注意事项
在第30行语句与31行语句之间,增加一条模拟按键按下的语句。
MOV A,#11100111B```
在第24行语句与25行语句之间,增加一条模拟中断溢出的语句,使程序能进入到中断服务程序里运行。
SETB TF0`
2.模拟仿真中注意事项
(1)注意观察程序运行路线。当第一次扫描检测到有键按下时,由于第09行语句将中断屏蔽,所以程序运行不能进入到中断服务子程序,输出端P2.4没有输出。
在第二次扫描再次确认有键按下时,由于第24行语句将中断开启,所以定时器T0将产生中断信号,通过程序标号TIM0进入中断服务子程序,使P2.4端口产生输出。
(2)去掉模拟按键和模拟中断溢出语句,观察程序运行情况,由于没有按键按下,所以P2.4没有输出。
2.12.5 实例测试
将写入程序的单片机插入实验板插座内,并检查实验板上的蜂鸣器接口是否与程序中声音输出端口一致,当检查无误后接通电源,即可按图2-19所示进行音乐弹奏。
2.12.6 经验总结
电子琴程序从功能设计上可分为两大部分。
利用单片机内定时器T0产生16个音符,16个音符代码在表中的排列顺序与16个按键上所标出的音符是对应的。
通过对键盘不断进行扫描,当确定有按键操作后,将所按下的键转换成相对应的音符,并通过输出端口使蜂鸣器发出相对应的声音,声音的长短由按键的时间决定。