关于STM32的USB复合设备CDC+MSC中CDC发送问题的解决方法
关于如何复合CDC+MSC已经有很多文章教程了,但一般都会有个问题,复合出来的虚拟串口只能接收不能发送,或者说只能在接收的时候发送。
为啥什么会出现这样的问题呢,教程中的示例代码是这样的,
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
CDC_Transmit_FS(Buf, *Len); /* +++lakun:将收到的数据在发回去 */
usb_rx_len = *Len;
memcpy(usb_rx_buf, Buf, usb_rx_len);
return (USBD_OK);
/* USER CODE END 6 */
}
这时候的收发是没问题,因为在接收时触发了USB的中断
我们到usbd_cdc.c看实例化的对象
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,
};
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);
#if (USBD_SUPPORT_USER_STRING_DESC == 1U)
uint8_t *(*GetUsrStrDescriptor)(struct _USBD_HandleTypeDef *pdev, uint8_t index, uint16_t *length);
#endif
} USBD_ClassTypeDef;
其中有一个函数 uint8_t (*EP0_RxReady)(struct _USBD_HandleTypeDef *pdev),从字面理解就是接收准备,而uint8_t (*EP0_TxSent)(struct _USBD_HandleTypeDef *pdev)却填了NULL,但单独使用CDC而不是复合CDC+MSC时也能正常收发,说明跟EP0_TxSent无关。
跟踪一下发现EP0_RxReady在USBD_LL_DataOutStage调用了:
USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev,
uint8_t epnum, uint8_t *pdata)
{
USBD_EndpointTypeDef *pep;
if (epnum == 0U)
{
pep = &pdev->ep_out[0];
if (pdev->ep0_state == USBD_EP0_DATA_OUT)
{
if (pep->rem_length > pep->maxpacket)
{
pep->rem_length -= pep->maxpacket;
USBD_CtlContinueRx(pdev, pdata,
(uint16_t)MIN(pep->rem_length, pep->maxpacket));
}
else
{
if ((pdev->pClass->EP0_RxReady != NULL) &&
(pdev->dev_state == USBD_STATE_CONFIGURED))
{
pdev->pClass->EP0_RxReady(pdev); // 这里调用了
}
USBD_CtlSendStatus(pdev);
}
}
else
{
if (pdev->ep0_state == USBD_EP0_STATUS_OUT)
{
/*
* STATUS PHASE completed, update ep0_state to idle
*/
pdev->ep0_state = USBD_EP0_IDLE;
USBD_LL_StallEP(pdev, 0U);
}
}
}
else if ((pdev->pClass->DataOut != NULL) &&
(pdev->dev_state == USBD_STATE_CONFIGURED))
{
pdev->pClass->DataOut(pdev, epnum);
}
else
{
/* should never be in this condition */
return USBD_FAIL;
}
return USBD_OK;
}
接着再看看USBD_LL_DataOutStage的调用
void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum)
#endif /* USE_HAL_PCD_REGISTER_CALLBACKS */
{
USBD_LL_DataOutStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->OUT_ep[epnum].xfer_buff);
}
已经发现Callback了,继续跟踪会发现在
static HAL_StatusTypeDef PCD_EP_ISR_Handler(PCD_HandleTypeDef *hpcd)调用了HAL_PCD_DataOutStageCallback,继续往上
void HAL_PCD_IRQHandler(PCD_HandleTypeDef *hpcd)
{
uint16_t store_ep[8];
uint8_t i;
if (__HAL_PCD_GET_FLAG(hpcd, USB_ISTR_CTR))
{
/* servicing of the endpoint correct transfer interrupt */
/* clear of the CTR flag into the sub */
(void)PCD_EP_ISR_Handler(hpcd);
}
void USB_HP_CAN1_TX_IRQHandler(void)
{
/* USER CODE BEGIN USB_HP_CAN1_TX_IRQn 0 */
/* USER CODE END USB_HP_CAN1_TX_IRQn 0 */
HAL_PCD_IRQHandler(&hpcd_USB_FS);
/* USER CODE BEGIN USB_HP_CAN1_TX_IRQn 1 */
/* USER CODE END USB_HP_CAN1_TX_IRQn 1 */
}
/**
* @brief This function handles USB low priority or CAN RX0 interrupts.
*/
void USB_LP_CAN1_RX0_IRQHandler(void)
{
/* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 0 */
/* USER CODE END USB_LP_CAN1_RX0_IRQn 0 */
HAL_PCD_IRQHandler(&hpcd_USB_FS);
/* USER CODE BEGIN USB_LP_CAN1_RX0_IRQn 1 */
/* USER CODE END USB_LP_CAN1_RX0_IRQn 1 */
}
所以可以确定uint8_t (*EP0_RxReady)(struct _USBD_HandleTypeDef *pdev)就是产生收发中断时会调用的函数,既然硬件触发中断有问题,那我们就手动触发。
传进HAL_PCD_DataOutStageCallback的hpcd(即全局变量hpcd_USB_FS)是PCD_HandleTypeDef类型,然后在USBD_LL_DataOutStage((USBD_HandleTypeDef*)hpcd->pData, epnum, hpcd->OUT_ep[epnum].xfer_buff)做了强转(USBD_HandleTypeDef*)hpcd->pData,然后调用pdev->pClass->EP0_RxReady(pdev)。
那我们就在使用发送函数前调用一次
((USBD_HandleTypeDef*)(hpcd_USB_FS.pData))->pClass->EP0_RxReady(((USBD_HandleTypeDef*)(hpcd_USB_FS.pData)))
如:
((USBD_HandleTypeDef*)(hpcd_USB_FS.pData))->pClass->EP0_RxReady(((USBD_HandleTypeDef*)(hpcd_USB_FS.pData)));
CDC_Transmit_FS(buf, len);
问题解决。
3416

被折叠的 条评论
为什么被折叠?



