USB复合设备(Keyboard+joystick)

本人想要做一个USB设备,同时支持keyboard和joystick功能。之前有试过直接两段描述符拼接。PC能识别,但是用来玩某些游戏的时候Joystick会无法识别出来。所以只能改做成USB复合设备,经过查阅资料。总算是做出来了,STM32CubeMX生成代码就直接跳过了。
HAL库生成代码后要做如下调整:

修改usbd_customhid.h文件中的发送与接收长度为64

#define CUSTOM_HID_EPIN_SIZE                 0x40U
#define HID_EPIN_SIZE                 		   0x40U

需要发送报告时包括发送函数头文件以及声明一个外部定义

#include "usbd_customhid.h" //包括发送函数头文件
extern USBD_HandleTypeDef hUsbDeviceFS; //外部声明USB发送函数

在usbd_custom_hid_if.c文件中修改如下

__ALIGN_BEGIN static uint8_t CUSTOM_HID_ReportDesc_FS[USBD_CUSTOM_HID_REPORT_DESC_SIZE] __ALIGN_END =
{
  /* USER CODE BEGIN 0 */
  0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
  0x09, 0x04,                    // USAGE (Joystick)
  0xa1, 0x01,                    // COLLECTION (Application)
    0x85, 0x02,                    // REPORT_ID (2)
		0x05, 0x09,                    // USAGE_PAGE (Button)
    0x19, 0x01,                    // USAGE_MINIMUM (Button 1)
    0x29, 0x20,                    // USAGE_MAXIMUM (Button 32)
    0x15, 0x00,                    // LOGICAL_MINIMUM (0)
    0x25, 0x01,                    // LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    // REPORT_SIZE (1)
    0x95, 0x20,                    // REPORT_COUNT (32)
    0x81, 0x02,                    // INPUT (Data,Var,Abs)	
		0x05, 0x01,                    //   USAGE_PAGE (Generic Desktop)
    0x09, 0x33,                    // USAGE (Rx)
    0x15, 0x00,                    // LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              // LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    // REPORT_SIZE (8)
    0x95, 0x01,                    // REPORT_COUNT (1)
    0x81, 0x02,                    // INPUT (Data,Var,Abs)
  /* USER CODE END 0 */
  0xC0    /*     END_COLLECTION	             */
};

接收函数是中断接收的,建立工程时已经默认开启了。在usbd_custom_hid_if.c文件中找到 static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state) 函数,当USB接收完数据后,就会进入到这个函数,我们可以在这里添加自定义接收函数ReceiveDispose();

unsigned char USB_Recive_Buffer[64]; //USB接收缓存
unsigned char USB_Received_Count = 0;//USB接收数据计数

void ReceiveDispose()
{
USBD_CUSTOM_HID_HandleTypeDef   *hhid; //定义一个指向USBD_CUSTOM_HID_HandleTypeDef结构体的指针
  hhid = (USBD_CUSTOM_HID_HandleTypeDef*)hUsbDeviceFS.pClassData;//得到USB接收数据的储存地址
    
    for(i=0;i<64;i++) 
    {
        USB_Recive_Buffer[i]=hhid->Report_buf[i];  //Report_buf[i]为USB的接收缓存区
    } 
}
/**
  * @brief  Manage the CUSTOM HID class events
  * @param  event_idx: Event index
  * @param  state: Event state
  * @retval USBD_OK if all operations are OK else USBD_FAIL
  */
static int8_t CUSTOM_HID_OutEvent_FS(uint8_t event_idx, uint8_t state)
{
  /* USER CODE BEGIN 6 */
  ReceiveDispose();
  return (USBD_OK);
  /* USER CODE END 6 */
}

以上就完成了一个通用USB设备的基础代码了。

接下来需要加入一个interface,组成USB复合设备。在usbd_customhid.c文件中找到如下定义

