AT/PS2 Keybord - PIC Interface?
Has anyone ever interfaced an AT or PS2 keyboard to a PIC that might be able to give me an idea on how to set it up?
I know that the keyboard has data and clock lines which i will obviously have to hook up to a couple of the PICs I/O pins, but im not sure how to use pbp to recieve the data.
Any help would be greatly apreciated.
*edit* Im using a PIC16F84A
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,
1 Attachment(s)
Installment #2: Keyscan Translation Tables
Kamikaze47:
Quote:
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:
Quote:
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 :eek: 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)
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)
1 Attachment(s)
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.
1 Attachment(s)
PS2 interface code Updated to Version 1.1
I tweaked the code in order to better mimic the actions of the keypad on a PC. Specifically I now have shifted functions in both NumLock and non-NumLock modes as outlined below:
NumLock Mode:
Shift-Up --- numbers are generated 0,1,2,3... (ascii $30 - $39)
Shift-Dn --- standard navigation INSERT, END, DOWN... (ascii $90 - $99)
non-NumLock Mode:
Shift-Up --- standard navigation INSERT, END, DOWN... (ascii $90 - $99)
Shift-Dn --- numbers are generated 0,1,2,3... (ascii $30 - $39)
Note: when in a number output mode, the delete key on the numpad = ".", else it generates the ascii $7F code for delete. Also please notice that all ascii assignments above $7F are of my choosing, and not a part of the standard ascii code set.
Code space requirements have gone up to 2666 with this new version.
1 Attachment(s)
PS2 interface code Updated to Version 1.2
srob:
Quote:
There is a pre-programmed PIC called PAK-VI available from milford instuments. I beleive they cost around £25.
$29.95 USD
Yes I am familiar with this chip, and it certainly is an option. However when a person can go out and buy something like a PIC18F252 for about $8.00, put this ready-to-run piece of code into it, and still have 14K worth of programming space left, it really doesn't make much sense to do it the other way. The PAK-VI was really aimed more at a situation where a) you don't have an alternative method (i.e.; no keyboard code), or b) the existing processor is just not fast enough, or has enough memory to do this, and be able run your main program as well (i.e; BASIC Stamp).
Well I think I solved the "A" problem.
But don't get me wroung srob, it is always good to look at a problem from different angles and avail yourself of all the possible solutions.
Here is another update to the PS2 interface code. In this version I've added the ability to decode the Print Screen and Pause/Break keys. Also I've included an example of how to read the "alt" register and do something usefull. In the example a Ctrl+Alt+Delete key combo will do a Reset of your PIC. To really see this in action, initiate CapsLock, ScrollLock, or NumLock prior to the reset, and you should see the LED indicator turn off when the PIC re-initializes.
I moved the KBDAT I/O assignment...
On the last PS2 Keyboard Interface update (V1.2) I failed to mention that I moved KBDAT from PortB.3 to PortB.4
This allowed me to use the hardware PWM aspect of PortB.3 for something else. As I had stated in an earlier post, the KBDAT I/O line can be freely moved anywhere of your choosing, by simply changing one equate, and without impacting anything else in the code. I simply took advantage of this fact.
Kamikaze47 where are you??
You are right. Last seen, June 2006.
You expect to see people disappear from threads labeled:
Interfacing AC power lines....
...transformerless DC supply...
How to design without opto-couplers ...
How to measure high voltage while standing in a bucket of water...
But not from a thread labeled, “AT/PS2 Keyboard - PIC Interface?” ?
Then again we have to consider his user name!
Kamikaze47 where are you??
-Adam-
PIC16f84 PS/2 kbd to asccii @9600 bps n 1
this project is a milford chip 29.95$ buster!!! I've used it in several of my designs
http://www.electronic-engineering.ch...#faq_kbd_input