设计目的:
1.熟悉PS/2协议的原理
2.学习单片机的使用
设计内容:
依照PS/2协议,在51芯片上开发汇编程序,使得按下开发板上的按键时,能够在PC机上输出a、b、c。
实验环境:
开发板、STC12C2052AD芯片、PS/2线、PC机、Keil软件、STC-ISP程序下载器
相关参数:
单片机振荡频率6MHz(具体情况请看此型号单片机用户手册)
指令周期0.5微秒(51型单片机的指令周期是其振荡频率的12分之一)
PS/2连接器引脚定义(见参考文献《PS/2技术参考》)
能够接受的键盘时钟信号频率10-20KHz(最大33KHz,推荐设为15KHz)
设计思想:
使用开发板上的按钮产生中断信号,使单片机进入相应的中断处理程序,从而向PC机发送相应的扫描码。PC机主板中有一个解码芯片,此芯片在收到扫描码之后,引发PC机操作系统的中断(I/O的中断),转到操作系统中的中断处理程序,从面在系统中输出相应字符。
原理:
实际的通用键盘上的每一个键都对应一个唯一个扫描码,这些键会在极短的时间内被扫描一遍,以确认它们有没有被按下,如果某个键被按下了,就发送其相应的扫描码。而在开发板上,51芯片有三个按键,对应芯片上的两个中断口和一个定时/计数器输入口。中断接口可以直接使用,当按下键时就触发中断,而定时/计数器需要设置为初始值为FFH,一旦按下键便会溢出而产生中断的计数器。
主机与键盘有四根线相连,时钟信号数、数据线、接地线、电源线。
时钟信号在任何时刻都是由键盘产生的,PC机在时种的下降沿接收数据线上的信号,要是PC机向键盘发送数据,则键盘在时钟的上升沿接收数据线上的数据。虽是由键盘产生时钟信号,但是PC机对总线有着绝对的控制权,只要其将时钟线拉低(高低电平信号有这样的特性:只有一端的信号为低电平,则整条线的信号都为低电平。这就是为什么要向单片机I/O口输出一个信号,要先将外部输入单片机这个I/O口的信号释放(拉高)。否则输出的永远都是低电平),就要以抵制键盘的输出。
总线上有这些几种状态:
1、PC等待键盘输出状态(键盘可以输出状态):CLK=1,DATA=1;
2、键盘等待PC输出状态(PC可以输出状态):CLK=1,DATA=0(这个状态之前还有一些状态要生产生的,详情请见参考文献《PS/2技术参考》)
3、正在输出状态:此时的时钟线与数据线上的信号没有定值,依据所要发送的数据不同而不同,CLK=X,DATA=X
键盘从外部存储器中读取相应键位的扫描码,当要改变某个键所要发送的扫描码时,只需改变外部存储器中对应位置的扫描码,这样就可以方便地改变某键所要发送的扫描码。在存放扫描码时,只需将扫描码按顺序存放,如E0,70按存储地址从小到大顺序地排列。因为通码(make code)最多只有两个十六进制数,且如有两个则第一个必是E0H,而断码(break code)最多只有三个,且如果有三个,则第一个必是E0H,如果有二个,则第一个必是F0H。所以,当发送时,如果发现第一个是E0H或F0H的话,则再发送其后的一个扫描码。
注意事项:
1、 发送8个数据位时,是低位先发送(least significant bit first),如果是用高位先发送,主机自然是无法识别会发送的数据,会报警;
2、 如果想要精确计算时间,那么像子程序调用语句会占用两个指令周期也应该考虑到;
3、 连接线应该连在指示灯靠近51芯片的一端,否则无法接收到主机发来的信号;PS/2
4、 汇编程序前的org 语句必须按其指示的地址大小排列,小的在前,大的在后;
程序:
;=======================================
;program
;check clk and data before every 8 bit data sending
;a function is created for send all 11bit data
;a function is created for parity calculating
;CAPSLOCK :BIG LETTER P1.3=0,SMALL LETTER P1.3=1
;=======================================
;=======================================
;VARS USED LOCALY
;R0 IS USED LOCALY AS A COUNTER
;20H.1 20H.2 20H.3 20H.4 IS USED IN FUNCTION AS TEMP VAR
;R3,R4,R5 USED LOCALLY IN DELAY()
;===================================
;Define Samples
;====================================
MCLK EQU P1.2 ;VALUE OF CLOCK LINE
MDATA EQU P1.1 ;VALUE OF DATA LINE
SCAN EQU R1 ;VALUE OF THE SCAN CODE
RECV EQU R2 ;VALUE RECEIVED BY KEYBOARD
RECV2 EQU R6 ;THE VALUE RECEIVED SECONDLY
NEXTBIT EQU CY ;VALUE OF THE BIT WILL SENT NEXT
PARITY EQU 20H.1 ;VALUE OF PARITY
TEMP EQU 20H.2 ;THIS BIT IS FOR TEMPORITARY USE
TEMP1 EQU 20H.3 ;THIS BIT IS FOR TEMP USE
TEMP8BIT EQU 21H
LOOPCOUNTER EQU 30H ;COUNTER SPECIAL FOR SENDALLBIT
SCROLLLOCK EQU P1.5
CAPSLOCK EQU P1.6
NUMLOCK EQU P1.7
ERROR_LIGHT EQU P1.4 ;PARITY ERROR
ERROR_LIGHT_STOPBIT EQU P1.3 ;STOP BIT RECEIVE ERROR
;===================================;===================================
;ORG
;===================================
ORG 00H
JMP MAINORG 0003H
LJMP K1DOWNORG 000BH
LJMP K3DOWNORG 0013H
LJMP K2DOWN;============================================================
;MAIN
;============================================================
MAIN:;==============================
;SET INTERRUPTER AND TIMER
mov ie,#10000101B ;中断使能
mov ip,#00H ;中断优先
mov tcon,#00000101b ;中断为电平触发 ;TIME0 AS A BUTTON
MOV TMOD,#06H
MOV TH0,#0FFH
MOV TL0,#0FFH
SETB ET0
SETB TR0
;==============================
;==============================
;SET CLK AND DATA LINE TO NORMAL STATUS
SETB MCLK
SETB MDATA
;============================== SETB CAPSLOCK ;CAPSLOCK OFF(1)
SETB NUMLOCK ;NUMLOCK OFF(1)
SETB SCROLLLOCK ;SCROLLLOCK OFF(1)CHKSTATUS:
JNB MCLK,CHKSTATUS ;IF MCLK==0 THEN GOTO CHKSTATUS
JB MDATA,CHKSTATUS ;IF MDATA==1 THEN GOTO CHKSTAUS
;NOW MCLK=1 AND MDATA =0 ,READY TO RECEIVE DATA FROM PC
LCALL RECV_CHK_SEND ;RECEIVE DATA FROM PCLJMP CHKSTATUS ;WAIT FOR BREAK;============================================================
;///////MAIN
;============================================================
;===============================================================================================
; AREA FOR FUNCTION
;===============================================================================================
LJMP ENDOFFILE
;====================================
;IN THIS FUNCTION: RECEIVE ALL 10BIT DATA BY RECEIVEALLBIT() AND CHK WHETHER IT'S CORRECT THEN SEND CORRESPONDING SCANCODE TO PC
;====================================
RECV_CHK_SEND:
CLR P1.0 ;INDICATE START RECEIVEING
LCALL RECEIVEALLBIT ;RECV PARITY TEMP1
MOV A,RECV
CJNE A,#0EDH,RECV_CHK_SEND_END
;=====================
;RECV==EQ NOW SET LED
;CHKSTATUS_2:
; JNB MCLK,CHKSTATUS_2 ;IF MCLK==0 THEN GOTO CHKSTATUS
; JB MDATA,CHKSTATUS_2 ;IF MDATA==1 THEN GOTO CHKSTAUS
LCALL DELAY50
LCALL DELAY50
LCALL RECEIVEALLBIT ;RECV PARITY TEMP1
;========================
;THE CODE IS RIGHT AFTER THIS ,TESTED BY MOV "RECV,#00000110"
MOV A,RECV RRC A ;NOW C=LEAST BIT OF A
CPL C ;PC: C=1 ON ;C=0 OFF NOW IN KEYBOARD C=1 OFF(LIGHT OFF) C=0 ON(LIGHT ON)
MOV SCROLLLOCK,C RRC A ;NOW C=LEAST BIT OF A
CPL C ;PC: C=1 ON ;C=0 OFF NOW IN KEYBOARD C=1 OFF(LIGHT OFF) C=0 ON(LIGHT ON)
MOV NUMLOCK,C RRC A ;NOW C=LEAST BIT OF A
CPL C ;PC: C=1 ON ;C=0 OFF NOW IN KEYBOARD C=1 OFF(LIGHT OFF) C=0 ON(LIGHT ON)
MOV CAPSLOCK,C SETB P1.0 LJMP RECV_CHK_SEND_END
;/====================RECV_CHK_SEND_END:
RET
;====================================
;////////IN THIS FUNCTION: RECEIVE ALL 10BIT DATA BY RECEIVEALLBIT() AND CHK WHETHER IT'S CORRECT THEN SEND CORRESPONDING SCANCODE TO PC
;====================================;====================================
;RECIEVE ALL THE 10BIT DATA FROM PC AND CHECK WHETHER IT'S RIGHT
;SAVE THE 8BIT DATA TO RECV
;TEMP1: PARITY FIGURED OUT
;PARITY: PARITY RECEIVED
;====================================
RECEIVEALLBIT:
MOV LOOPCOUNTER,#0008H
RECV8BIT_LOOP:
LCALL RECEIVEONE ;GET ONE BIT AND SAVE IT TO CY
RRC A ;RIGHT MOVE THIS BIT TO A
DJNZ LOOPCOUNTER,RECV8BIT_LOOP ;LOOP FOR 8 TIMES
MOV C,P ;C=P(THIS DOUBLE PARITY JUST RECEIVED
CPL C ;CHANG TO ODD PARITY
MOV TEMP1,C ;STORE THE ODD PARITY TO TEMP1
MOV RECV,A ;RECV=A LCALL RECEIVEONE ;GET PARITY BIT
MOV PARITY,C ;PARITY=C LCALL RECEIVEONE ;PC PULL UP DATA LINE
MOV TEMP,C ;RECEIVE TERMINAL BIT(1) ;MOV P1,RECV ;JUST FOR TESTING
JNB MDATA,STOPBIT_ERROR ; 1==MDATA THEN RECEIVEALLBIT_ERROR ;SEND ACK
LCALL SENDACK MOV C,TEMP1 ; C = PARITY FIGURED OUT
MOV A,#00H ;A=OOH
RRC A ;SAVE C TO A
MOV TEMP8BIT,A ;SAVE A TO TEMP8BIT MOV C,PARITY
MOV A,#00H
RRC A CJNE A,TEMP8BIT,PARITY_ERROR ;DATA RECEIV SUCCESSFUL
MOV SCAN,#00H
LCALL SENDALLBIT ;IF THE PROGRAM CAN RUN HERE,INDICATE ALL IS IN GOOD CONDICION
;SCAN 00H TO TELL PC ALL IN IN GOOD
;MOV SCAN,#00H
;LCALL SENDALLBIT LJMP ENDOFSENDALLBIT
;===================
;THE PARITY IS WRONG ,SO KEYBOARD RECEIVED A WRONG DATA
PARITY_ERROR:
CLR ERROR_LIGHT
;MOV SCAN,#0FFH
;LCALL SENDALLBIT
;/==================
LJMP ENDOFSENDALLBIT ;===================
;STOPBIT ERROR
STOPBIT_ERROR:
CLR ERROR_LIGHT_STOPBIT
;MOV SCAN,#0FFH
;LCALL SENDALLBIT
LJMP ENDOFSENDALLBIT
ENDOFSENDALLBIT:
RET
;====================================
;/////////RECIEVE ALL THE 10BIT DATA FROM PC AND CHECK WHETHER IT'S RIGHT
;/////////SAVE THE 8BIT DATA TO RECV
;====================================
;====================================
;SENDACK
;====================================
SENDACK:
LCALL DELAY15
CLR MDATA
LCALL DELAY5
CLR MCLK
LCALL DELAY40
SETB MCLK
LCALL DELAY5
SETB MDATA
RET
;//==================================;====================================
;RECEIVE ONE BIT DATA FROM PC
;SAVE THIS BIT TO CY
;====================================
RECEIVEONE:
LCALL DELAY20
CLR MCLK
LCALL DELAY40
SETB MCLK
LCALL DELAY20
MOV C,MDATA ;SAVE THIS BIT OF DATA TO CY,SAVE IT TO RECV LATER
RET
;====================================
;/////RECEIVE ONE BIT DATA FROM PC
;====================================;====================================
;INVOKE THIS FUNCTION WHEN K1 IS DOWN
;====================================
K1DOWN:
MOV SCAN,#01CH
LCALL SENDALLBIT MOV SCAN,#0F0H
LCALL SENDALLBIT MOV SCAN,#01CH
LCALL SENDALLBIT ;LCALL DELAY ;DON'T PRINT SO FAST
RETI
;====================================
;//////INVOKE THIS FUNCTION WHEN K1 IS DOWN
;====================================
;====================================
;INVOKE THIS FUNCTION WHEN K2 IS DOWN
;====================================
K2DOWN:
MOV SCAN,#032H
LCALL SENDALLBIT MOV SCAN,#0F0H
LCALL SENDALLBIT MOV SCAN,#032H
LCALL SENDALLBIT ;LCALL DELAY ;DON'T PRINT SO FAST
RETI
;====================================
;//////INVOKE THIS FUNCTION WHEN K2 IS DOWN
;====================================;====================================
;INVOKE THIS FUNCTION WHEN K3 IS DOWN
;====================================
K3DOWN:
MOV SCAN,#021H
LCALL SENDALLBIT MOV SCAN,#0F0H
LCALL SENDALLBIT MOV SCAN,#021H
LCALL SENDALLBIT ;LCALL DELAY ;DON'T PRINT SO FAST
RETI
;====================================
;//////INVOKE THIS FUNCTION WHEN K3 IS DOWN
;====================================;====================================
;SEND ALL 11 BIT DATA
;====================================
SENDALLBIT:
;THE VAR SCANCODE MUST BE SET BEFORE THE FUNCTION
;SET CLK AND DATA LINE TO 1
INIT:
SETB MCLK;
SETB MDATA; ;CHECK WHETHER THE CLK AND DATA IS IN STATE THAT CAN TRANSFER DATA
CHKCLKHIGH:
JNB MCLK,CHKCLKHIGH ;IF CLK=0 THEN GOTO CHKCLKHIGH ELSE CONTINUE actually,this is the cache,wait for clk=1 to send the data
LCALL DELAY50 ;DELAY 50us
JNB MCLK,CHKCLKHIGH ;
JNB MDATA,CHKCLKHIGH;IF DATA=0 THEN GOTO CHKCLKHIGH ELSE CONTINUE
;NOW CLK=1 FOR 50US AND DATA=1
LCALL DELAY20; ;DELAY 20US ;================================
;CLK AND DATA CHECK FINISHED ,START DATA TRANSFAN
;================================
STARTSEND:
CLR NEXTBIT ;SET START BIT ;1 PR
LCALL SENDBIT;SENT THE START BIT ;170 PR
;SEND SCAN CODE
;NOW SCAN IS SET BEFOR SENDALLBIT()
;MOV SCAN,#035H ;LET SCAN= THE SCAN CODE OF KEY A ;2 PR
LCALL SENDSCAN ;SEND THE SCAN CODE ;1123 PR
; SEND PARITY ; #### COULD BE BETTER IN SENDING PARITY #########
LCALL CALPARITY
MOV C,PARITY LCALL SENDBIT ;140 PR
;SEND TERMINAL SIGN
SETB NEXTBIT ;TERMINAL SIGN=1 ;1 PR
LCALL SENDBIT ;140 PR
LCALL DELAY50 ;DELAY 50us
LCALL DELAY20 ;DELAY 50us
;FINISHED SENDING DATA USED 1950 PERIODS
RET ;SENDALLBIT END
;=====================================
;=====================================
;long time delay
;=====================================
Delay:
MOV R3, #000H
MOV R4, #000H
MOV R5, #0DH
Delay_Loop:
DJNZ R3, Delay_Loop
DJNZ R4, Delay_Loop
DJNZ R5, Delay_Loop
RET
;=====================================;=====================================================
;SEND THE SCAN CODE IN VAR SCAN
;PERIODS:1123 PR
;=====================================================
SENDSCAN:
CLR C ;1 PR
MOV A,SCAN ;2 PR
RRC A ;MOVE RIGHT ;1 PR
LCALL SENDBIT ;SEND 7TH ;140 PR RRC A ;MOVE RIGHT ;1 PR
LCALL SENDBIT ;SEND 6TH ;140 PR RRC A ;MOVE RIGHT ;1 PR
LCALL SENDBIT ;SEND 5TH ;170 PR RRC A ;MOVE RIGHT ;1 PR
LCALL SENDBIT ;SEND 4TH ;140 PR RRC A ;MOVE RIGHT ;1 PR
LCALL SENDBIT ;SEND 3TH ;140 PR RRC A ;MOVE RIGHT ;1 PR
LCALL SENDBIT ;SEND 2TH ;140 PR RRC A ;MOVE RIGHT ;1 PR
LCALL SENDBIT ;SEND 1TH ;140 PR RRC A ;MOVE RIGHT ;1 PR
LCALL SENDBIT ;SEND 0TH ;140 PR
RET
;=====================================================;=====================================================
;SEND THE NEXTBIT
;PERIODS:138 PERIODS
;=====================================================
SENDBIT:
MOV MDATA,C ;1 period
LCALL DELAY20 ;42 PERIODS
CLR MCLK ;PULL DOWN THE CLOCK LINE
LCALL DELAY40 ;82 PERIODS
SETB MCLK ;1 PERIODS
LCALL DELAY20 ;12 PERIODS
RET
;=====================================================
;======================================================
;SUB FOR DELAY 16US
;RAM USED:R0
;======================================================
DELAY16:
MOV R0,#003H ;2
DELAY16_IN:DJNZ R0,DELAY16_IN;THIS COMMAND WILL BE EXCUTE FOR 15 TIMES, 2period
RET
;======================================================
;======================================================
;SUB FOR DELAY 32US
;RAM USED:R0
;======================================================
DELAY32:
MOV R0,#00EH ;
DELAY32_IN:DJNZ R0,DELAY32_IN;THIS COMMAND WILL BE EXCUTE FOR 31 TIMES, 2period
RET
;======================================================;======================================================
;SUB FOR DELAY 50US correct?
;RAM USED:R0
;======================================================
DELAY50US:
MOV R0,#01aH ;
DELAY50_LOOP:DJNZ R0,DELAY50_LOOP;THIS COMMAND WILL BE EXCUTE FOR 31 TIMES, 2period
RET
;======================================================;======================================================
;SUB FOR DELAY 20US
;RAM USED:R0
;periods:TOTAL 40PERIODS
;======================================================
DELAY20:
MOV R0,#013H ;RUN 19TIMES 2period
DELAY20_IN:DJNZ R0,DELAY20_IN;THIS COMMAND WILL BE EXCUTE FOR 19 TIMES, 2period
RET
;======================================================;======================================================
;SUB FOR DELAY 5US
;RAM USED:R0
;periods:
;======================================================
DELAY5:
MOV R0,#004H ;RUN 10TIMES ;2period
DELAY5_IN:DJNZ R0,DELAY5_IN;THIS COMMAND WILL BE EXCUTE FOR 4 TIMES, 2period
RET
;======================================================;======================================================
;SUB FOR DELAY 20US correct?
;RAM USED:R0
;periods:TOTAL 40PERIODS
;======================================================
DELAY20US:
MOV R0,#00DH ;RUN 19TIMES 2period
DELAY20US_LOOP:DJNZ R0,DELAY20US_LOOP;THIS COMMAND WILL BE EXCUTE FOR 19 TIMES, 2period
RET
;======================================================
;======================================================
;SUB FOR DELAY 50US
;RAM USED:R0
;PERIODS:100 PERIODS
;======================================================
DELAY50:
MOV R0,#0B1H
DELAY50_IN:DJNZ R0,DELAY50_IN;
RET
;======================================================;======================================================
;SUB FOR DELAY 30US
;RAM USED:R0
;PERIODS:60 PERIODS
;======================================================
DELAY30:
MOV R0,#01DH
DELAY30_IN:DJNZ R0,DELAY30_IN;
RET
;======================================================
;======================================================
;SUB FOR DELAY 15US
;RAM USED:R0
;======================================================
DELAY15:
MOV R0,#00EH
DELAY15_IN:DJNZ R0,DELAY15_IN;
RET
;======================================================;======================================================
;SUB FOR DELAY 40US
;RAM USED:R0
;PERIODS:80 PERIODS
;======================================================
DELAY40:
MOV R0,#27H
DELAY40_IN:DJNZ R0,DELAY40_IN;
RET
;======================================================;======================================================
;CACULATE THE PARITY OF THE CODE
;RAM CHANGED:A,C,PARITY
;INPUT :SCAN
;RETURN VAR:PARITY
;======================================================
CALPARITY:
MOV A,SCAN
MOV C,P
CPL C
MOV PARITY,C
RET
;======================================================
;===================================================================================================================
ENDOFFILE:
END ;END OF THE PROGRAM