PS/2 Keyboard Module (swKBD.bas)

ps2_keyboardA PS/2 Keyboard can extend the functionality of a humble PIC microcontrollerby 101 Keys with 4 simple connections. There's no need for external components - the PIC Micro has more than enough on-board to handle the entire operation.

Before I press on, its important to note that I am using an AT (Advanced Technology) type keyboard. They aren't very advanced anymore, most have a 6 Mini-DIN connector and cost less than $10 from your local PC store or eBay.

Also, this is a explanatory guide as to how the Swordfish swKBD module can be used in the real world. More information about PS/2 Keyboard protocol other issues will be covered in a future article.



Almost everyone who's written a PIC program has relied at some-stage to use an array of switches or a matrix keypad for data entry. More often than not, I'd imagine the cost of the hardware, not to mention the I/Os required from the PIC would exceed that of what's required for a $5-10 keyboard.

If there's one more feature that might help cog application uses, perhaps its plug-and-play. There's no need to surrender a whole keyboard to one project - unplug it and use it on something else. The user module handles such scenarios quite well. (caveat: hot swapping can potentially cause damage on older devices - we're talking fairly old there, newer keyboards with PTC Fuses and robust port implementation will do fine).




The conventional AT PS/2 Keyboard either uses a 5 pin DIN connector, or a 6 pin Mini-DIN. As I've found Mini-DIN to be more popular these days, I'll focus on that. If you have a variation, then google the pinout and use the schematic below.

The picture on the right is what a 6 pin Mini-DIN looks like. There's a pinout for both male and female types. The wire color is also listed, but as many have found in the past with this sort of thing, wire colors are not a good indication to go by.


Keyboard - PIC

Typically, pull-up resistors are required for clock and data. Almost every PIC has pull-up resistors on PORTB, so I will be making use of those in software. If you plan on following suit, here is the schematic for interfacing with a PS/2 Keyboard:


PORTB not an option? Then connect a 1K-10K resistor for both clock and data to +5V.


Code Example

The code is well commented, so I will keep the introduction short.. Typical use of the module:

PIC Micro to Keyboard Example
Device = 18F2520                            // 18F2520 PIC in use, could be any 18F PIC
Clock = 32                                  // clock speed is 32Mhz (8MIPS)
Config MCLRE = Off                          // disable MCLR
Include "InternalOscillator.bas"            // search for "User Module Pack" at
Include "USART.bas"                         // used for displaying content on a uart terminal
Include "swKBD.bas"                         // PS2 Keyboard module. URL
SetBaudrate(br38400)                        // initialise USART for 38400 baud
USART.Write("Power On",13,10)               // send a message to the terminal
While True                                  // main program loop
    If swKBD.NewKey Then                    // checks the device for new information
        If KBD.ValidChar Then               // ensure the key is a valid non-white space character
            USART.Write(KBD.KeyChar)        // yes, display it via USART
        ElseIf KBD.KeyCode = KBD_ENTER Then // check if the 'Enter' key was pressed
            USART.Write(13,10)              // yes, send a line feed and carriage return
        EndIf                               //
    EndIf                                   //
    High(PORTB.7)                           // toggle PORTB.7 high then low,
    Low(PORTB.7)                            // to measure the time it takes between loops
Wend                                        //   


Important points


In the above code, calling swKBD.NewKey will initiate the Host (PIC) to check if there is new data on the Device (keyboard). If the device has nothing to send, it could take up to 4mS to return "False" and continue on in the program. With no keys being pressed, PORTB.5 would be toggled at 250Hz (I used this as an example of how they main program loop is not restricted to wait for a key to be pressed..)

In the above example, the additional task is toggling a pin, but what if you had a really long task, one that took seconds? Well, the keyboard will store all key-presses, and send them out, one by one each time swKBD.NewKey is called. How you handle this is up to you.

Caps Lock = Shift

Upon power-up, the keyboard is configured to 'Make Only' mode. That means data is ONLY sent when keys are pressed, nothing is sent when they are released. Other characters that would normally need 'Shift' to work (such as !@#$%^&*) are now accessed by pressing Caps lock (once) and then the keyboard buttons 12345678. Don't want to operate in 'Shift' mode? Then turn Caps Lock off by pressing it a second time.

The above method greatly reduce the code overhead required to parse key codes. If you're familiar with AT Keyboard Make/Break codes then you'll know what I mean.


Interesting points

KBD.ValidChar is only set true if a valid non-white space character is received. Other keys, such as 'Enter' or 'Escape' will not invoke the ValidChar flag, though they can still be captured very easily as shown in the example above.

Other non-white character codes can be found in the Public Const list, within the module.


Module Options

The module options are well commented, but if you want more information on any of them, have a read of the info that follows.

// default module options - user options can override these values...
#option KBD_CLK = PORTB.4                           // clock pin (connects to pin 5 of Mini-Din 6 pin)
#option KBD_DATA = PORTB.5                          // data pin (connects to pin 1 of Mini-Din 6 pin)
#option KBD_PULLUPS = true                          // a note: internal pullups can be used instead of external resistors
#option KBD_UpdateLEDs = true                       // monitor and handle Caps/Scroll/Num lock LEDs
#option KBD_DefaultASCII = LCASE                    // select UCASE or LCASE mode if LEDs are disabled



These are two of the four wires that connect to the keyboard. More info in the interfacing section of this article.

#Option KBD_PULLUPS = True

This option ensures the internal pullups on PORTB are used. That way, no external components are required when interfacing with the keyboard. Handy.

#Option KBD_UpdateLEDs

Everyone likes flashing LEDs, so there's no point in letting the three on the keyboard go to waste! This option will allow the module to monitor Caps, Scroll and Num lock presses. From there, the module will send the appropriate command to the keyboard to toggle the LEDs.

Also, when this option is enabled, if you press "A" with Caps off then the key will be encoded too "a". Alternatively, if Caps was on the it would return "A". Put simply, caps lock replaces shift (for reasons explained in "Important Points" above.)

#Option KBD_DefaultASCII

If you disable KBD_UpdateLEDs then the keyboard will either work in uppercase or lowercase mode. It's the equivalent of either holding shift, or leaving it released. "A" would translate to "a" in LCASE mode, and "1" would translate to "!" in UCASE mode.


The Module

Save the following program to the Swordfish UserLibrary folder, ensure it is named "swKBD.bas".


Program Flowchart

This is a basic sequence of events for the module. Click on the image to enlarge it.



External PS/2 AT Keyboard Links

Posted: 3 years 7 months ago by Jon Chandler #5492
Jon Chandler's Avatar
Nice work Graham!
Posted: 3 years 7 months ago by Graham Mitchell #5493
Graham Mitchell's Avatar
Cheers Jon

Version 1.1 Notes
  • FIX: Swordfish version no longer generates an error on KBD_UCASE elements.
  • FEATURE: Detects if the PS/2 Keyboard is connected, and handles all re-programming on-the-fly (allows keyboards to be disconnected/connected and remain functional).
  • IMPROVEMENT: Increased performance up to 250 samples/second (4mS per packet ... MAX).
  • IMPROVEMENT: Removed all interrupt routines - completely software driven (frees up TMR2 for other tasks)
  • IMPROVEMENT: In the unlikely event that a packet fails validation, the module will attempt to retrieve the packet instead of resetting the keyboard (up to 3 attempts)
Posted: 2 years 8 months ago by Roshan #9398
Roshan's Avatar
Dear Graham,

pls. clear me some aspect in your code.

When initing keyboard on power up, you are using following code.
// this routine will attempt to re-initialise the keyboard
// if an error occurs at any stage, the function will return false
// handy if you want to know if the keyboard is functional or not from main program..
Public Function ResetDevice() As Boolean
    Result = False                                  // reset the function result
    SendCommand($F5)                                // send "DISABLE" command    
    Result = Result Or TimeOut                      // check for good TX at each step
    SendCommand($F0)                                // send "SCAN CODE" command
    Result = Result Or TimeOut                      // check for good TX at each step
    SendCommand($03)                                // send argument "3" (scan code 3)
    Result = Result Or TimeOut                      // check for good TX at each step
    SendCommand($F9)                                // send "ALL KEYS MAKE" (break codes are no longer TXD)
    Result = Result Or TimeOut                      // check for good TX at each step
    SendCommand($F4)                                // send "ENABLE" command
    Result = Result Or TimeOut                      // check for good TX at each step
    Result = Not Result                             // invert the function result
End Function

when you send command to set scan code & after that you send again $03 as next required para. This will set scan code-3.

now you will send $F9, to support only make key.

it means keyboard will send only make key, it will not send break key.

Am i right ?

I am converting your code in pds & i have some problem. my code still send make & break code. I debug my code & get result after every command which is send to keyboard.

picture is attached.
Posted: 2 years 8 months ago by Roshan #9401
Roshan's Avatar
After posting code, i found return result is $FE, it means resend command.

I test my code & found i type $$03 instead of $03 & compiler does not make error.

btw now its working.


Forum Activity

Member Access