When you add the USB CDC serial to your project, you don't get much help about what to do next. The CDC_Transmit_FS call is pretty obvious. Give it a message and a length and it sends it. Fair enough.
Receive, though is via a callback named CDC_Receive_FS. You don't call this. The framework calls it for you.
Here's my version:
static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
uint32_t len=*Len;
if (hUsbDeviceFS.dev_state != USBD_STATE_CONFIGURED)
{
return USBD_FAIL;
}
if (((Buf == NULL) || (Len == NULL)) || (*Len <= 0))
{
return USBD_FAIL;
}
/* Get data */
uint8_t result = USBD_OK;
do
{
result = USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
}
while(result != USBD_OK);
do
{
result = USBD_CDC_ReceivePacket(&hUsbDeviceFS);
}
while(result != USBD_OK);
// add data to FIFO
while (len--)
if (FIFO_INCR(RX_FIFO.head)==RX_FIFO.tail)
return USBD_FAIL; // overrun
else
{
RX_FIFO.data[RX_FIFO.head]=*Buf++;
RX_FIFO.head=FIFO_INCR(RX_FIFO.head);
}
return (USBD_OK);
/* USER CODE END 6 */
}
This in in one of the headers:
#define FIFO_SIZE 32 // must be 2^N
#define FIFO_INCR(x) (((x)+1)&((FIFO_SIZE)-1))
/* Structure of FIFO*/
typedef struct FIFO
{
uint32_t head;
uint32_t tail;
uint8_t data[FIFO_SIZE];
} FIFO;
extern FIFO RX_FIFO;
Then you need a way to read it out, too
/* Create FIFO*/
FIFO RX_FIFO = {.head=0, .tail=0};
// returns bytes read (could be zero)
// would be easy to make it end early on a stop char (e.g., \r or \n)
uint8_t VCP_read(uint8_t* Buf, uint32_t Len)
{
uint32_t count=0;
/* Check inputs */
if ((Buf == NULL) || (Len == 0))
{
return 0;
}
while (Len--)
{
if (RX_FIFO.head==RX_FIFO.tail) return count;
count++;
*Buf++=RX_FIFO.data[RX_FIFO.tail];
RX_FIFO.tail=FIFO_INCR(RX_FIFO.tail);
}
return count;
}
Discussions
Become a Hackaday.io Member
Create an account to leave a comment. Already have an account? Log In.
@Al Williams, I stumbled upon your code while trying to get CDC working in my own project.
I compared it to this "similar" implementation (https://github.com/philrawlings/bluepill-usb-cdc-test); and while I do like that you added extra checks in the code, after trying both I realized yours will eventually cause a HardFault.
The issue has to do with this line in "CDC_Receive_FS":
RX_FIFO.data[RX_FIFO.head]=*Buf++;
Basically, you are incrementing the incoming "Buf" pointer, and the caller function is unaware, so every time a byte is processed, the base address of the buffer gets pushed up by 1. Depending on the size of the buffers used when calling "USBD_CDC_SetRxBuffer" in "CDC_Init_FS", the HardFault will happen sooner or later, since random RAM will start getting accessed and operated on by the the rest of the code.
The fix is to either use an auxiliary pointer to increment and dereference when getting the actual data, or to use index notation on the "Buf" pointer itself.
Another issue I found during testing is that "CDC_Receive_FS" may actually get called with *Len == 0, which your code treats as an error:
"if (((Buf == NULL) || (Len == NULL)) || (*Len <= 0))"
Removing the check for *Len<= 0 fixes that problem.
Overall, thanks for the post, it was super useful!
Are you sure? yes | no
Yeah that buf should be indexed. As for the Len of zero problem I haven't noticed that but could be. Thanks!
Are you sure? yes | no
!دمت گرم
Are you sure? yes | no