Also, could you post this subroutine: modstart_KBinit
Thanks
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
Kamikaze47:
This is not actually a separate subroutine, but just a different entry point into the KBmain routine.Also, could you post this subroutine: modstart_KBinit
Kamikaze47:
I should warn youIf 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?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:
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.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
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)
Last edited by mytekcontrols; - 8th November 2005 at 22:14.
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:
More to come <img src="http://www.picbasic.co.uk/forum/images/icons/icon2.gif"> Installment #3b: KBmain module (2nd half)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
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:
Here are the port I/O assignments that I used on my PIC18F2525: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
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).Code:KBCLK VAR PORTB.2 ; INT2 KBDAT VAR PORTB.3
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:
And this concludes the last of our series on Interfacing a PS2 Keyboard to your PICCode: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
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.
Last edited by mytekcontrols; - 9th November 2005 at 00:47.
Thanks heaps for that mytekcontrols. I really appreciate it
Now to go thru all the code and work out what its doing
I'll let u know how I go.
I hope it all works for you.
Here are some additional items to note:
If the code is used "as is" on a PIC18F type chip, then the following equates will probably need to be included:
Also if not using priority interrupts (another 18F feature) it will be necessary to change the define from "DEFINE INTLHAND myintL" to "DEFINE INTHAND myintL", and of course some of the following parameters will need to be changed to match your situation:Code:TMR0IP VAR INTCON2.2 ' TMR0 overflow interrupt priority bit INTEDG2 VAR INTCON2.4 ' External interrupt-2 edge select bit INT2IF VAR INTCON3.1 ' INT2 external interrupt flag INT2IE VAR INTCON3.4 ' INT2 external interrupt enable IPEN VAR RCON.7 ' Interrupt priority enable
Code: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!
If you're not using an 18F series PIC, then some of the following might apply:
In the interrupt routine; there is a syntax problem. Below you will see 2 lines listed. The first is the currently used one in the interrupt routine. The second is the correct one to use on something other then an 18F series device (PIC16..., PIC17...).
!!! IMPORTANT !!! If you use an input other than INT2 (RB2) as your KBCLK line, be sure to change any INT2IE or INT2IF references in both the interrupt and KBmain routines to match your new assignment.Code:rrncf _KBcode,F ; shift new bits right (do this only 7 times) ; rrf _KBcode,F ; use this instead for non-18F PIC's
Whew!!! I think that pretty much covers it. Good Luck!
Bookmarks