cancel
Showing results for 
Search instead for 
Did you mean: 

receive via USB_CDC a.k.a. virtual COM Port

PRenn.1
Associate II

I'm kind of stuck. I have USB_CDC running, data appears on the Kitty-Terminal in Windows10 via USB, presented as COM6:  serial device in Windows.

Sending data vis USB_CDC  is easy:

 

ret = CDC_Transmit_FS( buffer0, sizeof( buffer0 ) );
if( ret != USBD_OK ) { /* errors during sending ? .....

 

Receiving however doesn't work. That should be interrupt driven, not?
The interface code allocates one buffer.
And AFAIK there is a callback routine
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)

Callback would mean it gets called automatically "by the system" (acutally the ISR) when there is data.
Apparently I've to modify that routine into a non static version (correct?) and shovle away the *Len bytes at Buf
(USB_CDC ring buffer) into my application buffer, waiting for me.
However I think the callback never gets called. I type away at the terminal but nothing happens.

Thus there are some fundamental questions:
- is receiving via USB_CDC done interrupt driven?
- do I have to switch on that mechanism?
- bufferhandling: is CDC_Receive_FS the callback routine?
- when modifying it what about the "static" declaration ?
- what about concurrency? Do I need to prevent the callback from messing up my application buffer?
- how is flow control regulated on an USB_CDC serial connection?

Current non-functioning(?) callback :

 

extern uint8_t *buffer; /* application buffer reference , [0] != '\0' means data received */
int8_t CDC_Receive_FS( uint8_t *rec_buffer, uint32_t *sz )
{
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, rec_buffer);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
memset( buffer, '\0', 100 ); /* ugly, i know */
memcpy( buffer, rec_buffer, (size_t) *sz );
memset( rec_buffer, '\0', (size_t) *sz ) ;
return (USBD_OK);
}

 

Application code:

 

uint8_t *buffer; /* application buffer definition , [0] != '\0' means data received */
.. the following is inside a loop, ~2 seconds per iteration ...
HAL_Delay( 1000 ); /* wait a second */
if( buffer[0] != '\0' ) { /* did input data magically appear ? */
   HD44780_SetCursor(1,1); HD44780_PrintStr( "DATA !" ); /* yes, tell the user about it */
   HD44780_SetCursor(0,3); HD44780_PrintStr( (char *)buffer ); /* print the data */
   buffer[0] = '\0'; /* indicate app buffer has been used/displayed */
} else { /* no data has been received */
   HD44780_SetCursor(1,1); HD44780_PrintStr("-nix- "); /* tell the user the bad news */
}

 

1 ACCEPTED SOLUTION

Accepted Solutions
PRenn.1
Associate II

Status:  I tried to use some exmples on the Net, namely by Khaled Magdy https://deepbluembedded.com/stm32-usb-cdc-virtual-com-port-vcp-examples/ to no avail.  Sending runs fine, receiving does not work. 
Even after I fixed the (perceived) sequence mistake in CDC_Receive_FS  (correct would be : first do something with the buffer data the callback was called and AFTERWARDS do set RX buffer and Receive packet), still no input.

Then I found JRahlf on GitHub where he posted stm32_usb_cdc_improved_if
https://github.com/jrahlf/stm32_usb_cdc_improved_if 

What a great improvement over the stock ST code !
Simply replace usbd_cdc_if.c and usbd_cdc_if.h with his versions and you're done.  
Sending ist done via  CDC_Transmit(Buf, len)  and receiving is done via overloading
uint8_t CDC_DataReceivedHandler(const uint8_t *Buf, uint32_t len)

So to create a mirror-app (sending back all it receives from the PC-Terminal via USB_CDC COMx: Port)
one simply codes in main.c

 

uint8_t CDC_DataReceivedHandler(const uint8_t *Buf, uint32_t len)
{
    CDC_Transmit(Buf, len);
    return USBD_OK;
}

 

That is all!  In the case data arrives you get called with Buf and len and to process it (in my case send it back)
you simply call CDC_Transmit.   I thought that was exactly what I've been try to get working the better half of today, alas w/o success.    Won't get any easier and has a lot of nifty features :

  • Same API as provided as by ST but with additional and improved functionality
  • const correct
  • easy to use with automatic buffer management for sending and receiving data
  • achieves up to 1MB/s (USB FS) when transmitting to Linux host (tested on STM32F4 with fcpu > 100MHz)
  • Added functionality:
  • functions to check if packets were dropped both for transmission and reception
  • CDC_IsComPortOpen() can be used to check if transmitted packets are actually read by computer side
  • Attempts to avoid zero length packets (small latency vs speed tradeoff)
  • Can be configured as reentrant safe via #define


