STM32 USB HID 自定义设备 bulk 传输

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 可以下载。

你可能感兴趣的:(usb,stm32,libusb)