STM32 USB HID 自定义设备 bulk 传输

时间:2022-04-05 16:08:12

STM32 USB HID 自定义设备 bulk 传输


ST(意法半导体公司)为STM32系列处理器编写了外设USB的库,并提供了很好的参考例程,本文就是参考ST提供的例程,在STM32F4 discovery板子上实现usb bulk传输。Host端是在linux平台上利用libusb库函数写的读写USB应用。

本次实现在STM32 USB例程中的Device HID 鼠标例程基础上添加bulk传输端点修改而来。

usb_conf.h 文件中添加 bulk传输端点

/*
* endpoint 0x80 and 0x00 are used for enumerating device.
* endpoint 0x81 and 0x80 are used for control xfer.
*/

#define HID_IN_EP 0x81
#define HID_OUT_EP 0x01
#define HID_IN_PACKET 4
#define HID_OUT_PACKET 4

// add bulk xfer endpoint
#define HID_IN_BULK_EP 0x82 // endpoint in
#define HID_OUT_BULK_EP 0x02 // endpoint out
// define endpoint max packet size
#define HID_IN_BULK_PACKET 64
#define HID_OUT_BULK_PACKET 64

usb_desc.c 中修改设备描述符

把bDeviceClass值改为0xFF,表示用户自定义设备;修改VID 和 PID,例如以下程序中的值。

__ALIGN_BEGIN uint8_t USBD_DeviceDesc[USB_SIZ_DEVICE_DESC] __ALIGN_END =
{
// the size of device descriptor
0x12, /*bLength */

// descriptor type. device descripor type is 0x01
USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/

// we use usb2.0
0x00, /*bcdUSB */
0x02,

// user defined device
0xff, /*bDeviceClass*/
0x00, /*bDeviceSubClass*/

0x00, /*bDeviceProtocol*/

// endpoint 0 max packet size
USB_OTG_MAX_EP0_SIZE, /*bMaxPacketSize*/

// vendor ID
LOBYTE(USBD_VID), /*idVendor*/
HIBYTE(USBD_VID), /*idVendor*/

// Product ID
LOBYTE(USBD_PID), /*idVendor*/
HIBYTE(USBD_PID), /*idVendor*/

// Device version
0x00, /*bcdDevice rel. 2.00*/
0x02,
USBD_IDX_MFC_STR, /*Index of manufacturer string*/
USBD_IDX_PRODUCT_STR, /*Index of product string*/
USBD_IDX_SERIAL_STR, /*Index of serial number string*/

// the number of configurations 0x01
USBD_CFG_MAX_NUM /*bNumConfigurations*/
} ; /* USB_DeviceDescriptor */

修改配置描述符

在原有的鼠标配置描述符中添加bulk xfer的两个端点描述符(IN and OUT)。最后, 记得修改配置描述符的长度,不然配置描述符传输不完整。

在usbd_hid_core.h 文件中修改配置描述符的大小 USB_HID_CONFIG_DESC_SIZ

        #define USB_HID_CONFIG_DESC_SIZ 46

配置描述符中的接口描述符也要做相应的调整,将端点数目 bNumEndpoints 改为4个,将接口子类bInterfaceSubClass改成 0x00, 将接口协议改成0x00。nInterfaceProtocol 修改成0x00。具体描述见下面代码中的注释。

__ALIGN_BEGIN static uint8_t USBD_HID_CfgDesc[USB_HID_CONFIG_DESC_SIZ] __ALIGN_END =
{
0x09, /* bLength: Configuration Descriptor size */
USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
USB_HID_CONFIG_DESC_SIZ,
/* wTotalLength: Bytes returned */
0x00,
0x01, /*bNumInterfaces: 1 interface*/
0x01, /*bConfigurationValue: Configuration value*/
0x00, /*iConfiguration: Index of string descriptor describing
the configuration*/

0xE0, /*bmAttributes: bus powered and Support Remote Wake-up */
0x32, /*MaxPower 100 mA: this current is used for detecting Vbus*/

/************** Descriptor of Joystick Mouse interface ****************/
/* 09 */
0x09, /*bLength: Interface Descriptor size*/
USB_INTERFACE_DESCRIPTOR_TYPE,/*bDescriptorType: Interface descriptor type*/
0x00, /*bInterfaceNumber: Number of Interface*/
0x00, /*bAlternateSetting: Alternate setting*/
0x04, /*bNumEndpoints*/
0x00, /*bInterfaceClass: HID*/
0x00, /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
0x00, /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
0, /*iInterface: Index of string descriptor*/
/******************** Descriptor of Mouse endpoint ********************/
/* 18 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/

HID_IN_EP, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
HID_IN_PACKET, /*wMaxPacketSize: 4 Byte max */
0x00,
0x0A, /*bInterval: Polling Interval (10 ms)*/
/******************** Descriptor of Mouse endpoint ********************/
/* 25 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/

HID_OUT_EP, /*bEndpointAddress: Endpoint Address (IN)*/
0x03, /*bmAttributes: Interrupt endpoint*/
HID_IN_PACKET, /*wMaxPacketSize: 4 Byte max */
0x00,
0x0A, /*bInterval: Polling Interval (10 ms)*/
/******************** Descriptor of bulk xfer endpoint ********************/
/* 32 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/

HID_IN_BULK_EP, /*bEndpointAddress: Endpoint Address (IN)*/
0x02, /*bmAttributes: bulk endpoint*/
HID_IN_BULK_PACKET, /*wMaxPacketSize: 64 Byte max */
0x00,
0x0A, /*bInterval: Polling Interval (10 ms)*/
/******************** Descriptor of bulk xfer endpoint ********************/
/* 39 */
0x07, /*bLength: Endpoint Descriptor size*/
USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/

HID_OUT_BULK_EP, /*bEndpointAddress: Endpoint Address (OUT)*/
0x02, /*bmAttributes: bulk endpoint*/
HID_OUT_BULK_PACKET, /*wMaxPacketSize: 64 Byte max */
0x00,
0x0A, /*bInterval: Polling Interval (10 ms)*/
/* 46 */
} ;

