PS/2 Keyboard interface for NXP LPC1768 (problem with interrupt version)...

31 Dec 2009 . Edited: 31 Dec 2009

Hi I've been experimenting with a PS/2 keyboard i/f . I started of with the code from the cookbook entry for  the prototype MBED and have got a simpler version for the 1768. I use p21 for keyboard clock and p22 for keyboard data. The pinout in the original example seems wrong. I got my info from this site 'http://www.beyondlogic.org/keyboard/keybrd.htm', a very good explanation of how to read the clk/data lines etc.

The code below works really well (better than I expected).

 

DigitalInOut kbclk(p21);
DigitalInOut kbdata(p22);


/** Wait for single scan code from keyboard...
 *
 * @return  8-bit scan code...
 */
unsigned char kbget()
{
    int i;
    unsigned char data = 0;
    int start, stop, parity;
    
    for (i=0; i<11; i++)
    {    
        while (kbclk.read()==1) {}            // Wait for hi/lo transition on clock line...
 
        switch (i)
        {
            case 0:     // Start bit...
                start = kbdata.read();        // Start bit, should be 0...
                break;
            case 9:     // Parity bit...
                parity = kbdata.read();
                break;
            case 10:    // Stop bit...
                stop = kbdata.read();         // Stop bit, should be 1...
                break;
            default:    // Data bits..
                data >>=1;
                if (kbdata.read())
                    data |= 0x80;
                break;
        }

        while (kbclk.read()==0) {}            // Wait for lo/hi transition on clock line...
    }
    
    return data;
}


...however, this is a completely blocking function, so I thought this would be perfect for an interrrupt service handler, so voila...

 

DigitalInOut kbdata(p22);
InterruptIn kbclk(p21);

void kb_interrupt()
{
    static unsigned char data;
    int bit, start, stop, parity;
    static int state=0;

    bit = kbdata.read();

    switch (state)
    {
        case 0:
            start = bit;     // Start bit rxd.
            state = 1;       // Move state.
            data = 0;        // Reset data value.
            break;
        case 9:
            parity = bit;
            state = 10;      // Move state.
            break;
        case 10:
            stop = bit;
            ...at this point I push the data (scan code) into a circular buffer, details deleted for clarity.
            state = 0;       // Reset state machine.
            break;
        default:
            // Read in data bit and OR into data value...
            data >>=1;
            if (bit)
                data |= 0x80;
            state++;
            break;
    }
}



int main()
{
    .. yada yada

    kbclk.fall(&kb_interrupt);     // Set up interrupt handler (triggers on falling kb clock signal).


 
    while(1)
    {
       // Get scans from circular bufer, yada yada...
    }
}


I've edited above code for brevity. My problem is that this appears to work in that for every key press/release I get the expected number of interrupts (11, 1 start, 8 data, 1 parity, 1 stop) but the data is garbled (different codes for same keys, different codes for everty restart of mbed etc).

 

Any ideas??

I've tried servicing interrupt on rising clock, in snippets above I've left out test code where I'm counting interrupts etc.

PS: I made the interface cable from an old keyboard extension cable, I've put a two pin header on the power and the clk/data wires for easy use in breadboard.

06 Jan 2010

I wonder if the problem happens because you're doing the read inside the interrupt handler (could be related to mbed's library implementation). Maybe you should keep the read functionality in main() and just set a flag when you detect the incoming data. You can put __WFI(); in the loop body to let mbed sleep while waiting.

04 Aug 2010

I also have the same task to do but I am a beginner and I'm looking desperately for indormation sources. Could you help me with some? I'm especially interested about the wiring diagrams: which wires from the PS/2 corespond to which pins of the microcontroller? Thank you for your help.

04 Aug 2010 . Edited: 04 Aug 2010

 

static int state=0;
...
...
state++;


The state variable gets reset to 0 every time you have a falling clock edge, The increment for the default case is meaningless because at the next ISR, the state variable gets reset to 0.

04 Aug 2010

Nope, static variables are initialized only once per program run.

04 Aug 2010
user avatar Igor Skochinsky wrote:

Nope, static variables are initialized only once per program run.

So.. after all this time.. thats what static does. Haha..

04 Aug 2010

user avatar Igor Martinovski wrote:

So.. after all this time.. thats what static does. Haha..

Actually, it does different things in different situation.

1) static global variables are initialized once on startup and visible only in the file they're declared (i.e you can't 'extern' them in another file).
2) static local variable might be initialized on startup or when declaration is first reached by the code.. They're visible only in the scope they're declared
3) static member variables share the same value between all instances of a specific class (they're the most common way to implement a singleton).
4) static functions are visible only in the file they're declared and the compiler can easily inline or discard them without worrying about other files in the project. So it's useful to mark all "file local" functions as static, although this is less important these days with whole program optimization
5) static class methods do not use the instance data ('this' pointer) and so can be called using just the class name.

Here's a page describing it all in more detail for C and C++. (NB: it talks about static being deprecated in C++ but some people now realize that that decision might have been a mistake).

05 Aug 2010

Thanks, that's some interesting reading.

Anyway, back to the topic. It's possible that the InterruptIn is not fast enough (the clock for the PS/2 is 20-30kHz) and you're just queuing the ISRs.  Is it possible for you to test the code with a slower clock somehow?

27 Aug 2010

Would you mind posting the whole code for me to take a look, I tried something similar but some parts just won´t work. Thank you

29 Aug 2010 . Edited: 29 Aug 2010

I just tried to read codes from a PS/2 keyboard.

Please try my test program if you need it. PS2_TestProgram

And we can see the specification of the codes at http://download.microsoft.com/download/1/6/1/161ba512-40e2-4cc9-843a-923143f3456c/scancode.doc .

Shin.  :)

20 Sep 2010 . Edited: 20 Sep 2010

I was going to try a PS/2 mouse and keyboard setup and I noticed that Sparkfun has a $2 PS/2 connector breakout board. I needed several for a class setup. Might come in handy for anyone getting started with PS/2 if you don't want to cut cables.

http://www.sparkfun.com/commerce/product_info.php?products_id=8651 and connector http://www.sparkfun.com/commerce/product_info.php?products_id=8509

Then solder in a strip of SIP pins and plug it into a protoboard.

http://www.sparkfun.com/commerce/product_info.php?products_id=116

Some low cost serial GPS modules (i.e. GlobalSat BR355)  also get power off of a PS/2 connector and it would also be handy for that.