当前位置:   article > 正文

HAL库 USB_CDC数据流收发函数分析_usbd_cdc_receivepacket

usbd_cdc_receivepacket

在使用ARM的HAL库实现USB接口通信,常常因为有多个库文件和函数的多层调用而觉得使用起来很复杂,而事实上通过合理的配置后,HAL库函数的使用非常简单,本文对利用STM32CubeMX生成的USB_CDC工程的库函数进行分析,从而通过简单的修改实现USB接口的通信。

自顶而下:

-----------------------------------------------------------------------------

设备接收数据流图:

设备发送数据流图:

 

-----------------------------------------------------------------------------

文件usbd_cdc_if.c,函数如下:

函数描述:USB输出端点接收的数据通过此函数发送给CDC接口。

注意事项:此函数将阻止USB端点上的所有OUT数据包接收,直到退出此函数。 如果在CDC接口上的传输完成之前(即使用DMA控制器)退出此函数,将导致接收更多数据,而先前的数据仍未发送。

参数说明:参数Buf: 待接收数据的缓冲区;

参数Len:   接收的数据数(以字节为单位)

返回:   USBD_OK(如果所有操作均正常),否则返回USBD_FAIL

-----------------------------------------------------------------------------

  1. static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len)
  2. USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]);//设置接收buf
  3. USBD_CDC_ReceivePacket(&hUsbDeviceHS);//接收数据包
  4. return (USBD_OK);
  5. }

-----------------------------------------------------------------------------------

文件usbd_cdc.c,函数如下: 

函数描述:准备接收输出端点数据。

参数说明:参数pdev: USB设备结构体句柄;

返回:    USBD_OK(如果所有操作均正常),否则返回USBD_FAIL

-----------------------------------------------------------------------------

  1. uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev)
  2. {
  3. (void)USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer,
  4. CDC_DATA_HS_OUT_PACKET_SIZE); 
  5. return (uint8_t)USBD_OK;
  6. }

------------------------------------------------------------------------------------

文件usbd_conf.c,函数如下:

函数描述:准备一个端点接收数据。

参数说明:参数pdev:    USB设备结构体句柄;

                  参数ep_addr: 端点号;

                  参数pbuf:       指向要接收的数据的指针;

                  参数size:       数据大小;

                  返回:        USBD_OK(如果所有操作均正常),否则返回USBD_FAIL

------------------------------------------------------------------------------------

  1. USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint32_t size)
  2. {
  3. hal_status = HAL_PCD_EP_Receive(pdev->pData, ep_addr, pbuf, size);
  4. return usb_status;
  5. }

------------------------------------------------------------------------------------

文件stm32f4xx_hal_pcd.c,函数如下:

函数描述:接收大量数据。

参数说明:参数pdev:        PCD结构体句柄;

                  参数ep_addr:   端点号;

                 参数pbuf:          指向要接收的数据的指针;

                 参数len:             接收数据长度;

                 返回:              HAL_OK(如果所有操作均正常),否则返回HAL_FAIL     

-----------------------------------------------------------------------------------

  1. HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len)
  2. {
  3. /*设置和启动 Xfer */
  4. ep->xfer_buff = pBuf;
  5. ep->xfer_len = len;
  6. ep->xfer_count = 0U;
  7. ep->is_in = 0U;
  8. ep->num = ep_addr & EP_ADDR_MSK;
  9. if (hpcd->Init.dma_enable == 1U)//当DMA使能时,给DMA接收BUF的首地址。
  10. {
  11. ep->dma_addr = (uint32_t)pBuf;
  12. }
  13. if ((ep_addr & EP_ADDR_MSK) == 0U)//端点是EP0时,用USB_EP0StartXfer传输数据
  14. {
  15. (void)USB_EP0StartXfer(hpcd->Instance, ep, (uint8_t)hpcd->Init.dma_enable);
  16. }
  17. Else //端点不是EP0时,用USB_EPStartXfer传输数据
  18. {
  19. (void)USB_EPStartXfer(hpcd->Instance, ep, (uint8_t)hpcd->Init.dma_enable);
  20. }
  21. return HAL_OK;
  22. }

------------------------------------------------------------------------------------

文件stm32f4xx_ll_usb.c(底层函数,重点分析)

