STM32 USB CDC 长数据包接收

3

我需要将数据从计算机发送到我的STM32F3,因此我决定使用内置的uC USB。

但现在我有一个问题 - 我想一次性向stm32发送大量数据 - 我的意思是200-500字节左右。

当我用minicom从PC发送少于64个字符的数据包时 - 一切都很好 - 回调函数CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)会被触发一次 - 它使UsbRxFlag变为可用,只是为了通知正在运行的程序有可用的数据。

static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
  /* USER CODE BEGIN 6 */
  USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
  USBD_CDC_ReceivePacket(&hUsbDeviceFS);
  if( (Buf[0] == 'A') & (Buf[1] == 'T') ){
      GPIOB->BSRR = (uint32_t)RX_Led_Pin;
      UsbRxFlag = 1;
  }

  return (USBD_OK);
  /* USER CODE END 6 */
}

但是当我尝试发送更多的数据(只是来自minicom的长文本)到uC时,出现一些奇怪的情况 - 有时候uC根本不会有反应 - 有时候它不会考虑一些数据。
如何处理通过USB-CDC向STM32F3发送超过64个字节的数据?

2
附注:(Buf[0] == 'A') & (Buf[1] == 'T') 更常见的写法是 (Buf[0] == 'A') && (Buf[1] == 'T'),但在这里没有真正的功能差异。 &&& - chux - Reinstate Monica
2个回答

7
全速USB通信的最大数据包长度为64个字节。因此,数据将分为64字节的块传输,并需要在另一端重新组装。
USB CDC基于大容量传输端点实现数据流(也称为管道),而不是消息流。它基本上是一串字节。因此,如果您发送了200个字节,请不要期望任何关于这200个字节结束位置的指示。这些信息不会被传输。
您的代码看起来有些可疑:
- 如Reinstate Monica所指出的,您可能想使用'&&'而不是'&'。 - 除非您更改缓冲区,否则只需要在初始化时调用一次 'USBD_CDC_SetRxBuffer'。 - 当调用'CDC_Receive_FS'时,已经接收到了数据包。 'Buf'将指向用'USBD_CDC_SetRxBuffer'指定的缓冲区。 'Len'提供数据包的长度。因此,您首先要做的是处理接收到的数据。一旦数据被处理并且缓冲区可以再次重用,您将调用'USBD_CDC_ReceivePacket'来表示您准备好接收下一个数据包。因此,将'USBD_CDC_SetRxBuffer'移动到另一个函数中(除非您想使用多个缓冲区),并将'USBD_CDC_ReceivePacket'移动到'CDC_Receive_FS'的末尾。 - 函数调用的顺序不正确可能导致在处理数据时被覆盖。
但最大的问题可能是您期望整个数据以一个单独的块的形式接收,或者至少包含一条结束该块的指示。事实并非如此。您将不得不自己实现这一点。
如果您正在使用文本协议,可以缓冲所有传入数据,直到检测到换行符。然后,您就知道已经有一个完整的命令并且可以执行它了。

嗨Codo,感谢您的回复。那个带有“USBD_CDC_SetRxBuffer”的代码是由CubeMX生成器创建的。我是否正确理解 - “USBD_CDC_ReceivePacket(&hUsbDeviceFS)”是一个函数,当程序准备好新数据包时应该调用它? 告诉我,如果我可以使用“Len”来显示接收到多少数据,为什么我要使用换行符? - Sink
2
批量端点实现数据流而不是基于消息或数据包的协议。在发送方面,数据包可以按照发送者认为最优的方式进行组合或分割。事实上,Windows、Linux和macOS将组合数据,如果超过64字节,则必须将其拆分。例如,如果您短时间内发送10次10个字节,则第一个10个字节可能会立即发送,而其余的则会组合然后拆分成64字节的块和26字节的另一个块。 - Codo

1

以下是一种通用的实现方法,可用于读取任意数量的字节:https://github.com/philrawlings/bluepill-usb-cdc-test

完整代码太长无法在此处发布,但基本上修改了usb_cdc_if.c以创建循环缓冲区并公开其他函数(CDC_GetRxBufferBytesAvailable_FS()CDC_ReadRxBuffer_FS()CDC_FlushRxBuffer_FS()),可以从main.c中使用。主页上显示的readme.md文本描述了所需的所有代码更改。

正如@Codo所提到的,您需要在源数据中添加终止字符,或者在开始时包含一个“长度”值(本身将是一定数量的字节),然后指示数据有效载荷中有多少字节。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接