《51单片机应用开发从入门到精通》——2.12 电子琴

简介:

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

2.12 电子琴

本实例是采用44矩阵式键盘设计出16个音符,随意弹奏。

2.12.1 硬件设计

电子琴电路设计如图2-19所示。


screenshot

单片机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所示。


screenshot

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个按键上所标出的音符是对应的。
  •  通过对键盘不断进行扫描,当确定有按键操作后,将所按下的键转换成相对应的音符,并通过输出端口使蜂鸣器发出相对应的声音,声音的长短由按键的时间决定。
相关文章
|
SQL 算法 Java
【嵌入式入门篇】嵌入式0基础沉浸式刷题篇1
硬件工程师近年来也开始慢慢吃香,校招进大厂年薪总包不下30-40w的人数一大把!而且大厂的硬件人数并没有饱和!
【嵌入式入门篇】嵌入式0基础沉浸式刷题篇1
|
编译器 芯片 异构计算
第四章 FPGA 开发平台介绍(下)
第四章 FPGA 开发平台介绍
295 0
第四章 FPGA 开发平台介绍(下)
|
存储 开发工具 芯片
第四章 FPGA 开发平台介绍(上)
第四章 FPGA 开发平台介绍
447 0
第四章 FPGA 开发平台介绍(上)
|
存储 数据采集 定位技术
《51单片机应用开发从入门到精通》——导读
随着微处理器技术和SoC技术的发展,从较早的4位机到8位机,一直发展到现在的32位机,单片机的应用逐渐渗透到生产、生活的各个领域。本书主要针对MCS-51体系单片机,从硬件、软件两个方面的基础知识开始进行介绍。
1720 0