函数描述:建立和启动一次EP的数据传输,该函数是USB收发中最底层的寄存器操作函数,数据的组包和解包均在此函数中进行。

  参数说明:  参数USBx: USB_OTG核心寄存器定义结构体句柄;

  参数ep:      USB_OTG端点寄存器定义结构体句柄;

  参数dma:  DMA使能,1有效,0关闭;

  返回:    HAL_OK(如果所有操作均正常),否则返回HAL_FAIL

-------------------------------------------------------------------------------------

  1. HAL_StatusTypeDef USB_EPStartXfer(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep, uint8_t dma)
  2. { //定义变量
  3. uint32_t USBx_BASE = (uint32_t)USBx;
  4. uint32_t epnum = (uint32_t)ep->num;
  5. uint16_t pktcnt;
  6. /* ***************************数据发送************************** */
  7. /* 数据发送:输入端点 */
  8. if (ep->is_in == 1U) //判断端点方向:1-输入;0输出
  9. {
  10. /* ********************配置传输大小和包数****************** */
  11. if (ep->xfer_len == 0U) //当传输长度为0
  12. { //对DIEPTSIZ—IN端点的Txfer大小进行赋值,具体看协议。
  13. USBx_INEP(epnum)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_PKTCNT);
  14. USBx_INEP(epnum)->DIEPTSIZ |= (USB_OTG_DIEPTSIZ_PKTCNT & (1U << 19));
  15. USBx_INEP(epnum)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_XFRSIZ);
  16. }
  17. else //当传输长度不为0
  18. { //对DIEPTSIZ—IN端点的Txfer大小进行赋值,具体看协议。
  19. /* 配置传输大小和包数,例如:
  20. *xfersize = N * maxpacket+ short_packet pktcnt = N + (short_packet exist ? 1 : 0)
  21. */
  22. USBx_INEP(epnum)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_XFRSIZ);
  23. USBx_INEP(epnum)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_PKTCNT);
  24. USBx_INEP(epnum)->DIEPTSIZ |= (USB_OTG_DIEPTSIZ_PKTCNT & (((ep->xfer_len + ep->maxpacket - 1U) / ep->maxpacket) << 19));
  25. USBx_INEP(epnum)->DIEPTSIZ |= (USB_OTG_DIEPTSIZ_XFRSIZ & ep->xfer_len);
  26.  
  27. if (ep->type == EP_TYPE_ISOC) //判断端点的类型为同步端点
  28. { //对DIEPTSIZ—IN端点的Txfer大小进行赋值,具体看协议。
  29. USBx_INEP(epnum)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_MULCNT);
  30. USBx_INEP(epnum)->DIEPTSIZ |= (USB_OTG_DIEPTSIZ_MULCNT & (1U << 29));
  31. }
  32. }
  33.  /* ********************配置DMA****************** */
  34. if (dma == 1U) //判断DMA使能
  35. {
  36. if ((uint32_t)ep->dma_addr != 0U)
  37. { //配置IN端点DMA地址寄存器
  38. USBx_INEP(epnum)->DIEPDMA = (uint32_t)(ep->dma_addr);
  39. }
  40.  
  41. if (ep->type == EP_TYPE_ISOC) //判断端点的类型为同步端点
  42. {
  43. if ((USBx_DEVICE->DSTS & (1U << 8)) == 0U)//判断USB设备状态寄存器
  44. { //配置设备IN端点控制寄存器—设置为奇数帧
  45. USBx_INEP(epnum)->DIEPCTL |= USB_OTG_DIEPCTL_SODDFRM;
  46. }
  47. else
  48. {//配置设备IN端点控制寄存器—设置为数据 PID
  49. USBx_INEP(epnum)->DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM;
  50. }
  51. }
  52.  
  53. /* EP使能,输入数据FIFO */
  54. //配置设备IN端点控制寄存器—设置为清除NAK和端点使能
  55. USBx_INEP(epnum)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA);
  56. }
  57. else //DMA关闭
  58. {
  59. /* EP使能,输入数据FIFO */
  60. //配置设备IN端点控制寄存器—设置为清除NAK和端点使能
  61. USBx_INEP(epnum)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA);
  62. if (ep->type != EP_TYPE_ISOC) //判断端点的类型不为同步端点
  63. {
  64. /*为此EP启用Tx FIFO空中断 */
  65. if (ep->xfer_len > 0U)//且数据长度大于0
  66. { //开USB设备对应的发送数据的端点的中断
  67. USBx_DEVICE->DIEPEMPMSK |= 1UL << (ep->num & EP_ADDR_MSK);
  68. }
  69. }
  70. else //判断端点的类型为同步端点
  71. { //判断USB设备状态寄存器
  72. if ((USBx_DEVICE->DSTS & (1U << 8)) == 0U)
  73. {//配置设备IN端点控制寄存器—设置为奇数帧
  74. USBx_INEP(epnum)->DIEPCTL |= USB_OTG_DIEPCTL_SODDFRM;
  75. }
  76. else
  77. {//配置设备IN端点控制寄存器—设置为数据 PID
  78. USBx_INEP(epnum)->DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM;
  79. }
  80.    /* ********************写数据****************** */
  81. (void)USB_WritePacket(USBx, ep->xfer_buff, ep->num, (uint16_t)ep->xfer_len, dma);
  82. }
  83. }
  84. }
  85. /* ***************************数据接收************************** */
  86. /* 数据接收:输出端点 */
  87. else /* OUT 端点*/
  88. { /* ********************配置传输大小和包数****************** */
  89. /* 设置传输大小和包数,如下:
  90. * pktcnt = N; xfersize = N * maxpacket
  91. */
  92. USBx_OUTEP(epnum)->DOEPTSIZ &= ~(USB_OTG_DOEPTSIZ_XFRSIZ);
  93. USBx_OUTEP(epnum)->DOEPTSIZ &= ~(USB_OTG_DOEPTSIZ_PKTCNT);
  94.  
  95. if (ep->xfer_len == 0U)
  96. {
  97. USBx_OUTEP(epnum)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_XFRSIZ & ep->maxpacket);
  98. USBx_OUTEP(epnum)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_PKTCNT & (1U << 19));
  99. }
  100. else
  101. {
  102. pktcnt = (uint16_t)((ep->xfer_len + ep->maxpacket - 1U) / ep->maxpacket);
  103. USBx_OUTEP(epnum)->DOEPTSIZ |= USB_OTG_DOEPTSIZ_PKTCNT & ((uint32_t)pktcnt << 19);
  104. USBx_OUTEP(epnum)->DOEPTSIZ |= USB_OTG_DOEPTSIZ_XFRSIZ & (ep->maxpacket * pktcnt);
  105. }
  106.   /* ********************配置DMA****************** */
  107. if (dma == 1U)
  108. {
  109. if ((uint32_t)ep->xfer_buff != 0U)
  110. {//设置设备OUT端点DMA地址
  111. USBx_OUTEP(epnum)->DOEPDMA = (uint32_t)(ep->xfer_buff);
  112. }
  113. }
  114.  
  115. if (ep->type == EP_TYPE_ISOC)
  116. {
  117. if ((USBx_DEVICE->DSTS & (1U << 8)) == 0U)
  118. {
  119. USBx_OUTEP(epnum)->DOEPCTL |= USB_OTG_DOEPCTL_SODDFRM;
  120. }
  121. else
  122. {
  123. USBx_OUTEP(epnum)->DOEPCTL |= USB_OTG_DOEPCTL_SD0PID_SEVNFRM;
  124. }
  125. }
  126. /* EP 使能*/
  127. USBx_OUTEP(epnum)->DOEPCTL |= (USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA);
  128. }
  129.  
  130. return HAL_OK;
  131. }

