2016-05-25 02:27 PM
I used stm32cubeMX to generate a CDC project for the stm32f3 discovery board. After much fussing, I have a pretty stable echo going. If I use a program to generate output/input from the host, I'm finding writes of 64 bytes bring the whole thing to a halt. This of course is a magic number which should result in a zero-length packet (ZLP) being sent to complete the transaction.
So here are my questions -- for the CDC class code as generated from cube, 1) how are ZLPs handled on input ? Are they passed toCDC_
Receive
_FS ?
2) How should I generate them on output ? Is this handled automatically in USBD_CDC_TransmitPacket ?Just as a side note I found the generated example app code to be full of issues -- it took a lot longer than it should have to get echo going.Geoffrey2016-05-31 01:24 PM
Just to close this issue. If you transmit a buffer that is a multiple of the packet size (e.g. 64 for FS), you must follow it with another packet (e.g. one of zero length) to complete the transaction. Surprisingly, the generic CDC code generated by stm32cubemx does not do this. It took a while to find this bug -- it might be a feature if ST had documented their library routines sufficiently -- it's a bug since they didn't.
Actually, the absolute worst thing about the cube libraries is their pathetic documentation.Geoffrey2016-06-01 02:53 AM
Hi brown.geoffrey.001,
Thank you for your contribution. To make things more clear, you mean that the generated code does not behave like the code in library package (Hal or Std) ? Can you localize the missing step ( sending zero-length packet) ? which file and which function should exist ? Thanks. -Hannibal-2016-06-01 04:46 PM
There are several places this could be handled. In general USB application not sending a zero length packet until the end of several transmissions might be fine, but in the case of a virtual com port it is probably not the right behavior -- unless clearly documented. Here's what I did (follows). I employed busy waiting in CDC_Transmit_FS to make sure the transmission was completed. Unfortunately, there is no callback for this which makes using buffered output a challenge.
if (CDC_
Transmit
_FS(buf, len) == USBD_OK) {
if (!(len & 63)) // transmitting multiple of packet size
CDC_
Transmit
_FS(buf,0); // send zero length packet
return len;
It'd be fine ifUSBD_CDC_
Transmit
Packet
were properly documented - i.e.1) Completion needs to be checked separately2) In case of a transmission of multiples of 64 bytes (or whatever the maximum packet size is), you must follow this with another transmission -- possibly of zero length.</span>
2019-01-20 03:44 PM
Not sure if this is 100% right as I am only just learning how this works, but if anyone is interested I 'fixed'? this (for STM32H7) in "usb_cdc.c"
static uint8_t USBD_CDC_DataIn (USBD_HandleTypeDef *pdev, uint8_t epnum)
{
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*) pdev->pClassData;
if(pdev->pClassData != NULL)
{
/* My Change --------------------------------------------------------------------------------------*/
/*If it is a multiple of packet size, need to send zero length packet. */
if((hcdc->TxLength) && ((hcdc->TxLength & 63) == 0)){
/*Clear TxLength so it doesn't come back in here. */
hcdc->TxLength = 0;
/*Send zero length packet. */
USBD_LL_Transmit(pdev, CDC_IN_EP, NULL, 0);
/*NOTE: This will trigger new interrupt and next interrupt will clear TxState. */
}
else{
hcdc->TxState = 0;
}
/*--------------------------------------------------------------------------------------------------------*/
//This is the default ST library implementation. (Clear TxState every time.)
//hcdc->TxState = 0;
return USBD_OK;
}
else
{
return USBD_FAIL;
}
}
2019-01-20 05:35 PM
@David
IMHO this fix is not optimal, it will insert a ZLP after any max.size (64 bytes) packet.
Though it may work in many use cases.
Rather, the sender path should append a ZLP explicitly when it wants the receiving side to see the data ASAP.
For example:
USBD_StatusTypeDef CDC_send_alot(const void *buf, uint16_t len)
{
USBD_StatusTypeDef result;
while (1) {
result = CDC_Transmit_FS((void*)buf, len);
if (result != USBD_BUSY)
// this should break if USB reset or disconnect occurred
break;
}
if (result == USBD_OK && (len % 64 == 0) {
// Send ZLP
while(1) {
result = CDC_Transmit_FS("", 0);
if (result != USBD_BUSY)
break;
}
}
return result;
}
Regards,
-- pa