Keyboard testing methodology.
Copyright © 1999 Adam Wozniak, All Rights Reserved
Debugging a custom keyboard project can be a nightmare. Here is an inexpensive
method I used to debug my keyboard emulator project.
Caveat Emptor: I did this and it worked for me. If you do this and fry yourself,
your gear, or your mother, it is your own darn fault. Many people have pointed
out that I should have stuck opto-isolators in this thing, but I have a few spare
motherboards lying around which I didn't mind frying (much).
The trick is to monitor the keyboard line levels using a bidirectional parallel port.
They're all TTL level signals, so this really isn't hard once you get the thing
wired up properly. If you don't have a bidirectional port, get out of the stone ages.
========
Go get a keyboard extension cord, and a DB25 male. chop the keyboard extension in
half, and solder both sides to the DB25 male as follows:
Keyboard data (pin 2 on the DIN) --> parallel data 0 (pin 2 on the DB25)
Keyboard clock (pin 1 on the DIN) --> parallel data 1 (pin 3 on the DB25)
Keyboard GND (pin 4 on the DIN) --> parallel GND (I used pin 19 on the DB25)
Keyboard +5V (pin 5 on the DIN) --> parallel data 2 (pin 4 on the DB25)
You should now have a cable with three connectors hanging off of it.
DO NOT PLUG THIS CABLE INTO ANYTHING YET!!!
========
Here is some port fiddling code for Linux. note it MUST be compiled with gcc -O:
#include <stdio.h>
#include <unistd.h>
#include <asm/io.h>
#define PORTBASE 0x378
#define DATAPORT (PORTBASE + 0)
#define STATUSPORT (PORTBASE + 1)
#define CONTROLPORT (PORTBASE + 2)
//ok, we seem to be getting ff, fe, fc, and fd
// c == 1100
// d == 1101
// e == 1110
// f == 1111
// SO, bits 0 and 1 are clock and data (or vice-versa)
#define CLOCKBIT 1
#define DATABIT 0
void main(int argc, char **argv)
{
int val = -1;
int newval;
char buf[1024];
char *p;
if (argc == 2)
{
alarm(atoi(argv[1]));
}
nice (-20);
ioperm (PORTBASE, 3, 1); // lp*, 3 ports
outb( 0x20, CONTROLPORT); // kick us into read mode
outb( 0xFF, DATAPORT); // tie all the lines high
while (1)
{
newval = inb(DATAPORT);
if (val == -1 || newval != val)
{
sprintf(buf, "%c%c ",
(newval & (1 << CLOCKBIT)) ? 'C' : 'c',
(newval & (1 << DATABIT)) ? 'D' : 'd' );
write(1, buf, strlen(buf));
val = newval;
}
}
}
========
Procedure:
DO NOT PLUG THE TEST CABLE INTO ANYTHING YET!!!
You will need 2 computers. We will call one Monitor, and one Host
on Monitor, run kbmon, hit control-C
on Monitor, plug DB25 into parallel port
Use a voltmeter to confirm +5V on the test cable keyboard connectors data, clock, and +5V lines.
(use the test cable keyboard connector GND)
IF YOU DO NOT GET ANY VOLTAGE STOP!!! Your parallel port is not up to the challenge.
make sure the Host computer is on
plug one end of the test cable into Host's keyboard port
plug one end of the test cable into a keyboard
You are now set to test.
CAUTION:
Do not power off the EITHER machine while the test cable is connected.
Always power on BOTH machines before connecting anything.
When disconnecting, ALWAYS disconnect keyboard first, then Host, then Monitor
When connecting, ALWAYS connect Monitor, then Host, then keyboard
kbmon can now be used to monitor the clock and data line levels. You should
see output like this:
CD cD cd Cd
This indicates the clock and data lines start high, the clock goes low, the
data goes low, then the clock comes high again.
Note no timing information is given. You'll have to sort that out for yourself.
You can use the analyze program to get prettier pictures of what is going on:
#include <stdio.h>
void main(void)
{
char buf[16];
char out1[80];
char out2[80];
char out3[80];
int bitcount = 0;
int spot = 0;
int lastclock = 1;
int hootch;
out1[0] = out2[0] = out3[0] = 0;
while (scanf("%s", buf) == 1)
{
if (spot > 70)
{
out1[spot] = 0;
out2[spot] = 0;
out3[spot] = 0;
printf("clock %s\ndata %s\n %s\n\n", out1, out2, out3);
spot = 0;
}
out1[spot] = (buf[0] == 'C') ? '-' : '_';
out2[spot] = (buf[1] == 'D') ? '-' : '_';
out3[spot] = ' ';
if (lastclock && buf[0] == 'c')
{
out3[spot] = (buf[1] == 'D') ? '1' : '0';
bitcount++;
}
else if (!lastclock && buf[0] == 'C')
{
if ((bitcount % 12) == 0)
{
out3[spot] = '^';
}
}
lastclock = (buf[0] == 'C');
spot++;
}
out1[spot] = 0;
out2[spot] = 0;
out3[spot] = 0;
printf("clock %s\ndata %s\n %s\n\n", out1, out2, out3);
}
========
clock -_-_-_-_-_-_-_-_-_-_-_-___-_-_-_-_-_-_-_-_-_--_--_--_-_--_--_--_-_-_-_-
data ------------------------_--------------------___---_____---___---------
1 1 1 1 1 1 1 1 1 1 1 1 ^1 1 1 1 1 1 1 1 1 0 1 0^0 1 0 1 1 1 1
clock _-_-_-_--_-_--_--_--_--_--_--_--_-_-_--_-_-_-_-_-_-_-_-_--_-_-_-__-__-_
data --------_____---___---___---___-------___________________--------___---
1 1 1 1 0^0 1 0 1 0 1 0 1 1 1 0 0^0 0 0 0 0 0 0 1 1 1 1 0 ^1
clock -_-_-_-_-_-_-_-_--_--_--_-_--_--_--_-_-_-_-_-_-_--_-_--_--_--_--_--_--_
data -----------------___---_____---___---------------_____---___---___---__
1 1 1 1 1 1 1 1 0 1 0^0 1 0 1 1 1 1 1 1 1 0 0^ 1 0 1 0 1 0
========
Note: Who's driving?
While this method does allow you to watch what happens on the line, it does
not always give you an idea of who is driving the line. I contemplated
separating the male and female clock and data lines, and using the kbmon
program to drive and mirror each side onto the other. I stopped short because
I was afraid of introducing timing glitches. I also wasn't sure how to kick
individual pins on the parallel port to/from input/output.
A truly brave person may want to do this in the future...
========
Here's what I discovered using this methodology
========
While individual codes are discussed in the keyboard FAQ, the boot up sequence
is not really mentioned, and much of the response description is wrong or
misleading.
<hit reset button on host>
HOST : FF (reset)
KEYBD: FA AA (ack pot) [ 1.8 forgets to mention the ack ]
HOST : F2 (read id)
KEYBD: FA AB 83 [ 1.8 has this backwards ]
HOST : ED (set leds)
KEYBD: FA (ack)
HOST : ?? (option byte) [ 1.8 is misleading, and implies that the next ack is unnecessary ]
KEYBD: FA (ack)
HOST : F4 (enable)
KEYBD: FA (ack)
If anything goofs, you get a keyboard error.