ARM:0013 STM32CubeMX – USB HID keyboard

Now we will build a USB keyboard. This project is based on STM32CubeMX canned example, HID mouse (see previous article). But we will change mouse to keyboard. Both devices are from the same HID class. First we need to change the device descriptor. Descriptors are quite complex bunch of numbers with strict structure. For this example we will use another program and generate generic keyboard descriptor.

HID descriptor creator
This is a screenshot of a HID descriptor tool from www.usb.org.

Export descriptor as text file (.h). This file is also included in the source code archive at the end of this post.
Very pity, but there is no option to include this file directly. Only way to do it is edit the descriptor in source code. In file “usbd_hid.c”, from folder “\Middlewares\ST\STM32_USB_Device_Library\Class\HID\Src”. Also, we change “InterfaceProtocol” from mouse to keyboard:

/************** Descriptor of Joystick keyboard interface ****************/
  /* 09 */
  0x09,         /*bLength: Interface Descriptor size*/
  USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
  0x00,         /*bInterfaceNumber: Number of Interface*/
  0x00,         /*bAlternateSetting: Alternate setting*/
  0x01,         /*bNumEndpoints*/
  0x03,         /*bInterfaceClass: HID*/
  0x01,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
  0x01,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
  0,            /*iInterface: Index of string descriptor*/
  /******************** Descriptor of Joystick keyboard HID ********************/
  /* 18 */
  0x09,         /*bLength: HID Descriptor size*/
  HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
  0x11,         /*bcdHID: HID Class Spec release number*/
  0x01,
  0x00,         /*bCountryCode: Hardware target country*/
  0x01,         /*bNumDescriptors: Number of HID class descriptors to follow*/
  0x22,         /*bDescriptorType*/
  HID_KEYBOARD_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
  0x00,
  /******************** Descriptor of keyboard endpoint ********************/

For sake of mind, also change all mentions about the mouse to keyboard in report descriptor:
__ALIGN_BEGIN static uint8_t HID_MOUSE_ReportDesc[HID_MOUSE_REPORT_DESC_SIZE] __ALIGN_END = . . .
Also, mouse and keyboard descriptors have different lengths. This number is included from defines in file “usb-hid.h”, constant HID_KEYBOARD_REPORT_DESC_SIZE.
Start of the new descriptor is:

__ALIGN_BEGIN static uint8_t HID_KEYBOARD_ReportDesc[HID_KEYBOARD_REPORT_DESC_SIZE] __ALIGN_END =
 //char ReportDescriptor[63] = {
 0x05, 0x01, // USAGE_PAGE (Generic Desktop)
 0x09, 0x06, // USAGE (Keyboard)
 . . . .

Theoretically, source can be compiled and uploaded to MCU. And the host computer will see this MCU as a keyboard (if drivers are refreshed), but this new “keyboard” will do nothing.
Another important thing is, that if we regenerated code from Cube, all these changes will be lost. Do backups!

To make our “keyboard” usable, edit “main.c” file:

/* USER CODE BEGIN Includes */
#include "usbd_hid.h"
#include "PS2/PS2Keyboard.c"
/* USER CODE END Includes */


This add our PS2 keyboard code. Also, add USB device structure:

/* USER CODE BEGIN PV */
 extern USBD_HandleTypeDef hUsbDeviceFS;
 /* USER CODE END PV */

In the main function we will write all data translation from PS2 keyboard to USB keyboard. Quite interesting, but my PS2 keyboard scan codes are different from standard USB keyboard codes.
First of all, define the structure for the HID report.

 /* USER CODE BEGIN 1 */
int scancode;
unsigned char passcode=0;
char c;

// HID Keyboard
struct keyboardHID_t {
uint8_t modificator; // bitai: 0-LCTRL, 1-LSHIFT, 2-LALT, 3-LGUI, 4-RCTRL, 5-RSHIFT, 6-RALT, 7-RGUI
uint8_t res; // 0x00 nulis
uint8_t key[6];
};
struct keyboardHID_t kbdHID;
kbdHID.modificator=0;
kbdHID.res=0;
kbdHID.key[0]=0;
kbdHID.key[1]=0;
kbdHID.key[2]=0;
kbdHID.key[3]=0;
kbdHID.key[4]=0;
kbdHID.key[5]=0;
/* USER CODE END 1 */

In main loop:

 /* USER CODE BEGIN WHILE */
  while (1)
  {
   	scancode=get_PS2scan_code();
	if (scancode !=0) {
		if(scancode==0xF0)
			{ passcode=1;
			kbdHID.key[0]= 0;
			USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&kbdHID, sizeof(struct keyboardHID_t));
			}
		else
			{
			if(passcode==1)
				{passcode=0;}
			else
				{
				c=scan_decode(scancode);
				kbdHID.key[0]= c;
				USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&kbdHID, sizeof(struct keyboardHID_t));
				}
			}
		}  
 /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

Program the chip and connect it to the host computer. Do not forget to uninstall USB device drivers and reinstall drivers. This is because VID and PID number are the same as in previous examples.

All source code, compiled binaries (hex) for STM32F103 (BluePill PCB).

Here is a screenshot  of the USB descriptor reader:

USB descriptor dumper

About Administrator

I am owner of this site.
This entry was posted in Anything, MCU. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>