至此,描述符的修改就完成了。由于是bulk传输,所以不需要定义报告描述符,只有中断传输和控制传输需要定义报告描述符。接下来需要添加发送和接收数据的接口函数了。

初始化端点

usb_hid_core.c 中,HID 初始化函数中添加对 bulk I/O 端点的初始化,直接追加在鼠标所使用端点后面。在最后又添加了bulk xfer OUT 端口的发送初始化函数,以便让USB知道当接收到数据后数据该存储到哪儿。数组 USB_Rx_Buffer[HID_OUT_BULK_PACKET] 是用户自己定义的一个数据缓冲区,大小设为该端点的最大包长度即可。

static uint8_t  USBD_HID_Init (void  *pdev, 
uint8_t cfgidx)
{
printf("%s\r\n", __FUNCTION__);
/* Open EP IN */
DCD_EP_Open(pdev,
HID_IN_EP,
HID_IN_PACKET,
USB_OTG_EP_INT)
;

/* Open EP OUT */
DCD_EP_Open(pdev,
HID_OUT_EP,
HID_OUT_PACKET,
USB_OTG_EP_INT)
;

/* Open EP IN BULK */
DCD_EP_Open(pdev,
HID_IN_BULK_EP,
HID_IN_BULK_PACKET,
USB_OTG_EP_BULK)
;

/* Open EP OUT BULK*/
DCD_EP_Open(pdev,
HID_OUT_BULK_EP,
HID_OUT_BULK_PACKET,
USB_OTG_EP_BULK)
;

/* Prepare Out endpoint to receive next packet */
DCD_EP_PrepareRx(pdev,
HID_OUT_BULK_EP,
(uint8_t*)
(USB_Rx_Buffer),
HID_OUT_BULK_PACKET);

return USBD_OK;
}

添加 OUT 端点接收HOST发出数据的回调函数。类似地,添加 IN端点回调函数发送数据到HOST

在usb_hid_core.c 中,修改USB_HID_cb 函数数组中的函数指针。

USBD_Class_cb_TypeDef  USBD_HID_cb = 
{
USBD_HID_Init,
USBD_HID_DeInit,
USBD_HID_Setup,
NULL, /*EP0_TxSent*/
NULL, /*EP0_RxReady*/
USBD_HID_DataIn, /* add your IN point callback to send data to HOST */
USBD_HID_DataOut, /* add your OUT point callback to receive data from HOST*/
NULL, /*SOF */
NULL,
NULL,
USBD_HID_GetCfgDesc,
#ifdef USB_OTG_HS_CORE
USBD_HID_GetCfgDesc, /* use same config as per FS */
#endif
};

// USB OUT endpoint callback
static uint8_t USBD_HID_DataOut (void *pdev, uint8_t epnum)
{
uint16_t USB_Rx_Cnt;

USB_Rx_Cnt = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count;

// add your own data to process the received data
/*
* printf_the_received_data((char *)USB_Rx_Buffer, USB_Rx_Cnt);
*/


/* Prepare Out endpoint to receive next packet */
DCD_EP_PrepareRx(pdev,
HID_OUT_BULK_EP,
(uint8_t*)(USB_Rx_Buffer),
HID_OUT_BULK_PACKET);

return USBD_OK;
}

static uint8_t USBD_HID_DataIn(void *pdev, uint8_t epnum)
{
int USB_Tx_length = HID_IN_BULK_PACKET;

// copy your data to buffer, then send through USB IN endpoint
/*
* buffer_copy(APP_Rx_Buffer, USB_Tx_length);
*/

/* Prepare the available data buffer to be sent on IN endpoint */
DCD_EP_Tx (pdev,
CDC_IN_EP,
(uint8_t*)&APP_Rx_Buffer[USB_Tx_ptr],
USB_Tx_length);

return USBD_OK;
}

验证

将USB代码编译后下载到STM32F4Discovery 板子上运行,连接USB线到PC,windows操作系统下在设备管理器中可以看到HID设备,查看VID/PID 或者USB 显示出的Product string和你代码中设定的一致的就是我们的USB 设备。


STM32 USB 上位机软件实现

请查看 STM32 USB 上位机程序实现,利用 libusb 在 linux 平台上 使用 bulk xfer 和 STM32F4Discovery 传输数据。对应的源码在 http://download.csdn.net/detail/chengwenyang/9479835 可以下载。