cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F469 HID - add a keyboard to a custom HID

Jerry Hancock
Associate II

I have my custom HID device running and working fine. I would like it to also emulate a standard PC keyboard at the same time. So when my board enumerates, I would like the attached PC to find my custom device as well as a keyboard.

I think I can just add another device config descriptor, interface and end points to the existing custom HID descriptors and then add another report descriptor for the keyboard. Please confirm.

I then tried of setting up two complete sets of descriptors and calling USBD_init, USBD_RegisterClass and USBD_Start for each set of descriptors. I tried this and was never able to get the keyboard to enumerate, could be any of many reasons. Also, it looked like it was killing the first device, my custom HID.

Not being sure how to do this, I would appreciate advice. If both ways would work, then I guess I would use the first as it would require fewer lines of code.

I hesitate to try to the first way I described above as adding all the device descriptors, etc, are a lot of work to get them all aligned.

Thank you.

Jerry

28 REPLIES 28
Jerry Hancock
Associate II

OK, new issue. the keyboard which is the first interface, is working fine. The second interface, the encoder, has a problem.

I get this during enumeration:

This device cannot start. (Code 10)

Insufficient resources to allocate needed memory.

Haven't seen this before. If I look further down into the device information in the Device Manager, I see this:

{Buffer Too Small}

The buffer is too small to contain the entry. No information has been written to the buffer.

The second report descriptor is slightly larger than the first but I don't see that mattering. I'm struggling with where to begin shooting this bug.

One thing I notice as in my prior comment is that my code returns the entire combined descriptor three times (prompted by the computer). Is this correct or should it return just the applicable report descriptor?

Thanks

ps. sorry for flooding this board with my silly problem.

I am getting closer though, much closer.

Thanks for all the help.

Jerry

Jerry Hancock
Associate II

I'm hoping someone finds all this useful in the future.

I was able to get around the resources issue by replying to the config descriptor request with just the HID report of the respective interface. Both devices enumerate now without obvious errors.

I then tried sending keyboard characters and it doesn't work. This worked before so there could be a couple of issues. When I was returning the entire HID containing both HID report ID 01 and 02, the keyboard still worked but the encoder wouldn't enumerate and had the error above.

1) Since I now have to prepend a 0x01 on the front of the keyboard buffer and the length is still 8 bytes, maybe the driver is throwing away the incoming report?

2) I tried offsetting the keyboard character so instead of it being in position 2 with with the interface number in position 00, I shifted it over to position 3. This didn't help.

So maybe I have to change the incoming HID report length? Ideas ? I haven't tried the encoder. The encoder software finds the device but it could b just finding the keyboard since the both have the same VID/PID. But if that was the case, then the keyboard wouldn't have worked when the encoder software was running, and it did. Also, he keyboard doesn't work with the encoder software shutdown so that can't be impacting it.

I'm getting closer all the time. I think I am on the verge of it working. I checked another composite device and it sends separate HID descriptors instead of one longer, combined one. So I am going to keep comparing against it.

Anyone out there? 🙂

Jerry

Jerry Hancock
Associate II

OK, daily update...

The keyboard is working to some extent. Though I am trying to send a '1' every 5 seconds using the following code, I get 898 ones. It's like the keyboard repeat key is stuck between transmission. I send 00 00 1E 00 00 00 00 00 followed by 00 00 00 00 00 00 00 00. The reportid is 00 in this case.

I tried clearly the buffer between transmission, etc. No luck.

while (1)

{

for (x=0;x<10;x++) HIDbuffer[x]=0x00;

HAL_Delay(5);

HIDbuffer[0]=0x00;

HIDbuffer[2] = 0x1e; // usb keyboard id for '1'

HIDbuffer[3] = 0x00;

buf = HIDbuffer;

USBD_HID_SendReport(&hUsbDeviceFS, buf, 8);

HAL_Delay(5);

for (x=0;x<10;x++) HIDbuffer[x]=0x00;

HIDbuffer[2] =0x00;

HIDbuffer[3] =0x00;

buf = HIDbuffer;

USBD_HID_SendReport(&hUsbDeviceFS, buf, 8);

HAL_Delay(5); // wait 5ms

HAL_Delay(5000); // wait 5 seconds

}

I traced the code (0x1e) and release key (0x00) and see my character and character release getting to the PC in the proper time.

Anyway, I'm making progress.

Jerry

Jerry Hancock
Associate II

I think I found my problem with the PC buffering and I don't know why. If I use my descriptors with just a keyboard, all is fine. If I add the encoder with a keyboard report (instead of the encoder report), all is fine. If I use the keyboard with the encoder report, the keyboard buffers in the PC.

