Browse over 10,000 Electronics Projects

USB HID device development on the STM32 F042

USB HID device development on the STM32 F042

Custom HID devices

A custom HID device doesn’t conform to any of the known standard HID device reports. It’s not a mouse and not a keyboard. It’s your own ‘thing’. You get to decide on the content of the data packets that you will exchange with the host. If your data conforms to a standard unit defined in the HID class such as voltage, mass, time and many others then you can have that. Or if it’s just plain bytes then you can have those too. Most importantly you do not have to create a USB device driver on the host but you will have to use low level host APIs to communicate with your device.

Let’s have a look at an example HID descriptor that I could use for a custom device that can send and receive reports to and from the host.

enum {
  IN_REPORT_SIZE = 12,   // 1 byte report id + 11-byte report
  OUT_REPORT_SIZE = 10,  // 1 byte report id + 9-byte report
};

const uint8_t reportDescriptor[32] = {
  0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
  0x09, 0x00,                    // USAGE (Undefined)
  0xa1, 0x01,                    // COLLECTION (Application)

  0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
  0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)

  // IN report

  0x85, 0x01,                    //   REPORT_ID (1)
  0x75, 0x08,                    //   REPORT_SIZE (8)
  0x95, IN_REPORT_SIZE-1,        //   REPORT_COUNT (this is the byte length)
  0x09, 0x00,                    //   USAGE (Undefined)
  0x81, 0x82,                    //   INPUT (Data,Var,Abs,Vol)

  // OUT report

  0x85, 0x02,                    //   REPORT_ID (2)
  0x75, 0x08,                    //   REPORT_SIZE (8)
  0x95, OUT_REPORT_SIZE-1,       //   REPORT_COUNT (this is the byte length)
  0x09, 0x00,                    //   USAGE (Undefined)
  0x91, 0x82,                    //   OUTPUT (Data,Var,Abs,Vol)

  0xc0                           // END_COLLECTION
};

The above example shows how I define the two reports. The LOGICAL_MINIMUM and LOGICAL_MAXIMUM declarations set up the host for receiving full range 8-bit bytes. I then go on to define the IN and OUT reports.

In USB terminology the direction of data transfer is always expressed relative to the host. Therefore IN reports come from the device to the host and OUT reports are sent out from the host to the device.

My first report definition, id #1 will be 12 bytes long. The first byte of the report buffer is always the report id (forgetting this is a common gotcha) and the remaining 11-bytes are whatever I want them to be.

The second report definition, id #2 is for data sent to the host. It’s defined in the same way and it’s a 10 byte report with 9 of those bytes available for user data.



Advertisement1


HID reports are sent over interrupt endpoints and the maximum payload for an interrupt transfer is 64 bytes so if you have more to send then consider designing your protocol to break up your payload into 63 byte fragments and streaming them through the endpoint as a sequence of reports. If you have a lot more data to send, or you care about the bandwidth available to you, then perhaps your device is better suited to one of the other USB subprotocols because the full speed standard sets a limit of one 64-byte packet to be sent per millisecond.

USB devices on the F042

Now that we know a little bit about HID devices it’s time to look at what ST’s given us to work with on the F042 and there’s good and bad news here.

The USB peripheral supports version 1.1, which is full speed. The USB naming department totally failed and continue to fail to learn from the SCSI naming debacle and so we now have low, full, high and super speeds whatever the heck those subjective terms mean. As if today’s ‘super’ is going to be ‘super’ for evermore. Sigh.

The good news is that the USB peripheral on the F042 is little more than a PHY. Really, the highest level concept that it understands is that of the endpoint. All you really need to do is set up the device registers including those that define the endpoints and then I/O consists of reading from and writing to some dedicated FIFO registers. Interrupts are provided to let you know what’s going on that’s just about it. With no attempt to provide any higher level protocol support there are no constraints on the type of USB device that you could implement.

The not so good news is that ST’s ‘Cube’ library support is not particularly efficient. They’ve tried to be too generic as if they’re writing PC library code and the result is bloated compilation sizes and some truly horrendous ‘C’ macros that deserve an entry into the annual IOCC contest. For example, this innocent looking macro:

PCD_SET_EP_DBUF0_CNT(hpcd->Instance, ep->num, ep->is_in, len)

expands through 7 steps of nesting to all of this:

{ 
    if((ep->is_in) == PCD_EP_DBUF_OUT)
      /* OUT endpoint */ 
    {{
    uint16_t *pdwReg = ((uint16_t *)(((((hpcd->Instance)))->BTABLE+(((ep->num)))*8+2)+  ((uint32_t)(((hpcd->Instance))) + 0x400))); 
    {
    uint16_t wNBlocks;
    if((((len))) > 62){{
    (wNBlocks) = ((((len)))) >> 5;
    if((((((len)))) & 0x1f) == 0)
      (wNBlocks)--;
    *pdwReg = (uint16_t)(((wNBlocks) << 10) | 0x8000);
  };}
    else {{
    (wNBlocks) = ((((len)))) >> 1;
    if((((((len)))) & 0x1) != 0)
      (wNBlocks)++;
    *pdwReg = (uint16_t)((wNBlocks) << 10);
  };}
  };
  };} 
    else if((ep->is_in) == PCD_EP_DBUF_IN)
      /* IN endpoint */ 
      *((uint16_t *)((((hpcd->Instance))->BTABLE+((ep->num))*8+2)+  ((uint32_t)((hpcd->Instance)) + 0x400))) = (uint32_t)(len);  
  }

I abandoned the idea of using the Cube/HAL libraries except for reference and implemented a custom HID device using my stm32plus library and bare register access to the USB peripheral.

Pages: 1 2 3 4 5 6 7 8 9

 


Top