The really cool part ist that you can peek if the automagically maintained send buffer has
enough space for your outgoing message OR you can call CDC_Transmit* and check for error!
Here is the API :

uint8_t CDC_Transmit(const void* Buf, uint32_t Len);
uint8_t CDC_TransmitTimed(const void* Buf, uint32_t Len, uint32_t TimeoutMs);
uint8_t CDC_TransmitString(const char *string);
uint8_t CDC_IsBusy();
uint32_t CDC_RXQueue_Dequeue(void* Dst, uint32_t MaxLen);
uint32_t CDC_TXQueue_GetReadAvailable();
uint32_t CDC_TXQueue_GetWriteAvailable();
uint32_t CDC_RXQueue_GetReadAvailable();
uint32_t CDC_RXQueue_GetWriteAvailable();
uint32_t CDC_GetDroppedTxPackets();
uint32_t CDC_GetDroppedRxPackets();
void CDC_ResetDroppedTxPackets();
void CDC_ResetDroppedRxPackets();
uint8_t CDC_DataReceivedHandler(const uint8_t *Data, uint32_t len);
uint32_t CDC_GetLastTransmitStartTick();
uint32_t CDC_GetLastTransmitCompleteTick();
uint8_t CDC_IsComportOpen();

 

View solution in original post

1 REPLY 1
PRenn.1
Associate II

Status:  I tried to use some exmples on the Net, namely by Khaled Magdy https://deepbluembedded.com/stm32-usb-cdc-virtual-com-port-vcp-examples/ to no avail.  Sending runs fine, receiving does not work. 
Even after I fixed the (perceived) sequence mistake in CDC_Receive_FS  (correct would be : first do something with the buffer data the callback was called and AFTERWARDS do set RX buffer and Receive packet), still no input.

Then I found JRahlf on GitHub where he posted stm32_usb_cdc_improved_if
https://github.com/jrahlf/stm32_usb_cdc_improved_if 

What a great improvement over the stock ST code !
Simply replace usbd_cdc_if.c and usbd_cdc_if.h with his versions and you're done.  
Sending ist done via  CDC_Transmit(Buf, len)  and receiving is done via overloading
uint8_t CDC_DataReceivedHandler(const uint8_t *Buf, uint32_t len)

So to create a mirror-app (sending back all it receives from the PC-Terminal via USB_CDC COMx: Port)
one simply codes in main.c

 

uint8_t CDC_DataReceivedHandler(const uint8_t *Buf, uint32_t len)
{
    CDC_Transmit(Buf, len);
    return USBD_OK;
}

 

That is all!  In the case data arrives you get called with Buf and len and to process it (in my case send it back)
you simply call CDC_Transmit.   I thought that was exactly what I've been try to get working the better half of today, alas w/o success.    Won't get any easier and has a lot of nifty features :

  • Same API as provided as by ST but with additional and improved functionality
  • const correct
  • easy to use with automatic buffer management for sending and receiving data
  • achieves up to 1MB/s (USB FS) when transmitting to Linux host (tested on STM32F4 with fcpu > 100MHz)
  • Added functionality:
  • functions to check if packets were dropped both for transmission and reception
  • CDC_IsComPortOpen() can be used to check if transmitted packets are actually read by computer side
  • Attempts to avoid zero length packets (small latency vs speed tradeoff)
  • Can be configured as reentrant safe via #define


The really cool part ist that you can peek if the automagically maintained send buffer has
enough space for your outgoing message OR you can call CDC_Transmit* and check for error!
Here is the API :

uint8_t CDC_Transmit(const void* Buf, uint32_t Len);
uint8_t CDC_TransmitTimed(const void* Buf, uint32_t Len, uint32_t TimeoutMs);
uint8_t CDC_TransmitString(const char *string);
uint8_t CDC_IsBusy();
uint32_t CDC_RXQueue_Dequeue(void* Dst, uint32_t MaxLen);
uint32_t CDC_TXQueue_GetReadAvailable();
uint32_t CDC_TXQueue_GetWriteAvailable();
uint32_t CDC_RXQueue_GetReadAvailable();
uint32_t CDC_RXQueue_GetWriteAvailable();
uint32_t CDC_GetDroppedTxPackets();
uint32_t CDC_GetDroppedRxPackets();
void CDC_ResetDroppedTxPackets();
void CDC_ResetDroppedRxPackets();
uint8_t CDC_DataReceivedHandler(const uint8_t *Data, uint32_t len);
uint32_t CDC_GetLastTransmitStartTick();
uint32_t CDC_GetLastTransmitCompleteTick();
uint8_t CDC_IsComportOpen();