HIDKeyboard

This simple program demonstrates the use of a USB equipped PIC, such as the 18F4550, as a HID Keyboard device.

The program was designed using the PICDEM FS USB board, and using the FSUSB Bootloader program.

Once running it will enumerate with a connected PC as a "HID Keyboard Device". After a few seconds it will open a Wordpad window and begin typing by itself. If you press the CapsLock key on your keyboard you should notice it also change to reflect the current status on LED D4.

The program works by sending a HID packet with the required keypresses. The keypress code table can be found in the "KeyboardDefs.bas" file. Six individual keypresses can be sent simultaneously by using the KeyArray 0 - 5 bytes. The key will keep repeating unless either a blank packet is sent (indicating the key has been lifted), or another keypress combination is sent.

The shift, alt, ctrl and GUI(windows) keys are special modifier keys and are not sent in the same way (and do not count for the 6 keypresses). These are sent in the leading Modifier byte, coded as individual bits within the byte. As such multiple modifier bytes can be sent by ORing the bits together eg.

LeftShift = %00000010, LeftAlt = %00000100,

Setting the Modifier byte to %00000110 would send both a left shift and a left alt keypress.

The hid_rpt01 descriptor array is taken from the microchip hid keyboard demo program.

Demo Program

{
*****************************************************************************
*  Name    : USB Keyboard demo.bas                                          *
*  Author  : Nathan Herbert                                                 *
*  Notice  : Copyright (c) 2008                                             *
*          : All Rights Reserved                                            *
*  Date    : 03/02/2009                                                     *
*  Version : 1.1                                                            *
*  Notes   : 1.0 First Revision                                             *
*          : 1.1 Updated to read back LED status                            *
*		   : 1.2 Now opens own wordpad window to show possibile uses		*
*****************************************************************************
}
{ 
Up to 6 individual simultaneous keys can be pressed by using Keyarray 1 through 5
Alts, shift, ctrls etc are handled though modifer bytes. These can be combined by
ORing the required bytes together.
}

Device = 18F4550
Clock = 48

// 20Mhz crystal, 48Mhz internal (FS USB)
Config
   PLLDIV = 5,
   CPUDIV = OSC1_PLL2,
   USBDIV = 2,
   FOSC = HSPLL_HS,
   VREGEN = ON

#option HID_BUFFER_SIZE = 8
#option USB_DESCRIPTOR = "HIDKeyboardDesc.bas"

// Use with FSUSB Bootloader
#option org_reset = $0800

Include "usbhid.bas"
Include "keyboarddefs.bas"

// Keyboard HID buffer 
Structure TKeyReport
	Modifier	 	As Byte 	// Modifer Byte
	Reserved		As Byte     // Reserved
	KeyArray0		As Byte		// Buttons Pushed
	KeyArray1		As Byte
	KeyArray2		As Byte
	KeyArray3		As Byte
	KeyArray4		As Byte
	KeyArray5		As Byte	
End Structure
Dim KeyReport As TKeyReport Absolute TXReportRAM

// Status LED structure
Structure TLedsReport
	_byte As Byte
	NumLock 		As _byte.0
	CapsLock 		As _byte.1
	ScrollLock		As _byte.2
	Compose			As _byte.3
	Kana			As _byte.4
	Const1			As _byte.5
	Const2			As _byte.6
	Const3			As _byte.7
End Structure
Dim LedsReport As TLedsReport Absolute RXReportRAM

Dim 
LED0 As PORTD.0,
LED1 As PORTD.1,
LED3 As PORTD.3

{
********************************************************************************
* Name    : SendKey				                                               *
* Purpose : Sends Key Report  				                                   *
********************************************************************************
}
Sub SendKey(pKey As Byte, pModifier As Byte = $00)

	// Send desired key
	KeyReport.Modifier	= pModifier
	KeyReport.Reserved	= 0
	KeyReport.KeyArray0	= pKey
	KeyReport.KeyArray1	= 0
	KeyReport.KeyArray2	= 0
	KeyReport.KeyArray3	= 0
	KeyReport.KeyArray4	= 0
	KeyReport.KeyArray5	= 0

	WriteReport

	// Send Key Release
	KeyReport.Modifier	= 0
	KeyReport.Reserved	= 0
	KeyReport.KeyArray0	= 0
	KeyReport.KeyArray1	= 0
	KeyReport.KeyArray2	= 0
	KeyReport.KeyArray3	= 0
	KeyReport.KeyArray4	= 0
	KeyReport.KeyArray5	= 0

	WriteReport

End Sub

