96个key的零延时采集
HotPower 发表于 2003-11-5 18:04 侃单片机 ←返回版面 ;-------96键演示程序------------------------- ;这是1个回复题中的应用示例,已通过软仿真“验证” ;这只是键扫描技术的1个“缩影”,方法实在太多. ;有“难看之处”,敬请高手们批评指教. ;HotPower将虚心接受,坚决改正.重新做人. ;发表目的: 在21IC中壮大游击队. ;---------------------------------------------------- ;由于2051资源问题,本程序只取多任务键盘的压放键2个事件 ;废除长压键(压键1段时间后才激活)事件 ;废除长放键(放键1段时间后才激活)事件 ;废除双击键事件 ;废除任意组合键事件 ;----------常数定义------------------------------ TIME208US EQU -208;20mS/96=208uS TIME50MS EQU -5000;50000uS ;KEYCOUNT EQU 1;键盘键个数(软仿真时用) ;------------------------------------------------ KEYCOUNT EQU 96;键盘键个数(实际应用) ;----------RAM地址定义---------------------------- ;--------96键键状态标志位数组Bits[12*8位]-------- KEYBUFF1 DATA 08H;08H~13H(12个字节96位)对应96键 ;--------96键键跳变标志位数组Bits[12*8位]-------- KEYBUFF2 DATA 14H;14H~1FH(12个字节96位)对应96键 ;------------------------------------------------ KEYNUM DATA 30H; HotPower_55H DATA 6EH HotPower_AAH DATA 6FH ;--------------------------------------- SP_MIN DATA HotPower_AAH ;-------主程序开始---------------------- ORG 0000H START: LJMP MAINSTART;主程序开始 ORG 0003H ;-------掉电保护中断INT0服务程序-------- INT0_INTADDR: RETI ORG 000BH ;-------定时器T0中断服务程序------------ ;工作在8位自动装载方式,每208uS中断一次 T0_INTADDR: LJMP T0INTPROC;定时器T0中断服务程序 RETI ORG 0013H ;-------外部中断INT1服务程序------------ INT1_INTADDR: RETI ORG 001BH ;-------定时器T1中断服务程序------------ T1_INTADDR: LJMP T1INTPROC;定时器T1中断服务程序 RETI ; ORG 0023H ;-------串行中断服务程序---------------- ;SINT_INTADDR: ; RETI ;------------------------------------------- ; ORG 002BH ;-------定时器T2中断服务程序------------ ; LJMP T2INTPROC;执行中断服务程序 ; RETI ;-------执行键盘命令---------------------- ;本程序利用散转回收技术(指针函数) ;它的最大优点是散转处的子程序可被它用(函数) ;它比JMP @A+DPTR指令要“游击”很多,灵活和隐蔽了许多 ;它在对付“反汇编”方面,比JMP @A+DPTR更“坏” ;HotPower打死也不用JMP @A+DPTR KEYPROC: ;入口: DPTR散转地址表 ; ACC 散转号 CJNE A,#KEYCOUNT,$+3 JNC KEYPROC_EXIT;非法键(96~255)防止程序飞,不散转 RL A;*2;地址需要2字节(像ARM的大端模式) ADD A,DPL MOV DPL,A CLR A ADDC A,DPH MOV DPH,A MOV A,#01H;低8位 MOVC A,@A+DPTR;取低8位地址 PUSH ACC;压入事件处理低8位地址 CLR A;高8位 MOVC A,@A+DPTR;取高8位地址 PUSH ACC;压入事件处理高8位地址 KEYPROC_EXIT: RET;执行键盘命令(散转JMP @A+DPTR) ;-------压键事件处理地址表-------------------- KEYJMPPROCTAB: DW KEYPROC0;0键压键 DW KEYPROC1 DW KEYPROC2 ;............................ DW KEYPROC95;95键压键 ;-------放键事件处理地址表-------------------- KEYJMPPROCTABX: DW KEYPROC0X;0键放键 DW KEYPROC1X DW KEYPROC2X ;............................ DW KEYPROC95X;95键放键 MAINSTART: ;-------P0口初始化------------------ MOV P0,#11111111B ;-------P1口初始化------------------ MOV P1,#11111111B ;-------P2口初始化------------------ MOV P2,#11111111B ;-------P3口初始化------------------ MOV P3,#11111111B ;-------------------------------- MOV IE,#00000000B;EA=0,ES=ET2=ET1=EX1=ET0=EX0=0 MOV SP,#SP_MIN; MOV PSW,#00000000B;RS1RS0=00,R0~R7=00H~07H MOV A,#LOW(MAINNEXT) PUSH ACC MOV A,#HIGH(MAINNEXT) PUSH ACC RETI MAINPROC: LCALL MAININIT;系统初始化 MOV IE,#10001010B;开中断 ;-------主循环------------------------------- MAINLOOP: ORL PCON,#10001101B;待机 SJMP MAINLOOP;死循环 MAINNEXT: MOV A,#LOW(MAINPROC) PUSH ACC MOV A,#HIGH(MAINPROC) PUSH ACC RETI ;-------主程序初始化------------------------ MAININIT: ;-------接口初始化-------------------------- ;-------内存初始化------------------------- MOV A,HotPower_55H XRL A,HotPower_AAH CPL A JZ MAININITNEXT;内存未破坏 MOV HotPower_55H,#055H MOV HotPower_AAH,#0AAH LCALL SYSTEMINIT;系统初始化 MAININITNEXT: ;-------运行初始化--------------- LCALL SYSTEMSETUP;系统设置 RET SYSTEMINIT: LCALL KEYBUFFINIT RET SYSTEMSETUP: ;-------系统主频12MHz--------------------------------- ; MOV IP,#00100001B;中断优先级EX0>ET2>ET0>EX1>ES MOV TMOD,#00010010B;T1=MODE1(16位定时器),T0=MODE2(8位定时器) MOV TCON,#01010101B;启动定时器TR1EQUTR0EQU1,IT1EQUIT0EQU1 ;------------------------------------------------------ MOV TL0,#TIME208US;设置定时器0时间常数 MOV TH0,#TIME208US;设置定时器0时间常数 ;------------------------------------------------------ ; MOV TL1,#LOW(TIMEXMS);设置定时器1时间常数 ; MOV TH1,#HIGH(TIMEXMS) ;------- KEYBUFFINIT: MOV KEYNUM,#00H MOV R0,#KEYBUFF1 KEYBUFFINITLOOP: MOV @R0,#00H INC R0 CJNE R0,#KEYBUFF2+12,KEYBUFFINITLOOP RET ;--------------------------------------------- INKEY: ;T0每中断1次,将进行1次键"扫描" LCALL TESTKEY;键盘测试(不扫但描) ;-------键盘软仿真测试点----------------- ;在此 A=0 无键压下,A<>0有键压下 ;若调试n个键,需将KEYCOUNT设置为n.(KEYCOUNT=1~96) ;---------------------------------------- JNZ INKEY1;有键压下 INKEY0: ;-------无键压下------------------------ LCALL GETKEYBIT;取键状态 JZ INKEY01;键状态未发生变化 LCALL CLRKEYBIT;设置放键标志 LCALL SETKEYBITK;设置跳变标志(防止放键抖动) RET INKEY01: LCALL GETKEYBITK;取键跳变标志 JZ INKEY02;键未发生跳变(防止2次事件处理) LCALL CLRKEYBITX;设置重入标志 MOV A,KEYNUM;取键号 MOV DPTR,#KEYJMPPROCTABX;键盘放键事件处理表 LCALL KEYPROC;执行键盘放键事件处理 INKEY02: RET INKEY1: ;-------有键压下------------------------ LCALL GETKEYBIT;取键状态 JNZ INKEY11;键状态未发生变化(防止2次事件处理) LCALL SETKEYBIT;设置压键标志 LCALL SETKEYBITK;设置跳变标志(防止压键抖动) RET INKEY11: LCALL GETKEYBITK;取键跳变标志 JZ INKEY12;键未发生跳变 LCALL CLRKEYBITX;设置重入标志 MOV A,KEYNUM;取键号 MOV DPTR,#KEYJMPPROCTAB;键盘压键事件处理表 LCALL KEYPROC;执行键盘压键事件处理 INKEY12: RET GETKEYBITK: MOV A,R0 ADD A,#12 MOV R0,A SJMP GETKEYBITX GETKEYBIT: LCALL GETKEYBITADDR LCALL GETKEYBITVAL GETKEYBITX: MOV A,@R0 ANL A,B RET SETKEYBITK: MOV A,R0 ADD A,#12 MOV R0,A SJMP SETKEYBITX SETKEYBIT: LCALL GETKEYBITADDR LCALL GETKEYBITVAL SETKEYBITX: MOV A,@R0 ORL A,B MOV @R0,A RET CLRKEYBITK: MOV A,R0 ADD A,#12 MOV R0,A SJMP CLRKEYBITX CLRKEYBIT: LCALL GETKEYBITADDR LCALL GETKEYBITVAL CLRKEYBITX: MOV A,@R0 XRL B,#0FFH;取反B ANL A,B XRL B,#0FFH;还原B MOV @R0,A RET ;----------------------------------- ;CPLKEYBITK: ; MOV A,R0 ; ADD A,#12 ; MOV R0,A ; SJMP CPLKEYBITX ;CPLKEYBIT: ; LCALL GETKEYBITADDR ; LCALL GETKEYBITVAL ;CPLKEYBITX: ; MOV A,@R0 ; XRL A,B ; MOV @R0,A ; RET ;--------------------------------------------- GETKEYBITVAL: MOV A,KEYNUM ANL A,#07H ADD A,#GETKEYBITTAB-GETKEYBITTABOFF MOVC A,@A+PC GETKEYBITTABOFF: MOV B,A RET GETKEYBITTAB: DB 00000001B DB 00000010B DB 00000100B DB 00001000B DB 00010000B DB 00100000B DB 01000000B DB 10000000B RET ;---------------------------------------------- GETKEYBITADDR: MOV A,KEYNUM ANL A,#01111000B RR A RR A RR A ADD A,#KEYBUFF1 MOV R0,A RET ;-------键测试子程序-------------------------- TESTKEY: ;键号KEYNUM=000 0000B~101 1111B(0~95) ;入口 无 ;出口 ACC==0 无键压下(键号KEYNUM) ; ACC<>0 有键压下(键号KEYNUM) MOV A,KEYNUM;取键号 ANL A,#0FH;取行号(键号低4位) ANL P3,#0F0H;清行信号 ORL P3,A;发送行扫描信号DCBA;P3.3~P3.0 MOV A,KEYNUM;取键号 ANL A,#01110000B;取列号(键号高3位) SWAP A;变换到低3位 ADD A,#TESTKEYTAB-TESTKEYTABOFF;得到表地址 MOVC A,@A+PC;取列表值 TESTKEYTABOFF: JZ TESTKEYEXIT;6,7非法列,认为无键压下 PUSH B;保护现场 MOV B,A;暂存 ANL A,P1;接收列值P1.7~P1.2,有键压下为0 XRL A,B;有键压下非0 POP B;恢复现场 TESTKEYEXIT: RET TESTKEYTAB: DB 00000100B;0列 DB 00001000B;1列 DB 00010000B;2列 DB 00100000B;3列 DB 01000000B;4列 DB 10000000B;5列 DB 00000000B;6列非法 DB 00000000B;7列非法 ;-------定时器T0中断服务程序-------------------- ;每个键20mS扫描1次,并自动进行压键或放键消抖处理 ;这是1个大规模(96键)的键盘游击战的非典战例 ;特点: ;1.不需键扫描.(T0中断的次序即为键扫描号) ;2.不需键消抖.(在T0中断96次后自动消抖) ;3.压键放键事件分离(散转回收技术) ;4.用户事件"并行处理"(mS级分时) T0INTPROC: PUSH PSW PUSH ACC PUSH B PUSH DPL PUSH DPH T0INTPROC_START: LCALL INKEY;键扫描并执行压放键事件处理 INC KEYNUM;准备下一键号(T0中断计数) MOV A,KEYNUM CJNE A,#KEYCOUNT,T0INTPROC_EXIT MOV KEYNUM,#00H;开始下1轮键扫描 T0INTPROC_EXIT: POP DPH POP DPL POP B POP ACC POP PSW RETI ;-------定时器T1中断服务程序------------ T1INTPROC: RETI ;-------0键压键事件处理--------------------- KEYPROC0: ;在此添加用户压键事件 RET ;-------1键压键事件处理--------------------- KEYPROC1: ;在此添加用户压键事件 RET ;-------2键压键事件处理--------------------- KEYPROC2: ;在此添加用户压键事件 RET ;-------95键压键事件处理--------------------- KEYPROC95: ;在此添加用户压键事件 RET ;-------0键放键事件处理--------------------- KEYPROC0X: ;在此添加用户放键事件 RET ;-------1键放键事件处理--------------------- KEYPROC1X: ;在此添加用户放键事件 RET ;-------2键放键事件处理--------------------- KEYPROC2X: ;在此添加用户放键事件 RET ;-------95键放键事件处理--------------------- KEYPROC95X: ;在此添加用户放键事件 RET ;-------全部程序结束-------------------------------------- END
|