__ALIGN_BEGIN static uint8_t USBD_CUSTOM_HID_CfgFSDesc[USB_CUSTOM_HID_CONFIG_DESC_SIZ] __ALIGN_END =
{
    0x09, /* bLength: Configuration Descriptor size */
    USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
    USB_CUSTOM_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*/
    0xC0,         /*bmAttributes: bus powered */
    0x32,         /*MaxPower 100 mA: this current is used for detecting Vbus*/

    /************** Descriptor of CUSTOM HID interface ****************/
    /* 09 */
    0x09,         /*bLength: Interface Descriptor size*/
    USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
    0x00,         /*bInterfaceNumber: Number of Interface*/
    0x00,         /*bAlternateSetting: Alternate setting*/
    0x02,         /*bNumEndpoints*/
    0x03,         /*bInterfaceClass: CUSTOM_HID*/
    0x00,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
    0x00,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
    0,            /*iInterface: Index of string descriptor*/
    /******************** Descriptor of CUSTOM_HID *************************/
    /* 18 */
    0x09,         /*bLength: CUSTOM_HID Descriptor size*/
    CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: CUSTOM_HID*/
    0x11,         /*bCUSTOM_HIDUSTOM_HID: CUSTOM_HID Class Spec release number*/
    0x01,
    0x00,         /*bCountryCode: Hardware target country*/
    0x01,         /*bNumDescriptors: Number of CUSTOM_HID class descriptors to follow*/
    0x22,         /*bDescriptorType*/
    USBD_CUSTOM_HID_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
    0x00,
    /******************** Descriptor of Custom HID endpoints ********************/
    /* 27 */
    0x07,          /*bLength: Endpoint Descriptor size*/
    USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/

    CUSTOM_HID_EPIN_ADDR,     /*bEndpointAddress: Endpoint Address (IN)*/
    0x03,          /*bmAttributes: Interrupt endpoint*/
    CUSTOM_HID_EPIN_SIZE, /*wMaxPacketSize: 2 Byte max */
    0x00,
    CUSTOM_HID_FS_BINTERVAL,          /*bInterval: Polling Interval */
    /* 34 */

    0x07,          /* bLength: Endpoint Descriptor size */
    USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: */
    CUSTOM_HID_EPOUT_ADDR,  /*bEndpointAddress: Endpoint Address (OUT)*/
    0x03, /* bmAttributes: Interrupt endpoint */
    CUSTOM_HID_EPOUT_SIZE,  /* wMaxPacketSize: 2 Bytes max  */
    0x00,
    CUSTOM_HID_FS_BINTERVAL,  /* bInterval: Polling Interval */
    /* 41 */
};

其中

 0x01,         /*bNumInterfaces: 1 interface*/

修改为

0x02,         /*bNumInterfaces: 2 interface*/

修改后的完整代码为