-------------------------------------------------------------------------------------

在HAL库USB接口CDC模式的函数中,收发功能函数到底层寄存器的操作中间嵌套了很多层。在实际使用过程中,我们并不需要关心中间的过程,仅需调用usbd_cdc_if.c文件中的相关函数就能实现数据的收发。

  1. static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len)
  2.       uint8_t CDC_Transmit_HS(uint8_t* Buf, uint16_t Len)

例如:对接收函数稍作改变,就能实现将收到的数据通过USB发送回去,同时通过串口将数据打印。

  1. static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len)
  2. {
  3. /* USER CODE BEGIN 11 */
  4. int i;
  5. uint8_t my_RxBuf[100];
  6. uint32_t my_RxLength;
  7. memcpy(my_RxBuf,Buf,*Len);
  8. my_RxLength=*Len;
  9. CDC_Transmit_HS(my_RxBuf, my_RxLength);
  10. for(i=0;i<my_RxLength;i++)
  11. {printf("%02x ",my_RxBuf[i]);}
  12. USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]);
  13. USBD_CDC_ReceivePacket(&hUsbDeviceHS);
  14. return (USBD_OK);
  15. /* USER CODE END 11 */
  16. }

结果如下:左为USB发送和接收,右为串口打印。

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/244898
推荐阅读
相关标签
  

闽ICP备14008679号