当前位置:   article > 正文

STM32F7xx基于HAL库的USB_CDC接收数据的函数调用_otg_hs_irqhandler

otg_hs_irqhandler

在STM32F7xx的HAL库实现的USB通信中,里面存在着多个库文件和函数的调用,这一章节主要对USB接收数据的函数调用流程来进行分析,USB的数据发送部分相对来说比较容易分析。

在usb通信中,STM32F7xx作为从设备,当USB接收到数据时,从而产生中断;

  1. /**
  2. * @File: stm32f7xx_it.c
  3. * @brief This function handles USB On the Go Hs global interrupt.
  4. */
  5. extern PCD_HandleTypeDef hpcd_USB_OTG_HS;
  6. void OTG_HS_IRQHandler(void)
  7. {
  8.  HAL_PCD_IRQHandler(&hpcd_USB_OTG_HS);
  9. }

进入HAL_PCD_IRQHandler(&hpcd_USB_OTG_HS)函数,部分函数代码如下;

  1. /**
  2. * @File: stm32f7xx_hal_pcd.c
  3. * @brief This function handles USB On the Go Hs global interrupt.
  4. */
  5. void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
  6. {
  7.    //...
  8.     /* Read in the device interrupt bits */
  9.      ep_intr = USB_ReadDevAllOutEpInterrupt(hpcd->Instance);
  10.      while (ep_intr != 0U)
  11.     {
  12.        if ((ep_intr & 0x1U) != 0U)
  13.       {
  14.          epint = USB_ReadDevOutEPInterrupt(hpcd->Instance, (uint8_t)epnum);
  15.          if ((epint & USB_OTG_DOEPINT_XFRC) == USB_OTG_DOEPINT_XFRC)
  16.         {
  17.            CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_XFRC);
  18.           (void)PCD_EP_OutXfrComplete_int(hpcd, epnum);
  19.         }
  20.            //...
  21.       }
  22.          //...
  23.     }
  24.    //...
  25. }

读取device out endpoint interrupt寄存器后判断是否数据传输完成,然后清除传输完成中断标志位并进入函数PCD_EP_OutXfrComplete_int(hpcd, epnum),其部分函数代码如下;

  1. /**
  2. * @File: stm32f7xx_hal_pcd.c
  3. * @brief process EP OUT transfer complete interrupt.
  4. */
  5. static HAL_StatusTypeDef PCD_EP_OutXfrComplete_int(PCD_HandleTypeDef *hpcd, uint32_t epnum)
  6. {
  7.    //...
  8. #if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U)
  9.        hpcd->DataOutStageCallback(hpcd, (uint8_t)epnum);
  10. #else
  11.        HAL_PCD_DataOutStageCallback(hpcd, (uint8_t)epnum);
  12. #endif /* USE_HAL_PCD_REGISTER_CALLBACKS */
  13.    //...
  14. }

然后会进入数据处理回调函数HAL_PCD_DataOutStageCallback,主要是调用USB的底层函数USBD_LL_DataOutStage;

  1. /**
  2. * @File: usbd_conf.c
  3. * @brief Data Out stage callback.
  4. */
  5. void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
  6. {
  7.  USBD_LL_DataOutStage((USBD_HandleTypeDef*)hpcd->pData, epnum,
  8.                       hpcd->OUT_ep[epnum].xfer_buff);
  9. }

其函数USBD_LL_DataOutStage()的部分代码如下;

  1. /**
  2. * @File: usbd_core.c
  3. * @brief USBD_LL_DataOutStage.
  4. */
  5. USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev,
  6.                                        uint8_t epnum, uint8_t *pdata)
  7. {
  8.    USBD_EndpointTypeDef *pep;
  9.    USBD_StatusTypeDef ret;
  10.    if(epnum == 0U)
  11.   {
  12.        //...
  13.   }
  14.   else if ((pdev->pClass->DataOut != NULL) &&
  15.           (pdev->dev_state == USBD_STATE_CONFIGURED))
  16. {
  17.    ret = (USBD_StatusTypeDef)pdev->pClass->DataOut(pdev, epnum);
  18.    if (ret != USBD_OK)
  19.   {
  20.      return ret;
  21.   }
  22. }
  23.    //...
  24. }

里面主要是通过pdev->pClass->DataOut(pdev, epnum)来调用函数USBD_CDC_DataOut;

