赞
踩
在使用ARM的HAL库实现USB接口通信,常常因为有多个库文件和函数的多层调用而觉得使用起来很复杂,而事实上通过合理的配置后,HAL库函数的使用非常简单,本文对利用STM32CubeMX生成的USB_CDC工程的库函数进行分析,从而通过简单的修改实现USB接口的通信。
自顶而下:
-----------------------------------------------------------------------------
设备接收数据流图:
设备发送数据流图:
-----------------------------------------------------------------------------
文件usbd_cdc_if.c,函数如下:
函数描述:USB输出端点接收的数据通过此函数发送给CDC接口。
注意事项:此函数将阻止USB端点上的所有OUT数据包接收,直到退出此函数。 如果在CDC接口上的传输完成之前(即使用DMA控制器)退出此函数,将导致接收更多数据,而先前的数据仍未发送。
参数说明:参数Buf: 待接收数据的缓冲区;
参数Len: 接收的数据数(以字节为单位)
返回: USBD_OK(如果所有操作均正常),否则返回USBD_FAIL
-----------------------------------------------------------------------------
- static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len)
- {
- USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]);//设置接收buf
- USBD_CDC_ReceivePacket(&hUsbDeviceHS);//接收数据包
- return (USBD_OK);
- }
-----------------------------------------------------------------------------------
文件usbd_cdc.c,函数如下:
函数描述:准备接收输出端点数据。
参数说明:参数pdev: USB设备结构体句柄;
返回: USBD_OK(如果所有操作均正常),否则返回USBD_FAIL
-----------------------------------------------------------------------------
- uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev)
- {
- (void)USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer,
- CDC_DATA_HS_OUT_PACKET_SIZE);
- return (uint8_t)USBD_OK;
- }
------------------------------------------------------------------------------------
文件usbd_conf.c,函数如下:
函数描述:准备一个端点接收数据。
参数说明:参数pdev: USB设备结构体句柄;
参数ep_addr: 端点号;
参数pbuf: 指向要接收的数据的指针;
参数size: 数据大小;
返回: USBD_OK(如果所有操作均正常),否则返回USBD_FAIL
------------------------------------------------------------------------------------
- USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, uint8_t ep_addr, uint8_t *pbuf, uint32_t size)
- {
- hal_status = HAL_PCD_EP_Receive(pdev->pData, ep_addr, pbuf, size);
- return usb_status;
- }
------------------------------------------------------------------------------------
文件stm32f4xx_hal_pcd.c,函数如下:
函数描述:接收大量数据。
参数说明:参数pdev: PCD结构体句柄;
参数ep_addr: 端点号;
参数pbuf: 指向要接收的数据的指针;
参数len: 接收数据长度;
返回: HAL_OK(如果所有操作均正常),否则返回HAL_FAIL
-----------------------------------------------------------------------------------
- HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len)
- {
- /*设置和启动 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)//当DMA使能时,给DMA接收BUF的首地址。
- {
- ep->dma_addr = (uint32_t)pBuf;
- }
- if ((ep_addr & EP_ADDR_MSK) == 0U)//端点是EP0时,用USB_EP0StartXfer传输数据
- {
- (void)USB_EP0StartXfer(hpcd->Instance, ep, (uint8_t)hpcd->Init.dma_enable);
- }
- Else //端点不是EP0时,用USB_EPStartXfer传输数据
- {
- (void)USB_EPStartXfer(hpcd->Instance, ep, (uint8_t)hpcd->Init.dma_enable);
- }
- return HAL_OK;
- }

------------------------------------------------------------------------------------
文件stm32f4xx_ll_usb.c(底层函数,重点分析)
函数描述:建立和启动一次EP的数据传输,该函数是USB收发中最底层的寄存器操作函数,数据的组包和解包均在此函数中进行。
参数说明: 参数USBx: USB_OTG核心寄存器定义结构体句柄;
参数ep: USB_OTG端点寄存器定义结构体句柄;
参数dma: DMA使能,1有效,0关闭;
返回: HAL_OK(如果所有操作均正常),否则返回HAL_FAIL
-------------------------------------------------------------------------------------
- HAL_StatusTypeDef USB_EPStartXfer(USB_OTG_GlobalTypeDef *USBx, USB_OTG_EPTypeDef *ep, uint8_t dma)
- { //定义变量
- uint32_t USBx_BASE = (uint32_t)USBx;
- uint32_t epnum = (uint32_t)ep->num;
- uint16_t pktcnt;
- /* ***************************数据发送************************** */
- /* 数据发送:输入端点 */
- if (ep->is_in == 1U) //判断端点方向:1-输入;0输出
- {
- /* ********************配置传输大小和包数****************** */
- if (ep->xfer_len == 0U) //当传输长度为0
- { //对DIEPTSIZ—IN端点的Txfer大小进行赋值,具体看协议。
- USBx_INEP(epnum)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_PKTCNT);
- USBx_INEP(epnum)->DIEPTSIZ |= (USB_OTG_DIEPTSIZ_PKTCNT & (1U << 19));
- USBx_INEP(epnum)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_XFRSIZ);
- }
- else //当传输长度不为0
- { //对DIEPTSIZ—IN端点的Txfer大小进行赋值,具体看协议。
- /* 配置传输大小和包数,例如:
- *xfersize = N * maxpacket+ short_packet pktcnt = N + (short_packet exist ? 1 : 0)
- */
- USBx_INEP(epnum)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_XFRSIZ);
- USBx_INEP(epnum)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_PKTCNT);
- USBx_INEP(epnum)->DIEPTSIZ |= (USB_OTG_DIEPTSIZ_PKTCNT & (((ep->xfer_len + ep->maxpacket - 1U) / ep->maxpacket) << 19));
- USBx_INEP(epnum)->DIEPTSIZ |= (USB_OTG_DIEPTSIZ_XFRSIZ & ep->xfer_len);
-
- if (ep->type == EP_TYPE_ISOC) //判断端点的类型为同步端点
- { //对DIEPTSIZ—IN端点的Txfer大小进行赋值,具体看协议。
- USBx_INEP(epnum)->DIEPTSIZ &= ~(USB_OTG_DIEPTSIZ_MULCNT);
- USBx_INEP(epnum)->DIEPTSIZ |= (USB_OTG_DIEPTSIZ_MULCNT & (1U << 29));
- }
- }
- /* ********************配置DMA****************** */
- if (dma == 1U) //判断DMA使能
- {
- if ((uint32_t)ep->dma_addr != 0U)
- { //配置IN端点DMA地址寄存器
- USBx_INEP(epnum)->DIEPDMA = (uint32_t)(ep->dma_addr);
- }
-
- if (ep->type == EP_TYPE_ISOC) //判断端点的类型为同步端点
- {
- if ((USBx_DEVICE->DSTS & (1U << 8)) == 0U)//判断USB设备状态寄存器
- { //配置设备IN端点控制寄存器—设置为奇数帧
- USBx_INEP(epnum)->DIEPCTL |= USB_OTG_DIEPCTL_SODDFRM;
- }
- else
- {//配置设备IN端点控制寄存器—设置为数据 PID
- USBx_INEP(epnum)->DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM;
- }
- }
-
- /* EP使能,输入数据FIFO */
- //配置设备IN端点控制寄存器—设置为清除NAK和端点使能
- USBx_INEP(epnum)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA);
- }
- else //DMA关闭
- {
- /* EP使能,输入数据FIFO */
- //配置设备IN端点控制寄存器—设置为清除NAK和端点使能
- USBx_INEP(epnum)->DIEPCTL |= (USB_OTG_DIEPCTL_CNAK | USB_OTG_DIEPCTL_EPENA);
- if (ep->type != EP_TYPE_ISOC) //判断端点的类型不为同步端点
- {
- /*为此EP启用Tx FIFO空中断 */
- if (ep->xfer_len > 0U)//且数据长度大于0
- { //开USB设备对应的发送数据的端点的中断
- USBx_DEVICE->DIEPEMPMSK |= 1UL << (ep->num & EP_ADDR_MSK);
- }
- }
- else //判断端点的类型为同步端点
- { //判断USB设备状态寄存器
- if ((USBx_DEVICE->DSTS & (1U << 8)) == 0U)
- {//配置设备IN端点控制寄存器—设置为奇数帧
- USBx_INEP(epnum)->DIEPCTL |= USB_OTG_DIEPCTL_SODDFRM;
- }
- else
- {//配置设备IN端点控制寄存器—设置为数据 PID
- USBx_INEP(epnum)->DIEPCTL |= USB_OTG_DIEPCTL_SD0PID_SEVNFRM;
- }
- /* ********************写数据****************** */
- (void)USB_WritePacket(USBx, ep->xfer_buff, ep->num, (uint16_t)ep->xfer_len, dma);
- }
- }
- }
- /* ***************************数据接收************************** */
- /* 数据接收:输出端点 */
- else /* OUT 端点*/
- { /* ********************配置传输大小和包数****************** */
- /* 设置传输大小和包数,如下:
- * 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);
- }
- /* ********************配置DMA****************** */
- if (dma == 1U)
- {
- if ((uint32_t)ep->xfer_buff != 0U)
- {//设置设备OUT端点DMA地址
- USBx_OUTEP(epnum)->DOEPDMA = (uint32_t)(ep->xfer_buff);
- }
- }
-
- if (ep->type == EP_TYPE_ISOC)
- {
- if ((USBx_DEVICE->DSTS & (1U << 8)) == 0U)
- {
- USBx_OUTEP(epnum)->DOEPCTL |= USB_OTG_DOEPCTL_SODDFRM;
- }
- else
- {
- USBx_OUTEP(epnum)->DOEPCTL |= USB_OTG_DOEPCTL_SD0PID_SEVNFRM;
- }
- }
- /* EP 使能*/
- USBx_OUTEP(epnum)->DOEPCTL |= (USB_OTG_DOEPCTL_CNAK | USB_OTG_DOEPCTL_EPENA);
- }
-
- return HAL_OK;
- }

-------------------------------------------------------------------------------------
在HAL库USB接口CDC模式的函数中,收发功能函数到底层寄存器的操作中间嵌套了很多层。在实际使用过程中,我们并不需要关心中间的过程,仅需调用usbd_cdc_if.c文件中的相关函数就能实现数据的收发。
- static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len)
-
- uint8_t CDC_Transmit_HS(uint8_t* Buf, uint16_t Len)
例如:对接收函数稍作改变,就能实现将收到的数据通过USB发送回去,同时通过串口将数据打印。
- static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len)
- {
- /* USER CODE BEGIN 11 */
- int i;
- uint8_t my_RxBuf[100];
- uint32_t my_RxLength;
-
- memcpy(my_RxBuf,Buf,*Len);
- my_RxLength=*Len;
- CDC_Transmit_HS(my_RxBuf, my_RxLength);
- for(i=0;i<my_RxLength;i++)
- {printf("%02x ",my_RxBuf[i]);}
-
- USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]);
- USBD_CDC_ReceivePacket(&hUsbDeviceHS);
- return (USBD_OK);
- /* USER CODE END 11 */
- }

结果如下:左为USB发送和接收,右为串口打印。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。