cancel
Showing results for 
Search instead for 
Did you mean: 

STM32 USB HID - more buttons problem (button box / joystick buttons)

QRCHuCK
Visitor

Hello, dear community!

I have a problem with adding more than one button to my STM32 Button Box / joystick with buttons. I am using USB HID.
I made it as simple as possible - only two buttons, simplest descriptor. The problem may be trivial, but after 4 days and ~8 hours per day - I can't find solution. I watched most of the STM32 USB training course (on yt), I was digging deep in st forum and google/bing/yahoo, but nothing helped. I think there is not a ready answer to my problem in the whole internet.

About the problem: One button (the first one) is working flawlessly, but when i try to USBD_HID_SendReport for another button - nothing happens.

 

My descriptor: 

 

// HID Report Descriptor
0x05, 0x01,        // Usage Page (Generic Desktop)
0x09, 0x04,        // Usage (Joystick)
0xA1, 0x01,        // Collection (Application)
0x15, 0x00,        // Logical Minimum (0)
0x25, 0x01,        // Logical Maximum (1)
0x75, 0x01,        // Report Size (1 bit)
0x95, 0x02,        // Report Count (2 buttons)
0x05, 0x09,        // Usage Page (Button)
0x19, 0x01,        // Usage Minimum (Button 1)
0x29, 0x02,        // Usage Maximum (Button 2)
0x81, 0x02,        // Input (Data, Var, Abs) - Button states

// Padding to align to a byte
0x75, 0x07,        // Report Size (7 bits, for padding)
0x95, 0x02,        // Report Count (2 bits for padding)
0x81, 0x03,        // Input (Const, Var, Abs) - Padding byte

0xC0               // End Collection

 

 

 

 

 

 

 

And code from main.c:

 

int main(void)
{

  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */
  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */
  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USB_DEVICE_Init();
  /* USER CODE BEGIN 2 */
  extern USBD_HandleTypeDef hUsbDeviceFS;

	typedef struct
	{
		uint8_t button1;
		uint8_t button2;
	} joystickHID;

  joystickHID joystickhid = {0};



  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {		// start probing every of the input pins
	  if( 		 HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1)
			  || HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2)
			  || HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3)
			  || HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_4)
			  || HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_5)
			  || HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_6)
			  || HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7)
			  || HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0)
	  )  // end probing
	  { // if any input pin get input then
		  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET); 	// turn on onboard LED
		  joystickhid.button1 = 1; 								// set button1 to active
		  joystickhid.button2 = 1;								// set button2 to active
		  HAL_Delay(200);										// wait
	  }
	  else{ // if none of the input pins get input then
		  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // turn off onboard LED
		  joystickhid.button1 = 0;								// set button1 to non-active
		  joystickhid.button2 = 0;								// set button2 to non-active
		  HAL_Delay(200);										// wait
	  }


	  USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&joystickhid, sizeof( joystickHID )); // send usb report with buttons states
	  HAL_Delay(5);

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

 

 

 

 

 

 

 

And how it looks in windows gaming devices (sorry for non-english screenshot, but I think you'll get the point. Button1 on, Button2 off):

Bez tytułu.png

 

 

Summary: Descriptor - works, device is properly recognized as 2 buttons device

button1 - works as intended

button2 - does not work and this is my problem

 

 

Disclaimer: I will not leave this thread unresolved - I want to share the solution that will be achieved either by you, the best programmers :D, or me (if I find the solution, I will share it here).

1 ACCEPTED SOLUTION

Accepted Solutions

HA!

I think I found the solution!

Proper USBD_HID_SendReport values for my project are:

USBD_HID_SendReport(&hUsbDeviceFS, &joystickhid, 9);

 

First problem was solved thanks to liaifat85. Thank you liaifat85. 

Summary of solution for first problem: You can send button states for multiple buttons using only one variable (applies for up to 8 buttons, haven't tested no solution for more buttons, probably will in the future).

 

Second problem was indeed lying in the USBD_HID_SendReport  LEN parameter. 

As it turns out, you have to specify right LEN parameter for USB HID to accept SendReport (now it is obvious for me, it wasn't few hours ago). In my case i had descriptor with values:

0x75, 0x01,        // Report Size (1 bit) 
0x75, 0x08,        // Report Size (8 bits, for padding)

So you have to add Report Size and Report Size padding. 8+1 = 9

 

Summary of solution for second problem: You have to use proper LEN parameter for USBD_HID_SendReport. When you use Report Size of 1 and Report Size padding of 8, you want to send 8+1, so proper value for USBD_HID_SendReport LEN will be 9.

I hope my pain-filled journey could help any soul in the future. 

Regards.

View solution in original post

4 REPLIES 4
liaifat85
Senior III

 

You should update the joystickHID struct to match the HID descriptor. Change the button1 and button2 variables from uint8_t to uint8_t buttons so it fits in a single byte. The updated struct should look like this:

 

typedef struct {
uint8_t buttons; // 2 bits for buttons, 6 bits for padding
} joystickHID;

Now, you can set the button states using bitwise operations:

 

joystickHID joystickhid = {0};

// Set button1 state
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1)) {
    joystickhid.buttons |= (1 << 0);  // Set bit 0 (button 1)
} else {
    joystickhid.buttons &= ~(1 << 0); // Clear bit 0
}

// Set button2 state
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2)) {
    joystickhid.buttons |= (1 << 1);  // Set bit 1 (button 2)
} else {
    joystickhid.buttons &= ~(1 << 1); // Clear bit 1
}

 

 

