LUFA keyboard ignoring repeated scancodes

Go To Last Post
4 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

It's my first time playing with LUFA (Thanks Dean!) and I've gotten the high-level keyboard demo to work.  My goal is to turn this into a keyboard wedge device that reads the data output from up to 4 Mitutoyo measuring devices and send it to the host as if typed from the keyboard.  I plan on adding a few tabs and enter key scan codes in the data stream so that Excel will automatically add the data into columns and rows.

 

My first experiments were successful, but I've hit a small stumbling block.  I've set up a ring buffer to handle the data being output from the "keyboard" and initialized it with some test data in my main():

 

	RingBuffer_Insert( &CharBuffer, 'H');
	RingBuffer_Insert( &CharBuffer, 'E');
	RingBuffer_Insert( &CharBuffer, 'L');
	RingBuffer_Insert( &CharBuffer, 'L');
	RingBuffer_Insert( &CharBuffer, 'O');
	RingBuffer_Insert( &CharBuffer, 0x09);
	RingBuffer_Insert( &CharBuffer, '1');
	RingBuffer_Insert( &CharBuffer, '.');
	RingBuffer_Insert( &CharBuffer, '2');
	RingBuffer_Insert( &CharBuffer, 0x09);
	RingBuffer_Insert( &CharBuffer, 'a');
	RingBuffer_Insert( &CharBuffer, 'c');
	RingBuffer_Insert( &CharBuffer, 'z');
	RingBuffer_Insert( &CharBuffer, 0x0D);
	RingBuffer_Insert( &CharBuffer, 'O');

And I've modified the HID report callback function to pop characters from the queue, convert to scancodes, and send them out:

	if( ! RingBuffer_IsEmpty( &CharBuffer))
	{
		uint8_t character = RingBuffer_Remove( &CharBuffer);
	
		switch( character)
		{
			case 0x09: /* Horizontal Tab */
			{
				KeyboardReport->KeyCode[0] = HID_KEYBOARD_SC_TAB;
				break;
			}
			case 0x0D: /* Carriage Return */
			{
				KeyboardReport->KeyCode[0] = HID_KEYBOARD_SC_ENTER;
				break;
			}
			case ' ':
			{
				KeyboardReport->KeyCode[0] = HID_KEYBOARD_SC_SPACE;
				break;
			}
			case '.':
			{
				KeyboardReport->KeyCode[0] = HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN;
				break;
			}
			case '0':
			{
				KeyboardReport->KeyCode[0] = HID_KEYBOARD_SC_0_AND_CLOSING_PARENTHESIS;
				break;
			}
			case '1' ... '9':
			{
				KeyboardReport->KeyCode[0] = character - 0x13;
				break;
			}
			case 'A' ... 'Z':
			{
				KeyboardReport->KeyCode[0] = character - 0x3D;
				KeyboardReport->Modifier = HID_KEYBOARD_MODIFIER_LEFTSHIFT;
				break;
			}
			case 'a' ... 'z':
			{
				KeyboardReport->KeyCode[0] = character - 0x5D;
				break;
			}
			default:
			{
				// ignore all other characters
			}
		}	
	}

	*ReportSize = sizeof(USB_KeyboardReport_Data_t);
	return false;

 

This works as expected except for duplicate characters.  You can see from the output that the second 'L' in "HELLO" is being dropped.

HELO	1.2	acz
O

How can I tell LUFA to allow multiple characters in the scancode stream?  Returning TRUE from CALLBACK_HID_Device_CreateHIDReport does not seem to make a difference.

 

Cheers,

Tom

Last Edited: Mon. Mar 9, 2015 - 03:05 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, it's not pretty, but it works.  The solution I came up with was to send a null report back between repeated characters:

 

bool CALLBACK_HID_Device_CreateHIDReport(USB_ClassInfo_HID_Device_t* const HIDInterfaceInfo,
                                         uint8_t* const ReportID,
                                         const uint8_t ReportType,
                                         void* ReportData,
                                         uint16_t* const ReportSize)
{
	USB_KeyboardReport_Data_t* KeyboardReport = (USB_KeyboardReport_Data_t*)ReportData;
	static uint8_t prevScanCode, scanCode, modifier, repeat;
	
	if( repeat)
	{
		/* this is a kludge to get the keyboard to send repeated characters.  If we are repeating,
		    do not get a new character from the buffer.  Instead, use the previously determined 
		    scancode and modifier from the last cycle. */ 
	}
	else
	{
		// not repeating so check for a new character to transmit
		scanCode = 0;
		modifier = 0;
		
		if( ! RingBuffer_IsEmpty( &CharBuffer))
		{
			uint8_t character = RingBuffer_Remove( &CharBuffer);
		
			switch( character)
			{
				case 0x09: // Horizontal Tab
				{
					scanCode = HID_KEYBOARD_SC_TAB;
					break;
				}
				case 0x0D: // Carriage Return
				{
					scanCode = HID_KEYBOARD_SC_ENTER;
					break;
				}
				case ' ':
				{
					scanCode = HID_KEYBOARD_SC_SPACE;
					break;
				}
				case '.':
				{
					scanCode = HID_KEYBOARD_SC_DOT_AND_GREATER_THAN_SIGN;
					break;
				}
				case '0':
				{
					scanCode = HID_KEYBOARD_SC_0_AND_CLOSING_PARENTHESIS;
					break;
				}
				case '1' ... '9':
				{
					scanCode = character - 0x13;
					break;
				}
				case 'A' ... 'Z':
				{
					scanCode = character - 0x3D;
					modifier = HID_KEYBOARD_MODIFIER_LEFTSHIFT;
					break;
				}
				case 'a' ... 'z':
				{
					scanCode = character - 0x5D;
					break;
				}
				default:
				{
					// ignore all other characters
				}
			}	
		}
	}
	
	// don't set repeating on null reports
	if( (scanCode != 0) && (scanCode == prevScanCode))
	{
		// we have a new repeat
		repeat = TRUE;
		KeyboardReport->KeyCode[0] = 0;
		KeyboardReport->Modifier = 0;
	}
	else
	{
		repeat = FALSE;
		KeyboardReport->KeyCode[0] = scanCode;
		KeyboardReport->Modifier = modifier;
	}
	
	prevScanCode = KeyboardReport->KeyCode[0];
	*ReportSize = sizeof(USB_KeyboardReport_Data_t);
	return false;
}

 

Cheers,

Tom

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If USB keyboards work the way that PS/2 keyboards work, shouldn't you have a "key up" scan code sent after each "key press" scan code?

(otherwise the repeated scan codes look like autorepeats.)

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm with westy. You can run a usb sniffer program (i think microsoft even has a free one) to see what is happening on a real vs your keyboard.