2024-06-15 04:59 AM - edited 2024-06-15 05:00 AM
Hello ST Community,
I am working on a project using an STM32 microcontroller with USB CDC class for communication. I am using the CDC_Transmit_FS function to send data over USB, but I am looking for best practices to handle the completion of data transmission effectively.
In my current implementation, I am transmitting data using the following code:
char usb_buffer[64];
snprintf((char *)usb_buffer, sizeof(usb_buffer), "Hello, World!\r\n");
while (CDC_Transmit_FS((uint8_t*)usb_buffer, strlen(usb_buffer)) == USBD_BUSY) {
HAL_Delay(100); // Delay to allow USB stack to process
}
While this works, it is not efficient as it relies on a blocking delay which may not be optimal for all applications.
My goal is to improve the efficiency and responsiveness of the USB transmission in my application. I want to ensure that the data is transmitted reliably without unnecessary delays and that the system can perform other tasks while waiting for the transmission to complete.
I would greatly appreciate any guidance, examples, or best practices on how to handle CDC transmission completion effectively.
Thank you for your assistance!
Solved! Go to Solution.
2024-06-20 03:06 AM - edited 2024-06-20 03:51 AM
Yes, the F3 firmware pack uses an old version of the USB device library (2.5.3) from April 2019.
This callback was added in 2.6.0 December 2019.
You can try to simply replace the library (or even just the CDC library files your project uses) with a newer version (from the G4 firmware pack for example) and work through the compiler errors, if any. Maybe you'll get lucky.
The invocation of the callback in the new version of the lib comes from a simple change to
USBD_CDC_DataIn:
static uint8_t USBD_CDC_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum)
{
// ...
else
{
hcdc->TxState = 0U;
// this was added in 2.6.0
if (((USBD_CDC_ItfTypeDef *)pdev->pUserData)->TransmitCplt != NULL)
{
((USBD_CDC_ItfTypeDef *)pdev->pUserData)->TransmitCplt(hcdc->TxBuffer, &hcdc->TxLength, epnum);
}
}
return (uint8_t)USBD_OK;
}
So, in the worst case, you can diff the two versions and pick out only the changes relevant to CDC (added struct members, updated code generation templates, etc), and apply them. It's not fun, but it's doable.
2024-06-15 10:39 AM - edited 2024-06-18 01:20 AM
Throughput with CDC VCP is around 1Mbyte/Sec. USB FS packets are 64 bytes. That's ~16kpackets/s.
Even if you're running with a 64,48 Mhz or lower (IIRC even if it is *much* lower), it's not a challenge for the MCU to send as fast as the link (and host) allows.
FYI, ST is transitioning towards the USBX library to replace the ST Device library. There's a guide here somewhere about running it standalone (without threadx RTOS). You don't have to switch, and in fact there are far more online resources covering the legacy USB stack. But be aware that ST seems to favor USBX for new designs. Personally, I haven't tried it yet.
> Completion Callback: Is there a way to use a completion callback to notify when the transmission is complete?
The USB device library provides the callaback `CDC_TransmitCplt_FS`
> Non-blocking Transmission: What is the best way to implement non-blocking USB transmissions using CDC_Transmit_FS?
> Handling Busy State: How can I handle the USBD_BUSY state more effectively without using blocking delays?
It's beneficial to look at the HAL code you're calling to understand what it's doing. USBD_BUSY says your data has not been accepted for transmission, and you need to retry. But there's little point in immediately calling it again. Instead of waiting in a busy-loop, organize all the work the processor needs to do inside the main while loop.
Nothing inside this main loop should block, and any work you do inside (or in function you call from it) should take a bounded (small) amount of time. If you have lots of processing to do, do a little bit of it at a time in each iteration. That ensures that with each iteration you make incremental progress on everything you need to take care of, and that any I/O that needs to be done isn't delayed by the processor being busy elsewhere. Remember, once you submit the data, the transmission is handled by the hardware, it doesn't consume cycles (except the overhead of breaking up the submitted the buffer into packets). But if the processor is too busy calculating digits of pi to submit the next buffer for transmission, the throughput will plummet. Any callbacks you implement should set a (global variable, or inside a global context struct) flag to let the main loop know something has changed. The main loop checks for this flag at each iteration and responds accordingly.
Keeping everything inside the main-loop gets you surprisingly far if you're disciplined. If/when your application grows complex enough, structuring your application as a single main loop becomes unwieldy. At that point you should consider porting your application to an RTOS (like FreeRTOS or ThreadX).
2024-06-17 09:57 PM - edited 2024-06-17 09:57 PM
Thank you for the detailed insights and guidance on managing USB communication with ST microcontrollers. I appreciate your advice regarding transitioning to USBX and the considerations for handling non-blocking transmissions effectively.
Regarding the completion callback CDC_TransmitCplt_FS, could you please elaborate on how to implement this callback function in practice? Specifically, could you confirm the exact name and details of the callback function used for notifying when a transmission is complete?
2024-06-18 01:27 AM - edited 2024-06-19 02:32 AM
> could you confirm the exact name and details of the callback function used for notifying when a transmission is complete?
'CDC_TransmitCplt_FS()' is generated by the CubeMX code generator embedded in CubeIDE.
It can be found inside the file `<PROJECT_DIR>\USB_Device\App\usbd_cdc_if.c`. What you do inside it is up to you.
If you really had trouble locating this function in the code, you should spend some time learning how to use the IDE's features. Learning to use your tools efficiently is usually a very wise investment of time.
2024-06-18 11:35 PM
But iam not getting the CubeIDE generated function 'CDC_TransmitCplt_FS()',
Is there any ioc setting we have to do?
2024-06-18 11:55 PM - edited 2024-06-18 11:58 PM
Since you're calling CDC_Transmit_FS, it should be there. Did you look inside the file I mentioned?
Which specific STM32 chip are you using?
2024-06-19 12:00 AM
I am using STM32F303CCT6 MCU
2024-06-19 02:32 AM
In that case, I believe everything I said above applies.
2024-06-19 04:07 AM
> 'CDC_TransmitCplt_FS()' is generated by the CubeMX code generator embedded in CubeIDE.
It can be found inside the file `<PROJECT_DIR>\USB_Device\App\usbd_cdc_if.c`. What you do inside it is up to you.
It appears that there is no 'CDC_TransmitCplt_FS()' function generated by Cube IDE. Can you get this 'CDC_TransmitCplt_FS()' function in cubeIDE generated file.
2024-06-19 09:15 AM
Can you share your ioc file?