Weird keyboard issues
Posted: Sat Oct 15, 2011 12:05 am
				
				Hi everyone. I am having some weird keyboard problems with VUSB. My project is convert a BBC Micro keyboard to USB, a path well trodden by others who have converted keyboards. For some reason they keyboard only seems to work properly if plugged in after the OS has loaded. If plugged in when the machine is powered on it works in the BIOS but sometimes the OS hangs when booting (Windows XP, Windows 7, Ubuntu) and when the OS is booted key presses are only sometimes reported.
I really can't figure this one out. Been trying various options, updated to the latest version of VUSB etc. I can't see why one some keypresses are being reported, and why it works normally if plugged in after the machine has booted. Anyone got any ideas? Hardware problems seem unlikely.
Any help would be greatly appreciated.
I have attached the copy of the source code (GPLv3):
			I really can't figure this one out. Been trying various options, updated to the latest version of VUSB etc. I can't see why one some keypresses are being reported, and why it works normally if plugged in after the machine has booted. Anyone got any ideas? Hardware problems seem unlikely.
Any help would be greatly appreciated.
I have attached the copy of the source code (GPLv3):
Code: Select all
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/delay.h>
#include <string.h>
#include "usbdrv.h"
#include "keyboard.h"
void setCol(uchar col);
/*   LED PW   C1      01 - 02      C6   Column
   Row      C2      03 - 04      C7   Break
   Row      C3      05 - 06      E2   Row
   Column   C4      07 - 08      E1   Row
   Column   C5      09 - 10      E0   Column
   Column   C0      11 - 12      A7   Column
   Column   D5      13 - 14      A6   Column
   Column   D4      15 - 16      A5   Column
   Column   B4      17 - 18      A4   Column
   Row      B3      19 - 20      A3   SHIFT+CTRL
   Row      B2      21 - 22      A2   Column
   LED CA   B1      23 - 24      A1   Column
   LED A   B0      25 - 26      A0   LED SH
*/
#define   LED_CAB      1
#define   LED_CAP      PORTB
#define   LED_SHB      0
#define   LED_SHP      PORTA
#define   LED_PWB      1
#define   LED_PWP      PORTC
#define   NUMROWS      7
#define   NUMCOLS      13
static uchar    reportBuffer[8];    /* buffer for HID reports */
static uchar    idleRate;           /* in 4 ms units */
static uchar   LEDstate = 0;
static uchar   expectReport = 0;
static uchar   protocolVer = 1;    /* 0 is boot protocol, 1 is report protocol */
static uchar   cache[NUMCOLS+1];
//static uchar   testkey = K_A;
const uchar bitmask[8] = {
   0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
};
/*
{   N8   W   9   N9   T   7      0   _   I   DN   F10   E      }   // 0 E1
{   N+   2   O   N-   R   6   Ne   P   [   U   UP   1   D      }   // 1 E2
{   N4   F1   F8   N5   F3   F5   N2   F9   <<   F6   RT   ESC   F2      }   // 2 B3
{   N6   3   F7   N7   5   F4      -   ^   8   LF   Q   4      }   // 3 B4
{   #   S   L   Na   G   H   N,   ;   ]   N   BKS   SL   C      }   // 4 D4
{   N0   Z   ,   N1   V   B   N3   .   /   M   CPY   TAB   SPC      }   // 5 C3
{   N/   A   K   DEL   F   Y   Nd   []   :   J   ENT   CL   X      }   // 6 C2
*/
const unsigned char keymap[NUMROWS][NUMCOLS] PROGMEM = {
//   E0      A1      A2      C4      A4      A5      A6      A7      C0      D5      C5      B2      C6
//   0      1      2      3      4      5      6      7      8      9      A      B      C
{   K_NUM8,   K_W,   K_9,   K_NUM9,   K_T,   K_7,   K_NON,   K_0,   S_UND,   K_I,   K_DARR,   K_F10,   K_E      },   // 0 E1
{   K_NUMp,   K_2,   K_O,   K_NUMm,   K_R,   K_6,   K_NUMe,   K_P,   K_LSQB,   K_U,   K_UARR,   K_1,   K_D      },   // 1 E2
{   K_NUM4,   K_F1,   K_F8,   K_NUM5,   K_F3,   K_F5,   K_NUM2,   K_F9,   S_BKSL,   K_F6,   K_RARR,   K_ESC,   K_F2   },   // 2 B3
{   K_NUM6,   K_3,   K_F7,   K_NUM7,   K_5,   K_F4,   K_NON,   K_MIN,   S_KARA,   K_8,   K_LARR,   K_Q,   K_4      },   // 3 B4
{   S_NUMH,   K_S,   K_L,   K_NUMa,   K_G,   K_H,   S_NUMc,   K_SCOL,   K_RSQB,   K_N,   K_BKS,   S_SLK,   K_C      },   // 4 D4
{   K_NUM0,   K_Z,   K_COMM,   K_NUM1,   K_V,   K_B,   K_NUM3,   K_DOT,   K_SLSH,   K_M,   S_COPY,   K_TAB,   K_SPC   },   // 5 C3
{   K_NUMs,   K_A,   K_K,   S_DEL,   K_F,   K_Y,   S_NUMd,   S_CODE,   S_COL,   K_J,   K_ENT,   K_CPSL,   K_X      },   // 6 C2
//{   K_NON,   M_CTRL,   K_NON,   K_NON,   K_NON,   K_NON,   K_NON,   K_NON,   K_NON,   K_NON,   K_NON,   M_SHIFT,K_NON   }   // 7 A3
};
//   SHIFT   BREAK   CTRL
uchar   reportcount;
void addKey(uchar key)
{
   if ((reportcount < 8) && (key != 0))
   {
      if (key > K_Modifiers)
      {
         switch (key)
         {
            case M_SHIFT:
               reportBuffer[0] |= MOD_SHIFT_LEFT;
               break;
            case M_CTRL:
               reportBuffer[0] |= MOD_CONTROL_LEFT;
               break;
            case M_ALT:
               reportBuffer[0] |= MOD_ALT_LEFT;
               break;
         }
      }
      else
      {
         reportBuffer[reportcount] = key;
         reportcount++;
      }
   }
}
static uchar scanKeys(void)
{
   //uchar   temp;
   uchar   i;
   uchar   bitmap[NUMCOLS+1];
   uchar   changed = 0;
   memset(reportBuffer, 0, sizeof(reportBuffer));
   memset(bitmap, 0xff, sizeof(bitmap));
   for (i = 0; i < NUMCOLS; i++)
   {
      setCol(i);
      if (!(PINE & (1<<1))) bitmap[i] &= ~(1<<0);
      if (!(PINE & (1<<2))) bitmap[i] &= ~(1<<1);
      if (!(PINB & (1<<3))) bitmap[i] &= ~(1<<2);
      if (!(PINB & (1<<4))) bitmap[i] &= ~(1<<3);
      if (!(PIND & (1<<4))) bitmap[i] &= ~(1<<4);
      if (!(PINC & (1<<3))) bitmap[i] &= ~(1<<5);
      if (!(PINC & (1<<2))) bitmap[i] &= ~(1<<6);
      if (bitmap[i] ^ cache[i]) changed = 1;
   }
   // CTRL
   setCol(1);
   if (!(PINA & (1<<3)))
   {
      bitmap[NUMCOLS] &= ~(1<<2);
   }
   // SHIFT
   setCol(11);
   if (!(PINA & (1<<3)))
   {
      bitmap[NUMCOLS] &= ~(1<<0);
   }
   // BREAK
   LED_PWP &= ~(1<<LED_PWB);
   if (!(PINC & (1<<7)))
   {
      bitmap[NUMCOLS] &= ~(1<<1);
   }
   if (LEDstate & LED_SCROLL)      // reset power LED state
      LED_PWP |= (1<<LED_PWB);
   else
      LED_PWP &= ~(1<<LED_PWB);
   if (bitmap[NUMCOLS] ^ cache[NUMCOLS]) changed = 1;
   if (changed)
   {
      reportcount = 1;
      for (i = 0; i < NUMCOLS; i++)
      {
         //if (!(bitmap[i])){ addKey(testkey); testkey++; }
         if (!(bitmap[i] & (1<<0))) addKey(pgm_read_byte(&keymap[0][i]));
         if (!(bitmap[i] & (1<<1))) addKey(pgm_read_byte(&keymap[1][i]));
         if (!(bitmap[i] & (1<<2))) addKey(pgm_read_byte(&keymap[2][i]));
         if (!(bitmap[i] & (1<<3))) addKey(pgm_read_byte(&keymap[3][i]));
         if (!(bitmap[i] & (1<<4))) addKey(pgm_read_byte(&keymap[4][i]));
         if (!(bitmap[i] & (1<<5))) addKey(pgm_read_byte(&keymap[5][i]));
         if (!(bitmap[i] & (1<<6))) addKey(pgm_read_byte(&keymap[6][i]));
      }
      if (!(bitmap[NUMCOLS] & (1<<0))) addKey(M_SHIFT);
      if (!(bitmap[NUMCOLS] & (1<<2))) addKey(M_CTRL);
      if (!(bitmap[NUMCOLS] & (1<<1))) addKey(K_F12); // K_BREAK
      memcpy(cache, bitmap, sizeof(bitmap));
      return 1;
   }
   else
      return 0;
}
void setCol(uchar col)
{
   PORTA   |= 0b11110110;
   DDRA   &= ~0b11110110;
   PORTB   |= (1<<2);
   DDRB   &= ~(1<<2);
   PORTC   |= 0b01110001;
   DDRC   &= ~0b01110001;
   PORTD   |= (1<<5);
   DDRD   &= ~(1<<5);
   PORTE   |= (1<<0);
   DDRE   &= ~(1<<0);
   switch (col)
   {
      case 0:
         PORTE &= ~(1<<0);
         DDRE |= (1<<0);
         break;
      case 1:
         PORTA &= ~(1<<1);
         DDRA |= (1<<1);
         break;
      case 2:
         PORTA &= ~(1<<2);
         DDRA |= (1<<2);
         break;
      case 3:
         PORTC &= ~(1<<4);
         DDRC |= (1<<4);
         break;
      case 4:
         PORTA &= ~(1<<4);
         DDRA |= (1<<4);
         break;
      case 5:
         PORTA &= ~(1<<5);
         DDRA |= (1<<5);
         break;
      case 6:
         PORTA &= ~(1<<6);
         DDRA |= (1<<6);
         break;
      case 7:
         PORTA &= ~(1<<7);
         DDRA |= (1<<7);
         break;
      case 8:
         PORTC &= ~(1<<0);
         DDRC |= (1<<0);
         break;
      case 9:
         PORTD &= ~(1<<5);
         DDRD |= (1<<5);
         break;
      case 10:
         PORTC &= ~(1<<5);
         DDRC |= (1<<5);
         break;
      case 11:
         PORTB &= ~(1<<2);
         DDRB |= (1<<2);
         break;
      case 12:
         PORTC &= ~(1<<6);
         DDRC |= (1<<6);
         break;
   }
   _delay_us(100);
}
/* ----------------------- hardware I/O abstraction ------------------------ */
static void hardwareInit(void)
{
   PORTA   = 0b11111110;
   DDRA   = 0b00000001;
   PORTB   = 0b11111111;
   DDRB   = 0b00000011;
   PORTC   = 0b11111111;
   DDRC   = 0b00000010;
   PORTD   = 0b11110011;
   DDRD   = 0b00000000;
   PORTE   = 0b11111111;
   DDRE   = 0b00000000;
   TCCR0   = 5;         // timer 0 prescaler: 1024, 45.78Hz
   memset(cache, 0xff, sizeof(cache));
}
/* ------------------------------------------------------------------------- */
PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = {
   0x05, 0x01,            // USAGE_PAGE (Generic Desktop)
   0x09, 0x06,            // USAGE (Keyboard)
   0xa1, 0x01,            // COLLECTION (Application)
   0x05, 0x07,            //   USAGE_PAGE (Keyboard)
   0x19, 0xe0,            //   USAGE_MINIMUM (Keyboard LeftControl)
   0x29, 0xe7,            //   USAGE_MAXIMUM (Keyboard Right GUI)
   0x15, 0x00,            //   LOGICAL_MINIMUM (0)
   0x25, 0x01,            //   LOGICAL_MAXIMUM (1)
   0x75, 0x01,            //   REPORT_SIZE (1)
   0x95, 0x08,            //   REPORT_COUNT (8)
   0x81, 0x02,            //   INPUT (Data,Var,Abs)
   0x95, 0x01,            //   REPORT_COUNT (1)
   0x75, 0x08,            //   REPORT_SIZE (8)
   0x81, 0x03,            //   INPUT (Cnst,Var,Abs)
   0x95, 0x05,            //   REPORT_COUNT (5)
   0x75, 0x01,            //   REPORT_SIZE (1)
   0x05, 0x08,            //   USAGE_PAGE (LEDs)
   0x19, 0x01,            //   USAGE_MINIMUM (Num Lock)
   0x29, 0x05,            //   USAGE_MAXIMUM (Kana)
   0x91, 0x02,            //   OUTPUT (Data,Var,Abs)
   0x95, 0x01,            //   REPORT_COUNT (1)
   0x75, 0x03,            //   REPORT_SIZE (3)
   0x91, 0x03,            //   OUTPUT (Cnst,Var,Abs)
   0x95, 0x06,            //   REPORT_COUNT (6)
   0x75, 0x08,            //   REPORT_SIZE (8)
   0x15, 0x00,            //   LOGICAL_MINIMUM (0)
   0x25, 0x65,            //   LOGICAL_MAXIMUM (101)
   0x05, 0x07,            //   USAGE_PAGE (Keyboard)
   0x19, 0x00,            //   USAGE_MINIMUM (Reserved (no event indicated))
   0x29, 0x65,            //   USAGE_MAXIMUM (Keyboard Application)
   0x81, 0x00,            //   INPUT (Data,Ary,Abs)
   0xc0                   // END_COLLECTION
};
/* ------------------------------------------------------------------------- */
uchar usbFunctionSetup(uchar data[8])
{
   usbRequest_t    *rq = (void *)data;
    usbMsgPtr = reportBuffer;
    if((rq->bmRequestType & USBRQ_TYPE_MASK) != USBRQ_TYPE_CLASS)
      return 0;
   switch (rq->bRequest)
   {
        case USBRQ_HID_GET_REPORT:
         return sizeof(reportBuffer);
      case USBRQ_HID_SET_REPORT:
         if (rq->wLength.word == 1)
         {
            expectReport = 1;
            return 0xFF;
         }
         return 0;
      case USBRQ_HID_GET_IDLE:
         usbMsgPtr = &idleRate;
         return 1;
      case USBRQ_HID_SET_IDLE:
            idleRate = rq->wValue.bytes[1];
         return 0;
      case USBRQ_HID_GET_PROTOCOL:
         if (rq->wValue.bytes[1] < 1)
            protocolVer = rq->wValue.bytes[1];
         return 0;
      case USBRQ_HID_SET_PROTOCOL:
         usbMsgPtr = &protocolVer;
         return 1;
      default:
         return 0;
   }
}
/* ------------------------------------------------------------------------- */
uchar usbFunctionWrite(uchar *data, uchar len) {
   if ((expectReport) && (len == 1)) {
      // Set the state of all 5 LEDs
      LEDstate = data[0];
      if (LEDstate & LED_CAPS)
         LED_CAP &= ~(1<<LED_CAB);
      else
         LED_CAP |= (1<<LED_CAB);
      // Shift Lock LED is reversed Num Lock
      if (LEDstate & LED_NUM)
         LED_SHP |= (1<<LED_SHB);
      else
         LED_SHP &= ~(1<<LED_SHB);
      // Power LED is reversed Scroll Lock
      if (LEDstate & LED_SCROLL)
         LED_PWP |= (1<<LED_PWB);
      else
         LED_PWP &= ~(1<<LED_PWB);
   }
   expectReport = 0;
   return 0x01;
}
/* ------------------------------------------------------------------------- */
int   main(void)
{
   uchar   updateNeeded = 0;
   uchar   idleCounter = 0;
    hardwareInit();
   usbInit();
   cli();                  // disable interrupts
    usbDeviceDisconnect();
   DDRD |= (1<<2) | (1<<3);   // USB reset
   _delay_ms(255);            // disconnect for >250ms
    usbDeviceConnect();
   DDRD &= ~((1<<2) | (1<<3));   // clear reset
   sei();                  // restart interrupts
   LED_PWP &= ~(1<<LED_PWB);
   LED_CAP |= (1<<LED_CAB);
   //LED_SHP |= (1<<LED_SHB);
   LED_SHP &= ~(1<<LED_SHB);
   for(;;)
   {
      usbPoll();
      updateNeeded = scanKeys();
      // Check timer if we need periodic reports
      if (TIFR & (1 << TOV0))
      {
         // Reset flag
         TIFR = 1 << TOV0;
         // Do we need periodic reports?
         if (idleRate != 0)
         {
            if (idleCounter > 4)
            {
               // Yes, but not yet
               // 22 ms in units of 4 ms
               idleCounter -= 5;
            }
            else
            {
               // Yes, it is time now
               updateNeeded = 1;
               idleCounter = idleRate;
            }
         }
      }
      if (updateNeeded && usbInterruptIsReady())
      {
         updateNeeded = 0;
         usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
      }
   }
   return 0;
}