Skip to main content
MRosc.1
Associate II
July 22, 2021
Solved

Custom HID and CUBEIDE with stm32f103/stm32l462 are too slow.

  • July 22, 2021
  • 1 reply
  • 2214 views

I have read online and saw that people can use the USB custom hid class generated by the CubeIde and they get good transfer speeds.

Also in the past I have worked with stm32(F103RE/L462RE) and a older usblib for custom HID and KeilIDE. I used this together with a c# app that read and wrote data to it pretty fast (just a few seconds to transfer some blocks of data of max 3kb).

Now with the same processor and CUBEIDE with the newest usb custom hid library the same transfer takes aver 1 min and 30 sec. 

On the PC side to read data from the stm32 with the old library I used HidD_GetInputReport method from hid.dll. With the newer usb hid library I have to use ReadFile(overlaped) since HidD_GetInputReport is not working.

This is a very significant difference so I think I am not using the new USB CUSTOM HID library properly.

Here is what I did so far:

- The project uses freertos v2.0 some IOs / ADC coverter / USART2 / DMA(usart and adc) and a few timmers.

1. I am using CUBEIDE

2. I set the USB interrupt priority to 0.

3. I set the CUSTOM_HID_FS_BINTERVAL to 1.

4. Set the CUSTOM_HID_OUTREPORT_BUF_SIZE to 2 or 64. (tested with 2 and 64 bytes - for 64 bytes the readfile (stm32->pc) fails more often)

5. Updated the report descriptor and in/out endpoint sizes (for 64 bytes / 2 bytes)

6. The usb transfer works as follows: PC sends a request, the stm32 processes it and creates a reply and the PC must read this reply then send a new request and so on.

Here is the code for read/write on the STM32(c/c++ language)

//====================================================================================
__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
 /* USER CODE BEGIN 0 */
		0x06, 0x00, 0xFF, /*HID_UsagePageVendor( 0x00)*/
		0x09, 0x01, /*HID_Usage ( 0x01)*/
		0xA1, 0x01, /*HID_Collection ( HID_Application)*/
		0x15, 0x00, /*HID_LogicalMin ( 0 ) value range: 0 - 0xFF */
		0x26, 0xFF, 0x00, /*HID_LogicalMaxS ( 0xFF)*/
		0x75, 0x08, /*HID_ReportSize ( 8 ) 8 bits */
		//in
		0x95, 0x40, /*HID_ReportCount ( HID_INPUT_REPORT_BYTES ) HERE I Tested with 64 or 2 bytes */
		0x09, 0x01, /*HID_Usage ( 0x01 )*/
		0x81, 0x02, /*HID_Input ( HID_Data | HID_Variable | HID_Absolute )*/
		//out
		0x95, 0x40, /*HID_ReportCount ( HID_OUTPUT_REPORT_BYTES ) HERE I Tested with 64 or 2 bytes */
		0x09, 0x01, /*HID_Usage ( 0x01 )*/
		0x91, 0x02, /*HID_Output ( HID_Data | HID_Variable | HID_Absolute )*/
		//feature
		0x95, 0x01, /*HID_ReportCount ( HID_FEATURE_REPORT_BYTES )*/
		0x09, 0x01, /*HID_Usage ( 0x01 )*/
		0xB1, 0x02, /*HID_Feature ( HID_Data | HID_Variable | HID_Absolute )*/
 /* USER CODE END 0 */
 0xC0 /* END_COLLECTION	 */
};
//====================================================================================
/**
 * @brief Manage the CUSTOM HID class events
 * @param event_idx: Event index
 * @param state: Event state
 * @retval USBD_OK if all operations are OK else USBD_FAIL
 */
