IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32F7xx基于HAL库的USB_CDC接收数据的函数调用 -> 正文阅读

[嵌入式]STM32F7xx基于HAL库的USB_CDC接收数据的函数调用

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

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2021-08-01 14:40:08  更:2021-08-01 14:41:52 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/25 18:50:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码