AT/PS2 Keybord - PIC Interface?


Closed Thread
Results 1 to 40 of 74

Hybrid View

  1. #1
    mytekcontrols's Avatar
    mytekcontrols Guest


    Did you find this post helpful? Yes | No

    Default Here's an interrupt routine I use to retrieve the key...

    Kamikaze47,

    Here is the approach I've taken with decoding a PS2 style keyboard. I'll be dishing this out in chunks. The first module is an ASM interrupt service routine that retrieves the key, does some error checking, and stuffs it into a ring buffer for later retrieval and decoding within the PBP program.

    Code:
    '===========================================================================
    ' Interrupt Handler (Low Priority)
    '===========================================================================
    ' Keyboard "getkey" handler
    
    ;                                    KB_getkey
    ;                            Keyboard to Host Protocol
    ;                 ___   _   _   _   _   _   _   _   _   _   _   ___
    ;          CLOCK     |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| 
    ;
    ;          DATA    | S | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | P | E |
    ;
    ;              S = start bit = 0    P = odd parity   E = stop bit = 1
    ;                      data valid on falling edge of clock
    
    
    ' The getkey handler responds to a high-to-low transition of the KBCLK line,
    ' which is the INT2 interrupt connection. Each time this occurs, data bits
    ' coming in through the KBDAT line (RB4) are temporarily stored in the KBcode
    ' variable. When a valid stop bit has been received, the data is transferred
    ' to a ring buffer KBbuf. This information will be later extracted by the
    ' PicBasic routine mod_keybd.bas and translated into ASCII and ASCIIE code.
    
    '===========================================================================
    
    asm
    myintL
    
    ; save the state of critical registers
        movwf	wsave            ; Save W
        swapf	STATUS,W         ; Swap STATUS to W
        clrf	STATUS           ; bank 0, regardless of current bank
        movwf	ssave            ; Save swapped STATUS
    
    ; save the FSR value because it gets changed below	
        movf    FSR0L, W
        movwf   fsave
        movf    FSR0H, W
        movwf   fsave+1
    
    ;===========================================================================
    ;AT Keyboard Service Routine
    	;*** check start bit ***
    KB_getkey
        movf    _KBcnt,W        ; check keyboard scan code bit counter
        bnz     KB_getdat       ; branch if not zero (start bit)
        btfsc   _KBDAT          ; test start bit of keyboard data input
        goto    KB_abort        ; no valid start bit, abort
        goto    KB_exit         ; exit
    
    	;*** keyboard scan code acquisition ***
    KB_getdat
        movf    _KBcnt,W        ; get keyboard scan code bit counter
        sublw   d'8'            ; w = d'8' - KBDcnt (*)
        bnc     KB_parity       ; branch if negative (carry == 0)
        btfss   _KBDAT          ; Test keyboard data input
        bcf     _KBcode,7       ; clear bit 7 if KBDAT = 0
        btfsc   _KBDAT          ; Test keyboard data input
        bsf     _KBcode,7       ; set bit 7 if KBDAT = 1	
        bz      KB_exit         ; exit on zero (zero flag still valid from (*))
        rrncf   _KBcode,F       ; shift new bits right (do this only 7 times)
        goto    KB_exit         ; exit
    
    	;*** check for parity and stop bit ***
    KB_parity
        movf    _KBcnt,W        ; get keyboard scan code counter
        sublw   d'9'            ; w = d'9' - KBDcnt
        bnc     KB_stop         ; branch if negative (carry == 0)
        goto    KB_exit         ; ignore parity bit
    
    	;*** check stop bit ***
    KB_stop
        btfss   _KBDAT          ; check if stop bit is valid
        goto    KB_abort        ; if not set, abort
    
    	;*** increment the buffer input index & test for buffer overrun ***
        incf    _KBindxI,W
        subwf   _KBindxO,W      ; subtract indexes to test buffer
        btfsc   STATUS,Z        ; check for zero (KBindxI = KBindxO)
        goto    KB_stall        ; if error stall keyboard
    
    	;*** increment the buffer input index ***
        incf    _KBindxI,F
        movf    _KBindxI,W
        sublw   _KBbufsize-1    ; test if index is outside the ring buffer
        btfss   STATUS,C        ; if it is...
        clrf    _KBindxI        ; reset it
    
        ; Set FSR0 with the location of the next empty location in buffer
        LFSR    FSR0, _KBbuf    ; set counter base address
    
        ; Read and store the character from the USART		
        movf	_KBindxI, W	    ; W = offset value for the next empty location
        movff	_KBcode, PLUSW0	; Move the character in KBcode to address (FSR0+W)
                                                        
    	;*** stall keyboard ***
    	; to prevent the arrival of more data before having finished decoding
    KB_stall                    ; hold keyboard (with keyboard clk low):
        bcf     TRISB,2         ; set clkline to output (back to input externally)
        bcf     _KBCLK          ; set keyboard clk line low (stall)
        bcf     _INT2IE         ; disable keyboard IRQ (re-enabled externally)
        goto    KB_term	        ; terminate successfully
    
    KB_abort
        clrf    _KBcode         ; abort / invalid data
    KB_term
        clrf    _KBcnt          ; reset keyboard scan code acquisition counter
        goto    KB_end          ; terminate execution of keyboard IRQ
    KB_exit
        incf    _KBcnt,F        ; increment acquisition counter
    KB_end
        bcf     _INT2IF         ; clear INT2 interrupt flag
    
    ;restore registers
        movf	fsave, W
        movwf	FSR0L
        movf	fsave+1, W
        movwf	FSR0H
        swapf	ssave,W         ; Retrieve the swapped STATUS value
        movwf	STATUS          ; Restore it to STATUS
        swapf	wsave,F         ; Swap the stored W value
        swapf	wsave,W         ; Restore it to W
        retfie                  ; Return from the interrupt
    
    endasm
    And here is the PBP equates (please forgive me if I've missed any) and initialization code:
    Code:
    ' PIC18F series processor used
    
    DEFINE OSC 40
    DEFINE INTLHAND myintL
    
    '===========================================================================
    ' Equates
    '===========================================================================
    
    'Variables for saving state in interrupt handler
    wsave	VAR	BYTE bankA system 	      ' Saves W
    ssave	VAR	BYTE bankA system	      ' Saves STATUS
    fsave	VAR	WORD bankA system	      ' Saves FSR0
    
    
    'Keyboard (Low Priority) Interrupt Handler equates
    KBbufsize con 10                    ' keyboard buffer size
    KBcnt var byte bankA                ' bit counter for keyboard data
    KBbuf var byte[KBbufsize] bankA     ' keyboard ring buffer
    KBindxI var byte bankA              ' keyboard buffer Input index pointer
    KBindxO var byte bankA              ' keyboard buffer Output index pointer
    KBcode var byte bankA               ' temporary storage for scan code
    
    'Ketboard Handler equates
    LED var byte
    scrlock var LED.0       ' If true, scroll lock is on (active)
    numlock var LED.1       ' If true, num lock is on (active)
    caplock var LED.2       ' If true, caps lock is on (active)
    pscrlck var LED.3       ' If true, scroll lock pressed
    pnumlck var LED.4       ' If true, numlock pressed 
    pcaplck var LED.5       ' If true, caps lock pressed
    
    keystat var byte
    Lshift var keystat.0    ' If true, left Shift pressed
    Rshift var keystat.1    ' If true, right Shift pressed
    L_ctrl var keystat.2    ' If true, left Ctrl pressed
    R_ctrl var keystat.3    ' If true, right Ctrl pressed
    L_alt var keystat.4     ' If true, left Alt pressed
    R_alt var keystat.5     ' If true, right Alt pressed
    keydec var keystat.6    ' If true, a key has been decoded
    
    scancode var byte       ' temporary storage for raw keyboard scan code
    keycode var byte        ' temporary storage for converted scancode
    ascii var byte          ' temporary storage for Ascii equivalent
    parity var byte         ' used during getkey for Parity tracking
    bitcnt var byte         ' bit counter for putkey routines
    shift var bit           ' If true, either shift key is pressed
    ctrl var bit            ' If true, either control key is pressed
    alt var bit             ' If true, either alt key is pressed
    
    KBrelease var bit       ' flags the release of a standard key
    KBextend var bit        ' flags that an extended key has been pressed
    KBrelextend var bit     ' flags the release of an extended key
    enav var bit            ' flags navigation key as extended version
    
    KB_timeout var byte
    TOvalue con $FF
    TOcntDLY con 10
    
    '===========================================================================
    ' Intitialize keyboard module, check for keyboard presence, and enable IRQ
    '===========================================================================
    
    'Check for presence of keyboard
    startupKB:
        init = 1
        Gosub modstart_KBinit   ' initialize & start keyboard module
        If KB_timeout = TOvalue Then
    ; *** error code goes here *** (keyboard not present on start-up)
        Endif
    
        Pause 1000
    
    'Set-up Interrupts
        INTEDG2 = 0             ' INT2 (KBCLK) interrupt on falling edge
        INT2IP = 0              ' INT2 (KBCLK) set as low priority interrupt
        INT2IF = 0              ' INT2 (KBCLK) clear flag
        INT2IE = 1              ' INT2 (KBCLK) interrupt enabled
    
        IPEN = 1                ' Enable priority levels on interrupts
        INTCON = %11000000      ' Enable all interrupts!
    
    '===========================================================================
    ' Program Start (MAIN)
    '===========================================================================
    
    mainloop:
    
        Gosub KBmain            ' checking for keyboard activity...
                                ' and decode any incoming characters.
    ' ---------------------------------
    '
    ' program code here --- KBmain will return result in ascii variable
    '
    ' ---------------------------------
        Goto mainloop
    The nice thing about using the assembly interrupt routine, is that there will be very little overhead to the PBP part of the program, and no matter how busy it gets, you will never miss a key.

    If you are interested in seeing more, then on the next installment I'll post the KBmain code (decodes keyscan codes), followed by the lookup table. Please let me know

    Cheers,
    Last edited by mytekcontrols; - 8th November 2005 at 05:13. Reason: missed something

  2. #2
    Join Date
    Nov 2005
    Location
    Perth, Australia
    Posts
    429


    Did you find this post helpful? Yes | No

    Default

    mytekcontrols: Thanks heaps for that.

    I managed to get it somewhat going without interrupts but it messes up whenever I type fast on the keyboard, so i'll give ur code a try. I'll just have to go through it all 1st to make sure I understand it

    If you could post your code that decodes the keyscan codes that would be excellent. I tried writing my own but the code was so long that it wouldn’t fit on the PIC16F84A that im using. It may be the case that I need to go with a different PIC?

  3. #3
    Join Date
    Nov 2005
    Location
    Perth, Australia
    Posts
    429


    Did you find this post helpful? Yes | No

    Default

    Also, could you post this subroutine: modstart_KBinit

    Thanks

  4. #4
    Join Date
    Sep 2005
    Posts
    2


    Did you find this post helpful? Yes | No

    Default

    Hi,

    Although it is written for another compiler, but I am sure it can be adapted to PBP.

    http://users.picbasic.org/Howto/PC_k...c_keyboard.htm


    Regards
    Mohammed

  5. #5
    mytekcontrols's Avatar
    mytekcontrols Guest


    Did you find this post helpful? Yes | No

    Arrow Installment #2: Keyscan Translation Tables

    Kamikaze47:
    Also, could you post this subroutine: modstart_KBinit
    This is not actually a separate subroutine, but just a different entry point into the KBmain routine.

    Kamikaze47:
    If you could post your code that decodes the keyscan codes that would be excellent. I tried writing my own but the code was so long that it wouldn’t fit on the PIC16F84A that im using. It may be the case that I need to go with a different PIC?
    I should warn you Doing a full decode of a PS2 keyboard is not trivial, and does require a substantial amount of code. There are shortcuts, but usually this will leave you without many of the features people come to expect from these style of keyboards. The first problem is that the keyscan codes as they are called, do not follow a very logical pattern. This was made even worse by the evolution from the older 84 key keyboards to the modern 104+ key keyboards. It is really unfortunate that a better standard wasn't developed early on.

    To develop my decode routines; I first did as you probably have done, looked around the net for something that was close. There is a lot of info out there, but not too much that is geared towards a PIC, and nothing I could find that would do it in PBP. What I did find, and which was my inspiration, is this piece from Craig Peacock written in 1998 for a 68HC705 Interfacing the AT keyboard. Understandably it is written for an entirely different processor (CISC instead of RISC), but I had a good grounding in 6502 from my early Atari days as a hobbiest assembly language programmer, and this was fairly close to 68HC705 syntax. So I initially took on the task of translating the 68HC705 code over to PBP. The end result worked, but it was massive!

    The next step of the process was one of refinement. The biggest memory drain were the extensive tables required in order to decode the convoluted keyscan code mess, into usable ascii codes. I decided what was needed first and foremost was to transform the keyscan codes into something more sequential, without all the gaps and disorder of the original. This way much of the decoding could then be done through purely mathamatically methods of adding or subtracting an offset value in order to get an ascii number code that equated to a given key or ctrl+key/shift+key combination. I ended up calling my keyscan translation "keycodes". Here is a look at both the keycodes and the ascii mapping that I used (includes extended numbers for non-ascii keypresses such as the function keys): KEYS.pdf.

    And here is the actual table code utilized:
    Code:
    keycode_tbl:
    asm
        movlw  Low keycode_tbl
        movff  WREG, _ktable
        movlw  High keycode_tbl
        movff  WREG, _ktable + 1
    endasm
    
        Readcode ktable + scancode, keycode
        Return
    
    asm
    keycode_tbl
        DB 0x80,0x39,0x80,0x35,0x33,0x31,0x32,0x3C
        DB 0x80,0x3A,0x38,0x36,0x34,0x80,0x1B,0x80
        DB 0x80,0x80,0x80,0x80,0x80,0x11,0x21,0x80
        DB 0x80,0x80,0x1A,0x13,0x01,0x17,0x22,0x3D
        DB 0x80,0x03,0x18,0x04,0x05,0x24,0x23,0x3E
        DB 0x80,0x80,0x16,0x06,0x14,0x12,0x25,0x3F
        DB 0x80,0x0E,0x02,0x08,0x07,0x19,0x26,0x80
        DB 0x80,0x80,0x0D,0x0A,0x15,0x27,0x28,0x80
        DB 0x80,0x2D,0x0B,0x09,0x0F,0x20,0x29,0x80
        DB 0x80,0x2E,0x2F,0x0C,0x2B,0x10,0x1C,0x80
        DB 0x80,0x80,0x2C,0x80,0x1E,0x1D,0x80,0x80
        DB 0x80,0x80,0x80,0x1F,0x80,0x2A,0x80,0x80
        DB 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80
        DB 0x80,0x41,0x80,0x44,0x47,0x80,0x80,0x80
        DB 0x40,0x4A,0x42,0x45,0x46,0x48,0x30,0x80
        DB 0x3B,0x4D,0x43,0x4C,0x4B,0x49,0x80,0x80
        DB 0x80,0x80,0x80,0x37
    endasm
            
    
    noshift_tbl:
    asm
        movlw  Low noshift_tbl
        movff  WREG, _ktable
        movlw  High noshift_tbl
        movff  WREG, _ktable + 1
    endasm
    
        Goto keytrans
    
    asm
    noshift_tbl
        DB "`","-","=","[","]","0","1","2"
        DB "3","4","5","6","7","8","9",0x5C
        DB ";","'",",",".","/",0x1B,0x7F,"*"
        DB "-","+"
    endasm
    
    
    shift_tbl:
    asm
        movlw  Low shift_tbl
        movff  WREG, _ktable
        movlw  High shift_tbl
        movff  WREG, _ktable + 1
    endasm
    
        Goto keytrans
    
    asm
    shift_tbl
        DB "~","_","+","{","}",")","!","@"
        DB "#","$","%","^","&","*","(","|"
        DB ":",0x22,"<",">","?",0xA0,0xBF,"*"
        DB "-","+"
    endasm
    
    
    ctrl_tbl:
    asm
        movlw  Low ctrl_tbl
        movff  WREG, _ktable
        movlw  High ctrl_tbl
        movff  WREG, _ktable + 1
    endasm
    
    keytrans:
        Readcode ktable + (keycode - $1B), ascii
        Return
    
    asm
    ctrl_tbl
        DB 0x00,0x1F,0xEA,0x1B,0x1D,0xE0,0xE1,0xE2
        DB 0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0x1C
        DB 0xEB,0xEC,0xED,0xEE,0x1E,0xC0,0xDF,"*"
        DB "-","+"
    endasm
    You'll notice if you scroll through the table code, that there are sub-tables marked as noshift, shift, and ctrl. These were required for some of the special navigation and Number Pad keys, which are part of the extended keyscan codes added during the 84 to 104 key transition period.

    Of course wouldn't you know it, they require special handling since their actual code assignment mimics those of other existing keys (they have an additional $E0 scan code sent in order to distinguish them from standard keys).

    Luckily the addition of these very small sub-tables doesn't really impact too greatly on code size requirements. Overall if you compare this table to the one used in Craig Peacock's 68HC705 code you will still see a dramatic reduction in size.

    More to come <img src="http://www.picbasic.co.uk/forum/images/icons/icon2.gif" align="absmiddle"> Installment #3: KBmain module (the decoding routines)
    Attached Images Attached Images
    Last edited by mytekcontrols; - 8th November 2005 at 23:14.

  6. #6
    mytekcontrols's Avatar
    mytekcontrols Guest


    Did you find this post helpful? Yes | No

    Arrow Installment #3a: KBmain module (first half)

    Here is the 1st half of the KBmain module, which is responsible for the keyboard keyscan-to-ascii decoding process.

    The KBmain Module Part A:
    Code:
    ;===============================================================================
    ; modstart_keybd - Initialization Entry Point for Keyboard Decoding Module.
    ;===============================================================================
    
    modstart_KBinit:
        Input KBCLK         ' set-up keyboard clock
        Input KBDAT         ' and keyboard data lines as digital inputs.
        KB_timeout = 0      ' initialize some registers... 
        KBcnt = 0
                            ' No attempt will be made to reset the keyboard as it
                            ' would still be in POST or BAT tests if power were
                            ' applied at the same time as the PIC.
        
    ;===============================================================================
    ; rstflag - Resets Status and LED Flags.
    ;===============================================================================
    
    rstflag:
        keystat = 0
        LED = 0
        Goto LEDshow
    
    ;===============================================================================    
    ; KBmain - Main Entry Point for Keyboard Decoding Module.
    ;===============================================================================
    
    KBmain:
        If KBindxO = KBindxI Then KBexit        ' if key buffer is empty then exit
    
    ' update combined shift/ctrl/alt registers from individual registers
        If Lshift = 1 or Rshift =1 Then
        shift = 1
        Else
        Shift = 0
        Endif
        If L_ctrl = 1 or R_ctrl =1 Then
        ctrl = 1
        Else
        ctrl = 0
        Endif
        If L_alt = 1 or R_alt =1 Then
        alt = 1
        Else
        alt = 0
        Endif
    
    ' check to see if this is a key coming up
        If KBrelease = 1 Then release           ' release code previously sent
        If KBextend = 1 Then extend             ' extended code previously sent
        If KBrelextend = 1 Then rel_ext         ' ext/release code previously sent
            
    ' go get keyboard byte from ring buffer for interpretation
        Gosub getbuf
        
    ' check for special codes
        Select Case scancode
        Case $F0            ' key has been released
        KBrelease = 1
        Goto KBexit
    
        Case $FE            ' key resend requested
        Gosub putkey
        Goto kbmain
        
        Case $FF            ' keyboard error
        Gosub putkey
        Goto rstflag
        
        Case $AA            ' successful completion of BAT
        Goto rstflag
        
        Case $E0            ' Extended key pressed
        KBextend = 1
        Goto KBexit
        
        Case $29            ' Space bar pressed
        ascii = $20
        Goto dec_done
    
        Case $0D            ' Tab key pressed
        ascii = $09
        Goto dec_done
    
        Case $66            ' Backspace key pressed
        ascii = $08
        Goto dec_done
    
        Case $5A            ' Enter key pressed
        ascii = $0D
        Goto dec_done
        
        Case $12            ' Left shift key pressed
        Lshift = 1
        
        Case $59            ' Right shift key pressed
        Rshift = 1
        
        Case $14            ' Left Ctrl key pressed
        L_ctrl = 1
        
        Case $11            ' Left Alt key pressed
        L_alt = 1
        
        Case $58            ' Caps lock key pressed
        If pcaplck = 0 Then caps
        pcaplck = 1
        
        Case $77            ' Numlock key pressed
        If pnumlck = 0 Then nums
        pnumlck = 1
        
        Case $7E            ' Scroll lock key pressed
        If pscrlck = 0 Then scrl
        pscrlck = 1
        End Select
        
    ' no special codes received, so verify byte as legal
        If scancode > $83 Then KBexit         ' if it exceeds table boundry leave!
    
    ' translation of keyboard scan code to our keycode
        Gosub keycode_tbl                     ' Else, translate it to keycode
        If keycode = $80 Then KBexit          ' Check it, and if it is un-
                                              ' decodeable then leave!
        If keycode < $31 Then KBmain1         ' if true, decode for ascii
                                              ' Else, this requires some fiddling...
        If keycode > $3F Then                 ' start of kepad number assignments
            If keycode < $4A Then             ' end of keypad number assignments
                If numlock = 1 Then           ' are we in numlock mode?
                    If ctrl = 0 Then          ' are there no control or
                        If shift = 0 Then     ' shift keys pressed?
                        keycode = keycode - $20 ' re-index for numbers 0-9
                        Goto KBmain1
                        Endif
                    Endif
                Endif          
            Endif
        Endif
        Goto navkey
    
    KBmain1:
    ' if this is a ctrl+key situation then decode as such    
        If ctrl = 1 Then
        If keycode < $1B Then ascii = keycode   ' ascii control char
        If keycode > $1A Then Gosub ctrl_tbl    ' special char decode
        Goto dec_done
        Endif
    
    ' Here is where we sort out the Caps-lock and Shift dilema, which is
    ' especially tricky when dealing with the Num Pad keys.
        Select Case caplock
        Case 0  ' cap lock OFF
            If shift = 0 Then
                If keycode < $1B Then           ' if alphanumeric...
                    ascii = keycode + $60       ' convert to ascii lowercase alpha
                Else                            ' if punctuation...
                    Gosub noshift_tbl           ' get non-shifted value
                Endif
            Else
                If keycode < $1B Then           ' if alphanumeric...
                    ascii = keycode + $40       ' convert to ascii uppercase alpha
                Else                            ' if punctuation...
                    Gosub shift_tbl             ' get shifted value
                Endif
            Endif
        Case 1  ' cap lock ON
            If shift = 0 Then
                If keycode < $1B Then           ' if alphanumeric...
                    ascii = keycode + $40       ' convert to ascii uppercase alpha
                Else                            ' if punctuation...
                    Gosub noshift_tbl           ' get non-shifted value
                Endif
            Else
                If keycode < $1B Then           ' if alphanumeric...
                    ascii = keycode + $60       ' convert to ascii lowercase alpha
                Else                            ' if punctuation...
                    Gosub shift_tbl             ' get shifted value
                Endif        
            Endif
        End Select
       
    ' decoding of key is complete by the time it reaches this point
    dec_done:
        keydec = 1                              'key decoded, set flag
        Gosub modstart_prnt2scrn
    
    ' Check to see if there are any more bytes left to process in the
    ' ring buffer. If there is, go get it and process it!
    KBexit:
        If KBindxO != KBindxI Then KBmain       ' more bytes left 
        Input KBCLK                             ' release clock line
        INT2IE = 1                              ' re-enable IRQ
        Return
    
    getbuf:
        KBindxO = KBindxO + 1                   ' increment index
        If KBindxO => KBbufsize Then KBindxO = 0
        scancode = KBbuf[KBindxO]               ' fetch next scancode
        Return
    More to come <img src="http://www.picbasic.co.uk/forum/images/icons/icon2.gif"> Installment #3b: KBmain module (2nd half)

  7. #7
    mytekcontrols's Avatar
    mytekcontrols Guest


    Did you find this post helpful? Yes | No

    Arrow Installment #3b: KBmain module (2nd half)

    Here is the 2nd and final piece of the KBmain module, which is responsible for the keyboard keyscan-to-ascii decoding process. This code covers all the subroutines utilized within the first part.

    The KBmain Module Part B:
    Code:
    ;===============================================================================
    ; scrl -    Toggle Status of Scroll lock and Echo to Keyboard
    ;===============================================================================
    
    scrl:
        pscrlck = 1            'set flag to prevent routine recall
        LED = (LED ^ %001)     'toggle Scroll lock flag
        Goto LEDshow
    
    ;===============================================================================
    ; nums -    Toggle Status of Num lock and Echo to Keyboard.
    ;===============================================================================
        
    nums:
        pnumlck = 1            'set flag to prevent routine recall
        LED = (LED ^ %010)     'toggle Num lock flag
        Goto LEDshow
    
    ;===============================================================================
    ; caps -    Toggle Status of Caps lock and Echo to Keyboard.
    ;===============================================================================
    
    caps:
        pcaplck = 1            'set flag to prevent routine recall
        LED = (LED ^ %100)     'toggle Caps lock flag
        Goto LEDshow
    
    ;===============================================================================
    ; extend -  An Extended key has been pressed.
    ;===============================================================================    
    
    extend:
        KBextend = 0
        Gosub getbuf
        Select Case scancode
        Case $F0        'an extended key has been released
        KBrelextend = 1
        Goto KBexit
        
        Case $11        'Right Alt pressed
        R_alt = 1
        
        Case $14        'Right Ctrl pressed
        R_ctrl = 1
        
        Case $5A        'Enter key on Numpad pressed
        ascii = $0D
        Goto dec_done
        
        Case $4A        '"/" on Numpad pressed
        ascii = "/"
        Goto dec_done
    
        Case $71        'Delete on Nav keys pressed
        keycode = $4A
        Goto navkey1
        End Select
        
        If scancode > $83 Then KBexit         'exceeds table boundry
        Gosub keycode_tbl                     'translate to keycode
        If keycode = $80 Then KBexit          'value not defined in table
        If keycode < $31 Then KBexit          'not a navigation key
        enav = 1
    navkey:
        If keycode = $45 Then KBexit          'ignore numpad 5 key
        If numlock = 1 Then
            If ctrl = 0 Then
                If shift = 0 Then
                    If keycode = $4A Then     ' if delete then...
                    ascii = "."               ' make as period
                    Goto dec_done
                    Endif
                Endif
            Endif
        Endif
        If keycode < $4A Then Goto navkey2
    
    navkey1:    
        keycode = keycode - $19               're-index for table read
        Goto KBmain1
    
    navkey2:
        ascii = keycode + $50                   'normal key
        If shift = 1 Then
            If enav or keycode < $40 Then
            ascii = keycode + $70 'shift modified key
            Else
            ascii = keycode - $10 're-index for numbers 0-9
            Endif
            enav = 0
        Endif
        If ctrl = 1 Then  ascii = keycode + $90 'ctrl modified key
        Goto dec_done
    
    ;===============================================================================
    ; release - A Key has been Released.
    ;===============================================================================        
    
    release:
        KBrelease = 0
        gosub getbuf    
        Select Case scancode
        Case $12        'Left shift key released
        Lshift = 0
        
        Case $59        'Right shift key released
        Rshift = 0
    
        Case $14        'Left Ctrl key released
        L_ctrl = 0
        
        Case $11        'Left Alt key released
        L_alt = 0
        
        Case $58        'Caps lock key released
        pcaplck = 0
        
        Case $7E        'Scroll lock key released
        pscrlck = 0
        
        Case $77        'Num lock key released
        pnumlck = 0
        End Select
       
        Goto KBexit
    
    ;===============================================================================
    ; rel_ext - An Extended Key has been Released.
    ;===============================================================================    
    
    rel_ext:
        KBrelextend = 0
        Gosub getbuf    
        Select Case scancode
        Case $11        'Right Alt key released
        R_alt = 0
        
        Case $14        'Right Ctrl key released
        R_ctrl = 0
        End Select
    
        Goto KBexit
    
    ;===============================================================================
    ; LEDshow - Copies the 3 LSB of the LED register to the keyboard for the
    ;           keyboard's Status LEDs (i.e.; Num Lock, Caps Lock, Scroll Lock).
    ;===============================================================================
    
    LEDshow:
        scancode = $ED
        Gosub putkey
        scancode = (LED & %111)
        Gosub putkey
        If init = 1 Then
        init = 0
        Return
        Endif
        Goto KBexit
        
    ;===============================================================================
    ;                                     _putkey
    ;                             Host to Keyboard Protocol
    ;             ___    _   _   _   _   _   _   _   _   _   _   _   _   ___
    ;       CLOCK    |__| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_|
    ;
    ;       DATA       |  S | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | P | E |ACK|
    ;
    ;          S = start bit = 0    P = odd parity   E = stop bit = 1   ACK =0
    ;                      data valid on falling edge of clock
    ;===============================================================================
    putkey:
        parity = 1                  'set-up parity register (odd parity)
        
                                    'Let's initiate a com request to the keyboard.
                                    
        Low KBCLK                   'pull clock line low.
        Pauseus 35                  'pause 35uSec...
        Low KBDAT                   'pull data line low.
        Pauseus 125                 'pause 125uSec longer...
        Input KBCLK                 'then release the clock line.
        
                                    'we have now signaled the keyboard
                                    'that we wish to send data to it
                                    'so let's do it!
    
        For bitcnt = 1 To 8         'set for 8 data bits
        Gosub sendbit               'send DATA BIT
        If scancode.0 = 0 Then nextbit  'if not a 1 then continue
        parity = parity + 1         'else, add up total # of 1's
    nextbit:
        scancode = scancode >> 1    'shift out next bit
        Next bitcnt
        scancode.0 = parity.0
        Gosub sendbit               'send PARITY BIT
        scancode.0 = 1
        Gosub sendbit               'send STOP BIT
                                    'all data bits sent so...
        Input KBDAT                 'release the data line
        Pauseus 100                 'and wait for ACK BIT to pass.
        Return                      'DONE --- Scancode sent to keyboard!!!
    
        
    sendbit:
    ' time-out counter --- requires keyboard response, else bail out!
        Pauseus TOcntDLY            'determines delay for each count
        KB_timeout = KB_timeout + 1
        If KB_timeout = TOvalue Then
        Return
        Endif
    
    ' looking for keyboard clock line to go low, then send data bit to it!
        If KBCLK = 1 Then sendbit   'loop until clock line goes low
        KBDAT = scancode.0          'send data bit
        KB_timeout = 0
    clkhigh:
        If KBCLK = 0 Then clkhigh   'loop until clock line goes high   
        Return
    Here are the port I/O assignments that I used on my PIC18F2525:
    Code:
    KBCLK   VAR PORTB.2 ; INT2
    KBDAT   VAR PORTB.3
    Actually any port pin can be used for the KBDAT I/O, and any port pin capable of generating an interrupt on a high-to-low transition could be used for KBCLK (with the proper changes in the interrupt code). Both ports are used for bi-directional communications with the keyboard, and should both be provided with 4.7K pull-up resistors to +5 Vdc. No initialization is required for either of these ports (it is all handled from within the KBmain module).

    Here is the pin-out for the mini-din plug used on a typical PS2 keyboard (I hope those guys in the Proton area don't mind me borrowing this):
    <img src="http://www.picbasic.co.uk/forum/attachment.php?attachmentid=603&stc=1&d=1131493312 ">

    And here is a test routine to see if everything is working properly:
    Code:
    DEFINE HSER_TXSTA 20h
    DEFINE HSER_BAUD 9600
    
    A var byte
    B var byte
    temp var byte
    
    '============================================
    ' Test Routine
    '============================================
    mainloop:
        Gosub KBmain                ' Do keyboard decode routine and if
                                    ' a key has been pressed then a jump
                                    ' will be made to "modstart_prnt2scrn".
        Goto mainloop               ' Else, keep looping!
    
    
    modstart_prnt2scrn:
        If keydec Then              ' check if decoded key is present
        keydec = 0                  ' If so... reset key decoded flag,
        Gosub dec2hex               ' create 2 digit hex value from ascii,
                                    ' and send formatted info out RS232.
                                    
                                    ' serial output example: ascii = A ($41)
                                     
        hserout["ascii = ",ascii," ($",A,B,")",$0D,$0A]
        Endif
        Return
    
    
    dec2hex:
        temp = ascii/16
        Gosub dec2hex_tbl
        A = temp
        temp = ascii//16
        Gosub dec2hex_tbl
        B = temp
        Return
    dec2hex_tbl:    
        Lookup temp,["0123456789ABCDEF"],temp
        Return
    And this concludes the last of our series on Interfacing a PS2 Keyboard to your PIC

    P.S. Let me know if I missed something. I all stripped this stuff out of a much larger program that I wrote, and hopefully I got all the bits.
    Attached Images Attached Images  
    Last edited by mytekcontrols; - 9th November 2005 at 01:47.

Similar Threads

  1. MXcom "C-BUS" interface to PIC question
    By tcbcats in forum mel PIC BASIC Pro
    Replies: 2
    Last Post: - 1st May 2014, 04:59
  2. Interface a pic with a Iphone/Itouch
    By Luckyborg in forum General
    Replies: 1
    Last Post: - 6th May 2009, 17:02
  3. 4 pin 4 x 4 keypad interface using pic basic pro
    By dunlao_john in forum mel PIC BASIC Pro
    Replies: 2
    Last Post: - 15th January 2009, 06:21
  4. USB Interface using PIC
    By Tissy in forum mel PIC BASIC Pro
    Replies: 21
    Last Post: - 22nd May 2006, 17:04
  5. Serial Pic to Pic using HSER
    By Chadhammer in forum mel PIC BASIC Pro
    Replies: 5
    Last Post: - 12th March 2005, 00:14

Members who have read this thread : 0

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts