cancel
Showing results for 
Search instead for 
Did you mean: 

Modify the USB Custom HID Program?

muralij21
Associate II
Posted on November 10, 2010 at 12:57

Modify the USB Custom HID Program?

9 REPLIES 9
domen23
Associate II
Posted on May 17, 2011 at 14:14

> I have programmed the custom hid program and is working fine. I tried to debug the program, but couldnt understand properly.

Wait. What?!

Anyways, there are no bulk transfers with HID. Maximum rate you can achieve with HID is 64B*1000Hz, for anything more, look into CDC.

And maybe first read beyondlogic.org article on USB to get a bit of idea what you're doing.
tsuneo
Senior
Posted on May 17, 2011 at 14:14

I'll tell you about the modification in a story line, so that you understand it better.

1) Descriptors

For HID device, a report descriptor determines the size and format of reports exchanged between the device and host. Host reads out the report descriptor, to know about the format of the reports.

The custom HID example has a lengthy report descriptor to describe fields on the reports. We simplify it. Here is the result.

usb_desc.h

#define CUSTOMHID_SIZ_REPORT_DESC       27

#define CUSTOMHID_SIZE_INPUT_REPORT     128

#define CUSTOMHID_SIZE_OUTPUT_REPORT    8

usb_desc.c

const uint8_t CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =

  {                    

    0x06, 0x00, 0xff,                   // USAGE_PAGE (Vendor Defined Page 1)

    0x09, 0x01,                         // USAGE (Vendor Usage 1)

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

                                        // ------ common globals ------

    0x15, 0x00,                         //   LOGICAL_MINIMUM (0)

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

    0x75, 0x08,                         //   REPORT_SIZE (8)   - bits

                                        // ------ input report ------

    0x95, CUSTOMHID_SIZE_INPUT_REPORT,  //   REPORT_COUNT      - bytes

    0x09, 0x01,                         //   USAGE (Vendor Usage 1)

    0x81, 0x02,                         //   INPUT (Data,Var,Abs)

                                        // ------ output report ------

    0x95, CUSTOMHID_SIZE_OUTPUT_REPORT, //   REPORT_COUNT      - bytes

    0x09, 0x01,                         //   USAGE (Vendor Usage 1)

    0x91, 0x02,                         //   OUTPUT (Data,Var,Abs)

    0xc0                                // END_COLLECTION

  }; /* CustomHID_ReportDescriptor */

This report descriptor is so simple without detailed field descriptions. HID report descriptor is a means for HID devices to share the report formats with host. But for custom (vendor-specific) HID, we design both sides, device and host, in most cases. We have many other ways to share the format, like a common header, documents, or even our memory 🙂 In this reason, the report descriptor is simplified just to define report size.

Endpoint descriptors are tuned in wMaxPacketSize and bInterval, as follows.

wMaxPacketSize determines the division unit, when a transfer is split into transactions (packets). For full-speed bus, 64 bytes is the maximum.

bInterval gives the polling interval for the endpoint.

usb_desc.c

const uint8_t CustomHID_ConfigDescriptor[CUSTOMHID_SIZ_CONFIG_DESC] =

  {

    ...

    ...

    /******************** Descriptor of Custom HID endpoints ******************/

    /* 27 */

    0x07,          /* bLength: Endpoint Descriptor size */

    USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */

    0x81,          /* bEndpointAddress: Endpoint Address (IN) */

    0x03,          /* bmAttributes: Interrupt endpoint */

    0x40,          /* wMaxPacketSize: 64 Bytes max */        // <------------

    0x00,

    0x01,          /* bInterval: Polling Interval (1 ms) */  // <------------

    /* 34 */

    

    0x07,          /* bLength: Endpoint Descriptor size */

    USB_ENDPOINT_DESCRIPTOR_TYPE, /* bDescriptorType: */

    0x01,          /* bEndpointAddress: Endpoint Address (OUT) */

    0x03,          /* bmAttributes: Interrupt endpoint */

    0x40,          /* wMaxPacketSize: 64 Bytes max  */       // <------------

    0x00,

    0x01,          /* bInterval: Polling Interval (1 ms) */  // <------------

    /* 41 */

  }; /* CustomHID_ConfigDescriptor */

For increased wMaxPacketSize, the size of the packet buffers for the endpoints are also increased.

usb_prop.c

void CustomHID_Reset(void)

