AT Keyboard Interface V1.04

Introduction

Sometimes you only need a simple and cheap RS232 terminal to get sufficient control over a PC or a RS232 device. There is no need, no space or even no power to place a monitor, a computer case and a keyboard. Maybe there exists also the problem, that the PC or the device is located somewhere else and you want to interact with it over a short distance.

The cheapest way to obtain a complete user interface is the use of standard components, such as LCD modules and PC keyboards. A standard PC keyboard (PS/2 type) costs about US-$ 12, a 2 lines by 40 characters dot matrix LCD module around US-$ 20.
To connect these items to the serial port by cable, a microcontroller and a RS232 level shifter are necessary. For longer distance control, there exists also the possibility to interconnect the terminal with the other device by a wireless physical transmission layer.

<КАРТИНКА 1>

Possible Applications

The RS232 terminal for instance is very convenient in conjunction with a PC based Juke-Box playing MP3 files. You only need a command line programmable MP3 player (or a player with a supplied Active-X interface) and a software-based connection between player and RS232 port. This software 'connection' could be realized using Visual Basic and the often supplied Active-X interfaces of the various Windows based MP3 players.

Another possible area for applications is PC observed access control. Therefore, the RS232 terminal is placed at the entrance to the supervised area.

A further enhancement to be able to satisfy todays needs for network-based communication would be a complete TCP/IP based communication layer together with an Ethernet front-end. Then it would be possible to control simple Ethernet appliances, e.g. your coffee maker, electrical rolling shutters, autonomous net-based lawn mower,... ;-) by this remote terminal. Brave new world ...

<КАРТИНКА 2>

Concept

The routine below contains no support for an LCD display. It only shows the complete fetch and decoding of AT keyboard scan patterns and RS232 transmission of ASCII characters to the RS232 client. If you want a PIC 16F84 based solution with additional LCD, have a look at the keyboard v2xx project.

The problem with the PIC 16F84 is the lack of RS232 hardware. The whole keyboard scan pattern fetch, decode and RS232 data transmission is done by software. Additional RS232 data reception has also to be carried out by software - based on interrupts - but is not implemented within this project. The current implementation features a preemptive interrupt-based keyboard scan pattern acquisition.

 

Недавнее фото моего рабочего места, подключение PS/2 клавиатуры Microsoft к PIC16F84 КАРТИНКА Старое фото моего рабочего места, тогда разработка только начиналась
A recent picture of my workplace
connecting a Microsoft PS/2 AT keyboard to the PIC16F84

An elderly picture of my workplace,
at which the initial development took place

How it works

Any key stroke on the local keyboard will send the corresponding scan patterns from the keyboard to the PIC microcontroller. Afterwards, the microcontroller converts the keyboard scan patterns to ASCII characters and transmits them to the RS232 target device.

The keyboard scan code capture is done by an interrupt service routine (ISR). The event, which triggers the interrupt is a falling edge on the keyboard clock line (PORTB,0). Keyboard scan pattern acquisition takes place at the keyboard data line (PORTA,4). After 11 clocks (i.e. 11 external interrupts on RB0/INT), the interrupt service routine has completely captured an 8 bit element of the entire scan pattern and sets a ready flag. The decoding of this 8 bit element is then carried out during normal operation mode, activated by a valid ready flag whilst keeping the keyboard stalled (keyboard clock line low).

The fact, that the scan pattern acquisition is carried out using an interrupt service routine and the decoding thereof is done during normal operation mode allows for performing other tasks concurrently: That's why I call the acquisition methodology preemptive, it does not block the processor in the main loop while acquiring keyboard data - therefore passing processing resources to other services. Explicitely, it works as follows:
After proper acquisition, the corresponding flag KBDflag is set (at the end of the ISR) and the decoded keyboard character resides in the register KBD. The KBDflag is cleared at the end of the service routine KBDdecode.

Infinitive main loop to acquire keyboard input (keyboard_v1xx.asm), keyboard data is in register KBD:

