硬汉嵌入式论坛

 找回密码
 立即注册
查看: 5350|回复: 15
收起左侧

[USB] [已解决]STM32H7用USB HS做CDC虚拟串口,打开IP DMA后传输卡死

  [复制链接]

2

主题

9

回帖

20

积分

新手上路

积分
20
发表于 2021-12-3 09:48:30 | 显示全部楼层 |阅读模式
本帖最后由 archon 于 2021-12-7 15:19 编辑

各位好,因为需要进行较高速传输,所以我用STM32H7+USB3300做了一个基于USB HS的CDC虚拟串口,用的cubeMX生成的工程,启用了USB HS、外部PHY、CDC中间件,堆和栈空间都改大了。编译是通过的,枚举也正常,打开串口很OK,但问题是如果我在CUBEMX里不启用USB OTG HS外设的IP Internal DMA,那么串口是可以工作的,但启用以后,串口会在发送一定量数据后卡死,CDC_Transmit_HS()函数始终返回USBD_BUSY。这个一定量数据和每次调用发送函数时选择的数据量有关,如果是每次传输1字节,那么347次传输后固定卡死,如果每次传输的是8或32字节则1440字节后卡死,如果传输的是384、512、576字节,则会在第四次发送时卡死。

我试过在枚举完成后在调试状态下手动填充对应的寄存器启动发送,同样也是一样的现象,576字节的发送只能成功三次。单步跟踪发现在HAL库中执行这一句:
USBx_INEP(epnum)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA);
后,EP1会通过DMA自动启动传输过程并在传输是自动更新对应的DMA地址和传输量与包数计数器,传输完成后EP1会自动停止,传输量寄存器自动清零,但第四次调用后,EP1会停留在ENABLE状态,传输量寄存器计数清零但包数不清零,同时USB协议栈也不在调用CDC_DataIn回调。在PC上分析USB端口活动发现出现持续不断的大量的USBD_STATUS_XACT_ERROR。

卡死时没有发现明确的寄存器状态变化,手工停止EP1并重新配置对应寄存器也无法继续发送,但插拔USB重新枚举后可以再次发送3次。

请问有任何人遇到过同样的情况吗?USB HS CDC这个内部DMA到底可以用吗?谢谢!




问题已经找到。STM32CUBEMX生成默认工程时,usbd_conf.c文件中的如下函数:
  1. USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev)
  2. {
  3.   /* Init USB Ip. */
  4.   if (pdev->id == DEVICE_HS) {
  5.   /* Link the driver to the stack. */
  6.   hpcd_USB_OTG_HS.pData = pdev;
  7.   pdev->pData = &hpcd_USB_OTG_HS;

  8.   hpcd_USB_OTG_HS.Instance = USB_OTG_HS;
  9.   hpcd_USB_OTG_HS.Init.dev_endpoints = 9;
  10.   hpcd_USB_OTG_HS.Init.speed = PCD_SPEED_HIGH;
  11.   hpcd_USB_OTG_HS.Init.dma_enable = ENABLE;
  12.   hpcd_USB_OTG_HS.Init.phy_itface = USB_OTG_ULPI_PHY;
  13.   hpcd_USB_OTG_HS.Init.Sof_enable = DISABLE;
  14.   hpcd_USB_OTG_HS.Init.low_power_enable = DISABLE;
  15.   hpcd_USB_OTG_HS.Init.lpm_enable = DISABLE;
  16.   hpcd_USB_OTG_HS.Init.vbus_sensing_enable = DISABLE;
  17.   hpcd_USB_OTG_HS.Init.use_dedicated_ep1 = DISABLE;
  18.   hpcd_USB_OTG_HS.Init.use_external_vbus = DISABLE;
  19.   if (HAL_PCD_Init(&hpcd_USB_OTG_HS) != HAL_OK)
  20.   {
  21.     Error_Handler( );
  22.   }

  23. #if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U)
  24.   /* Register USB PCD CallBacks */
  25.   HAL_PCD_RegisterCallback(&hpcd_USB_OTG_HS, HAL_PCD_SOF_CB_ID, PCD_SOFCallback);
  26.   HAL_PCD_RegisterCallback(&hpcd_USB_OTG_HS, HAL_PCD_SETUPSTAGE_CB_ID, PCD_SetupStageCallback);
  27.   HAL_PCD_RegisterCallback(&hpcd_USB_OTG_HS, HAL_PCD_RESET_CB_ID, PCD_ResetCallback);
  28.   HAL_PCD_RegisterCallback(&hpcd_USB_OTG_HS, HAL_PCD_SUSPEND_CB_ID, PCD_SuspendCallback);
  29.   HAL_PCD_RegisterCallback(&hpcd_USB_OTG_HS, HAL_PCD_RESUME_CB_ID, PCD_ResumeCallback);
  30.   HAL_PCD_RegisterCallback(&hpcd_USB_OTG_HS, HAL_PCD_CONNECT_CB_ID, PCD_ConnectCallback);
  31.   HAL_PCD_RegisterCallback(&hpcd_USB_OTG_HS, HAL_PCD_DISCONNECT_CB_ID, PCD_DisconnectCallback);

  32.   HAL_PCD_RegisterDataOutStageCallback(&hpcd_USB_OTG_HS, PCD_DataOutStageCallback);
  33.   HAL_PCD_RegisterDataInStageCallback(&hpcd_USB_OTG_HS, PCD_DataInStageCallback);
  34.   HAL_PCD_RegisterIsoOutIncpltCallback(&hpcd_USB_OTG_HS, PCD_ISOOUTIncompleteCallback);
  35.   HAL_PCD_RegisterIsoInIncpltCallback(&hpcd_USB_OTG_HS, PCD_ISOINIncompleteCallback);
  36. #endif /* USE_HAL_PCD_REGISTER_CALLBACKS */
  37.   /* USER CODE BEGIN TxRx_Configuration */
  38.   HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_HS, 0x200);
  39.   HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 0, 0x80);
  40.   HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 1, 0x174);
  41.   /* USER CODE END TxRx_Configuration */
  42.   }
  43.   return USBD_OK;
  44. }