static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)
{
 /* USER CODE BEGIN 6 */
		 UNUSED(event_idx);
		 UNUSED(state);
 
		 /*get the data from the PC*/
		 USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef*)hUsbDeviceFS.pClassData;
		 for(unsigned char i=0;i<USBD_CUSTOMHID_OUTREPORT_BUF_SIZE;i++)
			 USB_RX_Buffer[i] = hhid->Report_buf[i];
 
		 /*update the TX buffer with the corect response*/
		 ProcessMultipleRequest();
 
		 /*send back the answers for the requests*/
		 USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS, USB_TX_Buffer, USBD_CUSTOMHID_OUTREPORT_BUF_SIZE);
 
		 return (USBD_OK);
 /* USER CODE END 6 */
}
//====================================================================================

Here is the code for read/write on the PC

//...
 System.Threading.Thread.CurrentThread.Priority = ThreadPriority.Highest;
//...
 readHandle = FileIO.CreateFile(myDevicePathName, FileIO.GENERIC_READ, FileIO.FILE_SHARE_READ | FileIO.FILE_SHARE_WRITE, IntPtr.Zero, FileIO.OPEN_EXISTING, FileIO.FILE_FLAG_OVERLAPPED, 0);
//...
 EventObject = FileIO.CreateEvent(IntPtr.Zero, false, false, "");
 HidOverlapped.OffsetLow = 0;
 HidOverlapped.OffsetHigh = 0;
 HidOverlapped.EventHandle = eventObject;
 nonManagedBuffer = Marshal.AllocHGlobal(inputReportBuffer.Length);
 nonManagedOverlapped = Marshal.AllocHGlobal(Marshal.SizeOf(HidOverlapped));
 Marshal.StructureToPtr(HidOverlapped, nonManagedOverlapped, false);
 success = FileIO.ReadFile(readHandle, nonManagedBuffer, inputReportBuffer.Length, ref numberOfBytesRead, nonManagedOverlapped);
 if (!success)
 {
 result = FileIO.WaitForSingleObject(eventObject, 3000);
	 switch (result)
	{
		case (System.Int32)FileIO.WAIT_OBJECT_0:
			// ReadFile has completed
			success = true; 
			FileIO.GetOverlappedResult(readHandle, nonManagedOverlapped, ref numberOfBytesRead, false);
 
			break;
 
		case FileIO.WAIT_TIMEOUT:
 
			/ Cancel the operation on other error.
			CancelTransfer(hidHandle, readHandle, writeHandle, eventObject); 
			success = false; 
			break;
		default:
 
			// Cancel the operation on other error.
			CancelTransfer(hidHandle, readHandle, writeHandle, eventObject); 
			success = false; 
			break;
	}
 
 }
 if (success)
 {
	// A report was received.
	// Copy the received data to inputReportBuffer for the application to use.
 
	Marshal.Copy(nonManagedBuffer, inputReportBuffer, 0, numberOfBytesRead);
 }
//.......

I also attempted to make a thread with priority set to highest that continuously reads from the smt32 but this does not work. 

I use read with overlapping but when the read thread starts both write and read operations fail.

Read fails with timeout even though the USBD_CUSTOM_HID_SendReport was called.

Has anyone achieved better transfer times?

Does pc write and read fail often in your experience?

Any idea what I am doing wrong or what I could do to improve this?

This topic has been closed for replies.
Best answer by MRosc.1

The main issue why most of the packets failed was in the interrupt routine for the usb.

After a lot of debugging I finally looked at the irq and saw that after I transmitted any data the IRQ allways excecuted a portion of code from stm32l4xx_hal_pcd.c.