{
********************************************************************************
* Name    : Main				                                               *
* Purpose : Main Routine  				                                   *
********************************************************************************
}
Sub Main()
	Dim Key As Byte
	Dim UpperCase As Boolean
	Dim pModifier As Byte

	// Open a Wordpad Window by sending GUI + R to open the run dialog
	SendKey(r, LeftGUI)

	// Wait a little
	DelayMS(100)

	// Write Wordpad.exe
	SendKey(w, LeftShift)
	SendKey(o)	
	SendKey(r)	
	SendKey(d)	
	SendKey(p)	
	SendKey(a)	
	SendKey(d)	
	SendKey(period)	
	SendKey(e)	
	SendKey(x)	
	SendKey(e)
	SendKey(enter)		

	// Wait a little to load
	DelayMS(1000)

	// Write Welcome Message
	SendKey(b, LeftCtrl)
	SendKey(s, LeftShift)
	SendKey(w)
	SendKey(o)
	SendKey(r)
	SendKey(d)
	SendKey(f)
	SendKey(i)
	SendKey(s)
	SendKey(h)
	SendKey(b, LeftCtrl)
	SendKey(spacebar)
	SendKey(u, LeftShift)
	SendKey(s, LeftShift)
	SendKey(b, LeftShift)
	SendKey(spacebar)
	SendKey(d)
	SendKey(e)
	SendKey(m)
	SendKey(o)
	SendKey(enter)
	SendKey(enter)
	SendKey(enter)

	Uppercase = true

	// Let the Keyboard type away!
	While true

		// Modifer byte controls Shift, Alts, Ctrls
		If Uppercase = true Then
	    	pModifier = None
        	Uppercase = false
		Else
			pModifier = LeftShift
			Uppercase = true
		EndIf            

		// Cycle throught A-Z, 1-0
		For key = 4 To 40

        	// Activity LED
    		Toggle(LED1)

			// Send the Key
			SendKey(key, pmodifier)

			DelayMS(500)

			// Read LED status
			If  HID.DataAvailable Then

				// Bring Report into structure
				ReadReport 

				// Set LED 3 to show caps lock status
				LED3 = LedsReport.CapsLock

			EndIf
		Next
	Wend	
End Sub

{
********************************************************************************
* Name    : Program Start		                                               *
* Purpose : 				  				                                   *
********************************************************************************
}

Low(LED0)
Low(LED1)
Low(LED3)

// Wait for USB attach
Repeat
Until HID.Attached

// Attached
High(LED0)

// Let PC finish enumerating
DelayMS(5000)

Main

HID Descriptors

{
*****************************************************************************
*  Name    : HIDKeyboardDesc.bas                                            *
*  Author  : David John Barker                                              *
*  Notice  : Copyright (c) 2007 Mecanique                                   *
*          : All Rights Reserved                                            *
*  Date    : 10/01/2007                                                     *
*  Version : 1.0                                                            *
*  Notes   : EasyHID USB HID descriptor module                              *
*****************************************************************************
}
Module HIDDescriptor

// EasyHID options...
#option USB_HAVE_SERIAL_STRING = false
#option USB_VID                = 6017
#option USB_PID                = 2000
#option HID_EP_OUT_POLLING_MS  = $0A
#option HID_EP_IN_POLLING_MS   = $0A
#option USB_BUS_POWER          = $FA
#option HID_REPORT_BYTES_IN    = $08
#option HID_REPORT_BYTES_OUT   = $08
#option USB_SERVICE            = true

// import modules...
Include "usbconfig.bas"
Include "usbdefs.bas"

// Device Descriptor 
Public Const device_dsc(18) As Byte = (
	SizeOf(USB_DEV_DSC),          // bLength - size of this descriptor IN bytes
	DSC_DEV,	                     // bDescriptorType - device descriptor type
	$00,	                        // bcdUSB (low byte)  - USB Spec Release Number IN BCD format (2.0)
	$02,	                        // bcdUSB (high byte) - USB Spec Release Number IN BCD format (2.0)
	$00,	                        // bDeviceClass - class Code
	$00,	                        // bDeviceSubClass - subclass code
	$00,	                        // bDeviceProtocol - protocol code
	USB_EP0_BUFF_SIZE,	         // bMaxPacketSize - max packet size for EP0
	USB_VID And $00FF,            // idVendor (low byte)
	USB_VID >> 8,                 // idVendor (high byte)
	USB_PID And $00FF,            // idProduct (low byte)
	USB_PID >> 8,                 // idProduct (high byte)
	USB_DEVICE_VERSION And $00FF, // bcdDevice (low byte) - device release number IN BCD format
	USB_DEVICE_VERSION >> 8,      // bcdDevice (high byte) - device release number IN BCD format
	USB_MANUFACTURER_INDEX,       // iManufacturer - manufacturer string index
	USB_PRODUCT_INDEX,            // iProduct - product string index
	USB_SERIAL_INDEX,             // iSerialNumber - device serial number string index
	$01	                        // bNumConfigurations - mumber of possible configurations
)

