ps/2 keyboard interface with 8051 Assembly code
PS/2 KEYBOARD

General description
The PS/2 keyboard was originally an extension of the AT device. It supported a few additional host-to-keyboard commands and featured a smaller connector. These were the only differences between the two devices. However, computer hardware has never been about standards as much as compatibility. For this reason, any keyboard you buy today will be compatible with PS/2 and AT systems, but it may not fully support all the features of the original devices.
Modern PS/2 (AT) compatible keyboards
- Any number of keys (usually 101 or 104)
- 5-pin or 6-pin connector; adaptor usually included
- Bi-directional serial protocol
- Only scan code set 2 guaranteed.
- Acknowledges all commands; may not act on all of them.
Keyboards consist of a large matrix of keys, all of which are monitored by an on-board processor (called the “keyboard encoder”.) The specific processor varies from keyboard-to-keyboard but they all basically do the same thing: Monitor which key(s) is being pressed/released and send the appropriate data to the host. This processor takes care of all the debouncing and buffers any data in its 16-byte buffer, if needed. Your motherboard contains a “keyboard controller” that is in charge of decoding all of the data received from the keyboard and informing your software of what’s going on. All communication between the host and the keyboard uses an IBM protocol.
Scan Codes
The keyboard’s processor spends most of its time “scanning”, or monitoring, the matrix of keys. If it finds that any key is being pressed, released, or held down, the keyboard will send a packet of information known as a “scan code” to your computer. There is two different types of scan codes: “make codes” and “break codes”. A make code is sent when a key is pressed or held down. A break code is sent when a key is released. Every key is assigned its own unique make code and break code so the host can determine exactly what happened to which key by looking at a single scan code. The set of make and break codes for every key comprises a “scan code set”. There are three standard scan code sets, named one, two, and three. All modern keyboards default to set two.
Fig3.2.1 Scan Code1
Fig3.2.2 Scan Code2
Make Codes, Break Codes, and Typematic Repeat
Whenever a key is pressed, that key’s make code is sent to the computer. Keep in mind that a make code only represents a key on a keyboard–it does not represent the character printed on that key. This means that there is no defined relationship between a make code and an ASCII code. It’s up to the host to translate scan codes to characters or commands.
Although most set two make codes are only one-byte wide, there are a handful of “extended keys” whose make codes are two or four bytes wide. These make codes can be identified by the fact that their first byte is E0h.
Just as a make code is sent to the computer whenever a key is pressed, a break code is sent whenever a key is released. In addition to every key having its own unique make code, they all have their own unique break code. Fortunately, however, you won’t always have to use lookup tables to figure out a key’s break code–certain relationships do exist between make codes and break codes. Most set two break codes are two bytes long where the first byte is F0h and the second byte is the make code for that key. Break codes for extended keys are usually three bytes long where the first two bytes are E0h, F0h, and the last byte is the last byte of that key’s make code
Physical Interface
The physical PS/2 port is one of two styles of connectors: The 5-pin DIN or the 6-pin mini-DIN. Both connectors are completely (electrically) similar; the only practical difference between the two is the arrangement of pins. This means the two types of connectors can easily be changed with simple hard-wired adaptors. These costs about $6 each or you can make your own by matching the pins on any two connectors. The DIN standard was created by the German Standardization Organization (Deutsches Institut fuer Norm). PC keyboards use either a 6-pin mini-DIN or a 5-pin DIN connector. Keyboards with the 6-pin mini-DIN are often referred to as “PS/2″ keyboards, while those with the 5-pin DIN are called “AT” devices (”XT” keyboards also used the 5-pin DIN, but they are quite old and haven’t been made for many years.) All modern keyboards built for the PC is either PS/2, AT, or USB.
The cable connecting the keyboard/mouse to the computer is usually about six feet long and consists of four to six 26 AWG wires surrounded by a thin layer of Mylar foil shielding. If you need a longer cable, you can buy PS/2 extension cables from most consumer electronics stores. You should not connect multiple extension cables together. If you need a 30-foot keyboard cable, buy a 30-foot keyboard cable. Do not simply connect five 6-foot cables together. Doing so could result in poor communication between the keyboard/mouse and the host.
As a side note, there is one other type of connector you may run into on keyboards. While most keyboard cables are hard-wired to the keyboard, there are some whose cable is not permanently attached and come as a separate component. These cables have a DIN connector on one end (the end that connects to the computer) and a SDL (Shielded Data Link) connector on the keyboard end. A company called “AMP” created SDL. This connector is somewhat similar to a telephone connector in that it has wires and springs rather than pins, and a clip holds it in place. The pinouts for each connector are shown below:
Connector diagram
|
Male (Plug) |
Female (Socket) |
5-pin DIN (AT/XT): 1 – Clock 2 – Data 3 – Not Implemented 4 – Ground 5 – Vcc (+5V) |
|
Male (Plug) |
Female (Socket) |
6-pin Mini-DIN (PS/2): 1 – Data 2 – Not Implemented 3 – Ground 4 – Vcc (+5V) 5 – Clock 6 – Not Implemented |
||
|
|
|
6-pin SDL: A – Not Implemented B – Data C – Ground D – Clock E – Vcc (+5V) F – Not Implemented |
||
Fig3.4 Connector Diagram
Electrical Interface
Vcc/Ground provide power to the keyboard/mouse. The keyboard or mouse should not draw more than 275 mA from the host and care must be taken to avoid transient surges. Such surges can be caused by “hot-plugging” a keyboard/mouse (ie, connect/disconnect the device while the computer’s power is on.) Older motherboards had a surface-mounted fuse protecting the keyboard and mouse ports. When this fuse blew, the motherboard was useless to the consumer, and non-fixable to the average technician. Most new motherboards use auto-reset “Poly” fuses that go a long way to remedy this problem.
Summary: Power Specifications
Vcc = +4.5V to +5.5V.
Max Current = 275 mA.
The Data and Clock lines are both open-collector with pullup resistors to Vcc. An “open-collector” interface has two possible state: low, or high impedance. In the “low” state, a transistor pulls the line to ground level. In the “high impedance” state, the interface acts as an open circuit and doesn’t drive the line low or high. Furthermore, a “pullup” resistor is connected between the bus and Vcc so the bus is pulled high if none of the devices on the bus are actively pulling it low. The exact value of this resistor isn’t too important (1~10 kOhms); larger resistances result in less power consumption and smaller resistances result in a faster rise time. A general open-collector interface is shown below:
Figure 3.5: General open-collector interface.
A line is pulled to ground by setting the corresponding pin to output, and writing a “zero” to that port. The line is set to the “high impedance” state by setting the pin to input.
Communication
The PS/2 mouse and keyboard implement a bi-directional synchronous serial protocol. The bus is “idle” when both lines are high (open-collector). This is the only state where the keyboard/mouse is allowed begin transmitting data. The host has ultimate control over the bus and may inhibit communication at any time by pulling the Clock line low.
The device always generates the clock signal. If the host wants to send data, it must first inhibit communication from the device by pulling Clock low. The host then pulls Data low and releases Clock. This is the “Request-to-Send” state and signals the device to start generating clock pulses.
Summary: Bus States
Data = high, Clock = high: Idle state.
Data = high, Clock = low: Communication Inhibited.
Data = low, Clock = high: Host Request-to-Send
All data is transmitted one byte at a time and each byte is sent in a frame consisting of 11-12 bits. These bits are:
- 1 start bit. This is always 0.
- 8 data bits, least significant bit first.
- 1 parity bit (odd parity).
- 1 stop bit. This is always 1.
- 1 acknowledge bit (host-to-device communication only)
The parity bit is set if there is an even number of 1’s in the data bits and reset (0) if there is an odd number of 1’s in the data bits. The number of 1’s in the data bits plus the parity bit always add up to an odd number (odd parity.) This is used for error detection. The keyboard/mouse must check this bit and if incorrect it should respond as if it had received an invalid command.
Data sent from the device to the host is read on the falling edge of the clock signal; data sent from the host to the device is read on the rising edge. The clock frequency must be in the range 10 – 16.7 kHz. This means clock must be high for 30 – 50 microseconds and low for 30 – 50 microseconds.. If you’re designing a keyboard, mouse, or host emulator, you should modify/sample the Data line in the middle of each cell. I.e. 15 – 25 microseconds after the appropriate clock transition. Again, the keyboard/mouse always generates the clock signal, but the host always has ultimate control over communication.
Communication: Device-to-Host
The Data and Clock lines are both open collector. A resistor is connected between each line and +5V, so the idle state of the bus is high. When the keyboard or mouse wants to send information, it first checks the Clock line to make sure it’s at a high logic level. If it’s not, the host is inhibiting communication and the device must buffer any to-be-sent data until the host releases Clock. The Clock line must be continuously high for at least 50 microseconds before the device can begin to transmit its data.
As I mentioned in the previous section, the keyboard and mouse use a serial protocol with 11-bit frames. These bits are:
- 1 start bit. This is always 0.
- 8 data bits, least significant bit first.
- 1 parity bit (odd parity).
- 1 stop bit. This is always 1.
The keyboard/mouse writes a bit on the Data line when Clock is high, and it is read by the host when Clock is low. Figures 2 and 3 illustrate this.
Fig3.6.1: Device-to-host communication.
The clock frequency is 10-16.7 kHz. The time from the rising edge of a clock pulse to a Data transition must be at least 5 microseconds. The time from a data transition to the falling edge of a clock pulse must be at least 5 microseconds and no greater than 25 microseconds.
The host may inhibit communication at any time by pulling the Clock line low for at least 100 microseconds. If a transmission is inhibited before the 11th clock pulse, the device must abort the current transmission and prepare to retransmit the current “chunk” of data when host releases Clock. A “chunk” of data could be a make code, break code, device ID, mouse movement packet, etc. For example, if a keyboard is interrupted while sending the second byte of a two-byte break code, it will need to retransmit both bytes of that break code, not just the one that was interrupted.
If the host pulls clock low before the first high-to-low clock transition, or after the falling edge of the last clock pulse, the keyboard/mouse does not need to retransmit any data. However, if new data is created that needs to be transmitted, it will have to be buffered until the host releases Clock. Keyboards have a 16-byte buffer for this purpose. If more than 16 bytes worth of keystrokes occur, further keystrokes will be ignored until there’s room in the buffer. Mice only store the most current movement packet for transmission.
Host-to-Device Communication
The packet is sent a little differently in host-to-device communication…
First of all, the PS/2 device always generates the clock signal. If the host wants to send data, it must first put the Clock and Data lines in a “Request-to-send” state as follows:
- Inhibit communication by pulling Clock low for at least 100 microseconds.
- Apply “Request-to-send” by pulling Data low, and then release Clock.
The device should check for this state at intervals not to exceed 10 milliseconds. When the device detects this state, it will begin generating Clock signals and clock in eight data bits and one stop bit. The host changes the Data line only when the Clock line is low, and the device reads data when Clock is high. This is opposite of what occurs in device-to-host communication.
After the stop bit is received, the device will acknowledge the received byte by bringing the Data line low and generating one last clock pulse. If the host does not release the Data line after the 11th clock pulse, the device will continue to generate clock pulses until the Data line is released (the device will then generate an error.)
The host may abort transmission at time before the 11th clock pulse (acknowledge bit) by holding Clock low for at least 100 microseconds.
To make this process a little easier to understand, here’s the steps the host must follow to send data to a PS/2 device:
1) Bring the Clock line low for at least 100 microseconds.
2) Bring the Data line low.
3) Release the Clock line.
4) Wait for the device to bring the Clock line low.
5) Set/reset the Data line to send the first data bit
6) Wait for the device to bring Clock high.
7) Wait for the device to bring Clock low.
8) Repeat steps 5-7 for the other seven data bits and the parity bit
9) Release the Data line.
10) Wait for the device to bring Data low.
11) Wait for the device to bring Clock low.
12) Wait for the device to release Data and Clock
Figure 3 shows this graphically and Figure 4 separates the timing to show which signals are generated by the host, and which are generated by the PS/2 device. Notice the change in timing for the “ack” bit–the data transition occurs when the Clock line is high (rather than when it is low as is the case for the other 11 bits.)
Fig 3.6.2: Host-to-Device Communication.
Fig3.6.3: Detailed host-to-device communication.
Referring to Figure 4, there are two time quantities the host looks for. (a) is the time it takes the device to begin generating clock pulses after the host initially takes the Clock line low, which must be no greater than 15 ms. (b) is the time it takes for the packet to be sent, which must be no greater than 2ms. If either of these time limits is not met, the host should generate an error. Immediately after the “ack” is received, the host may bring the Clock line low to inhibit communication while it processes data. If the command sent by the host requires a response, that response must be received no later than 20 ms after the host releases the Clock line. If this does not happen, the host generates an error.
|
Keyboard Scan Codes: Set 2 101-, 102-, and 104-key keyboards:
Table 3.1 keyboard scan codes
ACPI Scan Codes:
Windows Multimedia Scan Codes:
ps/2 keyboard Assembly code with lcd:
$mod51
KEYB_DATA EQU P1.3 ;Line to which PS/2 keyboard data line is connected
KEYB_CLOCK EQU P1.2 ;Line to which PS/2 keyboard clock line is connected
KB_ACK EQU 0FAh ;Constant which represents the ‘ACK’ code sent back from keyboard
KB_BREAK EQU 0F0h ;Constant which represents that a key is no longer being pressed
KB_EXTENDED EQU 0E0h ;Constant which represents an extended scan code sequence
;==========================================================
;===========================================================
;SendSerial EQU 0041h
;SendSerialHexByte EQU 0044h
;SendSerialByte EQU 0047h
;==========================================================
; PROGRAM LOCATION
;
;modified to 0000h rather than 8000h.
;===========================================================
ORG 0000h
;==========================================================
; PROGRAM CODE
;
;This is the actual guts of the demonstration code.
;===========================================================
LCALL lcdi
LCALL SendSerial ;Send ‘initializing’ message
DB ”Initializing keyboard “,13,0
LCALL PS2_GenericInit ;Initialize the keyboard
LCALL SendSerial ;Send the ‘init done’ message
DB ”Init successful dumping PS/2 Communication “,13,0
CLR RI
MOV R7,#00
Loop:
JB RI,ExitProgram ;If a key has been pressed, exit
; LCALL PS2_GetByte ;Get a scan code from the keyboard
LCALL PS2_GetScanCode
JNC Loop ;If no key was pressed, keep waiting
;See if the key we got IS the key we’re looking for
;JZ skip
LCALL datawrt
skip:PUSH ACC ;Otherwise, save the value returned by the keyboard
MOV A,#’{’ ;Display a leading bracket
LCALL SendSerialByte ;Send it to the serial port
POP ACC ;Restore the value returned by the keyboard
LCALL SendSerialHexByte ;Send it to the terminal as a 2-byte hex value
MOV A,#’}’ ;Display a trailing bracket
LCALL SendSerialByte ;Send it to the serial port
SJMP Loop ;Wait for the next keypress
ExitProgram:
CLR RI ;Clear serial input and exit back to SBCMON
RET
;*****************************************************************
;* Function: ByteTo2Hex *
;* Purpose: Convert a single byte into two hex digits *
;* Input: A = Byte to convert (0×00-0xFF) *
;* Output: A = High nibble (ASCII 0×30-0×39,0×41-0×46) *
;* R0 = Low nibble (ASCII 0×30-0×39, 0×41-0×46) *
;* Destroyed Registers: None *
;*****************************************************************
ByteTo2Hex:
MOV R0,A
ANL A,#0Fh
ADD A,#0F6h
JNC byte_to_bcd_2
ADD A,#07h
byte_to_bcd_2:
ADD A,#3Ah
XCH A,R0
SWAP A
ANL A,#0Fh
ADD A,#0F6h
JNC byte_to_bcd_3
ADD A,#07h
byte_to_bcd_3:
ADD A,#3Ah
RET
;*****************************************************************
;* Function: SendSerialHexByte *
;* Purpose: Sends the byte in the accumulator to the serial *
;* port as two hexadecimal nibbles. *
;* Input: ACC: Byte to send *
;* Output: None. *
;* Destroyed Registers: None *
;*****************************************************************
SendSerialHexByte:
PUSH 00h ;Save R0
PUSH ACC ;Save ACC
LCALL ByteTo2Hex ;Convert high byte to two bytes of BCD
LCALL SendSerialByte ;Send the high byte
MOV A,R0 ;Send the low byte
LCALL SendSerialByte ;Send it
POP ACC ;Restore R0
POP 00h ;Restore R0
RET
;*****************************************************************
;* Function: SendSerialByte *
;* Purpose: Sends the byte in the accumulator to the serial *
;* port and waits for it to be sent before returning. *
;* Input: ACC: Byte to send *
;* Output: None. *
;* Destroyed Registers: None *
;*****************************************************************
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SendSerialByte:
CLR TI ;Prepare for transmit
MOV SBUF,A ;Send the byte out
JNB TI,$ ;Wait for byte to be sent
RET ;Done!
;*****************************************************************
;* Function: SendSerial *
;* Purpose: Sends the null-terminated string that immediately *
;* follows the LCALL that called the function to the *
;* serial port. *
;* Input: ACC: Byte to send *
;* Output: None. *
;* Destroyed Registers: None *
;*****************************************************************
SendSerial:
MOV TMOD,#22h ;Timer 1 and timer 0in Auto-reload mode
MOV TH1,#0FDh ;Reload value for 9600 baud @ 11.059 Mhz
SETB TR1 ;Turn on timer 1 for baud rate generator
MOV SCON,#52h ;Receiver Enable/Timer 1 Baud Rate 8-bit
POP DPH
POP DPL
PUSH ACC
PUSH PSW
CLR TI
SendSerial1:
CLR A
MOVC A,@A+DPTR
INC DPTR
JZ SendSerialExit
MOV SBUF,A
JNB TI,$
CLR TI
;(v1.3.1) – Removed LF-appending
;———————————-
CJNE A,#13,SendSerial1
MOV A,#10
MOV SBUF,A
JNB TI,$
CLR TI
;———————————-
SJMP SendSerial1
SendSerialExit:
POP PSW
POP ACC
PUSH DPL
PUSH DPH
RET
PS2_GenericInit:
MOV A,#0FFh ;Initializtation command
ACALL PS2_SendByte ;Send command to keyboard
ACALL PS2_GetByte ;Get a response byte, should be ACK
ACALL PS2_ChkAck ;Check to see if it was ACK
JNC PS2_GenericInit ;If it wasn’t repeat and try to initialize again
MOV A,#0F4h ;Enable reporting
ACALL PS2_SendByte ;Send command to keyboard
ACALL PS2_GetByte ;Get a response byte, should be ACK
ACALL PS2_ChkAck ;Check to see if it was ACK
JNC PS2_GenericInit ;If it wasn’t repeat and try to initialize again
RET
;//////////////////////////////////////////////////////////////////////////////////////////////
;PS2_MouseInit:
; MOV A,#0FFh ;Initializtation command
; ACALL PS2_SendByte ;Send command to keyboard
; ACALL PS2_GetByte ;Get a response byte, should be ACK
; ACALL PS2_ChkAck ;Check to see if it was ACK
; JNC PS2_MouseInit ;If it wasn’t repeat and try to initialize again
; MOV A,#0F6h ;Set default values
; ACALL PS2_SendByte ;Send command to keyboard
; ACALL PS2_GetByte ;Get a response byte, should be ACK
; ACALL PS2_ChkAck ;Check to see if it was ACK
; JNC PS2_MouseInit ;If it wasn’t repeat and try to initialize again
; MOV A,#0F4h ;Enable reporting
; ACALL PS2_SendByte ;Send command to keyboard
; ACALL PS2_GetByte ;Get a response byte, should be ACK
; ACALL PS2_ChkAck ;Check to see if it was ACK
; JNC PS2_MouseInit ;If it wasn’t repeat and try to initialize again
; RET
;////////////////////////////////////////////////////////////////////////////////////////////////
;***************************************************************
;* Function: Scan code translation table *
;* Author: Craig Steiner *
;*————————————————————-*
;* Description: The following codes are the scan codes returned*
;* by PS2_GetScanCode. The keyboard sends a byte(s) when a *
;* key is pressed and another sequence of bytes when the key *
;* is released. These sequences can be 1-3 bytes in length. *
;* The PS2_GetScanCode routine converts the keyboard scan *
;* codes into an internal representation that can be fit in *
;* a single byte. The constants below are the values that *
;* will be returned in the case of certain keypresses. In *
;* all cases, a value of 0-127 means the key was pressed *
;* while a value between 128-255 means the key was released. *
;***************************************************************
KS_KP_SLASH EQU ‘a’
KS_RIGHTCTRL EQU ‘b’
KS_RIGHTGUI EQU ‘c’
KS_RIGHTALT EQU ‘d’
KS_APPS EQU ‘e’
KS_PRTSCREEN1 EQU ‘f’
KS_PRTSCREEN2 EQU ‘g’
KS_INSERT EQU ‘h’
KS_HOME EQU ‘i’
KS_PGUP EQU ‘j’
KS_DELETE EQU ‘k’
KS_END EQU ‘l’
KS_PGDOWN EQU ‘m’
KS_UPARROW EQU ‘n’
KS_LEFTARROW EQU ‘o’
KS_DOWNARROW EQU ‘p’
KS_RIGHTARROW EQU ‘r’
KS_KP_ENTER EQU ’s’
KS_BACKSPACE EQU ‘t’
KS_TAB EQU ‘u’
KS_CAPS EQU ‘v’
KS_LEFTSHIFT EQU ‘w’
KS_LEFTCTRL EQU ‘x’
KS_LEFTGUI EQU ‘y’
KS_LEFTALT EQU ‘z’
KS_F1 EQU 01h
KS_F2 EQU 02h
KS_F3 EQU 03h
KS_F4 EQU 04h
KS_F5 EQU 05h
KS_F6 EQU 06h
KS_F7 EQU 07h
KS_F8 EQU 08h
KS_F9 EQU 09h
KS_F10 EQU 0Ah
KS_F11 EQU 0Bh
KS_F12 EQU 0Ch
KS_RIGHTSHIFT EQU 0Dh
KS_ENTER EQU 0Eh
KS_SCROLL EQU 0Fh
KS_NUMLOCK EQU 10h
KS_KP_ASTERISK EQU 11h
KS_KP_MINUS EQU 12h
KS_KP_PLUS EQU 13h
KS_KP_POINT EQU 14h
KS_ACPI_POWER EQU 15h
KS_ACPI_SLEEP EQU 16h
KS_ACPI_WAKE EQU 17h
KS_ESCAPE EQU 1Bh
KS_KP_0 EQU ‘)’
KS_KP_1 EQU ‘!’
KS_KP_2 EQU ‘@’
KS_KP_3 EQU ‘#’
KS_KP_4 EQU ‘
|
$mod51
KEYB_DATA EQU P1.3 ;Line to which PS/2 keyboard data line is connected
KEYB_CLOCK EQU P1.2 ;Line to which PS/2 keyboard clock line is connected
KB_ACK EQU 0FAh ;Constant which represents the ‘ACK’ code sent back from keyboard
KB_BREAK EQU 0F0h ;Constant which represents that a key is no longer being pressed
KB_EXTENDED EQU 0E0h ;Constant which represents an extended scan code sequence
;==========================================================
;===========================================================
;SendSerial EQU 0041h
;SendSerialHexByte EQU 0044h
;SendSerialByte EQU 0047h
;==========================================================
; PROGRAM LOCATION
;
;modified to 0000h rather than 8000h.
;===========================================================
ORG 0000h
;==========================================================
; PROGRAM CODE
;
;This is the actual guts of the demonstration code.
;===========================================================
LCALL lcdi
LCALL SendSerial ;Send ‘initializing’ message
DB ”Initializing keyboard “,13,0
LCALL PS2_GenericInit ;Initialize the keyboard
LCALL SendSerial ;Send the ‘init done’ message
DB ”Init successful dumping PS/2 Communication “,13,0
CLR RI
MOV R7,#00
Loop:
JB RI,ExitProgram ;If a key has been pressed, exit
; LCALL PS2_GetByte ;Get a scan code from the keyboard
LCALL PS2_GetScanCode
JNC Loop ;If no key was pressed, keep waiting
;See if the key we got IS the key we’re looking for
;JZ skip
LCALL datawrt
skip:PUSH ACC ;Otherwise, save the value returned by the keyboard
MOV A,#’{’ ;Display a leading bracket
LCALL SendSerialByte ;Send it to the serial port
POP ACC ;Restore the value returned by the keyboard
LCALL SendSerialHexByte ;Send it to the terminal as a 2-byte hex value
MOV A,#’}’ ;Display a trailing bracket
LCALL SendSerialByte ;Send it to the serial port
SJMP Loop ;Wait for the next keypress
ExitProgram:
CLR RI ;Clear serial input and exit back to SBCMON
RET
;*****************************************************************
;* Function: ByteTo2Hex *
;* Purpose: Convert a single byte into two hex digits *
;* Input: A = Byte to convert (0×00-0xFF) *
;* Output: A = High nibble (ASCII 0×30-0×39,0×41-0×46) *
;* R0 = Low nibble (ASCII 0×30-0×39, 0×41-0×46) *
;* Destroyed Registers: None *
;*****************************************************************
ByteTo2Hex:
MOV R0,A
ANL A,#0Fh
ADD A,#0F6h
JNC byte_to_bcd_2
ADD A,#07h
byte_to_bcd_2:
ADD A,#3Ah
XCH A,R0
SWAP A
ANL A,#0Fh
ADD A,#0F6h
JNC byte_to_bcd_3
ADD A,#07h
byte_to_bcd_3:
ADD A,#3Ah
RET
;*****************************************************************
;* Function: SendSerialHexByte *
;* Purpose: Sends the byte in the accumulator to the serial *
;* port as two hexadecimal nibbles. *
;* Input: ACC: Byte to send *
;* Output: None. *
;* Destroyed Registers: None *
;*****************************************************************
SendSerialHexByte:
PUSH 00h ;Save R0
PUSH ACC ;Save ACC
LCALL ByteTo2Hex ;Convert high byte to two bytes of BCD
LCALL SendSerialByte ;Send the high byte
MOV A,R0 ;Send the low byte
LCALL SendSerialByte ;Send it
POP ACC ;Restore R0
POP 00h ;Restore R0
RET
;*****************************************************************
;* Function: SendSerialByte *
;* Purpose: Sends the byte in the accumulator to the serial *
;* port and waits for it to be sent before returning. *
;* Input: ACC: Byte to send *
;* Output: None. *
;* Destroyed Registers: None *
;*****************************************************************
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SendSerialByte:
CLR TI ;Prepare for transmit
MOV SBUF,A ;Send the byte out
JNB TI,$ ;Wait for byte to be sent
RET ;Done!
;*****************************************************************
;* Function: SendSerial *
;* Purpose: Sends the null-terminated string that immediately *
;* follows the LCALL that called the function to the *
;* serial port. *
;* Input: ACC: Byte to send *
;* Output: None. *
;* Destroyed Registers: None *
;*****************************************************************
SendSerial:
MOV TMOD,#22h ;Timer 1 and timer 0in Auto-reload mode
MOV TH1,#0FDh ;Reload value for 9600 baud @ 11.059 Mhz
SETB TR1 ;Turn on timer 1 for baud rate generator
MOV SCON,#52h ;Receiver Enable/Timer 1 Baud Rate 8-bit
POP DPH
POP DPL
PUSH ACC
PUSH PSW
CLR TI
SendSerial1:
CLR A
MOVC A,@A+DPTR
INC DPTR
JZ SendSerialExit
MOV SBUF,A
JNB TI,$
CLR TI
;(v1.3.1) – Removed LF-appending
;———————————-
CJNE A,#13,SendSerial1
MOV A,#10
MOV SBUF,A
JNB TI,$
CLR TI
;———————————-
SJMP SendSerial1
SendSerialExit:
POP PSW
POP ACC
PUSH DPL
PUSH DPH
RET
PS2_GenericInit:
MOV A,#0FFh ;Initializtation command
ACALL PS2_SendByte ;Send command to keyboard
ACALL PS2_GetByte ;Get a response byte, should be ACK
ACALL PS2_ChkAck ;Check to see if it was ACK
JNC PS2_GenericInit ;If it wasn’t repeat and try to initialize again
MOV A,#0F4h ;Enable reporting
ACALL PS2_SendByte ;Send command to keyboard
ACALL PS2_GetByte ;Get a response byte, should be ACK
ACALL PS2_ChkAck ;Check to see if it was ACK
JNC PS2_GenericInit ;If it wasn’t repeat and try to initialize again
RET
;//////////////////////////////////////////////////////////////////////////////////////////////
;PS2_MouseInit:
; MOV A,#0FFh ;Initializtation command
; ACALL PS2_SendByte ;Send command to keyboard
; ACALL PS2_GetByte ;Get a response byte, should be ACK
; ACALL PS2_ChkAck ;Check to see if it was ACK
; JNC PS2_MouseInit ;If it wasn’t repeat and try to initialize again
; MOV A,#0F6h ;Set default values
; ACALL PS2_SendByte ;Send command to keyboard
; ACALL PS2_GetByte ;Get a response byte, should be ACK
; ACALL PS2_ChkAck ;Check to see if it was ACK
; JNC PS2_MouseInit ;If it wasn’t repeat and try to initialize again
; MOV A,#0F4h ;Enable reporting
; ACALL PS2_SendByte ;Send command to keyboard
; ACALL PS2_GetByte ;Get a response byte, should be ACK
; ACALL PS2_ChkAck ;Check to see if it was ACK
; JNC PS2_MouseInit ;If it wasn’t repeat and try to initialize again
; RET
;////////////////////////////////////////////////////////////////////////////////////////////////
;***************************************************************
;* Function: Scan code translation table *
;* Author: Craig Steiner *
;*————————————————————-*
;* Description: The following codes are the scan codes returned*
;* by PS2_GetScanCode. The keyboard sends a byte(s) when a *
;* key is pressed and another sequence of bytes when the key *
;* is released. These sequences can be 1-3 bytes in length. *
;* The PS2_GetScanCode routine converts the keyboard scan *
;* codes into an internal representation that can be fit in *
;* a single byte. The constants below are the values that *
;* will be returned in the case of certain keypresses. In *
;* all cases, a value of 0-127 means the key was pressed *
;* while a value between 128-255 means the key was released. *
;***************************************************************
KS_KP_SLASH EQU ‘a’
KS_RIGHTCTRL EQU ‘b’
KS_RIGHTGUI EQU ‘c’
KS_RIGHTALT EQU ‘d’
KS_APPS EQU ‘e’
KS_PRTSCREEN1 EQU ‘f’
KS_PRTSCREEN2 EQU ‘g’
KS_INSERT EQU ‘h’
KS_HOME EQU ‘i’
KS_PGUP EQU ‘j’
KS_DELETE EQU ‘k’
KS_END EQU ‘l’
KS_PGDOWN EQU ‘m’
KS_UPARROW EQU ‘n’
KS_LEFTARROW EQU ‘o’
KS_DOWNARROW EQU ‘p’
KS_RIGHTARROW EQU ‘r’
KS_KP_ENTER EQU ’s’
KS_BACKSPACE EQU ‘t’
KS_TAB EQU ‘u’
KS_CAPS EQU ‘v’
KS_LEFTSHIFT EQU ‘w’
KS_LEFTCTRL EQU ‘x’
KS_LEFTGUI EQU ‘y’
KS_LEFTALT EQU ‘z’
KS_F1 EQU 01h
KS_F2 EQU 02h
KS_F3 EQU 03h
KS_F4 EQU 04h
KS_F5 EQU 05h
KS_F6 EQU 06h
KS_F7 EQU 07h
KS_F8 EQU 08h
KS_F9 EQU 09h
KS_F10 EQU 0Ah
KS_F11 EQU 0Bh
KS_F12 EQU 0Ch
KS_RIGHTSHIFT EQU 0Dh
KS_ENTER EQU 0Eh
KS_SCROLL EQU 0Fh
KS_NUMLOCK EQU 10h
KS_KP_ASTERISK EQU 11h
KS_KP_MINUS EQU 12h
KS_KP_PLUS EQU 13h
KS_KP_POINT EQU 14h
KS_ACPI_POWER EQU 15h
KS_ACPI_SLEEP EQU 16h
KS_ACPI_WAKE EQU 17h
KS_ESCAPE EQU 1Bh
KS_KP_0 EQU ‘)’
KS_KP_1 EQU ‘!’
KS_KP_2 EQU ‘@’
KS_KP_3 EQU ‘#’
KS_KP_4 EQU ‘$’
KS_KP_5 EQU ‘%’
KS_KP_6 EQU ‘^’
KS_KP_7 EQU ‘&’
KS_KP_8 EQU ‘*’
KS_KP_9 EQU ‘(’
;***************************************************************
;* Function: PS2_GetByte *
;* Author: Craig Steiner based on code by Gabriel Lour *
;* Input: None *
;* Output Carry bit: Clear=No key returned,Set=Key returned *
;* Accumulator: Byte returned by keyboard *
;* Registers Modified: None *
;*————————————————————-*
;* Description: Gets a single byte from the keyboard, if there *
;* is a byte to get. Returns it in the accumulator and sets *
;* carry bit. Otherwise clears the carry bit. *
;***************************************************************
PS2_GetByteSetR0:
MOV A,R0 ;We don’t save Accumulator since return value can be in accumulator
PUSH ACC ;Protect R0
SJMP PS2_GetByte2 ;Use R0 that is passed in
PS2_GetByte:
MOV A,R0 ;We don’t save Accumulator since return value can be in accumulator
PUSH ACC ;Protect R0
MOV R0, #50 ;Wait 50 loop cycles for data line to be lowered by keyboard
PS2_GetByte2:
SETB KEYB_DATA ;Raise data line so it can receive data
SETB KEYB_CLOCK ;Raise clock so that keyboard communication is enabled
PGB_CheckAgain:
JNB KEYB_CLOCK, PGB_KeyHit ;If clock line is now low that means keyboard is talking to us
DJNZ R0, PGB_CheckAgain ;Check R0 number of times
SJMP PGB_KeyEnd ;No keyboard response was detected in loop period
PGB_KeyHit:
JNB KEYB_DATA, PGB_StartOk ;Start bit must be 0
PGB_KeyEnd:
CLR KEYB_CLOCK ;Disable keyboard
CLR C ;Clear carry to indicate no keypress
PGB_PopExit:
XCH A,R0 ;Hold accumulator temporarily in R0
POP ACC ;Restore value of R0
XCH A,R0 ;Restore accumulator and R0
RET
PGB_StartOk:
MOV R0,#8 ;8 bits to clock into accumulator
CLR A ;Accumulator initially empty
PGB_KeyHit3:
ACALL PS2_GetBit ;Get one bit and shift into accumulator
DJNZ r0, PGB_KeyHit3 ;Execute for each of the 8 bits
PUSH ACC ;Save the value read from keyboard on the stack
CLR A
ACALL PS2_GetBit ;Get parity bit
ACALL PS2_GetBit ;Get stop bit
CLR KEYB_CLOCK
POP ACC ;Restore the byte we read
SETB C ;Set carry flag to indicate key press
SJMP PGB_PopExit ;Exit routine
;***************************************************************
;* Function: PS2_GetBit *
;* Author: Craig Steiner based on code by Gabriel Lour *
;* Input: Accumulator: Starting value of clocked-in data *
;* Output Accumulator: New value of cloced-in data *
;* Registers Modified: Carry bit *
;*————————————————————-*
;* Description: Waits for a 1-0 transition on the clock line *
;* from the keyborad. When this happens, we have valid data *
;* on the data line so we get it and rotate it into the *
;* accumulator. *
;***************************************************************
PS2_GetBit:
JNB KEYB_CLOCK, $
JB KEYB_CLOCK, $
MOV C, KEYB_DATA
RRC A
RET
;***************************************************************
;* Function: PS2_WaitClock *
;* Author: Craig Steiner based on code by Gabriel Lour *
;* Input: None *
;* Output None *
;* Registers Modified: Nine *
;*————————————————————-*
;* Description: Waits for a 1-0 transition on the clock line *
;* from the keyborad and returns. *
;***************************************************************
PS2_WaitClock:
JNB KEYB_CLOCK, $
JB KEYB_CLOCK, $
RET
;***************************************************************
;* Function: PS2_SendByte *
;* Author: Craig Steiner based on code by Gabriel Lour *
;* Input: Accumulator: Value to send to keyboard *
;* Output Accumulator: New value of cloced-in data *
;* Registers Modified: Carry bit *
;*————————————————————-*
;* Description: Waits for a 1-0 transition on the clock line *
;* from the keyborad. When this happens, we have valid data *
;* on the data line so we get it and rotate it into the *
;* accumulator. *
;***************************************************************
PS2_SendByte:
XCH A,R0 ;Swap A and R0 temporarily
PUSH ACC ;This saves the value of R0 on stack
XCH A,R0 ;And this restores the accumulator to its original value
CLR KEYB_CLOCK ;Break the Keyboard
MOV R0,#00h ;Some delay (safety reasons)
DJNZ R0,$ ;Loop for 256 cycles
CLR KEYB_DATA ;Request to send
SETB KEYB_CLOCK ;Enable the Keyboard
ACALL PS2_WaitClock ;Start Bit
PUSH ACC ;Protect original value of accumulator
MOV R0,#8 ; 8bits to receive
PSB_Xmit:
RRC A ;Shift bits into carry
MOV KEYB_DATA, C ;Send highest bit out to keyboard
ACALL PS2_WaitClock ;Wait for keyboard to acknowledge
DJNZ R0,PSB_Xmit ;Loop for each bit
POP ACC ;Restore original value of accumulator
MOV C,PSW.0 ;This is Even parity
CPL C ;And Keyboard needs Odd parity
MOV KEYB_DATA,C ;Send parity bit
ACALL PS2_WaitClock ;Wait for keyboard to acknowledge bit
SETB KEYB_DATA ;Send stop bit
ACALL PS2_WaitClock ;Wait for keyobard to acknowledge bit
ACALL PS2_WaitClock ;Wait for keyboard to acknowledge bit
MOV C, KEYB_DATA ;Get the ACK bit from the keyboard, store in carry
CLR KEYB_CLOCK ;Deselect the keyboard
XCH A,R0
POP ACC
XCH A,R0 ;Restore R0
ret
;***************************************************************
;* Function: PS2_CheckAck *
;* Author: Craig Steiner based on code by Gabriel Lour *
;* Input: Accumulator: Value received from keyboard *
;* Output Carry: Clear=NAK, Set=ACK receivedn data *
;*————————————————————-*
;* Description: Checks to see if the contents of the acc is *
;* the ACK code (FAh). If it is, it sets the carry bit. If *
;* it isn’t, it clears it. *
;***************************************************************
PS2_ChkAck:
CJNE A,#KB_ACK,PCA_NAK ;If character was not ACK, clr carry to indicate failure
SETB C ;Set carry to indicate successful ACK
RET
PCA_NAK:
CLR C ;Clear carry to indicate failure
RET
;***************************************************************
;* Function: PS2_Init *
;* Author: Craig Steiner based on code by Gabriel Lour *
;* Input: None *
;* Output Carry: Clear=Failure, Set=Success *
;*————————————————————-*
;* Description: Initializes the keyboard and indicates whether *
;* the initialization was successful or not. *
;***************************************************************
PS2_Init:
MOV A,#0FFh ;Initializtation command
ACALL PS2_SendByte ;Send command to keyboard
ACALL PS2_GetByte ;Get a response byte, should be ACK
ACALL PS2_ChkAck ;Check to see if it was ACK
JNC PS2_Init ;If it wasn’t repeat and try to initialize again
MOV A,#0F4h ; Enable keyboard command
ACALL PS2_SendByte ;Send command to keyboard
ACALL PS2_GetByte ;Get a response byte, should be ACK
ACALL PS2_ChkAck ;Check to see if it was ACK
JNC PS2_Err ;If not success, abort command and exit
MOV A, #0F3h ; Set Typematic
ACALL PS2_SendByte ;Send command to keyboard
ACALL PS2_GetByte ;Get a response byte, should be ACK
ACALL PS2_ChkAck ;Check to see if it was ACK
JNC PS2_Err ;If not success, abort command and exit
MOV A, #00h ; Typematic = 250 ms / 30 cps
ACALL PS2_SendByte ;Send command to keyboard
ACALL PS2_GetByte ;Get a response byte, should be ACK
ACALL PS2_ChkAck ;Check to see if it was ACK
JNC PS2_Err ;If not success, abort command and exit
MOV A,#0 ;Keyboard starts with LEDs off, fall through to Set LEDs
PS2_SetLeds:
PUSH ACC ;Protect LED setting passed in accumulator
MOV A, #0EDh ;Set Leds command
ACALL PS2_SendByte ;Send command to keyboard
ACALL PS2_GetByte ;Get a response byte, should be ACK
ACALL PS2_ChkAck ;Check to see if it was ACK
JNC PS2_Err ;If not success, abort command and exit
POP ACC ;Restore the LED setting that was passed in
ACALL PS2_SendByte ;Send command to keyboard
ACALL PS2_GetByte ;Get a response byte, should be ACK
ACALL PS2_ChkAck ;Check to see if it was ACK
PS2_Err:
RET
;***************************************************************
;* Function: PS2_GetScanCode *
;* Author: Craig Steiner based on code by Gabriel Lour *
;* Input: None *
;* Output Carry: Clear=No key received, Set=Key received *
;* Accumulator: Scan key code *
;*————————————————————-*
;* Description: Gets a scan code from the keyboard and returns *
;* it. A scan code is the printable alphabet and numbers in *
;* unshifted form. 0-127 means key pressed, 128+ means *
;* key released. *
;***************************************************************
PS2_GetScanCode:
LCALL PS2_GetByte ;Try to get first character
JC PGSC_GotKey ;If we got a byte, process it
PGSC_NoKey:
CLR C ;Clear carry to signify no character received
RET ;No byte received, so just exit
PGSC_GotKey:
;If we got a key then “B” is going to be used as our return
;register. It starts out clear
MOV B,#00h ;Clear our return code
MOV DPTR,#PGSC_NormalCodes ;Point initially to the normal codes
PGSC_ProcKey:
MOV R1,A ;Hold received character in R1
CJNE A,#KB_BREAK,PGSC_NotBreak ;If it’s not a break code (F0) then continue
;If it is a break code then we set the high bit of ‘B’ to indicate that the
;key was released.
SETB B.7 ;Set high bit
PGSC_AnotherKey:
MOV R0,#20 ;Try 20 times to wait for next byte from keyboard
PGSC_AKeyLoop:
LCALL PS2_GetByte ;We then get the next byte from keyboard
JC PGSC_ProcKey ;A byte was received, so process it
DJNZ R0,PGSC_AKeyLoop ;No key detected, so keep trying
SJMP PGSC_NoKey ;No key was found after multiple tries
PGSC_NotBreak:
;Check to see if it was an extended key
CJNE A,#KB_EXTENDED,PGSC_FindCode ;If not an extended code (E0), go process it
;This means we got an extended code. If so, we set our look-up table
;to the extended scan code table and get another key and process it.
MOV DPTR,#PGSC_ExtendedCodes ;Set lookup table to extended codes
SJMP PGSC_AnotherKey ;Go get another key and process it
PGSC_FindCode:
;This now looks for the code we received in the proper table
CLR A ;Make sure offset is zero
MOVC A,@A+DPTR ;Get next scan code from DPTR
JZ PGSC_NoKey ;If zero then end of table, so exit
XRL A,R1 ;See if the key we got IS the key we’re looking for
JZ PGSC_FoundKey ;If it is, so process it
;It wasn’t the right key, so we increment DPTR twice to point to the
;next table entry and process that
INC DPTR
; INC DPTR
SJMP PGSC_FindCode
PGSC_FoundKey:
INC DPTR ;Point to the translation
CLR A ;No offset
MOVC A,@A+DPTR ;Get the translated character
; ORL A,B ;Combine it with B which may hold a “break” code
SETB C ;Set carry flag to indicate we have a character
RET
PGSC_NormalCodes:
;These are the translations for the normal, non-extended codes. Basically
;these are the scan codes that consist of a single byte
DB 01Ch,’A’
DB 032h,’B’
DB 021h,’C’
DB 023h,’D’
DB 024h,’E’
DB 02Bh,’F’
DB 034h,’G’
DB 033h,’H’
DB 043h,’I’
DB 03Bh,’J’
DB 042h,’K’
DB 04Bh,’L’
DB 03Ah,’M’
DB 031h,’N’
DB 044h,’O’
DB 04Dh,’P’
DB 015h,’Q’
DB 02Dh,’R’
DB 01Bh,’S’
DB 02Ch,’T’
DB 03Ch,’U’
DB 02Ah,’V’
DB 01Dh,’W’
DB 022h,’X’
DB 035h,’Y’
DB 01Ah,’Z’
DB 045h,’0′
DB 016h,’1′
DB 01Eh,’2′
DB 026h,’3′
DB 025h,’4′
DB 02Eh,’5′
DB 036h,’6′
DB 03Dh,’7′
DB 03Eh,’8′
DB 046h,’9′
DB 00Eh,’`’
DB 04Eh,’-’
DB 055h,’=’
DB 05Dh,’\’
DB 066h,KS_BACKSPACE
DB 029h,’ ‘
DB 00Dh,KS_TAB
DB 058h,KS_CAPS
DB 012h,KS_LEFTSHIFT
DB 014h,KS_LEFTCTRL
DB 011h,KS_LEFTALT
DB 059h,KS_RIGHTSHIFT
DB 05Ah,KS_ENTER
DB 076h,KS_ESCAPE
DB 005h,KS_F1
DB 006h,KS_F2
DB 004h,KS_F3
DB 00Ch,KS_F4
DB 003h,KS_F5
DB 00Bh,KS_F6
DB 083h,KS_F7
DB 00Ah,KS_F8
DB 001h,KS_F9
DB 009h,KS_F10
DB 078h,KS_F11
DB 007h,KS_F12
DB 073h,KS_SCROLL
DB 054h,’['
DB 077h,KS_NUMLOCK
DB 07Ch,KS_KP_ASTERISK
DB 07Bh,KS_KP_MINUS
DB 079h,KS_KP_PLUS
DB 071h,KS_KP_POINT
DB 070h,KS_KP_0
DB 069h,KS_KP_1
DB 072h,KS_KP_2
DB 07Ah,KS_KP_3
DB 06Bh,KS_KP_4
DB 073h,KS_KP_5
DB 074h,KS_KP_6
DB 06Ch,KS_KP_7
DB 075h,KS_KP_8
DB 07Dh,KS_KP_9
DB 05Bh,']‘
DB 04Ch,’;’
; DB 052h,”’
DB 041h,’,’
DB 049h,’.’
DB 04Ah,’/’
PGSC_ExtendedCodes:
;These are the translations for the extended codes. These are the codes
;that start with E0 and then a second character. This table lists that
;second character
DB 01Fh,KS_LEFTGUI
DB 04Ah,KS_KP_SLASH
DB 014h,KS_RIGHTCTRL
DB 027h,KS_RIGHTGUI
DB 011h,KS_RIGHTALT
DB 02Fh,KS_APPS
DB 012h,KS_PRTSCREEN1
DB 07Ch,KS_PRTSCREEN2
DB 070h,KS_INSERT
DB 06Ch,KS_HOME
DB 07Dh,KS_PGUP
DB 071h,KS_DELETE
DB 069h,KS_END
DB 07Ah,KS_PGDOWN
DB 075h,KS_UPARROW
DB 06Bh,KS_LEFTARROW
DB 072h,KS_DOWNARROW
DB 074h,KS_RIGHTARROW
DB 05Ah,KS_KP_ENTER
DB 037h,KS_ACPI_POWER
DB 03Fh,KS_ACPI_SLEEP
DB 05Eh,KS_ACPI_WAKE
DB 0
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
; lcd Routine
;++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
commwrt:
mov a,#00h
mov dptr,#0fff8h
movx @dptr,a
mov a,r0
mov dptr,#0fff9h
movx @dptr,a
ret
lcdi: mov r0,#38h
acall commwrt
acall delay
mov r0,#0eh
acall commwrt
acall delay
mov r0,#01h
acall commwrt
acall delay
mov r0,#06h
acall commwrt
acall delay
mov r0,#80h
acall commwrt
acall delay
ret
datawrt:
mov R1,A
mov a,#01h
mov dptr,#0fff8h
movx @dptr,a
MOV A,R1
mov dptr,#0fff9h
movx @dptr,a
ret
delay: mov r3,#10
here: mov r4,#255
here1: djnz r4,here1
djnz r3,here
ret
end
Tags: 8051 with ps/2 keyboard circuit with code, 8051 with ps/2|embed4u.com, assembly code for ps/2 keyboard, code for ps/2 keyboard with 8051 fully tested, Fully working ps/2 keyboard code|embed4u.com, lcd with ps/2 code, ps/2 keyboard code interface with 8051 with lcd, ps/2 keyboard interface with 8051, ps/2 keyboard interface with 8051 Assembly code, ps/2 with 8051 interface circuit, ps/2 with lcd 8051 code|embed4u.com, scan code for ps/2|embed4u.com