Jump to content
Sign in to follow this  
Efcis

PS2 keyboard interface - USI

Recommended Posts

Hi !

 

I'm currently trying to interface a PS2 keyboard to the Launchpad and send the raw scan codes to the Host PC in a usual 9600 baud serial stream.

 

I used the well-known NJC serial transmission function, but I'm facing issues on the PS2 side.

 

On the HW side, the PS2 wires are directly connected to the P1.5 (PS2 clock) and P1.7 (PS2 data) pins (I only use the keyboard to the MSP 430 stream - its outputs are open collectors, so I guess there is no need for additional HW ?), and wired the +5v keyboard supply to the TP1 pin of the Launchpad. Of course, the GND is also wired.

 

I used the USI of the MSP 430G2231 to capture the PS2 data (Start + 8 bits + Parity + Stop), but surprisingly enough, it doesn't work well. There are many times where a bit is missing or an additional bit is inserted in the USISR register ?

 

Also, when using the debugger and setting a breakpoint two lines after the USI interrupt, the sTemp value is different from USISR whereas the last executed line should be sTemp = USISR ! But if I put the breakpoint _on_ the line sTemp = USISR and "Step Into", sTemp always equals USISR. Very strange... :?:

 

Of course the transmitted data is wrong, and whereas it is supposed to send only the "make" codes, it also sends garbage at the time of the "break" code :-(

 

See the code below.

 

/*
   PS2 keyboard interface by using the USI
*/

#include        "msp430g2231.h"
#include        "stdbool.h"

#define            PS2_CLOCK            BIT5        // SCLK
#define            PS2_DATA            BIT7        // SDI
#define            PS2_SCAN_BREAK        (char)0xF0

#define            TXD                    BIT1        // Pin 1.1
#define         TX_BIT_TIME            104

bool            bPs2HasReceived;
unsigned char    ucScanCode;
unsigned char    ucPs2LastByte;

short            sTemp;

unsigned char    ucTxBitCount;
unsigned short    usTxByte;

void transmit(void);

void main(void)
{
   WDTCTL = WDTPW + WDTHOLD;        // Stop WDT

   // Clock init

   BCSCTL1 = CALBC1_1MHZ;            // Set range
   DCOCTL = CALDCO_1MHZ;            // SMCLK = DCO = 1MHz

   // UART Init

   P1SEL |= TXD;                    // Port 1.1 as timer output
   P1DIR |= TXD;                    // Port 1.1 as output

   // USI Init

   P1SEL |= PS2_CLOCK + PS2_DATA;
   P1REN |= PS2_CLOCK + PS2_DATA;
   P1OUT |= PS2_CLOCK + PS2_DATA;

   USICTL0 = USISWRST;
   USICTL0 = USIPE7 + USIPE5 + USILSB + USISWRST;	// SDI, SCLK, LSB first
   USICTL1 = USICKPH + USIIE;						// Data sampling on falling edge, Interrupt when complete
   USICKCTL = USICKPL;								// Inactive state is High
   USISR = 0x0000;

   // Comm Init

   USICNT = 11 + USI16B;    // 11 bits : Start + 8 data + Parity + Stop
   USICTL0 &= ~USISWRST;

   //

   bPs2HasReceived = false;
   ucScanCode = 0x00;
   ucPs2LastByte = 0x00;

   while(true)
   {
       if (bPs2HasReceived)
       {
           bPs2HasReceived = false;
           if (usTxByte != 0xE0)        // Ignore E0 complex scan codes
               transmit();
       }

       if (bPs2HasReceived == false)
           __bis_SR_register(CPUOFF + GIE);
   }
}

// Function Transmits Character from usTxByte 

void transmit(void)
{
   TACCTL0 = OUT;
   TACTL = TASSEL_2 + MC_2;

   ucTxBitCount = 0x0A;                    // Load Bit counter, Start, 8 bits data, Stop
   usTxByte |= 0x100;                        // Add mark stop bit to usTxByte
   usTxByte = usTxByte << 1;                 // Add space start bit

   CCR0 = TAR;
   CCR0 += TX_BIT_TIME;

   TACCTL0 = CCIS0 + OUTMOD0 + CCIE;        // Set signal, intial value, enable interrupts
   while ( TACCTL0 & CCIE );                // Wait for TX completion
}

// USI interrupt routine

#pragma vector=USI_VECTOR
__interrupt void usi(void)
{
   sTemp = USISR;
   sTemp &= 0x3FD0;
   sTemp = sTemp >> 6;
   ucScanCode = (char) sTemp;

   USICNT = 11 + USI16B;
   USISR = 0x0000;

   if (ucScanCode == PS2_SCAN_BREAK)
       ucPs2LastByte = PS2_SCAN_BREAK;        
   else if (ucPs2LastByte != PS2_SCAN_BREAK)    // Ignore data after a break code
   {
       bPs2HasReceived = true;
       usTxByte = ucScanCode;                    // Load TxByte with data to send
       ucPs2LastByte = (char) usTxByte;

       __bic_SR_register_on_exit(CPUOFF);        // Enable CPU so the main while loop continues
                                               // and sends data
   }
}

// Timer A0 interrupt service routine

#pragma vector=TIMERA0_VECTOR
__interrupt void timerA(void)
{
   CCR0 += TX_BIT_TIME;            // Add Offset to CCR0 asap in the interrupt
                                   // to limit drift
   if (ucTxBitCount == 0)            // If all bits TXed
   {
       TACTL = TASSEL_2;            // SMCLK, timer off (to save power consumption)
       TACCTL0 &= ~CCIE ;            // Disable interrupt
   }
   else
   {
       CCTL0 |= OUTMOD2;             // TX Space
       if (usTxByte & 0x01)
           CCTL0 &= ~ OUTMOD2;        // TX Mark
       usTxByte = usTxByte >> 1;    // Shift usTxByte to delete the sent bit
       ucTxBitCount--;
   }
}

 

Did someone here experimented such a PS2 interface with the Launchpad ? If so, how did you made it ?

 

Thanks for any idea !

Share this post


Link to post
Share on other sites
Also, when using the debugger and setting a breakpoint two lines after the USI interrupt, the sTemp value is different from USISR whereas the last executed line should be sTemp = USISR ! But if I put the breakpoint _on_ the line sTemp = USISR and "Step Into", sTemp always equals USISR. Very strange..

 

Are you optimizing your code?

Share this post


Link to post
Share on other sites

Hi bluehash

 

Are you optimizing your code?

 

Apparently not. I'm using CCS4, and none option in Project / Properties / C/C++ Build / Optimizations is checked.

Share this post


Link to post
Share on other sites

Of course the transmitted data is wrong, and whereas it is supposed to send only the "make" codes, it also sends garbage at the time of the "break" code :-(

 

When you say the transmitted data is wrong, and that it is sending garbage at the time of the "break" code, you mean the software UART? not the Keyboard?

 

I've used the USI in SPI mode pretty extensively, but am not too familiar with the I2C mode. I do remember it being a bit difficult to get working, with all the registers needing to be set correctly. At first glance, I can recommend trying a few things.

 

First, you don't need to clear the USISR register, it is done automatically. Second, I would recommend simplifying the code a lot, and verifying that you can get the I2C working by itself. Maybe have the MSP430 send exactly what it receives over I2C to the computer using the software UART. It could be a multitude of things that cause this not to work, even my software UART code. Something could be interfering with the timing. It could be anything really. Once you can verify the I2C is working, things should become easier to debug.

 

Have you looked at TI's example code for the I2C? It might be able to help with setting up the registers correctly if the I2C isn't working.

 

Sorry I can't be of more help.

 

NJC

Share this post


Link to post
Share on other sites

Hi NJC

 

Nice to read from you.

 

Actually, I don't use the I2C protocol at all... I only take advantage of the USI shift (hardware) register to trigger an interrupt when a whole PS2 frame is (supposed to be) completed. Your UART code is not in cause (I guess).

 

To be honest, I'm now wondering if I missed something on the "hardware" side. I'm not so sure the SDI and SCLK inputs can be really set with internal pull-up resistors, and as the PS2 keypad outputs are supposed to be open-collectors that could lead to glitches and an unexpected behavior. As I don't own any Scope or DSO, I can't trace this :-(

 

Surprisingly enough, I couldn't find any example of such a use of the USI and/or a description of such a PS2 interfacing on the Web with the MSP430 series. Whereas it is very common with the Arduino for instance.

 

Thanks for your help and comment !

Share this post


Link to post
Share on other sites

Surprisingly enough, I couldn't find any example of such a use of the USI and/or a description of such a PS2 interfacing on the Web with the MSP430 series. Whereas it is very common with the Arduino for instance.

 

Could you use the Arduino circuits as an example?

Share this post


Link to post
Share on other sites

It seems they also use direct wiring from the PS2 device to the uC, like I did. Except the ATMega is powered under 5v when the MSP 430 is under 3.3v only. But that shouldn't make any difference as the PS2 device is supposed to have open-collectors Clk and Data outputs.

 

Anyway, if I don't find why it fails, I will (sadly) consider an Arduino for my project.

 

Thanks for your help !

Share this post


Link to post
Share on other sites

The keyboard is powered in +5v (wired from TP1).

 

I only have a doubt on wiring the keyboard outputs (O/C) directly to the MSP 430 inputs that are *supposed* to include pull-up resistors, when configured accordingly.

 

I'll check the threads you pointed to.

 

Thanks

Share this post


Link to post
Share on other sites

You sound like you have an arduino or 2 lying around. If you do, you can turn one into an o'scope. Check out the arduinoscope project: http://code.google.com/p/arduinoscope/ . This was immensely helpful in debugging why my PIR sensor wasn't working.

 

I'd suggest setting up a line from the LaunchPad's VCC, and then one to all of the broken-out lines of the PS/2 port. Then monitoring the output of the arduinoscope to see where the levels line up compared to the steady VCC line. If they're below a certain threshold (unique per chip, my G2231 seemed to need ~2.31V minimum), the signal will register as LOW to the MSP430.

 

Try this, and post your results, maybe there will be something we can help you with.

Check out my blog post here: http://blog.suspended-chord.info/?c=11

 

Hope this helps!

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this  

×
×
  • Create New...