I implemented your solution, but now not a single button can be activated. Of course i tested it with my input sensor in GPIOA PIN1 and GPIOA PIN2

Here's my code:

  typedef struct {
	  uint8_t buttons; // 2 bits for buttons, 6 bits for padding
  } joystickHID;


  joystickHID joystickhid = {0};
  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  // Set button1 state
	  if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1)) {
	      joystickhid.buttons |= (1 << 0);  // Set bit 0 (button 1)
	  } else {
	      joystickhid.buttons &= ~(1 << 0); // Clear bit 0
	  }

	  // Set button2 state
	  if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2)) {
	      joystickhid.buttons |= (1 << 1);  // Set bit 1 (button 2)
	  } else {
	      joystickhid.buttons &= ~(1 << 1); // Clear bit 1
	  }

	  USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&joystickhid, sizeof( joystickHID )); // send usb report with buttons states
	  HAL_Delay(5);
}

 

Despite everthing - now I understand i can send buttons state in a single byte, using a bitwise operations. I will dig deeper with this knowledge.

If you have any idea why this solution is not working, I'm still open to suggestions.

Greetings

I managed to fix the problem with bitwise operations used to send data. The problem was in the 

USBD_HID_SendReport(&hUsbDeviceFS, (uint8_t*)&joystickhid, sizeof( joystickHID )); // send usb report with buttons states

I changed the LEN value of  USBD_HID_SendReport from "sizeof( joystickHID )" to "2"

USBD_HID_SendReport(&hUsbDeviceFS, &joystickhid, 2); // send usb report with buttons states

Now it works as intended with two buttons. Descriptor was not changed at all.

 

I thought that when I'll get an idea on how to make 2 buttons work, I will extrapolate this idea to fullfil my project's requirements. In the end I need a total of 8 buttons for my project.



 Now to finish my project, I need to make it work with 8 buttons in total.
I changed my descriptor to:

	// HID Report Descriptor
	0x05, 0x01,        // Usage Page (Generic Desktop)
	0x09, 0x04,        // Usage (Joystick)
	0xA1, 0x01,        // Collection (Application)
	0x15, 0x00,        // Logical Minimum (0)
	0x25, 0x01,        // Logical Maximum (1)
	0x75, 0x01,        // Report Size (1 bit)
	0x95, 0x08,        // Report Count (8 buttons)
	0x05, 0x09,        // Usage Page (Button)
	0x19, 0x01,        // Usage Minimum (Button 1)
	0x29, 0x08,        // Usage Maximum (Button 8)
	0x81, 0x02,        // Input (Data, Var, Abs) - Button states

	// Padding to align to a byte
	0x75, 0x08,        // Report Size (8 bits, for padding)
	0x95, 0x08,        // Report Count (8 bits for padding)
	0x81, 0x03,        // Input (Const, Var, Abs) - Padding byte

	0xC0               // End Collection

 

And left my code as it is (to test if the first two buttons will still work). Aaand this two buttons stopped working.

 

I think the problem still lies somewhere between USBD_HID_SendReport and/or descriptor, so first I tried to play with different USBD_HID_SendReport LEN values:

USBD_HID_SendReport(&hUsbDeviceFS, &joystickhid, 1);
USBD_HID_SendReport(&hUsbDeviceFS, &joystickhid, 2);
USBD_HID_SendReport(&hUsbDeviceFS, &joystickhid, 4);
USBD_HID_SendReport(&hUsbDeviceFS, &joystickhid, 8);
USBD_HID_SendReport(&hUsbDeviceFS, &joystickhid, 16);

 

Success was not achieved yet.

What is wrong with my code?

HA!

I think I found the solution!

Proper USBD_HID_SendReport values for my project are:

USBD_HID_SendReport(&hUsbDeviceFS, &joystickhid, 9);

 

First problem was solved thanks to liaifat85. Thank you liaifat85. 

Summary of solution for first problem: You can send button states for multiple buttons using only one variable (applies for up to 8 buttons, haven't tested no solution for more buttons, probably will in the future).

 

Second problem was indeed lying in the USBD_HID_SendReport  LEN parameter. 

As it turns out, you have to specify right LEN parameter for USB HID to accept SendReport (now it is obvious for me, it wasn't few hours ago). In my case i had descriptor with values:

0x75, 0x01,        // Report Size (1 bit) 
0x75, 0x08,        // Report Size (8 bits, for padding)

So you have to add Report Size and Report Size padding. 8+1 = 9

 

Summary of solution for second problem: You have to use proper LEN parameter for USBD_HID_SendReport. When you use Report Size of 1 and Report Size padding of 8, you want to send 8+1, so proper value for USBD_HID_SendReport LEN will be 9.

I hope my pain-filled journey could help any soul in the future. 

Regards.