;******************************
_MLOOP btfsc KBDflag ; check scan pattern reception flag
call KBDdecode ; if set, call decoding routine
;btfsc your_other_flag
;call your_other_service
goto _MLOOP
;******************************

Only RS232 transmission is supported by this program, since PORTB,0 interrupt is already used by the keyboard clock line. There exists no possibility to implement also RS232 reception using my modules m_rsxxx.asm, because they require PORTB,0 as well and are laid out as non-preemptive data acquisition routines (see also 'Limitations').

For dedicated code adaptations, please refer to the section 'User-specific Customization' below.

Specifications

Processor: Microchip PIC 16F84
Clock Frequency: 4 MHz crystal
Throughput: 1 MIPS
RS232 Baud Rate: 9600 baud, 8 bit, no parity, 1 stopbit
Code Size of entire Program: 523 instruction words
Keyboard Routine Features: Capability of uni-directional communication between microcontroller and keyboard
Acquisition Methodology: Preemptive, interrupt-based keyboard scan pattern acquisition, decoding to ASCII characters during normal operation mode activated by ready flag
Required Hardware: AT keyboard, PS/2 connector, MAX232
Required Software: RS232 terminal software (or Excel 97 RS232 Debug Interface)

Features

  • Uni-directional communication between microcontroller application and remote RS232 client.
  • Uni-directional communication between microcontroller and keyboard.
  • Support for all keyboard characters typed with shift button active and inactive.
  • English and modified Swiss-German 'codepages' available (QWERTY and QWERTZ)
  • Caps Lock implemented
  • Num Lock always active
  • Possibility to implement short-cuts or user defined characters for 'Esc', 'Num Lock', 'Scroll Lock' and 'F1' - 'F12' keys.
  • Further enhancement, not implemented: Support of ASCII conversion from direct ALT-DEC and CTRL-HEX entries, e.g. ALT + 6 + 4 = @ or CTRL + 3 + F = ?

To visualize the ASCII data sent by this microcontroller application, use a terminal program like the Windows Hyperterminal. Below an example session, which proves the correct functionality of the keyboard interface. This terminal program and the Excel 97 RS232 Debug Interface have been used to debug the interface during implementation time.

Example of a session using the Windows HyperTerminal. The entire contents was sent by the PIC controller

Example of a session using the Windows HyperTerminal. The entire contents was sent by the PIC controller.

Limitations

In case you want RS232 reception and keyboard decoding simultaneously on a single PIC 16X84, you'll have to configure either the keyboard clock line or the RS232 reception data line (both targeting PORTB,0 interrupt) to another separate interrupt source (e.g. PORTB,4 - PORTB,7 change interrupt) and to alter the RS232 data fetch routine to a preemptive one. But then you'll also run into troubles by using the LCD modules, because they are written to work on entire 8 bit ports (such as PORTB on 16X84, and PORTC & PORTD on 16X74).

So if you really appreciate to run the RS232 terminal entirely on a PIC 16X84 - from a technical perspective it is possible - you'll have to rewrite the LCD modules and the software RS232 reception routine. Be aware that there won't be a lot of code space remaining for other enhancements after putting all terminal related stuff onto the 16X84.

A workaround to get RS232 reception on the PIC 16X84 using this software could be a solution based on polling. But make sure you are polling fast enough, also in worst case.

Important note from Jason Plumb:

«Hey,

First, lemme say that I like your site and thank you for providing excellent reference material for us home-hobbyist microcontroller geeks. I am currently working on a music/noise project that uses a PS/2 keyboard interfaced to a PIC16F84, and I used your page at http://www.electronic-engineering.ch/microchip/projects/keyboard/v1xx/keyboard_v1xx.html heavily as a reference when designing hardware and writing code. Anyway, I just thought that I would mention that I ran into a problem that I have since solved. The problem involved sending bytes *TO* the keyboard from the PIC (in order to light NumLock and ScrollLock). Your "Host To Keyboard Protocol" section indicates that the keyboard will take the data line low for a clock after the byte is sent to create an ACK bit. Apparently, the PS/2 keyboard that I have (generic $10 comp-USA brand) doesn't send an ACK bit, but rather sends a whole byte. If my code attempted to wait for the ACK bit, it hung indefinitely. I changed the wait to look for a byte (by calling my existing function) and everything worked perfectly. I stumbled on this idea by looking at other online references (most notably, some Linux kernel code at http://www.mscs.mu.edu/~georgec/Classes/207.1998/14Minix_book/S/src%20kernel%20keyboard.c.html#307). I have seen this ACK *byte* mentioned elsewhere too. I *think* the keyboard sends back 0xFA as an ACK byte, but I have not personally confirmed this. Perhaps your excellent documentation could just use a quick note of clarification so that other don't run into the same problem. Maybe something as simple as: "NOTE: Some keyboards send an ACK byte (value 0xFA) instead of an ACK bit.".

