A PIC-Based Keyboard Emulator


This unit will emulate an IBM compatible AT keyboard. It will receive data via a RS-232 port and transmit using a standard 5 pin DIN connector. The PIC16F84 micro controller, will translate between the ASCII values received from the RS-232 and the keyboard scan codes expected from a standard keyboard.

The user will be able to use one keyboard at the transmitting station to send information to both the transmitting and receiving PCs.

System Specifications:

The emulator is designed to function exactly like a PC keyboard. The main difference being extended keys. Many of the extended keys such as the function keys, do not transmit ASCII data. That is the ASCII codes sent are often the same as those sent by other keys. For this reason alternate keys have been used.

The emulator will interpret CTRL characters as many of these keys. Instead of using function keys 1 through 10 the user will use CTRLk-A, CTRL-B, CTRL-C etc. CTRL-Z has been programmed to act as CTRL-ALT-DEL. This way the user still has the majority of the functions of the standard keyboard.


The emulator was constructed on a PICPROTO board. It consists of the PIC16F84 micro controller and a MAX232 chip. The ASCII values are received using a standard DB9 connector, and the scan codes are transmitted using standard keyboard cables, a 5 pin DIN or a PS/2 connector, depending on the PC.

The MAX232 chip converts the RS-232 signal coming from the COM port of the transmitting system into recognizable logic levels. This is required because of the definition of logic levels used in the protocol. RS-232 states that a logic level of zero may be represented by a voltage level between -25V and -3V, and a logic level of one can be between +3V and +25V. This leaves an undefined region between -3V to +3V.

The transmitting PC sends the ASCII code using RS-232 levels to the MAX232. It is then the job of the MAX232 to translate these levels into a signal recognized by the PIC. The PIC receives this information using one line, RA0. Each ASCII value is received in a serial stream, and assembled into a byte by the PIC.

In transmitting to the receiving system, two lines of the PIC are used. These lines are both connected directly to the keyboard connector, with both having 4k pull up resistors. Both default to inputs, becoming outputs only when transmitting. If they remain outputs when not transmitting it causes the receiving PC to lock up.

View Schematic


The communication process is started when the PIC detects a low pulse on RA0, this signals incoming ASCII data. When no data is being transmitted, the RS-232 transmitting line floats high. It is the high to low transition that signals the beginning of transmission. A simple loop easily takes care of polling for this change in logic level. The PIC will continually bit test RA0 until it receives a zero. Upon detecting the zero, the PIC must then retrieve the data from RA0 at a constant baud rate of 1200. This frequency is achieved through a set delay. The PIC polls the RS-232 a total of eight times, to make up a complete byte.

After reading in the data, the PIC assembles each bit received into a one byte package. This package is the ASCII code of the key pressed on the transmitting PC. Both the reading and assembly are done using the following segment of code:

	goto	ReceiveZero
	goto	ShiftIncomingData
	rrf	keypressed,f
	call	Delay3
	call	Delay3
	decfsz  bit_count,f
	goto	ReceiveNextBit
The stop bit has already been detected; therefore the loop executes eight times. (bit_count is loaded with 8) First the RECEIVE is checked to determine its logic level, line 2. The carry bit is set to the same value as the incoming data and then shifted into the keypressed register using the RRF command. Doing this shift eight times has the effect of moving the data from the MSB position to the LSB, with each new bit of data falling in behind the previous one. When the bit_count register hits zero, the PIC stops polling for data thus ignoring the parity and stop bits.

The main hurdle when converting the ASCII values into scan codes is the fact that the ASCII represents characters whereas the keyboard scan codes represent keys. For example the capital letter 'A' has an ASCII value of 41 hex and a scan code of 1C hex. Looking at the small 'a' we see that the ASCII value is 61 hex and the scan code is also 1C hex, the same as the capital letter. When dealing with the scan codes, differentiating between certain characters is done through the shift key. This means that the PIC must determine whether to send the scan code alone or proceeded by a shift code. This is done through a sequence of tests to determine where in the ASCII table the character falls, and if a shift needed or not

