Cannot figure out how to send data to hat switch from stm32f103c8 to PC via USB

526 views Asked by At

I have recently restarted to play around with micro controllers and finally got kinda stuck. So what I am building is a custom game pad. I can simulate data correctly for buttons but nothing works when I bring in the hat switch. I assume I am sending wrong data packet but cannot figure out the correct structure. In the test code I am just trying to send some "button press" and also trying to press down a key from hat switch, but it looks like that pc cannot recognise my data packet. I did go through the hid documentation (especially page 64, 65) and still have no idea why this is not working.

My HID descriptor:

0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
0x09, 0x05,                    // USAGE (Game Pad)
0xa1, 0x01,                    // COLLECTION (Application)
0xa1, 0x00,                    //   COLLECTION (Physical)
0x05, 0x09,                    //     USAGE_PAGE (Button)
0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
0x29, 0x0e,                    //     USAGE_MAXIMUM (Button 14)
0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
0x95, 0x0e,                    //     REPORT_COUNT (14)
0x75, 0x01,                    //     REPORT_SIZE (1)
0x81, 0x02,                    //   INPUT (Data,Var,Abs)
0x95, 0x01,                    //   REPORT_COUNT (1)
0x75, 0x02,                    //   REPORT_SIZE (2)
0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
0x05, 0x01,                    //   USAGE_PAGE (Generic Desktop)
0x09, 0x39,                    //   USAGE (Hat switch)
0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
0x25, 0x03,                    //   LOGICAL_MAXIMUM (3)
0x35, 0x00,                    //   PHYSICAL_MINIMUM (0)
0x46, 0x0e, 0x01,              //   PHYSICAL_MAXIMUM (270)
0x95, 0x01,                    //   REPORT_COUNT (1)
0x75, 0x04,                    //   REPORT_SIZE (4)
0x81, 0x42,                    //   INPUT (Data,Var,Abs,Null)
0x95, 0x01,                    //   REPORT_COUNT (1)
0x75, 0x04,                    //   REPORT_SIZE (4)
0x81, 0x03,                    //   INPUT (Cnst,Var,Abs)
0xc0,                          //   END_COLLECTION
0xc0                           // END_COLLECTION

Basic test code:

struct gamepad_report_t
{
    uint16_t buttons;
    uint8_t hs0 : 1;
    uint8_t hs1 : 1;
    uint8_t hs2 : 1;
    uint8_t hs3 : 1;
};

struct gamepad_report_t gamepad_report;

gamepad_report.buttons = 0x0001;
gamepad_report.hs0 = 1;
gamepad_report.hs1 = 0;
gamepad_report.hs2 = 0;
gamepad_report.hs3 = 0;

int main()
{
    while (1)
    {
      gamepad_report.buttons = 0x0010;
      HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_4);
      USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, &gamepad_report, sizeof(struct gamepad_report_t));
      HAL_Delay(500);
    }
}

Data packet structure I have imaganed

What I see in the device properties when the uC is connected

2

There are 2 answers

0
rololo On

The problem I struggled here was the struct variable I wanted to send to the PC. If I am sending an array, everything works as expected... so the conclusion is that sizeof(struct) is not returning correct value, I should read through data structure alignment ... typically simple issue what can drive people crazy.

0
Addio On

First thing I see wrong is that you are trying to use the hat switch like buttons. The hat switch has a value for each direction. For example Up-Left has its own value, it is not a combination of Up and Left being pressed. You will have to read your buttons, and then encode them as a value. It can be a little finicky to set up for the first time.

Here are the values for each button, which should clear up any confusion. (Won't work with every report descriptor!)

#define HATSWITCH_UP            0x00
#define HATSWITCH_UPRIGHT       0x01
#define HATSWITCH_RIGHT         0x02
#define HATSWITCH_DOWNRIGHT     0x03
#define HATSWITCH_DOWN          0x04
#define HATSWITCH_DOWNLEFT      0x05
#define HATSWITCH_LEFT          0x06
#define HATSWITCH_UPLEFT        0x07
#define HATSWITCH_NONE          0x0F

And here is part of a descriptor that will work with those values. Just define a uint8_t for the hat switch in the report descriptor and you should be good to go.

    0x05, 0x01,         //Usage Page        : Generic Desktop
    0x09, 0x39,         //Usage             : Hat Switch,
    0x15, 0x00,         //Logical Min       : 0
    0x25, 0x07,         //Logical Max       : 7
    0x46, 0x3B, 0x01,   //Physical Maximum  : 315 degrees (Optional)
    0x75, 0x08,         //Report Size       : 8
    0x95, 0x01,         //Report Count      : 1
    0x65, 0x14,         //Unit              : English Rotation/Angular Position 1 degree (Optional)
    0x81, 0x42,         //Input             : Data, Var, Abs, Null State

I know this is an old topic, and you most likely figured it out, but it will help people in the future.