Thanks again,
Jason»

Note from the author:

The comment above refers to bi-directional communication between PIC microcontroller and AT keyboard, i.e. to the source code of the AT Keyboard Interface V2.xx and higher versions. The bi-directional communication between host and keyboard is designed to support both Ack bits and Ack bytes.

•Every command sent from the host to the keyboard needs to have an Odd Parity bit and an Ack bit at the end.
•Every command received by the keyboard from the host needs to be acknowledged by the keyboard by sending an Ack byte (0xFA) to the host. See also section 'Host to Keyboard Protocol'.
A corresponding PIC assembler code example is shown below:

;*** switch keyboard LEDs on (default status) ***
KBDcmd 0xED ; keyboard LEDs' control command
KBDexp 0xFA ; expect keyboard acknowledge (FA)
movfw KBDleds ; load keyboard LEDs' status
KBDcmdw ; send keyboard LEDs' control data
KBDexp 0xFA ; expect keyboard acknowledge (FA)

However, some AT keyboards may behave different and may need code adaptations to get bi-directional communication working properly.

AT Keyboard Theory

A complete functional description and timing diagram of the AT keyboard is available at Craig Peacock's website. Please refer to his website Interfacing the PC's Keyboard for an excellent and comprehensive description of all features and commands of the AT keyboard. At this place, I want to thank Craig Peacock for his outstanding work with his website.
Below I only want to sketch the most important technical aspects to be known when interfacing a PC's keyboard. Small parts of the introduction below are more or less copied from Craig Peacock's tutorial.

Keyboard Scan Codes

The diagram below shows the scan codes assigned to the individual keys for the english keyboard layout. The keys' corresponding scan codes are the numbers on the keys, for example the scan code of the ESC key is 0x76. All scan codes are shown in hexadecimal representation.

Keyboard Scan Codes

The scan code assignments are quite random (thanks to IBM and other early computer manufacturers) and appear to be really weird sometimes, for instance the break key. In many cases the easiest way to convert the scan code to ASCII characters would be to use a lookup table. Below are the scan codes shown for the extended part of the keyboard and the numeric keypad.

Cite of Craig Peacock: "How about E1,14,77,E1,F0,14,F0,77! Now that can't be a valid scan code? Wrong again. It happens to be sent when you press the pause/break key. Don't ask me why they have to make it so long! Maybe they were having a bad day or something?"

By the way, AT stands for Advanced Technology...

The AT keyboard sends different scan codes on pressing, holding and releasing of each button. An example is given at the table below:

Press & Hold Down Scan Code Release Scan Code
Normal Scan Code 73 F0 73
Extended Scan Code E0 4A E0 F0 4A

All scan patterns can easily be visualized and verified with the AT Scan Code Debug Routine and the RS232 Debug Interface.

Keyboard to Host Protocol

The data transfer is implemented as bi-directional protocol: The keyboard can send data to the host (microcontroller) and the host can send commands and data to the keyboard. The host has the ultimate priority over the direction. It can at anytime (although not recommended) send a command to the keyboard.

The keyboard is free to send data to the host when both KBD data and KBD clock lines are high (idle). The serial clock is generated by the keyboard, but the host can also use it as a clear-to-send line: If the host takes the KBD clock line low, the keyboard will buffer any data until the KBD clock is released, i.e. goes high. Should the host take also the KBD data line low, then the keyboard will prepare to accept a command from the host.