// Configuration 1 Descriptor
Public Const cfg01(41) As Byte = (

   // configuration Descriptor (cd01)
   SizeOf(USB_CFG_DSC),    // Size of this descriptor in bytes
   DSC_CFG,                // CONFIGURATION descriptor type
   41,                     // sizeof(cfg01) - Total length of data for this cfg
   0,                      // HIGH BYTE
   1,                      // Number of interfaces in this cfg
   1,                      // Index value of this configuration
   0,                      // Configuration string index
   _DEFAULT Or _RWU,       // Attributes, see usbdefs_std_dsc.h
   USB_BUS_POWER,          // Max power consumption (2X mA)

   // interface descriptor (i00a00)           
   SizeOf(USB_INTF_DSC),   // Size of this descriptor in bytes
   DSC_INTF,               // INTERFACE descriptor type
   0,                      // Interface Number
   0,                      // Alternate Setting Number
   2,                      // Number of endpoints in this intf
   HID_INTF,               // Class code
   BOOT_INTF_SUBCLASS,                      // Subclass code
   HID_PROTOCOL_KEYBOAD,                      // Protocol code  			// Spelling mistake in SF lib
   0,                      // Interface string index

   // HID class-specific descriptor (hid_i00a00)
   SizeOf(USB_HID_DSC),    // Size of this descriptor in bytes
   DSC_HID,                // HID descriptor type
   $10,                    // HID Spec Release Number in BCD format (Low)
   $01,                    // HID Spec Release Number in BCD format (High)   
   $00,                    // Country Code ($00 for Not supported)
   HID_NUM_OF_DSC,         // Number of class descriptors, see usbcfg.h
   DSC_RPT,                // Report descriptor type
   63,         // sizeof(hid_rpt01) - Size of the report descriptor      	// Now hardcoded here
   0,                      // HIGH BYTE

   // endpoint descriptor (IN) (ep01i_i00a00)
   SizeOf(USB_EP_DSC),		 // Size of this descriptor in bytes 
 	DSC_EP,						 // Endpoint descriptor type
	_EP01_IN,					 // Endpoint 1 IN	
	_INT,						    // Interrupt transfers
	HID_INT_IN_EP_SIZE,		 // Maximum packet size
	$00,                     // high byte
	HID_EP_IN_POLLING_MS,    // Polling interval (milliseconds)

	// enpoint descriptor (OUT) (ep01o_i00a00)
	SizeOf(USB_EP_DSC),      // Size of this descriptor in bytes 
	DSC_EP,						 // Endpoint descriptor type
	_EP01_OUT,					 // Endpoint 1 OUT	
	_INT,						    // Interrupt transfers
	HID_INT_OUT_EP_SIZE,	    // Maximum packet size
	$00,                     // high byte
	HID_EP_OUT_POLLING_MS    // Polling interval (milliseconds)
)

// language string - unicode format...  
Public Const sd000(4) As Byte = (
   4,              // Size of this descriptor in bytes 
   DSC_STR,        // DescriptorType
   $09,            // language ID - low byte   
   $04             // language ID - high byte 
)   

// manufacturer string - unicode format...
Public Const sd001(20) As Byte = (
   20,
   DSC_STR,
   "S", 0, "w", 0, "o", 0, "r", 0, "d", 0, "F", 0, "i", 0, "s", 0, "h", 0
)


// product string - unicode format...
Public Const sd002(26) As Byte = (
   26,
   DSC_STR,
   "K", 0, "e", 0, "y", 0, "b", 0, "o", 0, "a", 0, "r", 0, "d", 0, "D", 0, "e", 0, "m", 0, "o", 0
)


// product serial number...
#if USB_HAVE_SERIAL_STRING
// no serial string
#endif