/* USB CUSTOM_HID device FS Configuration Descriptor */
__ALIGN_BEGIN static uint8_t USBD_CUSTOM_HID_CfgFSDesc[USB_CUSTOM_HID_CONFIG_DESC_SIZ] __ALIGN_END =
{
    0x09, /* bLength: Configuration Descriptor size */
    USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */
    USB_CUSTOM_HID_CONFIG_DESC_SIZ,
    /* wTotalLength: Bytes returned */
    0x00,
    0x02,         /*bNumInterfaces: 2 interface*/
    0x01,         /*bConfigurationValue: Configuration value*/
    0x00,         /*iConfiguration: Index of string descriptor describing
  the configuration*/
    0xC0,         /*bmAttributes: bus powered */
    0x32,         /*MaxPower 100 mA: this current is used for detecting Vbus*/

	
	
    /************** Descriptor of CUSTOM HID interface ****************/
    /* 09 */
    0x09,         /*bLength: Interface Descriptor size*/
    USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
    0x00,         /*bInterfaceNumber: Number of Interface*/
    0x00,         /*bAlternateSetting: Alternate setting*/
    0x02,         /*bNumEndpoints*/
    0x03,         /*bInterfaceClass: CUSTOM_HID*/
    0x00,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
    0x00,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
    0,            /*iInterface: Index of string descriptor*/
    /******************** Descriptor of CUSTOM_HID *************************/
    /* 18 */
    0x09,         /*bLength: CUSTOM_HID Descriptor size*/
    CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: CUSTOM_HID*/
    0x11,         /*bCUSTOM_HIDUSTOM_HID: CUSTOM_HID Class Spec release number*/
    0x01,
    0x00,         /*bCountryCode: Hardware target country*/
    0x01,         /*bNumDescriptors: Number of CUSTOM_HID class descriptors to follow*/
    0x22,         /*bDescriptorType*/
    USBD_CUSTOM_HID_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
    0x00,
    /******************** Descriptor of Custom HID endpoints ********************/
    /* 27 */
    0x07,          /*bLength: Endpoint Descriptor size*/
    USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/

    CUSTOM_HID_EPIN_ADDR,     /*bEndpointAddress: Endpoint Address (IN)*/
    0x03,          /*bmAttributes: Interrupt endpoint*/
    CUSTOM_HID_EPIN_SIZE, /*wMaxPacketSize: 2 Byte max */
    0x00,
    CUSTOM_HID_FS_BINTERVAL,          /*bInterval: Polling Interval */
    /* 34 */

    0x07,          /* bLength: Endpoint Descriptor size */
    USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: */
    CUSTOM_HID_EPOUT_ADDR,  /*bEndpointAddress: Endpoint Address (OUT)*/
    0x03, /* bmAttributes: Interrupt endpoint */
    CUSTOM_HID_EPOUT_SIZE,  /* wMaxPacketSize: 2 Bytes max  */
    0x00,
    CUSTOM_HID_FS_BINTERVAL,  /* bInterval: Polling Interval */
    /* 41 */



    /************** Descriptor of Joystick Keyboard interface ****************/
    0x09,         /*bLength: Interface Descriptor size*/
    USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/
    0x01,         /*bInterfaceNumber: Number of Interface*/
    0x00,         /*bAlternateSetting: Alternate setting*/
    0x01,         /*bNumEndpoints*/
    0x03,         /*bInterfaceClass: HID*/
    0x01,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
    0x01,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
    0,            /*iInterface: Index of string descriptor*/
    /******************** Descriptor of Joystick Keyboard HID ********************/
    /* 50 */
    0x09,         /*bLength: HID Descriptor size*/
    CUSTOM_HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
    0x11,         /*bcdHID: HID Class Spec release number*/
    0x01,
    0x00,         /*bCountryCode: Hardware target country*/
    0x01,         /*bNumDescriptors: Number of HID class descriptors to follow*/
    0x22,         /*bDescriptorType*/
    HID_KEYBOARD_REPORT_DESC_SIZE,/*wItemLength: Total length of Report descriptor*/
    0x00,
    /******************** Descriptor of Keyboard endpoint ********************/
    /* 59 */
    0x07,          /*bLength: Endpoint Descriptor size*/
    USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/
    HID_EPIN_ADDR,     /*bEndpointAddress: Endpoint Address (IN)*/
    0x03,          /*bmAttributes: Interrupt endpoint*/
    HID_EPIN_SIZE, /*wMaxPacketSize: 8 Byte max */
    0x00,
    CUSTOM_HID_FS_BINTERVAL,          /*bInterval: Polling Interval (10 ms)*/
    /* 66 */
};

这里的/* 数字 */ 是用来记录当前数组有多大的。可以看到原来这个数组大小为USB_CUSTOM_HID_CONFIG_DESC_SIZ,go to definition过去可以看到是

#define USB_CUSTOM_HID_CONFIG_DESC_SIZ       41U

现在在结尾添加了一个接口的信息,总大小已经是66了,修改为66

#define USB_CUSTOM_HID_CONFIG_DESC_SIZ       66U

些宏定义是没有的,我们到usbd_conf.h文件夹中添加如下:

#define HID_KEYBOARD_REPORT_DESC_SIZE    63