If you look at the code for sending reports in HAL below, you'll see that it only sends when idle. But if the device is busy, it falls through and still returns OK. So I guess what was happening is that when I send a key, the key release was falling through because of busy but ultimately the buffer filled up or a key release got through. Tracing the USB though showed the release being sent.

But if I loop on busy before or after I am sending, all is fine. Maybe it has something to do with the polling? I don't know. An external analyzer would catch the issue but I am using Wireshark on the PC.

Here's the cubeMX generated HAL code:

uint8_t USBD_HID_SendReport   (USBD_HandleTypeDef *pdev,  uint8_t *report, uint16_t len)

{

 USBD_HID_HandleTypeDef   *hhid = (USBD_HID_HandleTypeDef*)pdev->pClassData;

  

 if (pdev->dev_state == USBD_STATE_CONFIGURED )

 {

  if(hhid->state == HID_IDLE) // transmit if idle but fall through without an error if busy!!!

  {

   hhid->state = HID_BUSY;

   USBD_LL_Transmit (pdev,  HID_EPIN_ADDR, report, len);

  }

 }

 return USBD_OK;

}

My code was delayed as much as 100ms between key presses so something on the PC isn't replying or triggering the dataInstage to reset Busy. I have to investigate more. But changing the code as below fixed the problem:

uint8_t USBD_HID_SendReport   (USBD_HandleTypeDef *pdev,  uint8_t *report, uint16_t len)

{

 USBD_HID_HandleTypeDef   *hhid = (USBD_HID_HandleTypeDef*)pdev->pClassData;

  

 if (pdev->dev_state == USBD_STATE_CONFIGURED )

 {

while(hhid->state == HID_BUSY); // sit and wait for not busy

  if(hhid->state == HID_IDLE) // Idle gets set during datainstage

  {

   hhid->state = HID_BUSY;

   USBD_LL_Transmit (pdev,  HID_EPIN_ADDR, report, len);

  }

 }

 return USBD_OK;

}

Anyway, I am able to move ahead with the project now but I'm sure I'll get hung-up again.

Thanks to Pavel and Ben for chiming in.

Jerry

Pavel A.
Evangelist III

You conclusion and fix seems correct, the 2nd report with all zeros is lost. There's a little surprise in the HID example code 😉

-- pa

AYılm.1
Associate II

Hello, I am making a CUSTOM HID Keyboard application with the stm32f103 processor, when I add the media keys (volume reduction, increase, etc.) to my project, the function that reads the status of the following leds with event_idx does not work. What is the reason, urgent help is needed. I made the descriptor declaration as report 1 keyboard and report 2 media

AYılm.1
Associate II

static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)

{

 /* USER CODE BEGIN 6 */

if( (event_idx & 0x01) == 0x01 )

{

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_10, GPIO_PIN_RESET);

}

else if( (event_idx & 0x01) != 0x01 )

{

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_10, GPIO_PIN_SET);

}

if( (event_idx & 0x02) == 0x02 )

{

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15, GPIO_PIN_RESET);

}

else if( (event_idx & 0x02) != 0x02 )

{

HAL_GPIO_WritePin(GPIOA,GPIO_PIN_15, GPIO_PIN_SET);

}

 return (USBD_OK);

 /* USER CODE END 6 */

}

AYılm.1
Associate II

__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =

{

 /* USER CODE BEGIN 0 */

 0x05, 0x01,          // USAGE_PAGE (Generic Desktop)

 0x09, 0x06,          // USAGE (Keyboard)

 0xa1, 0x01,          // COLLECTION (Application)

 0x05, 0x07,          //  USAGE_PAGE (Keyboard)

 0x85, 0x01, // Report ID (1)

 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        */

0x05, 0x0C, // Usage Page (Consumer)

0x09, 0x01, // Usage (Consumer Control)

0xA1, 0x01, // Collection (Application)

0x85, 0x02, // Report ID (2)

0x05, 0x0C, // Usage Page (Consumer)

0x15, 0x00, // Logical Minimum (0)

0x25, 0x01, // Logical Maximum (1)

0x75, 0x01, // Report Size (1)

0x95, 0x08, // Report Count (8)

0x09, 0x79, // Usage (Scan Next Track)

0x09, 0x7A, // Usage (Scan Previous Track)

0x09, 0xB7, // Usage (Stop)

0x09, 0xB8, // Usage (Eject)

0x09, 0xCD, // Usage (Play/Pause)

0x09, 0xE2, // Usage (Mute)

0x09, 0xE9, // Usage (Volume Increment)

0x09, 0xEA, // Usage (Volume Decrement)

0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)

0xc0       // End Collection*/

};

AYılm.1
Associate II

While sending the notification IDs in my main file, I send 0x01 keyboard 0x02 media in the order in which each key comes.