This code is transmitted to the receiving PC via RB0 and RB1, using a set frequency and data package pattern. The data is first disassembled using the carry flag and RRF command, the RRF command is used in conjunction with bit set and bit clear. Each time through the loop the LSB of the scan_code register is checked. If a one is detected, the DATABIT of PORTB is set and if a zero is detected, it is cleared, lines 6 and 11. The register is then shifted to the right, causing the LSB to overflow into the carry bit and the next bit becomes the LSB. The loop then repeats beginning with the check of the LSB.

The frequency of approximately 13kHz is taken care of by the call to a set delay. This delay is half of the required time delay. It must be done in this manner because the data must be sent when the clock line is low. By splitting the delay into two chunks it means that PIC can perform tasks such as sending data between each call. Nop's are used to balance out the frequency. Each instruction executed by the PIC while the clock line is either high or low changes the frequency. By using one nop for every instruction used to send the data the frequency remains fairly accurate.

One important aspect of the PIC to PC transmission is the calculation of the parity bit. The keyboard uses odd parity, which means that every piece of data sent must have an odd number of ones. When sending the scan codes, the PIC must also calculate the parity bit. This is done by keeping track of the number of ones sent and XORing the result with FF. Each time a one is sent, the parity register is incremented by one. Depending on the number of ones sent, the LSB of the parity bit will either be 1 or 0. If there is an odd number of ones, it means that the LSB of parity register will be one. When a one is XORed with a one, the result (parity bit) is zero. Similarly if an even number of ones is sent, the LSB of the parity register will be zero. XORed with a one, the zero becomes one. It is crucial that this calculation is correct, since if the parity bit is incorrect the PC will not recognize the code sent.

Upon completion of the transmission, the PIC returns to scanning for a low pulse on RA0, creating an infinite loop.

Download Code in MPASM format. This is the final version, with all basic keys as well as some extended keys functioning.

System Performance and Usage:

The prototype emulator was tested using two standard PCs, a transmitter and a receiver. The transmitter used Procom Plus to send ASCII data via COM1. (Note: Any program that sends ASCII data may be used.) The receiver was used Notepad (standard version packaged with Windows 95) to display the data and a 5 pin DIN with a PS/2 converter to receive.

On the whole the results were good, with accuracy close to one hundred percent. All letters, numbers and punctuation were sent with no problems. The tests were performed using a typing speed of approximately 45 words per minute. With an average word length of 6 characters, this translates to approximately 4.5 characters per second. Using a clock speed of 4MHz, the PIC is processing one instruction every microsecond, more than adequate to handle even the fastest typist.

Some problems are encountered when extended keys and the mouse are used. Often the extended keys that are not dealt with send the same ASCII values as keys that are dealt with. This means that if a user were to press the HOME key for example they may end up with an 'A'. Similarly, when the mouse is used within the Procom Plus window, it sends ASCII values to the COM port. The result is a string of unpredictable output on the receiving end. These problems are encountered because of the limitations of the ASCII codes and the program used to access and send data via the COM port. By making substitutions and making the user aware of these known problems, the emulator can be used in almost the same manner as a standard PC keyboard.

The one area where the emulator fails is during the boot up procedure. During a regular system boot up, the PC polls its keyboard connector checking for a keyboard. Once the PC sends the signal to the keyboard, the keyboard then replies with an acknowledgement that it is indeed a keyboard. If the PC does not receive this acknowledgement, the boot up procedure is halted. The emulator is unable to detect the signal from the PC and therefore unable to send the acknowledgement. This failure is the result of both inaccurate delays and the inability to decipher when the PC sends its signal. This problem is partly solved by the fact that the emulator can indeed send a F1 signal. When the boot up is halted, it can be restarted by pressing the F1 key. Therefore when the system halts, the user can press and the emulator will send the scan code for F1.