通过对结构体USBD_HandleTypeDef的分析,在usbd_conf.c文件中定义了两个结构体USBD_HandleTypeDef和USBD_ClassTypeDef:

  1. /**
  2. * @File: usbd_conf.c
  3. *
  4. */
  5. typedef struct _Device_cb
  6. {
  7.  uint8_t (*Init)(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx);
  8.  uint8_t (*DeInit)(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx);
  9.  /* Control Endpoints*/
  10.  uint8_t (*Setup)(struct _USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef  *req);
  11.  uint8_t (*EP0_TxSent)(struct _USBD_HandleTypeDef *pdev);
  12.  uint8_t (*EP0_RxReady)(struct _USBD_HandleTypeDef *pdev);
  13.  /* Class Specific Endpoints*/
  14.  uint8_t (*DataIn)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum);
  15.  uint8_t (*DataOut)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum);
  16.  uint8_t (*SOF)(struct _USBD_HandleTypeDef *pdev);
  17.  uint8_t (*IsoINIncomplete)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum);
  18.  uint8_t (*IsoOUTIncomplete)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum);
  19.  uint8_t  *(*GetHSConfigDescriptor)(uint16_t *length);
  20.  uint8_t  *(*GetFSConfigDescriptor)(uint16_t *length);
  21.  uint8_t  *(*GetOtherSpeedConfigDescriptor)(uint16_t *length);
  22.  uint8_t  *(*GetDeviceQualifierDescriptor)(uint16_t *length);
  23. } USBD_ClassTypeDef;
  24. /* USB Device handle structure */
  25. typedef struct _USBD_HandleTypeDef
  26. {
  27.  uint8_t                 id;
  28.  uint32_t                dev_config;
  29.  uint32_t                dev_default_config;
  30.  uint32_t                dev_config_status;
  31.  USBD_SpeedTypeDef       dev_speed;
  32.  USBD_EndpointTypeDef    ep_in[16];
  33.  USBD_EndpointTypeDef    ep_out[16];
  34.  uint32_t                ep0_state;
  35.  uint32_t                ep0_data_len;
  36.  uint8_t                 dev_state;
  37.  uint8_t                 dev_old_state;
  38.  uint8_t                 dev_address;
  39.  uint8_t                 dev_connection_status;
  40.  uint8_t                 dev_test_mode;
  41.  uint32_t                dev_remote_wakeup;
  42.  uint8_t                 ConfIdx;
  43.  USBD_SetupReqTypedef    request;
  44.  USBD_DescriptorsTypeDef *pDesc;
  45.  USBD_ClassTypeDef       *pClass;
  46.  void                    *pClassData;
  47.  void                    *pUserData;
  48.  void                    *pData;
  49.  void                    *pBosDesc;
  50.  void                    *pConfDesc;
  51. } USBD_HandleTypeDef;

并对结构体USBD_ClassTypeDef进行了实例化:

  1. /**
  2. * @File: usbd_conf.c
  3. *@brief: CDC interface class callbacks structure.
  4. */
  5. USBD_ClassTypeDef  USBD_CDC =
  6. {
  7.  USBD_CDC_Init,
  8.  USBD_CDC_DeInit,
  9.  USBD_CDC_Setup,
  10.  NULL,                 /* EP0_TxSent, */
  11.  USBD_CDC_EP0_RxReady,
  12.  USBD_CDC_DataIn,
  13.  USBD_CDC_DataOut,
  14.  NULL,
  15.  NULL,
  16.  NULL,
  17.  USBD_CDC_GetHSCfgDesc,
  18.  USBD_CDC_GetFSCfgDesc,
  19.  USBD_CDC_GetOtherSpeedCfgDesc,
  20.  USBD_CDC_GetDeviceQualifierDescriptor,
  21. };

而在USB进行设备初始化时,就会来调用函数USBD_RegisterClass(&hUsbDeviceHS, &USBD_CDC)来进行初始化,其函数USBD_RegisterClass()的部分代码如下;

  1. /**
  2. * @File: usbd_conf.c
  3. *@brief: Link class driver to Device Core.
  4. */
  5. USBD_StatusTypeDef USBD_RegisterClass(USBD_HandleTypeDef *pdev,
  6.                                      USBD_ClassTypeDef *pclass)
  7. {
  8.    //...
  9.    /* link the class to the USB Device handle */
  10.    pdev->pClass = pclass;
  11.    //...
  12. }

