上一篇文章已经详细讲解了如何在STM32平台上实现一个WinUSB免驱设备:
STM32 WinUSB(WCID)免驱高速通信 20M/s(附详细测试)_bingwueyi的博客-CSDN博客_winusb
那现在就在这个工程的基础上改造一下,实现多端点通信,实用性、可玩性、功能扩展性大大增加了!需要说明的是增加端点只会增加功能(数据通道逻辑区分),不会增加传输速率。
USB的接口(Interface)、端点(Endpoint)等概念自行了解一下吧,本文的目的是为了增加功能和区分数据通道,添加了两个端点(Endpoint),下一篇文章将会讲到增加接口,也就是复合设备。
先来张修改对比图,对下面6个文件进行了修改:
直接上代码,以下是对USB HS的修改, FS的配置同理修改即可:
1 修改usbd_cdc.h
1.1 宏定义增中EP3的定义
#define CDC_IN_EP 0x81U /* EP1 for data IN */
#define CDC_OUT_EP 0x01U /* EP1 for data OUT */
#define CDC_CMD_EP 0x82U /* EP2 for CDC commands */
//ADD
#define CDCUSER_IN_EP 0x83U /* EP3 for data IN */
#define CDCUSER_OUT_EP 0x03U /* EP3 for data OUT */
1.2 结构体 USBD_CDC_ItfTypeDef 的Receive定义 修改一下,增加endpoint参数,主要是为了在接收函数中区分是哪个端点的数据:
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, uint8_t ep); //CHANGE
int8_t (* TransmitCplt)(uint8_t *Buf, uint32_t *Len, uint8_t epnum);
} USBD_CDC_ItfTypeDef;
1.3 下面两个函数也增加 endpoint参数:
uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev, uint8_t ep); //CHANGE
uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev, uint8_t ep); //CHANGE
2 修改 usbd_cdc.c
2.1 将 WINUSB_CONFIG_DESC_SIZE 宏的值改为46,该值表示USBD_CDC_CfgHSDesc数组的长度,本文增加了两个端点,第个端点由7个字节表示,所以 32+7+7=46:
#define WINUSB_CONFIG_DESC_SIZE 46 //32 CHANGE
2.2 在数组USBD_CDC_CfgHSDesc中修改EndPoint数量为4,同时增加端点定义:
/*Data class interface descriptor*/
0x09, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */
0x00, /* bInterfaceNumber: Number of Interface, zero based index of this interface */
0x00, /* bAlternateSetting: Alternate setting */
0x04, /* bNumEndpoints: Two endpoints used */ //CHANGE
0xff, /* bInterfaceClass: vendor */
0x00, /* bInterfaceSubClass: */
0x00, /* bInterfaceProtocol: */
0x00, /* iInterface: */
//此处省略一些代码...
//---------------------------------------ADD--------------------------------------------------
/*Endpoint OUT Descriptor */
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDCUSER_OUT_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE),
0x00, /* bInterval: ignore for Bulk transfer */
/* Endpoint IN Descriptor */
0x07, /* bLength: Endpoint Descriptor size */
USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */
CDCUSER_IN_EP, /* bEndpointAddress */
0x02, /* bmAttributes: Bulk */
LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), /* wMaxPacketSize: */
HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE),
0x00 /* bInterval: ignore for Bulk transfer */
//---------------------------------------------------------------------------------------------
2.3 在 USBD_CDC_Init 函数中增加端点初始化(省略了一些代码,找准位置哦):
//--------------------ADD--------------------------------
/* Open USER EP IN */
(void)USBD_LL_OpenEP(pdev, CDCUSER_IN_EP, USBD_EP_TYPE_BULK,
CDC_DATA_HS_IN_PACKET_SIZE);
pdev->ep_in[CDCUSER_IN_EP & 0xFU].is_used = 1U;
/* Open USER EP OUT */
(void)USBD_LL_OpenEP(pdev, CDCUSER_OUT_EP, USBD_EP_TYPE_BULK,
CDC_DATA_HS_OUT_PACKET_SIZE);
pdev->ep_out[CDCUSER_OUT_EP & 0xFU].is_used = 1U;
//省略
(void)USBD_LL_PrepareReceive(pdev, CDCUSER_OUT_EP, hcdc->RxBuffer,
CDC_DATA_HS_OUT_PACKET_SIZE);
//-----------------------------------------------------------
2.4 在 USBD_CDC_DeInit 函数中增加:
//----------------------ADD----------------------
/* Close EP IN */
(void)USBD_LL_CloseEP(pdev, CDCUSER_IN_EP);
pdev->ep_in[CDCUSER_IN_EP & 0xFU].is_used = 0U;
/* Close EP OUT */
(void)USBD_LL_CloseEP(pdev, CDCUSER_OUT_EP);
pdev->ep_out[CDCUSER_OUT_EP & 0xFU].is_used = 0U;
//--------------------------------------------------
2.5 在 USBD_CDC_DataOut 函数中修改Receive,传入ep参数:
((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(hcdc->RxBuffer, &hcdc->RxLength, epnum); //CHANGE
同样的,下面两个函数也做相应的修改,传入ep参数:
uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev, uint8_t ep) //CHANGE
{
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData;
USBD_StatusTypeDef ret = USBD_BUSY;
if (pdev->pClassData == NULL)
{
return (uint8_t)USBD_FAIL;
}
if (hcdc->TxState == 0U)
{
/* Tx Transfer in progress */
hcdc->TxState = 1U;
/* Update the packet total length */
pdev->ep_in[ep & 0xFU].total_length = hcdc->TxLength; //CHANGE
/* Transmit next packet */
(void)USBD_LL_Transmit(pdev, ep, hcdc->TxBuffer, hcdc->TxLength); //CHANGE
ret = USBD_OK;
}
return (uint8_t)ret;
}
uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev, uint8_t ep) //CHANGE
{
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData;
if (pdev->pClassData == NULL)
{
return (uint8_t)USBD_FAIL;
}
if (pdev->dev_speed == USBD_SPEED_HIGH)
{
/* Prepare Out endpoint to receive next packet */
(void)USBD_LL_PrepareReceive(pdev, ep, hcdc->RxBuffer, //CHANGE
CDC_DATA_HS_OUT_PACKET_SIZE);
}
else
{
/* Prepare Out endpoint to receive next packet */
(void)USBD_LL_PrepareReceive(pdev, ep, hcdc->RxBuffer, //CHANGE
CDC_DATA_FS_OUT_PACKET_SIZE);
}
return (uint8_t)USBD_OK;
}
3 修改 usbd_cdc_if.c
3.1 修改 CDC_Receive_HS 函数定义:
static int8_t CDC_Receive_HS(uint8_t* pbuf, uint32_t *Len, uint8_t ep); //CHANGE
3.2 增加接收缓冲区和修改收发函数:
//-----------------ADD GUOXUAN-----------------------
extern volatile int8_t usb_ep1_rxne;
extern uint8_t usb_ep1_rx[2048];
extern volatile int8_t usb_ep3_rxne;
extern uint8_t usb_ep3_rx[2048];
//---------------------------------------------------
static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len, uint8_t ep) //CHANGE
{
/* USER CODE BEGIN 11 */
//-------------ADD GUOXUAN--------------------
switch(ep)
{
case CDC_OUT_EP:
memcpy(usb_ep1_rx, Buf, *Len);
usb_ep1_rxne = SET;
break;
case CDCUSER_OUT_EP:
memcpy(usb_ep3_rx, Buf, *Len);
usb_ep3_rxne = SET;
break;
default:
break;
}
//--------------------------------------------
USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceHS, ep); //CHANGE
return (USBD_OK);
/* USER CODE END 11 */
}
uint8_t CDC_Transmit_HS(uint8_t* Buf, uint16_t Len, uint8_t ep) //CHANGE
{
uint8_t result = USBD_OK;
/* USER CODE BEGIN 12 */
USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceHS.pClassData;
if (hcdc->TxState != 0){
return USBD_BUSY;
}
USBD_CDC_SetTxBuffer(&hUsbDeviceHS, Buf, Len); //todo:
result = USBD_CDC_TransmitPacket(&hUsbDeviceHS, ep); //CHANGE
/* USER CODE END 12 */
return result;
}
4 修改 usbd_cdc_if.h
4.1 函数 CDC_Transmit_HS 增加ep参数:
uint8_t CDC_Transmit_HS(uint8_t* Buf, uint16_t Len, uint8_t ep); //CHANGE
5 修改 usbd_conf.c
5.1 在 USBD_LL_Init 函数尾部增加txFifo初始化:
HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_HS, 0x200);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 0, 0x80);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 1, 0x174);
HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 3, 0x174); //ADD
6 最后在main.c里增加收发控制:
//--------------ADD GUOXUAN-----------------------
uint8_t usb_ep1_rx[2048];
uint8_t usb_ep1_tx[40960];
uint8_t usb_ep3_rx[2048];
uint8_t usb_ep3_tx[40960];
volatile int8_t usb_ep1_rxne = RESET;
volatile int8_t usb_ep3_rxne = RESET;
extern USBD_HandleTypeDef hUsbDeviceHS;
//-----------------------------------------------
//------------ADD GUOXUAN-------------------
void WinUSB_Receive_HS()
{
uint16_t i = 0;
if (usb_ep1_rxne == SET)
{
i = usb_ep1_rx[0] + (usb_ep1_rx[1]<<8);
CDC_Transmit_HS(usb_ep1_tx, i, CDC_IN_EP);
usb_ep1_rxne = RESET;
}
else if(usb_ep3_rxne == SET)
{
i = usb_ep3_rx[0] + (usb_ep3_rx[1]<<8);
CDC_Transmit_HS(usb_ep3_tx, i, CDCUSER_IN_EP);
usb_ep3_rxne = RESET;
}
}
//------------------------------------------
至此,修改已全部完成,下面进行测试。
在WIN10平台,使用VS2015创建C++工程,测试方法同上一篇文章,上位机发送一个64字节的包,前两字节表示要回复的长度,下位机收到后回复一个相应长度的包,重复收发1000次统计速率,按下数字选择包大小,回车即对两个端点进行测试,结果如下图所示(未使用多线程):
上位机原码下载: