cancel
Showing results for 
Search instead for 
Did you mean: 

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

MRosc.1
Associate II

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?

1 ACCEPTED SOLUTION

Accepted Solutions
MRosc.1
Associate II

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
         {

View solution in original post

1 REPLY 1
MRosc.1
Associate II

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
         {