{

  ...

  ...

  /* Initialize Endpoint 1 */

  SetEPType(ENDP1, EP_INTERRUPT);

  SetEPTxAddr(ENDP1, ENDP1_TXADDR);

  SetEPRxAddr(ENDP1, ENDP1_RXADDR);

  SetEPTxCount(ENDP1, 64);                      // <------------

  SetEPRxCount(ENDP1, 64);                      // <------------

  SetEPRxStatus(ENDP1, EP_RX_VALID);

  SetEPTxStatus(ENDP1, EP_TX_NAK);

  ...

to be continued..

Tsuneo

muralij21
Associate II
Posted on May 17, 2011 at 14:14

Thanks for the reply,

Yes, it ll help me a lot. I just need to send the 128 bytes of data for every one second. Could you please tell me what modifications i should do?

tsuneo
Senior
Posted on May 17, 2011 at 14:14

When properly modified, the custom HID example can carry 128 bytes input report over the HID interrupt IN endpoint. But such a long report is split into two transactions (packets) of 64 bytes each. Threfore, it takes at least 2ms to send both transactions over.

Does it fit to your requirement?

If so, I'll show you the details of this modification.

Tsuneo

muralij21
Associate II
Posted on May 17, 2011 at 14:14

Thanks Tsuneo.

I am confused about CustomHID_ReportDescriptor format. Is this function really necessary...?Is there any links available that can help me...?

Andrew Neil
Evangelist
Posted on May 17, 2011 at 14:14

''Is there any links available that can help me...?''

beyondlogic.org has already been mentioned - did you look there?

http://www.lvr.com/

 is also frequently recommended.

Also some links to books & other sites here: 

http://www.keil.com/usb/

 (not specific to Keil)

muralij21
Associate II
Posted on May 17, 2011 at 14:14

 

Hi Tsuneo

memcpy( Send_Buffer, buf, CUSTOMHID_SIZE_INPUT_REPORT ); function is not defined in the HID.It gives a warning msg as defined implicitly. I corrected it and the program is working fine. Thanks a lot.

And I need one more help. How to calculate the delay for timers..??

tsuneo
Senior
Posted on May 17, 2011 at 14:14

> I am confused about CustomHID_ReportDescriptor format. Is this function really necessary?

It's absolutely necessary. Also, this is the simplest one.

Here is a brief explanation of our report descriptor.

const uint8_t CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =

  {                    

    0x06, 0x00, 0xff,                   // USAGE_PAGE (Vendor Defined Page 1)  <---- (1)

    0x09, 0x01,                         // USAGE (Vendor Usage 1)              <---- (1)

    0xa1, 0x01,                         // COLLECTION (Application)            <---- (2)

                                        // ------ common globals ------

    0x15, 0x00,                         //   LOGICAL_MINIMUM (0)               <---- (3)

    0x26, 0xff, 0x00,                   //   LOGICAL_MAXIMUM (255)             <---- (3)

    0x75, 0x08,                         //   REPORT_SIZE (8)   - bits          <---- (4)

                                        // ------ input report ------

    0x95, CUSTOMHID_SIZE_INPUT_REPORT,  //   REPORT_COUNT      - bytes         <---- (5)

    0x09, 0x01,                         //   USAGE (Vendor Usage 1)            <---- (6)

    0x81, 0x02,                         //   INPUT (Data,Var,Abs)              <---- (7)

                                        // ------ output report ------

    0x95, CUSTOMHID_SIZE_OUTPUT_REPORT, //   REPORT_COUNT      - bytes         <---- (5)

    0x09, 0x01,                         //   USAGE (Vendor Usage 1)            <---- (6)

    0x91, 0x02,                         //   OUTPUT (Data,Var,Abs)             <---- (7)

    0xc0                                // END_COLLECTION                      <---- (2)

  }; /* CustomHID_ReportDescriptor */

(1) USAGE_PAGE / USAGE pair

This pair represents a tag which assigns mean to following line.

When this pair is placed before the Top-Level Collection (see next), it shows HID device type.

For example, this USAGE_PAGE/USAGE pair assigns the device as a mouse

const uint8_t CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =

  {                    

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

    0x09, 0x02,  // USAGE (Mouse)

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

    ...

    ...

    0xc0         // END_COLLECTION

  };

For our case, the pair specifies a vendor-specific HID device.

Pre-defined USAGE_PAGE/USAGE are provided by this document on USB.org

HID Usage Tables 1.12

http://www.usb.org/developers/devclass_docs/Hut1_12.pdf

(2) COLLECTION / END_COLLECTION

Collection groups data fields. Every report descriptor should have one Collection - End_Collection pair which encloses items. This pair can be nested, to represents sub-group(s). The outer most Collection is called Top-Level Collection. The property of the Top-Level Collection must be Application.

(3) LOGICAL_MINIMUM / LOGICAL_MAXIMUM

Possible value range of the data field

(4) REPORT_SIZE

Number of bits of the data field(s)

REPORT_SIZE (8) means 8 bits

(5) REPORT_COUNT

Number of data field

REPORT_COUNT (128) means 128 bytes, as we already define each data field as 8bits.

(6) USAGE

Tag for the data field. 

The ST example applies detailed tag, like USAGE (LED 1)

But we don't touch to the details on the report descriptor. We assign the same tag to all fields for simplicity.

(7) INPUT / OUTPUT

Define Input / Output report.

The direction of the transfer is,

Input report: Device --> Host

Output report: Host --> Device

You'll find more detailed explanations on HID report descriptor in Jan Axelson's book,

USB Complete, forth edition

Chapter 12. Human Interface Devices: Reports

http://www.lvr.com/usbc.htm

Of course, the last resort

HID spec on USB.org

http://www.usb.org/developers/devclass_docs/HID1_11.pdf

Tsuneo

tsuneo
Senior
Posted on May 17, 2011 at 14:14

OK, continue on the mods.

As we assign greater wMaxPacketSize on the endpoint descriptor, we have to touch to the buffer address on the USB RAM, so that the TX buffer doesn't overlap to the RX buffer.

usb_conf.h

/* EP1  */

/* tx buffer base address */

#define ENDP1_TXADDR        (0x100)

#define ENDP1_RXADDR        (0x140)    // <-------------

We finish the preparation for the descriptors and endpoint buffers.

Now, we run the endpoint (EP).

2) Interrupt IN EP

