2010-11-10 03:57 AM
Modify the USB Custom HID Program?
2011-05-17 05:14 AM
> 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.2011-05-17 05:14 AM
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
2011-05-17 05:14 AM
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?
2011-05-17 05:14 AM
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. Tsuneo2011-05-17 05:14 AM
Thanks Tsuneo.
I am confused about CustomHID_ReportDescriptor format. Is this function really necessary...?Is there any links available that can help me...?
2011-05-17 05:14 AM
''Is there any links available that can help me...?''
beyondlogic.org has already been mentioned - did you look there? is also frequently recommended. Also some links to books & other sites here: (not specific to Keil)2011-05-17 05:14 AM
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..??2011-05-17 05:14 AM
> 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
Of course, the last resort
HID spec on USB.org
http://www.usb.org/developers/devclass_docs/HID1_11.pdf
Tsuneo
2011-05-17 05:14 AM
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.