在usbd_custom.h中添加端点宏定义如下:

#define HID_EPIN_ADDR                 0x82U
#define HID_EPIN_SIZE                 0x08U

我们增加了一个0X82这个端点,所以我们需要在初始化函数中添加对其的初始化。打开usbd_customhid.c文件,可以找到
static uint8_t USBD_CUSTOM_HID_Init(USBD_HandleTypeDef *pdev,uint8_t cfgidx)的函数,需要添加初始化端点代码

  USBD_LL_OpenEP(pdev, HID_EPIN_ADDR, USBD_EP_TYPE_INTR,  //KEYBOARD
                   HID_EPIN_SIZE);

完整代码如下

/**
  * @brief  USBD_CUSTOM_HID_Init
  *         Initialize the CUSTOM_HID interface
  * @param  pdev: device instance
  * @param  cfgidx: Configuration index
  * @retval status
  */
static uint8_t  USBD_CUSTOM_HID_Init(USBD_HandleTypeDef *pdev,
                                     uint8_t cfgidx)
{
    uint8_t ret = 0U;
    USBD_CUSTOM_HID_HandleTypeDef     *hhid;

    /* Open EP IN */
    USBD_LL_OpenEP(pdev, CUSTOM_HID_EPIN_ADDR, USBD_EP_TYPE_INTR,
                   CUSTOM_HID_EPIN_SIZE);
    USBD_LL_OpenEP(pdev, HID_EPIN_ADDR, USBD_EP_TYPE_INTR,  //KEYBOARD
                   HID_EPIN_SIZE);

    pdev->ep_in[CUSTOM_HID_EPIN_ADDR & 0xFU].is_used = 1U;

    /* Open EP OUT */
    USBD_LL_OpenEP(pdev, CUSTOM_HID_EPOUT_ADDR, USBD_EP_TYPE_INTR,
                   CUSTOM_HID_EPOUT_SIZE);

    pdev->ep_out[CUSTOM_HID_EPOUT_ADDR & 0xFU].is_used = 1U;

    pdev->pClassData = USBD_malloc(sizeof(USBD_CUSTOM_HID_HandleTypeDef));

    if (pdev->pClassData == NULL)
    {
        ret = 1U;
    }
    else
    {
        hhid = (USBD_CUSTOM_HID_HandleTypeDef *) pdev->pClassData;

        hhid->state = CUSTOM_HID_IDLE;
        ((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->Init();

        /* Prepare Out endpoint to receive 1st packet */
        USBD_LL_PrepareReceive(pdev, CUSTOM_HID_EPOUT_ADDR, hhid->Report_buf,
                               USBD_CUSTOMHID_OUTREPORT_BUF_SIZE);
    }
    return ret;
}

我们在usbd_customhid.c文件中(在__ALIGN_BEGIN static uint8_t USBD_CUSTOM_HID_CfgFSDesc[USB_CUSTOM_HID_CONFIG_DESC_SIZ] __ALIGN_END之前)添加一个键盘的报表

__ALIGN_BEGIN static uint8_t HID_KEYBOARD_ReportDesc[HID_KEYBOARD_REPORT_DESC_SIZE]  __ALIGN_END =
{
    0x05, 0x01,// USAGE_PAGE (Generic Desktop)
    0x09, 0x06,// USAGE (Keyboard)
    0xa1, 0x01,// COLLECTION (Application)
    0x05, 0x07,// USAGE_PAGE (Keyboard)
    0x19, 0xe0,// USAGE_MINIMUM (Keyboard LeftControl)
    0x29, 0xe7,// USAGE_MAXIMUM (Keyboard Right GUI)
    0x15, 0x00,// LOGICAL_MINIMUM (0)
    0x25, 0x01,// LOGICAL_MAXIMUM (1)
    0x75, 0x01,// REPORT_SIZE (1)
    0x95, 0x08,// REPORT_COUNT (8)
    0x81, 0x02,// INPUT (Data,Var,Abs)
    0x95, 0x01,// REPORT_COUNT (1)
    0x75, 0x08,// REPORT_SIZE (8)
    0x81, 0x03,// INPUT (Cnst,Var,Abs)
    0x95, 0x05,// REPORT_COUNT (5)
    0x75, 0x01,// REPORT_SIZE (1)
    0x05, 0x08,// USAGE_PAGE (LEDs)
    0x19, 0x01,// USAGE_MINIMUM (Num Lock)
    0x29, 0x05,// USAGE_MAXIMUM (Kana)
    0x91, 0x02,// OUTPUT (Data,Var,Abs)
    0x95, 0x01,// REPORT_COUNT (1)
    0x75, 0x03,// REPORT_SIZE (3)
    0x91, 0x03,// OUTPUT (Cnst,Var,Abs)
    0x95, 0x06,// REPORT_COUNT (6)
    0x75, 0x08,// REPORT_SIZE (8)
    0x15, 0x00,// LOGICAL_MINIMUM (0)
    0x25, 0xFF,// LOGICAL_MAXIMUM (255)
    0x05, 0x07,// USAGE_PAGE (Keyboard)
    0x19, 0x00,// USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0x65,// USAGE_MAXIMUM (Keyboard Application)
    0x81, 0x00,// INPUT (Data,Ary,Abs)
    0xc0
};

还是在usbd_customhid.c文件中我们可以找到
static uint8_t USBD_CUSTOM_HID_Setup(USBD_HandleTypeDef *pdev,USBD_SetupReqTypedef *req)函数,在case USB_REQ_TYPE_STANDARD:下的case USB_REQ_GET_DESCRIPTOR:下 有如下代码

ase USB_REQ_GET_DESCRIPTOR:
          if (req->wValue >> 8 == CUSTOM_HID_REPORT_DESC)
          {
            len = MIN(USBD_CUSTOM_HID_REPORT_DESC_SIZE, req->wLength);
            pbuf = ((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->pReport;
          }
          else
          {
            if (req->wValue >> 8 == CUSTOM_HID_DESCRIPTOR_TYPE)
            {
              pbuf = USBD_CUSTOM_HID_Desc;
              len = MIN(USB_CUSTOM_HID_DESC_SIZ, req->wLength);
            }
          }

把其中第一个if里面的东西修改为:

if(req->wIndex == 0)                             //  如果是接口0 还是用以前的报表
{
    len = MIN(USBD_CUSTOM_HID_REPORT_DESC_SIZE, req->wLength);
    pbuf = ((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->pReport;
}
if(req->wIndex == 1)                             // 如果是接口1  则使用 键盘报表
{
    len = MIN(HID_KEYBOARD_REPORT_DESC_SIZE, req->wLength);
    pbuf = HID_KEYBOARD_ReportDesc;
}

写一个键盘的发送函数。我们在usbd_customhid.c中写出如下函数
注意在头文件中声明,方便引用

uint8_t USBD_KEYBOADR_HID_SendReport(USBD_HandleTypeDef  *pdev,
                                   uint8_t *report,
                                   uint16_t len)
{
  USBD_CUSTOM_HID_HandleTypeDef     *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)pdev->pClassData;
  
  if (pdev->dev_state == USBD_STATE_CONFIGURED)
  {
    if (hhid->state == CUSTOM_HID_IDLE)
    {
      hhid->state = CUSTOM_HID_BUSY;
      USBD_LL_Transmit(pdev, HID_EPIN_ADDR, report, len);
    }
    else
    {
      return USBD_BUSY;
    }
  }
  return USBD_OK;
}

发送函数的调用

uint8_t KEYBOARD_Buffer[8];

USBD_KEYBOADR_HID_SendReport(&hUsbDeviceFS, KEYBOARD_Buffer,8)


此方法为每个interface用独立的端点,有空再整整多interface共用一对input output端点试试
大概先这样吧,有空再整理一下!

  • 9
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值