/* Double Buffer Iso/bulk IN (bulk transfer Len > Ep_Mps) */
else {
		(void) HAL_PCD_EP_DB_Transmit(hpcd, ep, wEPVal);

I saw that other encountered this issue and applied a patch to fix it. I tried it and the speed is not a LOT better.

This is because now most of my requests are processed correctly. Before the patch a lot of packets with the direction STM32->PC failed.

Patch:

diff --git a/stm32l4xx_hal_pcd.c b/stm32l4xx_hal_pcd.c
index 3a3d967342b9560ad96b0c19c829dc800c8ee527..68fd199979281a75b840ae095286093895fa01f0 100644
--- a/stm32l4xx_hal_pcd.c
+++ b/stm32l4xx_hal_pcd.c
@@ -2497,6 +2497,29 @@ static HAL_StatusTypeDef PCD_EP_ISR_Handler(PCD_HandleTypeDef *hpcd)
 (void)USB_EPStartXfer(hpcd->Instance, ep);
 }
 }
+ else if((ep->type == EP_TYPE_INTR) && ep->doublebuffer == 0) {
+ /* multi-packet on the NON control IN endpoint */
+ TxByteNbre = (uint16_t)PCD_GET_EP_TX_CNT(hpcd->Instance, ep->num);
+ if (ep->xfer_len > TxByteNbre)
+ {
+ ep->xfer_len -= TxByteNbre;
+ }
+ else
+ {
+ ep->xfer_len = 0U;
+ }
+
+ /* Zero Length Packet? */
+ if (ep->xfer_len == 0U)
+ {
+ /* TX COMPLETE */
+ #if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U)
+ hpcd->DataInStageCallback(hpcd, ep->num);
+ #else
+ HAL_PCD_DataInStageCallback(hpcd, ep->num);
+ #endif /* USE_HAL_PCD_REGISTER_CALLBACKS */
+ }
+ }
 /* Double Buffer Iso/bulk IN (bulk transfer Len > Ep_Mps) */
 else
 {

1 reply

MRosc.1
MRosc.1AuthorBest answer
Associate II
July 27, 2021

The main issue why most of the packets failed was in the interrupt routine for the usb.

After a lot of debugging I finally looked at the irq and saw that after I transmitted any data the IRQ allways excecuted a portion of code from stm32l4xx_hal_pcd.c.

/* Double Buffer Iso/bulk IN (bulk transfer Len > Ep_Mps) */
else {
		(void) HAL_PCD_EP_DB_Transmit(hpcd, ep, wEPVal);

I saw that other encountered this issue and applied a patch to fix it. I tried it and the speed is not a LOT better.

This is because now most of my requests are processed correctly. Before the patch a lot of packets with the direction STM32->PC failed.

Patch:

diff --git a/stm32l4xx_hal_pcd.c b/stm32l4xx_hal_pcd.c
index 3a3d967342b9560ad96b0c19c829dc800c8ee527..68fd199979281a75b840ae095286093895fa01f0 100644
--- a/stm32l4xx_hal_pcd.c
+++ b/stm32l4xx_hal_pcd.c
@@ -2497,6 +2497,29 @@ static HAL_StatusTypeDef PCD_EP_ISR_Handler(PCD_HandleTypeDef *hpcd)
 (void)USB_EPStartXfer(hpcd->Instance, ep);
 }
 }
+ else if((ep->type == EP_TYPE_INTR) && ep->doublebuffer == 0) {
+ /* multi-packet on the NON control IN endpoint */
+ TxByteNbre = (uint16_t)PCD_GET_EP_TX_CNT(hpcd->Instance, ep->num);
+ if (ep->xfer_len > TxByteNbre)
+ {
+ ep->xfer_len -= TxByteNbre;
+ }
+ else
+ {
+ ep->xfer_len = 0U;
+ }
+
+ /* Zero Length Packet? */
+ if (ep->xfer_len == 0U)
+ {
+ /* TX COMPLETE */
+ #if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U)
+ hpcd->DataInStageCallback(hpcd, ep->num);
+ #else
+ HAL_PCD_DataInStageCallback(hpcd, ep->num);
+ #endif /* USE_HAL_PCD_REGISTER_CALLBACKS */
+ }
+ }
 /* Double Buffer Iso/bulk IN (bulk transfer Len > Ep_Mps) */
 else
 {