A 128 bytes input report is split into two packets, 64 bytes each. This number (64 bytes) derives from wMaxPacketSize of the endpoint descriptor - division unit. 

This routine sends the first 64 bytes. You may call this routine from everywhere as you like. Usually, just after the data for the input report is generated on a buffer.

This routine copies the report into Send_Buffer once, to ensure the buffer contents is not touched until the transactions for both packets complete.

uint8_t  Send_Buffer[CUSTOMHID_SIZE_INPUT_REPORT];

uint32_t send_count = 0;

void Send_Input_Report( uint8_t * buf )

{

  memcpy( Send_Buffer, buf, CUSTOMHID_SIZE_INPUT_REPORT );

  send_count = 1;

  USB_SIL_Write(EP1_IN, (uint8_t*) Send_Buffer, 64);  // pass the first half to the endpoint buffer

  SetEPTxValid(ENDP1);

}

The later half is sent in EP1 IN callback. EP1 IN callback is called when the transaction of the first 64 bytes completes successfully.

To enable the callback, comment this line on usb_conf.h

usb_conf.h

//#define  EP1_IN_Callback   NOP_Process       // <------------ comment this line

Add this EP1 IN callback to usb_endp.c

EP1 IN callback is also called, when the transaction of the second 64 bytes finishes. send_count distinguishes the first entry to this callback from the second one.

usb_endp.c

void EP1_IN_Callback(void)

{

  if ( send_count ) {     // we have to send the latter half?

    --send_count;

    USB_SIL_Write(EP1_IN, ((uint8_t*) Send_Buffer) + 64, 64);  // pass the latter half to the endpoint buffer

    SetEPTxValid(ENDP1);

  }

}

lastly, variables and flags defined on our side for USB communication, like send_count,  are initialize in SetConfiguration handler.

usb_prop.c

void CustomHID_SetConfiguration(void)

{

  if (pInformation->Current_Configuration != 0)

  {

    /* Device configured */

    bDeviceState = CONFIGURED;

    

    /* Initialize variables defined on our side for USB communication */ 

    send_count = 0;                            // <------

    /* Start ADC1 Software Conversion */ 

//    ADC_SoftwareStartConvCmd(ADC1, ENABLE);  // <------ drop this line

  }

}

3) For testing

Until you finish your PC application, SimpleHIDWrite utility is a good test bench for the HID device. Using this utility, you'll confirm the input report value sent from the device.

SimpleHIDWrite

http://www.lvr.com/files/SimpleHIDWrite3.zip

Tsuneo

[Revised]

Nov 17, 2010 SetEPTxValid(ENDP1); was dropped after USB_SIL_Write(), fixed it.