(*
   This example report descriptor for a "generic HID" defines one
   report of each type. Each report contains two bytes of data with 
   a vendor-defined Usage.

   HID_RPT01_SIZE is defined in usbcfg.h and must equal the number of bytes 
   in the report descriptor.
*)
Public Const hid_rpt01(63) As Byte = (                    // Hard coded size          
	$05, $01,                    // USAGE_PAGE (Generic Desktop)
    $09, $06,                    // USAGE (Keyboard)
    $a1, $01,                    // COLLECTION (Application)

    $05, $07,                    //   USAGE_PAGE (Keyboard)
    $19, $e0,                    //   USAGE_MINIMUM (Keyboard LeftControl)
    $29, $e7,                    //   USAGE_MAXIMUM (Keyboard Right GUI)
    $15, $00,                    //   LOGICAL_MINIMUM (0)

    $25, $01,                    //   LOGICAL_MAXIMUM (1)
    $75, $01,                    //   REPORT_SIZE (1)
    $95, $08,                    //   REPORT_COUNT (8)
    $81, $02,                    //   INPUT (Data,Var,Abs)

    $95, $01,                    //   REPORT_COUNT (1)
    $75, $08,                    //   REPORT_SIZE (8)
    $81, $03,                    //   INPUT (Cnst,Var,Abs)
    $95, $05,                    //   REPORT_COUNT (5)
    $75, $01,                    //   REPORT_SIZE (1)
    $05, $08,                    //   USAGE_PAGE (LEDs)
    $19, $01,                    //   USAGE_MINIMUM (Num Lock)
    $29, $05,                    //   USAGE_MAXIMUM (Kana)
    $91, $02,                    //   OUTPUT (Data,Var,Abs)
    $95, $01,                    //   REPORT_COUNT (1)
    $75, $03,                    //   REPORT_SIZE (3)
    $91, $03,                    //   OUTPUT (Cnst,Var,Abs)
    $95, $06,                    //   REPORT_COUNT (6)
    $75, $08,                    //   REPORT_SIZE (8)
    $15, $00,                    //   LOGICAL_MINIMUM (0)
    $25, $65,                    //   LOGICAL_MAXIMUM (101)
    $05, $07,                    //   USAGE_PAGE (Keyboard)
    $19, $00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    $29, $65,                    //   USAGE_MAXIMUM (Keyboard Application)
    $81, $00,                    //   INPUT (Data,Ary,Abs)
    $c0  
)

KeyBoard definitions

{
*****************************************************************************
*  Name    : KeyboardDefs.BAS                                               *
*  Author  : Nathan Herbert                                                 *
*  Notice  : Copyright (c) 2009                                             *
*          : All Rights Reserved                                            *
*  Date    : 02/02/2009                                                     *
*  Version : 1.0                                                            *
*  Notes   :                                                                *
*          :                                                                *
*****************************************************************************
}
Module KeyDefs

//*Definitions*/
Public Const
	a 		= 4,
	b		= 5,		
 	c		= 6,
	d		= 7,
	e		= 8,
	f		= 9,
	g		= 10,
	h		= 11,
	i		= 12,
	j		= 13,
	k		= 14,
	l		= 15,
	m		= 16,
	n		= 17,
	o		= 18,
	p		= 19,
	q		= 20,
	r		= 21,
	s		= 22,
	t		= 23,
	u		= 24,
	v		= 25,
	w		= 26,
	x		= 27,
	y		= 28,
 	z		= 29,
 one		= 30,
 two		= 31,
 three		= 32,
 four		= 33,
 five		= 34,
 six 		= 35,
 seven		= 36,
 eight		= 37,
 nine		= 38,
 zero		= 39,
 enter		= 40,
 escape		= 41,
 backspace   = 42,
 tab		= 43,
 spacebar	= 44,
 underscore	= 45,
 equals		= 46,
 leftbrace	= 47,
 rightbrace	= 48,
 backslash	= 49,
 hash		= 50,
 semicolon	= 51,

 comma		= 54,
 period		= 55,
 slash		= 56,
 capslock	= 57,
 f1			= 58,
 f2			= 59,
 f3			= 60,
 f4			= 61,
 f5			= 62,
 f6			= 63,
 f7			= 64,
 f8 		= 65,
 f9			= 66,
 f10		= 67,
 f11		= 68,
 f12		= 69,

 home		= 74,
 pageup		= 75,
 delete     = 76,				
 pagedown	= 78,
 rightarrow	= 79,
 leftarrow	= 80,
 downarrow	= 81,
 uparrow	= 82,

// add numpad keys for mouse keys?

// Modifier bytes
None		= %00000000,
LeftCtrl	= %00000001,
LeftShift	= %00000010,
LeftAlt		= %00000100,
LeftGUI		= %00001000,
RightCtrl	= %00010000,
RightShift	= %00100000,
RightAlt	= %01000000,
RightGUI	= %10000000