2012-05-26 03:32 PM
Hello,
I'm working with STM32 since some weeks now and I'd like to use the Custom-Hid code from the STM32_USB-FS-Device_Lib_V3.3.0 for one of my projects based on STM32L151. In contrary to the Custom-Hid example I'd like to use feature reports in addition to the input and output reports. In the example code feature reports are also defined, but I was not able to work with them. I don't understand if, where and how they are processed with the given code. The host will work with HidD_GetFeature/HidD_SetFeature, but what about the device? It would be great if someone had an idea that could help me. Best regards and thanks in advance, Michael #custom-usb-hid-device2012-05-28 04:42 AM
Unfortunately, the example doesn't implement Feature request handling.
Here is a snippet for Get_/Set_Report( Feature ) request.First, CustomHID_Data_Setup() is called by the stack just after Setup stage.At Data stage, CustomHID_GetReport_Feature() or CustomHID_SetReport_Feature() is called.For Set_Report, CustomHID_Status_In() is called at Status stage.\STM32_USB-FS-Device_Lib_V3.3.0\Project\Custom_HID\src\usb_prop.c/******************************************************************************** Function Name : CustomHID_Data_Setup* Description : Handle the data class specific requests.* Input : Request Nb.* Output : None.* Return : USB_UNSUPPORT or USB_SUCCESS.*******************************************************************************/typedef enum _HID_REPORTS{ HID_INPUT = 1, HID_OUTPUT = 2, HID_FEATURE = 3} HID_REPORTS;#define HID_INTERFACE_NO 0 // interface number // callback function prototypesuint8_t *CustomHID_GetReport_Feature(uint16_t Length);uint8_t *CustomHID_SetReport_Feature(uint16_t Length);uint8_t Request = 0; // completed request at status stageRESULT CustomHID_Data_Setup(uint8_t RequestNo){ uint8_t *(*CopyRoutine)(uint16_t); if (pInformation->USBwIndex != HID_INTERFACE_NO) return USB_UNSUPPORT; // just for this interface CopyRoutine = NULL; if ((RequestNo == GET_DESCRIPTOR) && (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT)) ) { if (pInformation->USBwValue1 == REPORT_DESCRIPTOR) { CopyRoutine = CustomHID_GetReportDescriptor; } else if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE) { CopyRoutine = CustomHID_GetHIDDescriptor; } } /* End of GET_DESCRIPTOR */ /*** GET_PROTOCOL, GET_REPORT, SET_REPORT ***/ else if ( (Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) ) { switch( RequestNo ) { case GET_PROTOCOL: CopyRoutine = CustomHID_GetProtocolValue; break; case GET_REPORT: switch( pInformation->USBwValue1 ) // report type { case HID_FEATURE: CopyRoutine = CustomHID_GetReport_Feature; break; case HID_INPUT: break; } break; case SET_REPORT: switch( pInformation->USBwValue1 ) // report type { case HID_FEATURE: CopyRoutine = CustomHID_SetReport_Feature; Request = SET_REPORT; break; case HID_OUTPUT: break; } break; default: break; } } if (CopyRoutine == NULL) { return USB_UNSUPPORT; } pInformation->Ctrl_Info.CopyData = CopyRoutine; pInformation->Ctrl_Info.Usb_wOffset = 0; (*CopyRoutine)(0); return USB_SUCCESS;}// callbacksuint8_t HID_GetReport_Value[2];uint8_t *CustomHID_GetReport_Feature(uint16_t Length){ switch ( pInformation->USBwValue0 ) // report ID { case 1: case 2: case 3: case 4: case 5: case 6: case 7: if (Length == 0) { // two bytes, report ID and report body pInformation->Ctrl_Info.Usb_wLength = 2; return NULL; } else { // // here, put value to HID_GetReport_Value depending on report ID // HID_GetReport_Value[0] = pInformation->USBwValue0; // report ID HID_GetReport_Value[1] = 0; // body return HID_GetReport_Value; } default: return NULL; }}uint8_t HID_SetReport_Value[2];uint8_t *CustomHID_SetReport_Feature(uint16_t Length){ switch ( pInformation->USBwValue0 ) // report ID { case 1: case 2: case 3: case 4: case 5: case 6: case 7: if (Length == 0) { // two bytes, report ID and report body pInformation->Ctrl_Info.Usb_wLength = 2; return NULL; } else { return HID_SetReport_Value; } default: return NULL; }}void CustomHID_Status_In(void){ if (Request == SET_REPORT) // SET_REPORT completion { Request = 0; // // now, stack has filled HID_SetReport_Value // HID_SetReport_Value[0] holds report ID // HID_SetReport_Value[1], report body // process it // }}2012-05-28 08:44 AM
For testing of Feature report implementation, Robert Marquardt's SimpleHidWrite is a good test bench on the PC side.
SimpleHidWrite (*1) on Jan Axelson's web pagehttp://www.lvr.com/files/SimpleHIDWrite3.zip1) Plug-in your device, and run SimpleHIDWrite.2) Click ''STM32 Custom HID'' on the top list of the window3) Type in- Report ID box - 1 (or 2, 3, ..., 7)- next box - any number4) Click ''Set Feature'' button at the bottomSet_Report( Feature ) request is put to the deviceOR3') Type in- Report ID box - 1 (or 2, 3, ..., 7)- next box - left in blank4') Click ''Get Feature'' button at the bottomGet_Report( Feature ) request is put to the device. Result is shown on the pane.(*1) Source code of SimpleHidWrite is included in HIDKomponente.zip (HIDKomponente\DEMOS\Delphi\ReadWriteDemo\)http://www.soft-gems.net/supplement/download.php?ID=37Tsuneo2012-06-05 12:05 AM
Hello Tsuneo,
thank you very much for the code. I implemented it the way you wrote and it works very well. The adaptation to my needs was no problem. Also thank you for the link to SimpleHidWrite. I knew Jan's site, but didn't notice that very useful tool before. Can you recommend a host library for using with C#? It should support interrupt transfers, getting/setting of feature reports and device notification. Michael2012-06-05 03:35 AM
> Can you recommend a host library for using with C#?
Mike O'brien / HidLibraryhttps://github.com/mikeobrien/HidLibraryThis library doesn't have API for Get_Report( Feature ), unless I miss it.But you may implement it, by referring to the source of WriteFeatureData().Tsuneo2012-06-06 03:19 PM
2012-06-11 02:45 AM
Hi Tsuneo,
I've adapted my report descriptor and reading and writing feature reports works fine so far. But if I write something likePrevXferComplete = 1;
while
(1)
{
if
(PrevXferComplete)
{
Send_Buffer[0] = 0x05;
if
(Send_Buffer[1] == 0x00)
{
Send_Buffer[1] = 0x01;
}
else
{
Send_Buffer[1] = 0x00;
}
USB_SIL_Write(EP1_IN, (uint8_t*) Send_Buffer, 2);
SetEPTxValid(ENDP1);
PrevXferComplete = 0;
}
Delay(500000);
}
(copied from stm32l1xx_it.c from the ST library) inside main, EP1_IN_Callback is never called and therefore PrevXferComplete is not set to 1 again. Also the host seems to not receive any data as I can't see anything happening, neither with SimpleHidWrite nor with Mike O'briens HidLibrary/TestHarness.
Is there anything special to be considered now with the modified CustomHID_Data_Setup?
Michael
2012-06-11 12:44 PM
Hi again,
it seems that the problem is caused by my report descriptor. When I take that one from the CustomHID example and the following code inside mainPrevXferComplete = 1;
while
(1)
{
if
(PrevXferComplete)
{
SendBuffer[0] = 0x05;
SendBuffer[1]++;
USB_SIL_Write(EP1_IN, (uint8_t*) SendBuffer, 2);
SetEPTxValid(ENDP1);
PrevXferComplete = 0;
}
Delay(750000);
}
I can see how the value increases. This works with SimpleHidWrite as well as with Mike O'briens HidLibrary/TestHarness.
But when I change the report descriptor to
#define CUSTOMHID_SIZ_REPORT_DESC 111
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)
0x15, 0x00,
// LOGICAL_MINIMUM (0)
0x26, 0xff, 0x00,
// LOGICAL_MAXIMUM (255)
0x75, 0x08,
// REPORT_SIZE (8)
0x95, 0x01,
// REPORT_COUNT (1)
0x85, 0x02,
// REPORT_ID (2)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0xb1, 0x02,
// FEATURE (Data,Var,Abs)
0x85, 0x04,
// REPORT_ID (4)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0xb1, 0x02,
// FEATURE (Data,Var,Abs)
0x85, 0x08,
// REPORT_ID (8)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0xb1, 0x02,
// FEATURE (Data,Var,Abs)
0x85, 0x53,
// REPORT_ID (83)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0xb1, 0x02,
// FEATURE (Data,Var,Abs)
0x85, 0x54,
// REPORT_ID (84)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0xb1, 0x02,
// FEATURE (Data,Var,Abs)
0x95, 0x04,
// REPORT_COUNT (4)
0x85, 0x51,
// REPORT_ID (81)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0xb1, 0x00,
// FEATURE (Data,Ary,Abs)
0x85, 0x52,
// REPORT_ID (82)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0xb1, 0x00,
// FEATURE (Data,Ary,Abs)
0x95, 0x06,
// REPORT_COUNT (6)
0x85, 0x01,
// REPORT_ID (1)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0x81, 0x00,
// INPUT (Data,Ary,Abs)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0x91, 0x00,
// OUTPUT (Data,Ary,Abs)
0x85, 0x03,
// REPORT_ID (3)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0xb1, 0x00,
// FEATURE (Data,Ary,Abs)
0x85, 0x05,
// REPORT_ID (5)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0xb1, 0x00,
// FEATURE (Data,Ary,Abs)
0x85, 0x50,
// REPORT_ID (80)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0xb1, 0x00,
// FEATURE (Data,Ary,Abs)
0x85, 0x55,
// REPORT_ID (85)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0xb1, 0x00,
// FEATURE (Data,Ary,Abs)
0x85, 0x56,
// REPORT_ID (86)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0xb1, 0x00,
// FEATURE (Data,Ary,Abs)
0x95, 0x0f,
// REPORT_COUNT (15)
0x85, 0x07,
// REPORT_ID (7)
0x09, 0x01,
// USAGE (Vendor Usage 1)
0xb1, 0x00,
// FEATURE (Data,Ary,Abs)
0xc0
// END_COLLECTION
};
/* CustomHID_ReportDescriptor */
and SendBuffer[0] = 0x01 inside main/while(1) it doesn't work any more. So there seems to be an error around ReportID 1, which I'd like to use as interrupt input and output.
Michael
2012-06-12 11:31 AM
I found the problem:
- in usb_prop.c I changed EPTxCount and EPRxCount to 7 (both for Endpoint 1) - in usb_desc.c I changed wMaxPacketSize to 0x07 (was: 0x02) on both appearances for (input and output) Now it works fine.2012-06-18 08:37 PM
Hopefully I am not hijacking this thread but I am trying to do the same thing for the most part.
Tsuneo, you mention the following: First, CustomHID_Data_Setup() is called by the stack just after Setup stage. At Data stage, CustomHID_GetReport_Feature() or CustomHID_SetReport_Feature() is called. For Set_Report, CustomHID_Status_In() is called at Status stage. What is causing the CustomHID_GetReport_Feature() or CustomHID_SetReport_Feature() to be called in the data stage? The reason I ask is I have the following code which is VERY similar. RESULT Joystick_Data_Setup(uint8_t RequestNo) { uint8_t *(*CopyRoutine)(uint16_t); uint16_t Length = 0; CopyRoutine = NULL; if ((RequestNo == GET_DESCRIPTOR) && (Type_Recipient == (STANDARD_REQUEST | INTERFACE_RECIPIENT)) && (pInformation->USBwIndex0 == 0)) { if (pInformation->USBwValue1 == REPORT_DESCRIPTOR) { CopyRoutine = Joystick_GetReportDescriptor; } else if (pInformation->USBwValue1 == HID_DESCRIPTOR_TYPE) { CopyRoutine = Joystick_GetHIDDescriptor; } } /* End of GET_DESCRIPTOR */ /*** GET_PROTOCOL ***/ else if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) && RequestNo == GET_PROTOCOL) { CopyRoutine = Joystick_GetProtocolValue; } /*** GET_REPORT ***/ else if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) && RequestNo == GET_REPORT) { CopyRoutine = Joystick_GetReportValue; } /*** SET_REPORT ***/ else if ((Type_Recipient == (CLASS_REQUEST | INTERFACE_RECIPIENT)) && RequestNo == SET_REPORT) { CopyRoutine = Joystick_SetReportValue; } if (CopyRoutine == NULL) { return USB_UNSUPPORT; } pInformation->Ctrl_Info.CopyData = CopyRoutine; pInformation->Ctrl_Info.Usb_wOffset = 0; (*CopyRoutine)(Length); return USB_SUCCESS; } uint8_t *Joystick_SetReportValue(uint16_t Length) { uint8_t mybuff[64]; uint32_t numread = 0; numread = USB_SIL_Read(EP0_OUT, mybuff); /* if (Length == 0) { pInformation->Ctrl_Info.Usb_wLength = 1; return NULL; } else { return (uint8_t *)(&ProtocolValue); } */ return USB_SUCCESS; When I look at the mybuff in Joystick_SetReportValue, it contains the data from the setup stage. I have a breakpoint at the return in Joystick_SetReportValue. I am sure I am missing it but have been staring at this way too long with no headway. I do have a USB analyzer and see both the setup and data stages. Gary