应用查询扫描编写键盘程序,由于要给按键去抖动,程序变得比较复杂和冗长(详见2013年9月29日博文《MSP430和AT89C51单片机4x4键盘C程序》),如果用中断编写,设置中断响应在下降沿时执行中断,则程序编写不用去抖动判断,所以相比较要简单很多!下面用汇编和C语言两种方式编写4X4键盘程序! 一、汇编程序 ORG 0000H LJMP MAIN ORG 0003h Ljmp ZD0 ORG 000Bh LJMP TZD0 ORG 0013h Ljmp ZD1 ORG 001Bh LJMP TZD1 ORG 0040H MAIN: Mov TMOD,#66h MOV TH0,#0ffh MOV TL0,#0ffh MOV TH1,#0ffh MOV TL1,#0ffh SETB EA SETB ET0 SETB TR0 SETB ET1 SETB TR1 SETB IT0 SETB IT1 SETB EX0 SETB EX1 xh: mov P1,#0feh Lcall Delay mov P1,#0fdh Lcall Delay mov P1,#0fbh Lcall Delay mov P1,#0f7h Lcall Delay SJMP xh ZD0: JNB P1.0,dat1 JNB P1.1,dat2 JNB P1.2,dat3 JNB P1.3,dat4 dat1: mov P2,#06h ;1 sjmp ZD0R dat2: mov P2,#5bh ;2 sjmp ZD0R dat3: mov P2,#4fh ;3 sjmp ZD0R dat4: mov P2,#66h ;4 ZD0R: reti ZD1: JNB P1.0,dat5 JNB P1.1,dat6 JNB P1.2,dat7 JNB P1.3,dat8 dat5: mov P2,#6dh ;5 0110 sjmp ZD1R dat6: mov P2,#7dh ;6 sjmp ZD1R dat7: mov P2,#07h ;7 sjmp ZD1R dat8: mov P2,#7fh ;8 ZD1R: reti
TZD0: JNB P1.0,dat9 JNB P1.1,dat0 JNB P1.2,dat10 JNB P1.3,dat11 dat9: mov P2,#6fh ;9 sjmp ZD0R dat0: mov P2,#3fh ;0 sjmp ZD0R dat10: mov P2,#77h ;A sjmp ZD0R dat11: mov P2,#7ch ;B TZD0R:reti TZD1: JNB P1.0,dat12 JNB P1.1,dat13 JNB P1.2,dat14 JNB P1.3,dat15 dat12: mov P2,#39h ;C sjmp TZD1R dat13:mov P2,#5eh ;D sjmp TZD1R dat14:mov P2,#79h ;E sjmp TZD1R dat15:mov P2,#71h ;F TZD1R:reti Delay:mov r7,#10d djnz r7,$ ret end 二、C语言程序(扫描P1) #include'reg51.h' int yu[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; int i,j,k,time,temp,dat; void delay(time) { while(time--) for(i=0;i<> } void main() { P2=0x40; TMOD=0x66; //设定定时计数器T0、T1为方式2计数模式 TH0=0xFF; TL0=0xFF; TH1=0xFF; TL1=0xFF; IE=0x8F; //开总中断和定时计数器两个中断,两个外中断 IT0=1; IT1=1; //设置外中断为下降沿触发 TR0=1; TR1=1; //打开定时计数器开始计数 while(1) { P1=0xfe; delay(1); P1=0xfd; delay(1); P1=0xfb; delay(1); P1=0xf7; delay(1); } } void intsvr0(void) interrupt 0 using 1 {
temp=P1; switch(temp) { case 0xfe:key=0;break; case 0xfd:key=1;break; case 0xfb:key=2;break; case 0xf7:key=3;break; default:break; } P2=yu[dat]; } void timer0(void) interrupt 1 using 1 {
temp=P1; if(temp==0xfe) dat=4; if(temp==0xfd) dat=5; if(temp==0xfb) dat=6; if(temp==0xf7) dat=7; P2=yu[dat]; } void intsvr1(void) interrupt 2 using 1 {
temp=P1; if(temp==0xfe) dat=8; if(temp==0xfd) dat=9; if(temp==0xfb) dat=10; if(temp==0xf7) dat=11; P2=yu[dat]; } void timer1(void) interrupt 3 using 1 { int dat; temp=P1; switch(temp) { case 0xfe:key=12;break; case 0xfd:key=13;break; case 0xfb:key=14;break; case 0xf7:key=15;break; default:break; } P2=yu[dat]; } /*************************************************************/ 三、C语言(扫描P3的P3.0、P3.1、P3.6、P3.7) 从这个程序也可以看出51单片机I/O口没有方向性,输入输出都可读写,而且在一个寄存器中。注意该程序对应电路图与上图的区别。 #include'reg51.h' sbit pp0=P3^0; sbit pp1=P3^1; sbit pp2=P3^6; sbit pp3=P3^7; int yu[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07, 0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71}; int i,j,k,time,temp,dat; void delay(time) { while(time--) for(i=0;i<> } void main() { P2=0x40; TMOD=0x66; //设定定时计数器T0、T1为方式2计数模式 TH0=0xFF; TL0=0xFF; TH1=0xFF; TL1=0xFF; IE=0x8F; //开总中断和定时计数器两个中断,两个外中断 IT0=1; IT1=1; //设置外中断为下降沿触发 TR0=1; TR1=1; //打开定时计数器开始计数 while(1) { pp0=0; delay(5); pp0=1; pp1=0; delay(5); pp1=1; pp2=0; delay(5); pp2=1; pp3=0; delay(5); pp3=1; /*以上可用下列这段代替 P3=0xfe; delay(5); P3=0xfd; delay(5); P3=0xbf; delay(5); P3=0x7f; delay(5);*/ } } void intsvr0(void) interrupt 0 using 1 {
temp=P3; switch(temp) { case 0xfa:dat=0;break; case 0xf9:dat=1;break; case 0xbb:dat=2;break; case 0x7b:dat=3;break; default: break; } P2=yu[dat]; } void timer0(void) interrupt 1 using 1 {
temp=P3; if(temp==0xee) dat=8; if(temp==0xed) dat=9; if(temp==0xaf) dat=10; if(temp==0x6f) dat=11; P2=yu[dat]; } void intsvr1(void) interrupt 2 using 3 {
temp=P3; if(temp==0xf6) dat=4; if(temp==0xf5) dat=5; if(temp==0xb7) dat=6; if(temp==0x77) dat=7; P2=yu[dat]; } void timer1(void) interrupt 3 using 1 { int dat; temp=P3; switch(temp) { case 0xde:dat=12;break; case 0xdd:dat=13;break; case 0x9f:dat=14;break; case 0x5f:dat=15;break; default: break; } P2=yu[dat]; }
|