复制代码
其中:
HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_HS, 0x200);
标红部分参数过大,修改为0x80后DMA传输正常。是否会影响数据接收,因为我的项目不需要接收大量数据所以暂无测试,如有需要可以尝试调整。




感觉这个函数可能问题挺多的,再次更新。USB CDC实际用到了3个EP,但函数中:
hpcd_USB_OTG_HS.Init.dev_endpoints = 9;
初始化为9个EP,实测修改为3个似乎并不影响使用。


此外在stm32h7xx_hal_pcd_ex.c中,关于HAL_PCDEx_SetTxFiFo函数有如下说明:
When DMA is used 3n * FIFO locations should be reserved for internal DMA registers
其中n为FIFO号。其实我不太看得懂这段注释,但感觉启用DMA后FIFO尽量不要设置的太大,尤其内部代码会将传入值翻倍以后写入寄存器。实测RxFIFO设置为0x20时无法工作,0x40时可以正常工作,TxFIFO设置为0x80到0x200均可正常工作。

评分

参与人数 1金币 +100 收起 理由
eric2013 + 100 很给力!

查看全部评分

回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106731
QQ
发表于 2021-12-3 09:51:47 | 显示全部楼层
可以用没问题。

使用USB DMA必须4字节对齐,估计你这部分处理有问题。
回复

使用道具 举报

2

主题

9

回帖

20

积分

新手上路

积分
20
 楼主| 发表于 2021-12-3 10:06:10 | 显示全部楼层
eric2013 发表于 2021-12-3 09:51
可以用没问题。

使用USB DMA必须4字节对齐,估计你这部分处理有问题。

实际上我用来发送的数据在外部SDRAM里,地址是0xC0000000,这应该是4字节对齐的吧?我监控过DMA地址寄存器,我所有测试的发送数据的长度除了1字节以外也都是4字节的倍数,所以地址里也没看到不对齐的情况,请问还有什么地方需要注意对齐问题?
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106731
QQ
发表于 2021-12-3 10:15:29 | 显示全部楼层
archon 发表于 2021-12-3 10:06
实际上我用来发送的数据在外部SDRAM里,地址是0xC0000000,这应该是4字节对齐的吧?我监控过DMA地址寄存 ...

不是长度,是首地址4对齐。

换了,换成内部SRAM,然后首地址是4字节对齐。然后就是分析源码,找到USB实际直接操作的缓冲地址(这个是根节),这个地址必须4字节对齐。

USB这个代码我没有分析过,像SDMMC自带的DMA是直接的操作的hsd->Instance->IDMABASE0 = (uint32_t) pData ;

这个pData必须是首地址4字节对齐。
回复

使用道具 举报

2

主题

9

回帖

20

积分

新手上路

积分
20
 楼主| 发表于 2021-12-3 10:21:36 | 显示全部楼层
eric2013 发表于 2021-12-3 10:15
不是长度,是首地址4对齐。

换了,换成内部SRAM,然后首地址是4字节对齐。然后就是分析源码,找到USB ...

好的谢谢,我试一试,如果还存在问题我会附带一些调试信息截图。
回复

使用道具 举报

2

主题

9

回帖

20

积分

新手上路

积分
20
 楼主| 发表于 2021-12-3 12:54:22 | 显示全部楼层
eric2013 发表于 2021-12-3 10:15
不是长度,是首地址4对齐。

换了,换成内部SRAM,然后首地址是4字节对齐。然后就是分析源码,找到USB ...

不好意思,检查了一下所有USB DMA操作的地址应该都是4字节对齐的,但问题依然。

