一个PS/2仿真键盘的设计

设计目的:
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 MAIN

ORG 0003H
LJMP K1DOWN

ORG    000BH
LJMP K3DOWN

ORG 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 PC     

LJMP 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

你可能感兴趣的:(c,汇编,存储,input,delay)