在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数据接收的函数调用流程。
|