| 
 22. 电子琴  1. 实验任务  (1. 由4X4组成16个按钮矩阵,设计成16个音。  (2. 可随意弹奏想要表达的音乐。  2. 电路原理图  file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wpsCCC9.tmp.png 图4.22.1  3. 系统板硬件连线  (1. 把“单片机系统”区域中的P1.0端口用导线连接到“音频放大模块”区域中的SPK IN端口上;  (2. 把“单片机系统“区域中的P3.0-P3.7端口用8芯排线连接到“4X4行列式键盘”区域中的C1-C4 R1-R4端口上;  4. 相关程序内容  (1. 4X4行列式键盘识别;  (2. 音乐产生的方法;  一首音乐是许多不同的音阶组成的,而每个音阶对应着不同的频率,这样我们就可以利用不同的频率的组合,即可构成我们所想要的音乐了,当然对于单片机来产生不同的频率非常方便,我们可以利用单片机的定时/计数器T0来产生这样方波频率信号,因此,我们只要把一首歌曲的音阶对应频率关系弄正确即可。现在以单片机12MHZ晶振为例,例出高中低音符与单片机计数T0相关的计数值如下表所示  音符  | 频率(HZ)  | 简谱码(T值)  | [size=12.0000pt]   | 音符  | 频率(HZ)  | 简谱码(T值)  |  低1 DO  | 262  | 63628  | # 4 FA#  | 740  | 64860  |  #1 DO#  | 277  | 63731  | 中 5 SO  | 784  | 64898  |  低2 RE  | 294  | 63835  | # 5 SO#  | 831  | 64934  |  #2 RE#  | 311  | 63928  | 中 6 LA  | 880  | 64968  |  低 3 M  | 330  | 64021  | # 6  | 932  | 64994  |  低 4 FA  | 349  | 64103  | 中 7 SI  | 988  | 65030  |  # 4 FA#  | 370  | 64185  | 高 1 DO  | 1046  | 65058  |  低 5 SO  | 392  | 64260  | # 1 DO#  | 1109  | 65085  |  # 5 SO#  | 415  | 64331  | 高 2 RE  | 1175  | 65110  |  低 6 LA  | 440  | 64400  | [size=12.0000pt]   | # 2 RE#  | 1245  | 65134  |  # 6  | 466  | 64463  | 高 3 M  | 1318  | 65157  |  低 7 SI  | 494  | 64524  | 高 4 FA  | 1397  | 65178  |  中 1 DO  | 523  | 64580  | # 4 FA#  | 1480  | 65198  |  # 1 DO#  | 554  | 64633  | 高 5 SO  | 1568  | 65217  |  中 2 RE  | 587  | 64684  | # 5 SO#  | 1661  | 65235  |  # 2 RE#  | 622  | 64732  | 高 6 LA  | 1760  | 65252  |  中 3 M  | 659  | 64777  | # 6  | 1865  | 65268  |  中 4 FA  | 698  | 64820  | 高 7 SI  | 1967  | 65283  |  
 下面我们要为这个音符建立一个表格,有助于单片机通过查表的方式来获得相应的数据  低音0-19之间,中音在20-39之间,高音在40-59之间  TABLE: DW 0,63628,63835,64021,64103,64260,64400,64524,0,0  DW 0,63731,63928,0,64185,64331,64463,0,0,0  DW 0,64580,64684,64777,64820,64898,64968,65030,0,0  DW 0,64633,64732,0,64860,64934,64994,0,0,0  DW 0,65058,65110,65157,65178,65217,65252,65283,0,0  DW 0,65085,65134,0,65198,65235,65268,0,0,0  DW 0  2、音乐的音拍,一个节拍为单位(C调)  曲调值  | DELAY  | [size=12.0000pt]   | 曲调值  | DELAY  |  调4/4  | 125ms  | [size=12.0000pt]   | 调4/4  | 62ms  |  调3/4  | 187ms  | [size=12.0000pt]   | 调3/4  | 94ms  |  调2/4  | 250ms  | [size=12.0000pt]   | 调2/4  | 125ms  |  
 对于不同的曲调我们也可以用单片机的另外一个定时/计数器来完成。  下面就用AT89S51单片机产生一首“生日快乐”歌曲来说明单片机如何产生的。  在这个程序中用到了两个定时/计数器来完成的。其中T0用来产生音符频率,T1用来产生音拍。  5. 程序框图  file:///C:\Users\ADMINI~1\AppData\Local\Temp\ksohtml\wpsCCE9.tmp.jpg 图4.22.2  6. 汇编源程序 
KEYBUF EQU 30H 
STH0 EQU 31H 
STL0 EQU 32H 
TEMP EQU 33H 
ORG 00H 
LJMP START 
ORG 0BH 
LJMP INT_T0 
START: MOV TMOD,#01H 
SETB ET0 
SETB EA 
WAIT: 
MOV P3,#0FFH 
CLR P3.4 
MOV A,P3 
ANL A,#0FH 
XRL A,#0FH 
JZ NOKEY1 
LCALL DELY10MS 
MOV A,P3 
ANL A,#0FH 
XRL A,#0FH 
JZ NOKEY1 
MOV A,P3 
ANL A,#0FH 
CJNE A,#0EH,NK1 
MOV KEYBUF,#0 
LJMP DK1 
NK1: CJNE A,#0DH,NK2 
MOV KEYBUF,#1 
LJMP DK1 
NK2: CJNE A,#0BH,NK3 
MOV KEYBUF,#2 
LJMP DK1 
NK3: CJNE A,#07H,NK4 
MOV KEYBUF,#3 
LJMP DK1 
NK4: NOP 
DK1: 
MOV A,KEYBUF 
MOV DPTR,#TABLE 
MOVC A,@A+DPTR 
MOV P0,A 
MOV A,KEYBUF 
MOV B,#2 
MUL AB 
MOV TEMP,A 
MOV DPTR,#TABLE1 
MOVC A,@A+DPTR 
MOV STH0,A 
MOV TH0,A 
INC TEMP 
MOV A,TEMP 
MOVC A,@A+DPTR 
MOV STL0,A 
MOV TL0,A 
SETB TR0 
 
DK1A: MOV A,P3 
ANL A,#0FH 
XRL A,#0FH 
JNZ DK1A 
CLR TR0 
NOKEY1: 
MOV P3,#0FFH 
CLR P3.5 
MOV A,P3 
ANL A,#0FH 
XRL A,#0FH 
JZ NOKEY2 
LCALL DELY10MS 
MOV A,P3 
ANL A,#0FH 
XRL A,#0FH 
JZ NOKEY2 
MOV A,P3 
ANL A,#0FH 
CJNE A,#0EH,NK5 
MOV KEYBUF,#4 
LJMP DK2 
NK5: CJNE A,#0DH,NK6 
MOV KEYBUF,#5 
LJMP DK2 
NK6: CJNE A,#0BH,NK7 
MOV KEYBUF,#6 
LJMP DK2 
NK7: CJNE A,#07H,NK8 
MOV KEYBUF,#7 
LJMP DK2 
NK8: NOP 
DK2: 
MOV A,KEYBUF 
MOV DPTR,#TABLE 
MOVC A,@A+DPTR 
MOV P0,A 
MOV A,KEYBUF 
MOV B,#2 
MUL AB 
MOV TEMP,A 
MOV DPTR,#TABLE1 
MOVC A,@A+DPTR 
MOV STH0,A 
MOV TH0,A 
INC TEMP 
MOV A,TEMP 
MOVC A,@A+DPTR 
MOV STL0,A 
MOV TL0,A 
SETB TR0 
 
 
DK2A: MOV A,P3 
ANL A,#0FH 
XRL A,#0FH 
JNZ DK2A 
CLR TR0 
NOKEY2: 
MOV P3,#0FFH 
CLR P3.6 
MOV A,P3 
ANL A,#0FH 
XRL A,#0FH 
JZ NOKEY3 
LCALL DELY10MS 
MOV A,P3 
ANL A,#0FH 
XRL A,#0FH 
JZ NOKEY3 
MOV A,P3 
ANL A,#0FH 
CJNE A,#0EH,NK9 
MOV KEYBUF,#8 
LJMP DK3 
NK9: CJNE A,#0DH,NK10 
MOV KEYBUF,#9 
LJMP DK3 
NK10: CJNE A,#0BH,NK11 
MOV KEYBUF,#10 
LJMP DK3 
NK11: CJNE A,#07H,NK12 
MOV KEYBUF,#11 
LJMP DK3 
NK12: NOP 
DK3: 
MOV A,KEYBUF 
MOV DPTR,#TABLE 
MOVC A,@A+DPTR 
MOV P0,A 
MOV A,KEYBUF 
MOV B,#2 
MUL AB 
MOV TEMP,A 
MOV DPTR,#TABLE1 
MOVC A,@A+DPTR 
MOV STH0,A 
MOV TH0,A 
INC TEMP 
MOV A,TEMP 
MOVC A,@A+DPTR 
MOV STL0,A 
MOV TL0,A 
SETB TR0 
 
DK3A: MOV A,P3 
ANL A,#0FH 
XRL A,#0FH 
JNZ DK3A 
CLR TR0 
NOKEY3: 
MOV P3,#0FFH 
CLR P3.7 
MOV A,P3 
ANL A,#0FH 
XRL A,#0FH 
JZ NOKEY4 
LCALL DELY10MS 
MOV A,P3 
ANL A,#0FH 
XRL A,#0FH 
JZ NOKEY4 
MOV A,P3 
ANL A,#0FH 
CJNE A,#0EH,NK13 
MOV KEYBUF,#12 
LJMP DK4 
NK13: CJNE A,#0DH,NK14 
MOV KEYBUF,#13 
LJMP DK4 
NK14: CJNE A,#0BH,NK15 
MOV KEYBUF,#14 
LJMP DK4 
NK15: CJNE A,#07H,NK16 
MOV KEYBUF,#15 
LJMP DK4 
NK16: NOP 
DK4: 
MOV A,KEYBUF 
MOV DPTR,#TABLE 
MOVC A,@A+DPTR 
MOV P0,A 
MOV A,KEYBUF 
MOV B,#2 
MUL AB 
MOV TEMP,A 
MOV DPTR,#TABLE1 
MOVC A,@A+DPTR 
MOV STH0,A 
MOV TH0,A 
INC TEMP 
MOV A,TEMP 
MOVC A,@A+DPTR 
MOV STL0,A 
MOV TL0,A 
SETB TR0 
 
DK4A: MOV A,P3 
ANL A,#0FH 
XRL A,#0FH 
JNZ DK4A 
CLR TR0 
NOKEY4: 
LJMP WAIT 
DELY10MS: 
MOV R6,#10 
D1: MOV R7,#248 
DJNZ R7,$ 
DJNZ R6,D1 
RET 
INT_T0: 
MOV TH0,STH0 
MOV TL0,STL0 
CPL P1.0 
RETI 
TABLE: DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H 
DB 7FH,6FH,77H,7CH,39H,5EH,79H,71H 
 
TABLE1: DW 64021,64103,64260,64400 
DW 64524,64580,64684,64777 
DW 64820,64898,64968,65030 
DW 65058,65110,65157,65178 
END 
7. C语言源程序 
#include <AT89X51.H> 
unsigned char code table[]={0x3f,0x06,0x5b,0x4f, 
0x66,0x6d,0x7d,0x07, 
0x7f,0x6f,0x77,0x7c, 
0x39,0x5e,0x79,0x71}; 
unsigned char temp; 
unsigned char key; 
unsigned char i,j; 
unsigned char STH0; 
unsigned char STL0; 
unsigned int code tab[]={64021,64103,64260,64400, 
64524,64580,64684,64777, 
64820,64898,64968,65030, 
65058,65110,65157,65178}; 
 
void main(void) 
{ 
TMOD=0x01; 
ET0=1; 
EA=1; 
 
while(1) 
{ 
P3=0xff; 
P3_4=0; 
temp=P3; 
temp=temp & 0x0f; 
if (temp!=0x0f) 
{ 
for(i=50;i>0;i--) 
for(j=200;j>0;j--); 
temp=P3; 
temp=temp & 0x0f; 
if (temp!=0x0f) 
{ 
temp=P3; 
temp=temp & 0x0f; 
switch(temp) 
{ 
case 0x0e: 
key=0; 
break; 
case 0x0d: 
key=1; 
break; 
case 0x0b: 
key=2; 
break; 
case 0x07: 
key=3; 
break; 
} 
temp=P3; 
P1_0=~P1_0; 
P0=table[key]; 
STH0=tab[key]/256; 
STL0=tab[key]%256; 
TR0=1; 
temp=temp & 0x0f; 
while(temp!=0x0f) 
{ 
temp=P3; 
temp=temp & 0x0f; 
} 
TR0=0; 
} 
} 
 
P3=0xff; 
P3_5=0; 
temp=P3; 
temp=temp & 0x0f; 
if (temp!=0x0f) 
{ 
for(i=50;i>0;i--) 
for(j=200;j>0;j--); 
temp=P3; 
temp=temp & 0x0f; 
if (temp!=0x0f) 
{ 
temp=P3; 
temp=temp & 0x0f; 
switch(temp) 
{ 
case 0x0e: 
key=4; 
break; 
case 0x0d: 
key=5; 
break; 
case 0x0b: 
key=6; 
break; 
case 0x07: 
key=7; 
break; 
} 
temp=P3; 
P1_0=~P1_0; 
P0=table[key]; 
STH0=tab[key]/256; 
STL0=tab[key]%256; 
TR0=1; 
temp=temp & 0x0f; 
while(temp!=0x0f) 
{ 
temp=P3; 
temp=temp & 0x0f; 
} 
TR0=0; 
} 
} 
 
P3=0xff; 
P3_6=0; 
temp=P3; 
temp=temp & 0x0f; 
if (temp!=0x0f) 
{ 
for(i=50;i>0;i--) 
for(j=200;j>0;j--); 
temp=P3; 
temp=temp & 0x0f; 
if (temp!=0x0f) 
{ 
temp=P3; 
temp=temp & 0x0f; 
switch(temp) 
{ 
case 0x0e: 
key=8; 
break; 
case 0x0d: 
key=9; 
break; 
case 0x0b: 
key=10; 
break; 
case 0x07: 
key=11; 
break; 
} 
temp=P3; 
P1_0=~P1_0; 
P0=table[key]; 
STH0=tab[key]/256; 
STL0=tab[key]%256; 
TR0=1; 
temp=temp & 0x0f; 
while(temp!=0x0f) 
{ 
temp=P3; 
temp=temp & 0x0f; 
} 
TR0=0; 
} 
} 
 
P3=0xff; 
P3_7=0; 
temp=P3; 
temp=temp & 0x0f; 
if (temp!=0x0f) 
{ 
for(i=50;i>0;i--) 
for(j=200;j>0;j--); 
temp=P3; 
temp=temp & 0x0f; 
if (temp!=0x0f) 
{ 
temp=P3; 
temp=temp & 0x0f; 
switch(temp) 
{ 
case 0x0e: 
key=12; 
break; 
case 0x0d: 
key=13; 
break; 
case 0x0b: 
key=14; 
break; 
case 0x07: 
key=15; 
break; 
} 
temp=P3; 
P1_0=~P1_0; 
P0=table[key]; 
STH0=tab[key]/256; 
STL0=tab[key]%256; 
TR0=1; 
temp=temp & 0x0f; 
while(temp!=0x0f) 
{ 
temp=P3; 
temp=temp & 0x0f; 
} 
TR0=0; 
} 
} 
} 
} 
 
void t0(void) interrupt 1 using 0 
{ 
TH0=STH0; 
TL0=STL0; 
P1_0=~P1_0; 
}    
 |