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