我直接使用了CUBEMX生成的UserTxBufferHS作为传入的发送缓冲,其地址为0x240005DC,这个地址应该是4字节对齐的。我启动一次32字节的USB CDC传输,HAL库的调用路径是这样的:
CDC_Transmit_HS(UserTxBufferHS, 32)
|
CDC_SetTxBuffer()
|
USBD_CDC_TransmitPacket()
|
USBD_LL_Transmit()
|
HAL_PCD_EP_Transmit()。此函数中设置ep->dma_addr = (uint32_t)pBuf,经确认pBuf传入参数为0x240005DC。
|
USB_EPStartXfer()。CDC使用EP1作为发送端点。此函数中会配置OTG_HS_DIEPTSIZ1,对于发送32字节,XFRSIZ=0x00000020,PKTCNT=0x0001,MCNT=0x00。之后配置DMA地址寄存器OTG_HS_DIEPDMA2。配置完成后,此寄存器的值为正确的0x240005DC。

最终通过如下代码启动自动数据传输:USBx_INEP(epnum)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA);
执行后,OTG_HS_DIEPDMA2值自动变为0x240005FC(+0x20),OTG_HS_DIEPTSIZ1下所有值清零。

当下一次调用CDC_Transmit_HS()后,会重新执行上述配置,将OTG_HS_DIEPDMA2地址重新改写为0x240005DC。因此正常工作时,USB DMA寄存器仅出现2个地址:0x240005DC与0x240005FC,这两个地址均为4字节对齐。

当发送完1440字节,即45次CDC_Transmit_HS()调用时,执行完USBx_INEP(epnum)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA);语句后,OTG_HS_DIEPDMA2变为0x240005FC,但OTG_HS_DIEPTSIZ1寄存器出现XFRSIZ=0x00000000,PKTCNT=0x0001,MCNT=0x00的疑似错误状态,同时OTG_HS_DIEPCTL1寄存器中EPENA位未被自动清空,PC端会检测到大量XACT错误,H750端无法继续发送任何数据。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106731
QQ
发表于 2021-12-3 13:34:29 | 显示全部楼层
archon 发表于 2021-12-3 12:54
不好意思,检查了一下所有USB DMA操作的地址应该都是4字节对齐的,但问题依然。

我直接使用了CUBEMX生 ...

别的问题不清楚了,找ST解决下吧
回复

使用道具 举报

2

主题

9

回帖

20

积分

新手上路

积分
20
 楼主| 发表于 2021-12-7 14:32:23 | 显示全部楼层
找到问题了,解决方式已编辑至主贴。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106731
QQ
发表于 2021-12-7 14:35:35 | 显示全部楼层
archon 发表于 2021-12-7 14:32
找到问题了,解决方式已编辑至主贴。

非常感谢楼主告知最终原因
回复

使用道具 举报

0

主题

4

回帖

4

积分

新手上路

积分
4
发表于 2022-10-9 16:35:47 | 显示全部楼层
大佬,请问这种方案USB传输速度可以达到多少;最近也有这个需求
回复

使用道具 举报

334

主题

2032

回帖

3039

积分

版主

Rank: 7Rank: 7Rank: 7

积分
3039
发表于 2022-10-10 08:03:56 | 显示全部楼层
archon 发表于 2021-12-7 14:32
找到问题了,解决方式已编辑至主贴。

还是匪夷所思。 rx就没有怎么使用,rx的fifo为什么会影响到tx的dma呢?
回复

使用道具 举报

5

主题

61

回帖

76

积分

初级会员

积分
76
发表于 2022-10-10 10:10:11 | 显示全部楼层
硬汉兄,USB CDC 虚拟串口和LPUART  哪个更快,我想用到波特率12Mbps ,LPUART我知道没问题,但是不知道USBCDC 硬件上可不可以 我的定时器是50K,定时器中断一次进行串口数据发送
回复

使用道具 举报

2

主题

9

回帖

20

积分

新手上路

积分
20
 楼主| 发表于 2023-3-29 10:09:38 | 显示全部楼层
ky625 发表于 2022-10-9 16:35
大佬,请问这种方案USB传输速度可以达到多少;最近也有这个需求

实测超过20MB/s,差不多可以跑满常规BULK传输的上限速度,但实际能否达到这个速度取决于上位机,因为如果上位机不能及时把数据读走,下位机会卡在发送函数上一直等,速度是上不去的。目前没有任何串口调试助手可以支持到这个速度,要么严重丢包要么一发就死,要达到这个速度你得自己写程序调串口读取才行。而且我测试下来发现windows默认的USB CDC驱动在这个速度下好像都不是很稳定,如果系统负载比较大,会有明显的丢包现象,这个需要注意一下。
回复

使用道具 举报

2

主题

4

回帖

10

积分

新手上路

积分
10
发表于 2023-3-30 11:04:08 | 显示全部楼层
我试了USB虚拟U盘,HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_HS, 0x200),这里设成0X100-0X180才行
回复

使用道具 举报

40

主题

113

回帖

233

积分

高级会员

积分
233
发表于 2023-8-21 13:59:56 | 显示全部楼层
楼主能分享个USB模拟串口的例子吗?谢谢!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-2 17:39 , Processed in 0.206334 second(s), 26 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表