所以在函数USBD_LL_DataOutStage()中的pdev->pClass->DataOut(pdev, epnum)就会来调用上面提到的函数USBD_CDC_DataOut,在文件usbd_conf.c中进行了定义;

  1. /**
  2. * @File: usbd_conf.c
  3. * @brief Data received on non-control Out endpoint
  4. */
  5. static uint8_t USBD_CDC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum)
  6. {
  7.    USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData;
  8.  if (pdev->pClassData == NULL)
  9. {
  10.    return (uint8_t)USBD_FAIL;
  11. }
  12.  /* Get the received data length */
  13.  hcdc->RxLength = USBD_LL_GetRxDataSize(pdev, epnum);
  14.  /* USB data will be immediately processed, this allow next USB traffic being
  15.  NAKed till the end of the application Xfer */
  16. ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(hcdc->RxBuffer, &hcdc->RxLength);
  17.    
  18.  return (uint8_t)USBD_OK;
  19. }

函数中的((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(hcdc->RxBuffer, &hcdc->RxLength)就会调用到函数CDC_Receive_HS。

同样,在文件usbd_conf.c中定义了结构体USBD_CDC_ItfTypeDef并进行了实例化;

  1. /**
  2. * @File: usbd_conf.c
  3. *
  4. */
  5. typedef struct _USBD_CDC_Itf
  6. {
  7.  int8_t (* Init)(void);
  8.  int8_t (* DeInit)(void);
  9.  int8_t (* Control)(uint8_t cmd, uint8_t *pbuf, uint16_t length);
  10.  int8_t (* Receive)(uint8_t *Buf, uint32_t *Len);
  11.  int8_t (* TransmitCplt)(uint8_t *Buf, uint32_t *Len, uint8_t epnum);
  12. } USBD_CDC_ItfTypeDef;
  13. /*
  14. *CD Interface callback
  15. */
  16. USBD_CDC_ItfTypeDef USBD_Interface_fops_HS =
  17. {
  18.  CDC_Init_HS,
  19.  CDC_DeInit_HS,
  20.  CDC_Control_HS,
  21.  CDC_Receive_HS,
  22.  CDC_TransmitCplt_HS
  23. };

并在usb的设备初始化中调用函数USBD_CDC_RegisterInterface(&hUsbDeviceHS, &USBD_Interface_fops_HS)进行了初始化,其USBD_CDC_RegisterInterface()函数代码如下;

  1. /**
  2. * @File: usbd_conf.c
  3. *@brief: USBD_CDC_RegisterInterface.
  4. */
  5. uint8_t USBD_CDC_RegisterInterface(USBD_HandleTypeDef *pdev,USBD_CDC_ItfTypeDef *fops)
  6. {
  7.  if (fops == NULL)
  8. {
  9.    return (uint8_t)USBD_FAIL;
  10. }
  11.  pdev->pUserData = fops;
  12.  return (uint8_t)USBD_OK;
  13. }

所以((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(hcdc->RxBuffer, &hcdc->RxLength)就会调用到函数CDC_Receive_HS,在文件usbd_cdc_if.c中进行了定义;

  1. /**
  2. * @File: usbd_cdc_if.c
  3. * @brief Data received over USB OUT endpoint are sent over CDC interface
  4. *         through this function.
  5. * @param Buf: Buffer of data to be received
  6. * @param Len: Number of data received (in bytes)
  7. */
  8. static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len)
  9. {
  10.  /* USER CODE BEGIN 11 */
  11.  USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]);
  12.  USBD_CDC_ReceivePacket(&hUsbDeviceHS);
  13.  return (USBD_OK);
  14.  /* USER CODE END 11 */
  15. }

前面提到的USB初始化函数,在文件usb_device.c中进行了定义,主要是通过函数MX_USB_DEVICE_Init()来对USB设备进行初始化;

  1. /**
  2. * @File: usb_device.c
  3. *@brief: Init USB device Library, add supported class and start the library.
  4. */
  5. void MX_USB_DEVICE_Init(void)
  6. {
  7.  /* Init Device Library, add supported class and start the library. */
  8.  if (USBD_Init(&hUsbDeviceHS, &HS_Desc, DEVICE_HS) != USBD_OK)
  9. {
  10.    Error_Handler();
  11. }
  12.  if (USBD_RegisterClass(&hUsbDeviceHS, &USBD_CDC) != USBD_OK)
  13. {
  14.    Error_Handler();
  15. }
  16.  if (USBD_CDC_RegisterInterface(&hUsbDeviceHS, &USBD_Interface_fops_HS) != USBD_OK)
  17. {
  18.    Error_Handler();
  19. }
  20.  if (USBD_Start(&hUsbDeviceHS) != USBD_OK)
  21. {
  22.    Error_Handler();
  23. }
  24. }
​文件usbd_cdc_if.c中的函数CDC_Receive_HS()就可以在里面进行对USB主设备发来的数据进行接收,指针uint8_t* Buf指向数据内容,指针uint32_t *Len指向数据的长度。

接下来在函数CDC_Receive_HS()中就会调用文件usbd_conf.c中的函数USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev)和USBD_LL_PrepareReceive()函数;

  1. /**
  2. * @File: usbd_cdc.c
  3. *@brief: prepare OUT Endpoint for reception.
  4. */
  5. uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev)
  6. {
  7.    //...
  8.    if (pdev->dev_speed == USBD_SPEED_HIGH)
  9.   {
  10.            /* Prepare Out endpoint to receive next packet */
  11.           (void)USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer,
  12.                                 CDC_DATA_HS_OUT_PACKET_SIZE);
  13.   }
  14.    //...
  15. }
  16. /**
  17. * @brief Prepares an endpoint for reception.
  18. */
  19. USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint32_t size)
  20. {
  21.  HAL_StatusTypeDef hal_status = HAL_OK;
  22.  USBD_StatusTypeDef usb_status = USBD_OK;
  23.  hal_status = HAL_PCD_EP_Receive(pdev->pData, ep_addr, pbuf, size);
  24.  usb_status =  USBD_Get_USB_Status(hal_status);
  25.  return usb_status;
  26. }