The transmission of data in the forward direction, i.e. keyboard to host, is done with a frame of 11 bits. The first bit is a start bit (logic 0) followed by 8 data bits (LSB first), one parity bit (odd parity) and a stop bit (logic 1). Each bit has to be read on the falling edge of the clock.

AT keyboard to host protocol, with odd parity bit

AT keyboard to host protocol, with odd parity bit

Keyboard Commands

Once the host commands are sent from the host to the keyboard, the keyboard commands must be
sent from the keyboard to the host. If you think this way, you must be correct. Below details of some of
the commands which the keyboard is able to send.

FA Acknowledge
AA Power on self test passed (BAT completed)
EE See echo command (host commands)
FE Resend - upon receipt of the resend command the host should re-transmit the last byte sent.
00 Error or buffer overflow
FF Error or buffer overflow

Host to Keyboard Protocol

The host to keyboard protocol is initiated by taking the KBD data line low. However to prevent the keyboard from sending data at the same time that you attempt to send the keyboard data, it is common to take the KBD clock line low for more than 60 us. This is more than one bit length. Then the KBD data line is taken low, while the KBD clock line is released. The keyboard will start generating a clock signal on its KBD clock line. This process can take up to 10 ms. After the first falling edge has been detected, you can load the first data bit on the KBD data line. This bit will be read into the keyboard on the next falling edge, after which you can place the next bit of data. This process is repeated for all 8 data bits. After the data bits comes an odd parity bit.

Host to AT keyboard protocol, with odd parity bit and acknowledge

Host to AT keyboard protocol, with odd parity bit and acknowledge

Odd parity = NOT(XOR(bits[0..7]))

Once the parity bit has been sent and the KBD data line is in a idle state (high) for the next clock cycle, the keyboard will acknowledge the reception of the new data. The keyboard does this by taking the KBD data line low for the next clock transition. If the KBD data line is not idle after the 10th bit (start, 8 data bits, and parity bit), the keyboard will continue to send a KBD clock signal until the KBD data line becomes idle (Note: see also Limitations).

Host Commands

These commands are sent by the host to the keyboard. The most common command would be the setting/resetting of the status indicators (i.e. the Num lock, Caps Lock & Scroll Lock LEDs). The more common and useful commands are shown below.

ED Set status LED's - This command can be used to turn on and off the Num Lock, Caps Lock & Scroll Lock LED's. After sending ED, keyboard will reply with ACK (FA) and wait for another byte which determines their status. Bit 0 controls the Scroll Lock, bit 1 the Num Lock and bit 2 the Caps lock. Bits 3 to 7 are ignored.
EE Echo - upon sending an echo command to the keyboard, the keyboard should reply with an echo (EE).
F0 Set scan code set. Upon sending F0, keyboard will reply with ACK (FA) and wait for another byte, 01-03 which determines the scan code used. Sending 00 as the second byte will return the scan code set currently in use.
F3 Set typematic repeat rate. Keyboard will acknowledge command with FA and wait for a second byte, which determines the typematic repeat rate.
F4 Keyboard enable - clears the keyboard's output buffer, enables keyboard scanning and returns an acknowledgment.
F5 Keyboard disable - resets the keyboard, disables keyboard scanning and returns an acknowledgment.
FE Resend - upon receipt of the re-send command, the keyboard will re-transmit the last byte sent.
FF Reset - resets the keyboard.

Please refer to Craig Peacock's website for more information: Interfacing the PC's Keyboard

Project Resources

Available Microchip PIC Assembler Code

Main File Main Keyboard Decode Lookup Table SHIFT Keyboard Decode Lookup Table HEX Files
Latest version:
kbd_1xx.asm English 'codepage' (QWERTY)
View: eng_main.html
Download: eng_main.asm English 'codepage' (QWERTY)
View: eng_shif.html
Download: eng_shif.asm QWERTY 'codepage':
kbd_104_eng.hex

QWERTZ 'codepage':
kbd_104_sg.hex

