赞
踩
在STM32F7xx的HAL库实现的USB通信中,里面存在着多个库文件和函数的调用,这一章节主要对USB接收数据的函数调用流程来进行分析,USB的数据发送部分相对来说比较容易分析。
在usb通信中,STM32F7xx作为从设备,当USB接收到数据时,从而产生中断;
- /**
- * @File: stm32f7xx_it.c
- * @brief This function handles USB On the Go Hs global interrupt.
- */
- extern PCD_HandleTypeDef hpcd_USB_OTG_HS;
- void OTG_HS_IRQHandler(void)
- {
- HAL_PCD_IRQHandler(&hpcd_USB_OTG_HS);
- }
进入HAL_PCD_IRQHandler(&hpcd_USB_OTG_HS)函数,部分函数代码如下;
- /**
- * @File: stm32f7xx_hal_pcd.c
- * @brief This function handles USB On the Go Hs global interrupt.
- */
- void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
- {
- //...
- /* Read in the device interrupt bits */
- ep_intr = USB_ReadDevAllOutEpInterrupt(hpcd->Instance);
- while (ep_intr != 0U)
- {
- if ((ep_intr & 0x1U) != 0U)
- {
- epint = USB_ReadDevOutEPInterrupt(hpcd->Instance, (uint8_t)epnum);
- if ((epint & USB_OTG_DOEPINT_XFRC) == USB_OTG_DOEPINT_XFRC)
- {
- CLEAR_OUT_EP_INTR(epnum, USB_OTG_DOEPINT_XFRC);
- (void)PCD_EP_OutXfrComplete_int(hpcd, epnum);
- }
- //...
- }
- //...
- }
- //...
- }
读取device out endpoint interrupt寄存器后判断是否数据传输完成,然后清除传输完成中断标志位并进入函数PCD_EP_OutXfrComplete_int(hpcd, epnum),其部分函数代码如下;
- /**
- * @File: stm32f7xx_hal_pcd.c
- * @brief process EP OUT transfer complete interrupt.
- */
- static HAL_StatusTypeDef PCD_EP_OutXfrComplete_int(PCD_HandleTypeDef *hpcd, uint32_t epnum)
- {
- //...
- #if (USE_HAL_PCD_REGISTER_CALLBACKS == 1U)
- hpcd->DataOutStageCallback(hpcd, (uint8_t)epnum);
- #else
- HAL_PCD_DataOutStageCallback(hpcd, (uint8_t)epnum);
- #endif /* USE_HAL_PCD_REGISTER_CALLBACKS */
- //...
- }
然后会进入数据处理回调函数HAL_PCD_DataOutStageCallback,主要是调用USB的底层函数USBD_LL_DataOutStage;
- /**
- * @File: usbd_conf.c
- * @brief Data Out stage callback.
- */
- void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
- {
- USBD_LL_DataOutStage((USBD_HandleTypeDef*)hpcd->pData, epnum,
- hpcd->OUT_ep[epnum].xfer_buff);
- }
其函数USBD_LL_DataOutStage()的部分代码如下;
- /**
- * @File: usbd_core.c
- * @brief USBD_LL_DataOutStage.
- */
- USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev,
- uint8_t epnum, uint8_t *pdata)
- {
- USBD_EndpointTypeDef *pep;
- USBD_StatusTypeDef ret;
- if(epnum == 0U)
- {
- //...
- }
- else if ((pdev->pClass->DataOut != NULL) &&
- (pdev->dev_state == USBD_STATE_CONFIGURED))
- {
- ret = (USBD_StatusTypeDef)pdev->pClass->DataOut(pdev, epnum);
- if (ret != USBD_OK)
- {
- return ret;
- }
- }
- //...
- }
里面主要是通过pdev->pClass->DataOut(pdev, epnum)来调用函数USBD_CDC_DataOut;
通过对结构体USBD_HandleTypeDef的分析,在usbd_conf.c文件中定义了两个结构体USBD_HandleTypeDef和USBD_ClassTypeDef:
- /**
- * @File: usbd_conf.c
- *
- */
- typedef struct _Device_cb
- {
- uint8_t (*Init)(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx);
- uint8_t (*DeInit)(struct _USBD_HandleTypeDef *pdev, uint8_t cfgidx);
- /* Control Endpoints*/
- uint8_t (*Setup)(struct _USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req);
- uint8_t (*EP0_TxSent)(struct _USBD_HandleTypeDef *pdev);
- uint8_t (*EP0_RxReady)(struct _USBD_HandleTypeDef *pdev);
- /* Class Specific Endpoints*/
- uint8_t (*DataIn)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum);
- uint8_t (*DataOut)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum);
- uint8_t (*SOF)(struct _USBD_HandleTypeDef *pdev);
- uint8_t (*IsoINIncomplete)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum);
- uint8_t (*IsoOUTIncomplete)(struct _USBD_HandleTypeDef *pdev, uint8_t epnum);
- uint8_t *(*GetHSConfigDescriptor)(uint16_t *length);
- uint8_t *(*GetFSConfigDescriptor)(uint16_t *length);
- uint8_t *(*GetOtherSpeedConfigDescriptor)(uint16_t *length);
- uint8_t *(*GetDeviceQualifierDescriptor)(uint16_t *length);
- } USBD_ClassTypeDef;
- /* USB Device handle structure */
- typedef struct _USBD_HandleTypeDef
- {
- uint8_t id;
- uint32_t dev_config;
- uint32_t dev_default_config;
- uint32_t dev_config_status;
- USBD_SpeedTypeDef dev_speed;
- USBD_EndpointTypeDef ep_in[16];
- USBD_EndpointTypeDef ep_out[16];
- uint32_t ep0_state;
- uint32_t ep0_data_len;
- uint8_t dev_state;
- uint8_t dev_old_state;
- uint8_t dev_address;
- uint8_t dev_connection_status;
- uint8_t dev_test_mode;
- uint32_t dev_remote_wakeup;
- uint8_t ConfIdx;
- USBD_SetupReqTypedef request;
- USBD_DescriptorsTypeDef *pDesc;
- USBD_ClassTypeDef *pClass;
- void *pClassData;
- void *pUserData;
- void *pData;
- void *pBosDesc;
- void *pConfDesc;
- } USBD_HandleTypeDef;
并对结构体USBD_ClassTypeDef进行了实例化:
- /**
- * @File: usbd_conf.c
- *@brief: CDC interface class callbacks structure.
- */
- USBD_ClassTypeDef USBD_CDC =
- {
- USBD_CDC_Init,
- USBD_CDC_DeInit,
- USBD_CDC_Setup,
- NULL, /* EP0_TxSent, */
- USBD_CDC_EP0_RxReady,
- USBD_CDC_DataIn,
- USBD_CDC_DataOut,
- NULL,
- NULL,
- NULL,
- USBD_CDC_GetHSCfgDesc,
- USBD_CDC_GetFSCfgDesc,
- USBD_CDC_GetOtherSpeedCfgDesc,
- USBD_CDC_GetDeviceQualifierDescriptor,
- };
而在USB进行设备初始化时,就会来调用函数USBD_RegisterClass(&hUsbDeviceHS, &USBD_CDC)来进行初始化,其函数USBD_RegisterClass()的部分代码如下;
- /**
- * @File: usbd_conf.c
- *@brief: Link class driver to Device Core.
- */
- USBD_StatusTypeDef USBD_RegisterClass(USBD_HandleTypeDef *pdev,
- USBD_ClassTypeDef *pclass)
- {
- //...
- /* link the class to the USB Device handle */
- pdev->pClass = pclass;
- //...
- }
所以在函数USBD_LL_DataOutStage()中的pdev->pClass->DataOut(pdev, epnum)就会来调用上面提到的函数USBD_CDC_DataOut,在文件usbd_conf.c中进行了定义;
- /**
- * @File: usbd_conf.c
- * @brief Data received on non-control Out endpoint
- */
- static uint8_t USBD_CDC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum)
- {
- USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData;
-
- if (pdev->pClassData == NULL)
- {
- return (uint8_t)USBD_FAIL;
- }
- /* Get the received data length */
- hcdc->RxLength = USBD_LL_GetRxDataSize(pdev, epnum);
- /* USB data will be immediately processed, this allow next USB traffic being
- NAKed till the end of the application Xfer */
- ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(hcdc->RxBuffer, &hcdc->RxLength);
-
- return (uint8_t)USBD_OK;
- }
函数中的((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(hcdc->RxBuffer, &hcdc->RxLength)就会调用到函数CDC_Receive_HS。
同样,在文件usbd_conf.c中定义了结构体USBD_CDC_ItfTypeDef并进行了实例化;
- /**
- * @File: usbd_conf.c
- *
- */
- typedef struct _USBD_CDC_Itf
- {
- int8_t (* Init)(void);
- int8_t (* DeInit)(void);
- int8_t (* Control)(uint8_t cmd, uint8_t *pbuf, uint16_t length);
- int8_t (* Receive)(uint8_t *Buf, uint32_t *Len);
- int8_t (* TransmitCplt)(uint8_t *Buf, uint32_t *Len, uint8_t epnum);
- } USBD_CDC_ItfTypeDef;
- /*
- *CD Interface callback
- */
- USBD_CDC_ItfTypeDef USBD_Interface_fops_HS =
- {
- CDC_Init_HS,
- CDC_DeInit_HS,
- CDC_Control_HS,
- CDC_Receive_HS,
- CDC_TransmitCplt_HS
- };
并在usb的设备初始化中调用函数USBD_CDC_RegisterInterface(&hUsbDeviceHS, &USBD_Interface_fops_HS)进行了初始化,其USBD_CDC_RegisterInterface()函数代码如下;
- /**
- * @File: usbd_conf.c
- *@brief: USBD_CDC_RegisterInterface.
- */
- uint8_t USBD_CDC_RegisterInterface(USBD_HandleTypeDef *pdev,USBD_CDC_ItfTypeDef *fops)
- {
- if (fops == NULL)
- {
- return (uint8_t)USBD_FAIL;
- }
- pdev->pUserData = fops;
- return (uint8_t)USBD_OK;
- }
所以((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(hcdc->RxBuffer, &hcdc->RxLength)就会调用到函数CDC_Receive_HS,在文件usbd_cdc_if.c中进行了定义;
- /**
- * @File: usbd_cdc_if.c
- * @brief Data received over USB OUT endpoint are sent over CDC interface
- * through this function.
- * @param Buf: Buffer of data to be received
- * @param Len: Number of data received (in bytes)
- */
- static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len)
- {
- /* USER CODE BEGIN 11 */
- USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]);
- USBD_CDC_ReceivePacket(&hUsbDeviceHS);
- return (USBD_OK);
- /* USER CODE END 11 */
- }
前面提到的USB初始化函数,在文件usb_device.c中进行了定义,主要是通过函数MX_USB_DEVICE_Init()来对USB设备进行初始化;
- /**
- * @File: usb_device.c
- *@brief: Init USB device Library, add supported class and start the library.
- */
- void MX_USB_DEVICE_Init(void)
- {
- /* Init Device Library, add supported class and start the library. */
- if (USBD_Init(&hUsbDeviceHS, &HS_Desc, DEVICE_HS) != USBD_OK)
- {
- Error_Handler();
- }
- if (USBD_RegisterClass(&hUsbDeviceHS, &USBD_CDC) != USBD_OK)
- {
- Error_Handler();
- }
- if (USBD_CDC_RegisterInterface(&hUsbDeviceHS, &USBD_Interface_fops_HS) != USBD_OK)
- {
- Error_Handler();
- }
- if (USBD_Start(&hUsbDeviceHS) != USBD_OK)
- {
- Error_Handler();
- }
- }
文件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()函数;
- /**
- * @File: usbd_cdc.c
- *@brief: prepare OUT Endpoint for reception.
- */
- uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev)
- {
- //...
- if (pdev->dev_speed == USBD_SPEED_HIGH)
- {
- /* Prepare Out endpoint to receive next packet */
- (void)USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer,
- CDC_DATA_HS_OUT_PACKET_SIZE);
- }
- //...
- }
- /**
- * @brief Prepares an endpoint for reception.
- */
- USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint32_t size)
- {
- HAL_StatusTypeDef hal_status = HAL_OK;
- USBD_StatusTypeDef usb_status = USBD_OK;
-
- hal_status = HAL_PCD_EP_Receive(pdev->pData, ep_addr, pbuf, size);
-
- usb_status = USBD_Get_USB_Status(hal_status);
-
- return usb_status;
- }
文件stm32f7xx_hal_pcd.c中的函数HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len);
- /**
- * @File: stm32f7xx_hal_pcd.c
- *@brief: Receive an amount of data.
- */
- HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len)
- {
- PCD_EPTypeDef *ep;
- ep = &hpcd->OUT_ep[ep_addr & EP_ADDR_MSK];
- /*setup and start the Xfer */
- ep->xfer_buff = pBuf;
- ep->xfer_len = len;
- ep->xfer_count = 0U;
- ep->is_in = 0U;
- ep->num = ep_addr & EP_ADDR_MSK;
- if (hpcd->Init.dma_enable == 1U)
- {
- ep->dma_addr = (uint32_t)pBuf;
- }
- if ((ep_addr & EP_ADDR_MSK) == 0U)
- {
- (void)USB_EP0StartXfer(hpcd->Instance, ep, (uint8_t)hpcd->Init.dma_enable);
- }
- else
- {
- (void)USB_EPStartXfer(hpcd->Instance, ep, (uint8_t)hpcd->Init.dma_enable);
- }
- return HAL_OK;
- }
接着就会调用文件stm32f7xx_ll_usb.c中的函数USB_EPStartXfer(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep, uint8_t dma);
- /**
- * @File: stm32f7xx_ll_usb.c
- *@brief: setup and starts a transfer over an EP.
- */
- HAL_StatusTypeDef USB_EPStartXfer(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep, uint8_t dma)
- {
- //...
- /* IN endpoint */
- if (ep->is_in == 1U)
- {
- //...
- }
- else /* OUT endpoint */
- {
- /* Program the transfer size and packet count as follows:
- * pktcnt = N
- * xfersize = N * maxpacket
- */
- USBx_OUTEP(epnum)->DOEPTSIZ &= ~(USB_OTG_DOEPTSIZ_XFRSIZ);
- USBx_OUTEP(epnum)->DOEPTSIZ &= ~(USB_OTG_DOEPTSIZ_PKTCNT);
- if (ep->xfer_len == 0U)
- {
- USBx_OUTEP(epnum)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_XFRSIZ & ep->maxpacket);
- USBx_OUTEP(epnum)->DOEPTSIZ |= (USB_OTG_DOEPTSIZ_PKTCNT & (1U << 19));
- }
- else
- {
- pktcnt = (uint16_t)((ep->xfer_len + ep->maxpacket - 1U) / ep->maxpacket);
- USBx_OUTEP(epnum)->DOEPTSIZ |= USB_OTG_DOEPTSIZ_PKTCNT & ((uint32_t)pktcnt << 19);
- USBx_OUTEP(epnum)->DOEPTSIZ |= USB_OTG_DOEPTSIZ_XFRSIZ & (ep->maxpacket * pktcnt);
- }
- //...
- /* EP enable */
- USBx_OUTEP(epnum)->DOEPCTL |= (USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA);
- }
-
- return HAL_OK;
- }
该函数主要是对USB接收中最底层的寄存器来进行配置。至此,这就是整个USB数据接收的函数调用流程。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。