文件stm32f7xx_hal_pcd.c中的函数HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len);

  1. /**
  2. * @File: stm32f7xx_hal_pcd.c
  3. *@brief: Receive an amount of data.
  4. */
  5. HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len)
  6. {
  7.  PCD_EPTypeDef *ep;
  8.  ep = &hpcd->OUT_ep[ep_addr & EP_ADDR_MSK];
  9.  /*setup and start the Xfer */
  10.  ep->xfer_buff = pBuf;
  11.  ep->xfer_len = len;
  12.  ep->xfer_count = 0U;
  13.  ep->is_in = 0U;
  14.  ep->num = ep_addr & EP_ADDR_MSK;
  15.  if (hpcd->Init.dma_enable == 1U)
  16. {
  17.    ep->dma_addr = (uint32_t)pBuf;
  18. }
  19.  if ((ep_addr & EP_ADDR_MSK) == 0U)
  20. {
  21.   (void)USB_EP0StartXfer(hpcd->Instance, ep, (uint8_t)hpcd->Init.dma_enable);
  22. }
  23.  else
  24. {
  25.   (void)USB_EPStartXfer(hpcd->Instance, ep, (uint8_t)hpcd->Init.dma_enable);
  26. }
  27.  return HAL_OK;
  28. }

接着就会调用文件stm32f7xx_ll_usb.c中的函数USB_EPStartXfer(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep, uint8_t dma);

  1. /**
  2. * @File: stm32f7xx_ll_usb.c
  3. *@brief: setup and starts a transfer over an EP.
  4. */
  5. HAL_StatusTypeDef USB_EPStartXfer(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep, uint8_t dma)
  6. {
  7.   //...
  8.      /* IN endpoint */
  9.  if (ep->is_in == 1U)
  10. {
  11.      //...
  12. }
  13.  else /* OUT endpoint */
  14. {
  15.    /* Program the transfer size and packet count as follows:
  16.    * pktcnt = N
  17.    * xfersize = N * maxpacket
  18.    */
  19.    USBx_OUTEP(epnum)->DOEPTSIZ &= ~(USB_OTG_DOEPTSIZ_XFRSIZ);
  20.    USBx_OUTEP(epnum)->DOEPTSIZ &= ~(USB_OTG_DOEPTSIZ_PKTCNT);
  21.    if (ep->xfer_len == 0U)
  22.   {
  23.      USBx_OUTEP(epnum)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_XFRSIZ & ep->maxpacket);
  24.      USBx_OUTEP(epnum)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_PKTCNT & (1U << 19));
  25.   }
  26.    else
  27.   {
  28.      pktcnt = (uint16_t)((ep->xfer_len + ep->maxpacket - 1U) / ep->maxpacket);
  29.      USBx_OUTEP(epnum)->DOEPTSIZ |= USB_OTG_DOEPTSIZ_PKTCNT & ((uint32_t)pktcnt << 19);
  30.      USBx_OUTEP(epnum)->DOEPTSIZ |= USB_OTG_DOEPTSIZ_XFRSIZ & (ep->maxpacket * pktcnt);
  31.   }
  32.    //...
  33.    /* EP enable */
  34.    USBx_OUTEP(epnum)->DOEPCTL |= (USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA);
  35. }
  36.  return HAL_OK;
  37. }

该函数主要是对USB接收中最底层的寄存器来进行配置。至此,这就是整个USB数据接收的函数调用流程。

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

闽ICP备14008679号