Modified Swiss German 'codepage' (QWERTZ)
View: ger_main.html
Download: ger_main.asm Modified Swiss German 'codepage' (QWERTZ)
View: ger_shif.html
Download: ger_shif.asm
The above programs need additional include files (modules) to get successfully assembled: m_bank.asm, m_wait.asm, m_rs096.asm
For those, who are not familiar with interfacing a PIC to the RS232 using a MAX232: RS232-Interface.pdf (9.7 kB)

Schematic, Data Sheets and Pinout
AT Keyboard Specification (PDF, 189 kB)

The schematic of the AT keyboard interface using the PIC 16F84: Keyboard_V1xx.pdf.

You don't know how a dot matrix LCD is working? Have a look at my data sheets page.

Download ASCII Character Map: ASCII-Map.pdf

You can get the pinout and a description of the various keyboard connectors .

User-specific Customization / FAQ

This section covers important details of the code structure. For a high level view, please refer to the section 'How it works' above. Basically, I have written this keyboard project in such a way that it is completely customizable depending on your programming/PIC assembler skills.

How do I use the AT Keyboard Input?

Question: I'm running your AT Keyboard code. But how do I use the decoded input of the AT keyboard...?

Answer: The decoded content resides usually in the register KBD. You can use the content of this register in the main routine in the infinitive loop. Please see the section 'How it works' above.

How does the Scan Pattern Decoding work exactly?

Question: How does your AT scan pattern decoding work exactly? You are using two strange look-up tables...

Answer: Have a look at some parts of the 'AT Keyboard Lookup Table', e.g. the eng_main.asm (QWERTY):

The simple lookup table decoding is done with retlw x and DT x. These directives just return the corresponding ASCII character. The more sophisticated decoding is done with subroutines, i.e. goto _XYZ. This means for instance that for a ENTER/RETURN key hit on the keyboard, the subroutine _CRLF is executed (carriage return, line feed) and for ALT, CTRL, SHIFT and CAPS_LOCK, the corresponding flags are set in their corresponding subroutines.

goto _ALT ; ALT (set/clear ALT flag)
goto _SHIFT ; SHIFT (set/clear SHIFT flag)
retlw 0 ;
goto _CTRL ; CTRL (set/clear CTRL flag)
DT "q1" ; DT: MPASM directive to create a table (retlw x)
goto _CRLF ; CR, LF 0x5A

Because the keyboard sends slightly different scan patterns for both, key hit and key release, there is need for a key release handling. This is done with the so-called release flag. So for every single character typed, the interrupt service routine is called twice (due to two different scan codes for hit and release) and the scan pattern decoding routine is executed twice.

#define RELflag FLAGreg,0x00 ; release flag (0xF0)
#define SHIflag FLAGreg,0x01 ; shift flag (0x12 / 0x59)
#define SPEflag FLAGreg,0x02 ; special code flag (0xE0)
#define CAPflag FLAGreg,0x03 ; caps lock flag (0x0D)
#define ALTflag FLAGreg,0x04 ; ALT flag (0x11)
#define CTRLflag FLAGreg,0x05 ; CTRL flag (0x14)
#define KBDflag FLAGreg,0x06 ; keyboard data reception flag

There are these four main code blocks in the keyboard assembler source:

1.Interrupt based keyboard scan pattern acquisition
2.Keyboard scan code pre-decoding, set / clear flags
3.Scan code range checking (consistency checks)
4.Scan code decoding (look-up tables) & ASCII character conversion and output (LCD, RS232)
To save code space, I've implemented the two look-up tables (main and shift) as follows:

•Small letters (a-z) are translated directly with the main look-up table, capital letters (A-Z) are obtained by simply adding d'224' (8 bit unsigned wrap-around) to the main look-up table results of small letters. Active shift button of the keyboard is only tracked by the shift flag.
•Shift table is only used for special character conversion, e.g. +, %, &, (, ), ?
•Compression is also applied to lookup sections, where scan patterns are quite distant, e.g. for the entire keyboard num-block (keypad).
If you apply changes to the existing code, you may need to change the ORG directives in order to realign the assembler code properly, due to the different lookup tables and the PIC page boundaries.

My own Key Customization

Question: I have built successfully your AT keyboard project and it is running very well.
Now I want to customize my code such as to print out a predefined phrase e.g. 'Hello World' whenever I hit a specific key...?

Answer: This is not a difficult task, if you just use one specific key, for instance F9.
(For sequences like 'asdf', you must write your own detection routine for the main loop by checking the values in register KBD.) First look at the look-up tables (LUT), for instance the eng_main.asm (QWERTY):

It is essentially a LUT which performs the decoding of the acquired keyboard scan codes into characters. At the top, the entries for the F1-F12 function keys are located.
If you want a specific text to be displayed by pressing F9, change the line
retlw A'9' ; F9 -> 9 0x01

KBDtable ; (not used for characters typed with shift button active)
addwf PCL,F
retlw 0 ; invalid entry
retlw A'9' ; F9 -> 9 0x01
retlw 0 ;
retlw A'5' ; F5 -> 5

to
goto _MyRoutine

KBDtable ; (not used for characters typed with shift button active)
addwf PCL,F
retlw 0 ; invalid entry
goto _MyRoutine ; NEW: user-specific decoding for F9
retlw 0 ;
retlw A'5' ; F5 -> 5

Then you implement your own key-specific handler/subroutine _MyRoutine in the main file (section sub-routines), which displays your string, e.g. something like:

_MyRoutine
SENDw 'H' ; send to RS232
SENDw 'e'
SENDw 'l'
SENDw 'l'
SENDw 'o'
SENDw ' '
SENDw 'W'
SENDw 'o'
SENDw 'r'
SENDw 'l'
RETLW 'd' ; return with last character in w
; alternative termination:
; SENDw 'd'
; RETLW 0 ; clear w to obtain invalid entry

You can do anything in this routine, just terminate with a RETLW 0 and ensure not to change any reserved registers...
If you want to alter the output for the keyboard characters in general, look at the the '_OUTP' section in the assembler source file kbd_1xx.asm:

_OUTP ;*** RS232 ***
movfw KBD
SENDw ; send actual pressed keyboard character
goto _ClrStall

There, the acquired keyboard character (in KBD) is sent to the RS232 interface.
If having a LCD display, the value of KBD might also be displayed using:

_OUTP ;*** LCD & RS232 ***
movfw KBD
LCDw ; display keyboard character on LCD
movfw KBD
SENDw ; send actual pressed keyboard character
goto _ClrStall

This is roughly what is done in the '_OUTP' section in the assembler source file kbd_2xx.asm.

My own Key Sequence

Question: I have built successfully your AT keyboard project and it is running very well.
Now I want to customize my code such as to detect a specific key sequence, e.g. 'asdf'. Is this feasible...?

Answer: This is a more challenging task. (Alternatively, consider to just use one specific key e.g. F9 as described above.)
You need to write your own subroutine in the main loop in order to detect the desired character sequence. The keyboard character is usually passed within the register KBD.

;******************************
_MLOOP btfsc KBDflag ; check scan pattern reception flag
call KBDdecode ; if set, call decoding routine
;btfsc your_other_flag
;call your_other_service
goto _MLOOP
;******************************

Confusion about Sweden and Switzerland...?

Do you know Microsoft? Are you a global player, too? Or do you travel sometimes to Europe?
Did it happen to you, that you ended up in Stockholm instead of Zurich?

Don't worry, even Microsoft is confused by the two european countries, Sweden and Switzerland.
You are excused as well if you mix this up...

Microsoft Wired Keyboard 500
I recently bought two cheap, but nice Microsoft PS/2 keyboards - before PS/2 products are fading out. Microsoft obviously does not know the difference between Sweden and Switzerland: They supplied a skandinavian manual (swedish, norwegian, danish, finnish) instead of a central europe one (german, french, italian, english). Ok, it could have been worse: they could have shipped a japanese keyboard layout...

Last updated: 23.04.2006

trash.net/~luethi/