[STM32F1]基于STM32F103实现Xbox 360 Controller for Windows 之Xinput

上个月由于家里有事,实现了并枚举成功的Xinput,就差数据处理没有完成,请假在家,客户急要,只好请同事改改,玩usb hid已经差不多两年了,至今也算没有实现Xbox 360 Controller for Windows 如下图,一来不知道怎么实现,二来没有画时间去琢磨;年底没啥事做,咱们现在就来实现这样手柄。



下面简单说一下实现步骤,并附上相关的参考资料:首先是实现步骤:
第一步,设计硬件,硬件还是我们之前设计的摇杆板,摇杆板只有一个摇杆和8个按键,但这并不妨碍我们学习Xinput协议。
第二步,设计软件,这里的软件设计最重要的在于USB hid相关的描述符修改。
第三步,下载验证,完成软件设计之后,我们将通过视频给大家展示我们所实现的效果。

接着是参考资料:
https://github.com/nesvera/STM32-X360-xinput
这个是GitHub上面的大佬开源的,大家想要学习,自己下载学习即可。

硬件设计:
首先我们设计一个基于STM32F103C8T6的评估板,原理图如下:

由原理图我们可以知道,我们设计了8颗轻触按键,另有摇杆一个,同时有WS2812B一颗用于装饰。
最终我们设计的成品如下图:



软件设计:我们的软件是基于STM32 HAL库的,HAL库中的USB HID是比较简单的,我们要向实现一个STM32只要通过修改几个描述符,增加几个厂商处理请求即可。

1.修改设备描述符:

__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END =

{

  0x12,                       /*bLength */

  USB_DESC_TYPE_DEVICE,       /*bDescriptorType*/

  0x00,                       /*bcdUSB */

  0x02,

  0x00,                       /*bDeviceClass*/

  0x00,                       /*bDeviceSubClass*/

  0x00,                       /*bDeviceProtocol*/

  USB_MAX_EP0_SIZE,           /*bMaxPacketSize*/

  LOBYTE(USBD_VID),           /*idVendor*/

  HIBYTE(USBD_VID),           /*idVendor*/

  LOBYTE(USBD_PID_FS),        /*idProduct*/

  HIBYTE(USBD_PID_FS),        /*idProduct*/

  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*/

  USBD_MAX_NUM_CONFIGURATION  /*bNumConfigurations*/

};

需要修改的设备描述符中的USBD_VID和USBD_PID_FS,这两个分别是厂商ID和产品ID,厂商ID是USB IF组织分配的,以下是微软的VID以及Xinput的PID
 

复制
#define USBD_VID     0x045E

#define USBD_PID_FS     0x028E

2.修改配置/接口/HID/端点/厂商描述符:

/* USB CUSTOM_HID device FS Configuration Descriptor */

__ALIGN_BEGIN static uint8_t USBD_CUSTOM_HID_CfgFSDesc[USB_CUSTOM_HID_CONFIG_DESC_SIZ] __ALIGN_END =

{

    /************** Configuration Descriptor 1 Bus Powered, 500 mA ****************/

    0x09, /* bLength: Configuration Descriptor size */

    USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */

    USB_CUSTOM_HID_CONFIG_DESC_SIZ,

    /* wTotalLength: Bytes returned */

    0x00,

    0x04,         /*bNumInterfaces: 1 interface*/

    0x01,         /*bConfigurationValue: Configuration value*/

    0x00,         /*iConfiguration: Index of string descriptor describing

  the configuration*/

    0xA0,         /*bmAttributes: Bus Powered, Remote Wakeup*/

    0xFA,         /*MaxPower 500 mA: this current is used for detecting Vbus*/



    /**************Interface Descriptor 0/0 Vendor-Specific, 2 Endpoints****************/

    /* 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*/

    0xFF,         /*Vendor-Specific*/

    0x5D,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/

    0x01,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/

    0x00,            /*iInterface: Index of string descriptor*/

    /**************Unrecognized Class-Specific Descriptor***********************/

    /* 18 */

    0x11,         /*bLength: CUSTOM_HID Descriptor size*/

    0x21, /*bDescriptorType: CUSTOM_HID*/

    0x10,0x01,0x01,0x25,0x81,0x14,0x03,0x03,

    0x03,0x04,0x13,0x02,0x08,0x03,0x03,

    /**************Endpoint Descriptor 81 1 In, Interrupt, 4 ms******************/

    /* 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 */

    /**************Endpoint Descriptor 02 2 Out, Interrupt, 8 ms******************/

    /* 34 */

    0x07,          /*bLength: Endpoint Descriptor size*/

    USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/



    0x02,     /*bEndpointAddress: Endpoint Address (IN)*/

    0x03,          /*bmAttributes: Interrupt endpoint*/

    0x20, /*wMaxPacketSize: 2 Byte max */

    0x00,

    0x08,          /*bInterval: Polling Interval */



    /**************Interface Descriptor 1/0 Vendor-Specific, 2 Endpoints****************/

    /* 09 */

    0x09,         /*bLength: Interface Descriptor size*/

    USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/

    0x01,         /*bInterfaceNumber: Number of Interface*/

    0x00,         /*bAlternateSetting: Alternate setting*/

    0x02,         /*bNumEndpoints*/

    0xFF,         /*Vendor-Specific*/

    0x5D,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/

    0x01,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/

    0x00,            /*iInterface: Index of string descriptor*/

    /**************Unrecognized Class-Specific Descriptor***********************/

    /* 18 */

    0x1B,         /*bLength: CUSTOM_HID Descriptor size*/

    0x21, /*bDescriptorType: CUSTOM_HID*/

    0x00,0x01,0x01,0x01,0x83,0x40,0x01,0x04,

    0x20,0x16,0x85,0x00,0x00,0x00,0x00,0x00,

    0x00,0x16,0x05,0x00,0x00,0x00,0x00,0x00,

    0x00,

    /**************Endpoint Descriptor 81 1 In, Interrupt, 4 ms******************/

    /* 27 */

    0x07,          /*bLength: Endpoint Descriptor size*/

    USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/



    0x83,     /*bEndpointAddress: Endpoint Address (IN)*/

    0x03,          /*bmAttributes: Interrupt endpoint*/

    0x20, /*wMaxPacketSize: 2 Byte max */

    0x00,

    0x02,          /*bInterval: Polling Interval */

    /**************Endpoint Descriptor 02 2 Out, Interrupt, 8 ms******************/

    /* 34 */

    0x07,          /*bLength: Endpoint Descriptor size*/

    USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/



    0x04,     /*bEndpointAddress: Endpoint Address (IN)*/

    0x03,          /*bmAttributes: Interrupt endpoint*/

    0x20, /*wMaxPacketSize: 2 Byte max */

    0x00,

    0x04,          /*bInterval: Polling Interval */



    /**************Interface Descriptor 2/0 Vendor-Specific, 2 Endpoints****************/

    /* 09 */

    0x09,         /*bLength: Interface Descriptor size*/

    USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/

    0x02,         /*bInterfaceNumber: Number of Interface*/

    0x00,         /*bAlternateSetting: Alternate setting*/

    0x01,         /*bNumEndpoints*/

    0xFF,         /*Vendor-Specific*/

    0x5D,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/

    0x02,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/

    0x00,            /*iInterface: Index of string descriptor*/

    /**************Unrecognized Class-Specific Descriptor***********************/

    /* 18 */

    0x09,         /*bLength: CUSTOM_HID Descriptor size*/

    0x21, /*bDescriptorType: CUSTOM_HID*/

    0x00,0x01,0x01,0x22,0x86,0x07,0x00,

    /**************Endpoint Descriptor 81 1 In, Interrupt, 4 ms******************/

    /* 27 */

    0x07,          /*bLength: Endpoint Descriptor size*/

    USB_DESC_TYPE_ENDPOINT, /*bDescriptorType:*/



    0x86,     /*bEndpointAddress: Endpoint Address (IN)*/

    0x03,          /*bmAttributes: Interrupt endpoint*/

    0x20, /*wMaxPacketSize: 2 Byte max */

    0x00,

    0x10,          /*bInterval: Polling Interval */



    /**************nterface Descriptor 3/0 Vendor-Specific, 0 Endpoints****************/

    /* 09 */

    0x09,         /*bLength: Interface Descriptor size*/

    USB_DESC_TYPE_INTERFACE,/*bDescriptorType: Interface descriptor type*/

    0x03,         /*bInterfaceNumber: Number of Interface*/

    0x00,         /*bAlternateSetting: Alternate setting*/

    0x00,         /*bNumEndpoints*/

    0xFF,         /*Vendor-Specific*/

    0xFD,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/

    0x13,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/

    0x04,            /*iInterface: Index of string descriptor*/

    /**************Unrecognized Class-Specific Descriptor***********************/

    /* 18 */

    0x06,         /*bLength: CUSTOM_HID Descriptor size*/

    0x41, /*bDescriptorType: CUSTOM_HID*/

    0x00,0x01,0x01,0x03

};

这个配置描述符即可比较长,不过也比较统一,大家可以直接复制参考代码中的也可以,不过要注意以下接口索引号,端点地址之类的,比如我们定义的

#define CUSTOM_HID_EPIN_ADDR                 0x81U

#define CUSTOM_HID_EPIN_SIZE   20U

这个端点1地址以及端点大小正是我们自己定义的,大小一定不能小于20,因为微软XBOX360手柄定义的报表描述符正是20个字节。
大家可能比较好奇,XBOX360手柄为什么没有报表没舒服,我们在代码中也没有实现,直接给它初始化为0,这是为何?
其实我也不清楚,是不是系统自己只要认对应的VID和PID以及配置描述符正确,应该就可以正确枚举了?其实我们Usb主机好像也确实没有问我们设备要报表描述符。

3. 添加厂商请求的处理。在USBD_CUSTOM_HID_Setup函数值,除了类请求USB_REQ_TYPE_CLASS、标准请求USB_REQ_TYPE_STANDARD,还需要增加一个厂商请求USB_REQ_TYPE_VENDOR,如下:

static uint8_t  USBD_CUSTOM_HID_Setup(USBD_HandleTypeDef *pdev,

                                      USBD_SetupReqTypedef *req)

{

  USBD_CUSTOM_HID_HandleTypeDef *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)pdev->pClassData;

  uint16_t len = 0U;

  uint8_t  *pbuf = NULL;

  uint16_t status_info = 0U;

  uint8_t ret = USBD_OK;



  switch (req->bmRequest & USB_REQ_TYPE_MASK)

  {

    case USB_REQ_TYPE_VENDOR:

        switch (req->bRequest)

        {

        case 0x01:

        {

            /* code */

            if(req->wLength == 0x14)

            {

                len  = MIN(0x14, req->wLength);

                pbuf = vendor_request_content;

            }

            else if(req->wLength == 0x08)

            {

                len  = MIN(0x8, req->wLength);

                pbuf = vendor_re2;

            }

            else if(req->wLength == 0x04)

            {

                len  = MIN(0x04, req->wLength);

                pbuf = vendor_re2;

            }

            break;

        }



        default:

            break;

        }

        USBD_CtlSendData(pdev, pbuf, len);

        break;

    default:

      USBD_CtlError(pdev, req);

      ret = USBD_FAIL;

      break;

  }

  return ret;

}


实现上面这些,基本在电脑端可以枚举成功一个Xbox 360 Controller for Windows 手柄设备。但是我们并不能满足一次。
4. 解析摇杆电位器和按键数据。

void Xinput_Handle(void)

{

        int16_t X=0,Y=0;

    //X-Y

    for(u8 i=0; i<AD_DATA_SIZE;)

    {

        AdXSum += AD_DATA[i];

        i++;

        AdYSum += AD_DATA[i];

        i++;

    }

    Xtemp=AdXSum/10;

    AdXSum=0;

    Ytemp=AdYSum/10;

    AdYSum=0;

    if(Xtemp>Xmax)

        Xtemp=Xmax;

    if(Xtemp<Xmin)

        Xtemp=Xmin;



    if(Ytemp>Ymax)

        Ytemp=Ymax;

    if(Ytemp<Ymin)

        Ytemp=Ymin;

    X=(int16_t)map( Xtemp, Xmin, Xmax, INT16_MAX, INT16_MIN );

    Y=(int16_t)map( Ytemp, Ymin, Ymax, INT16_MIN, INT16_MAX );

        

        

        //Button



    if((UPKEY)==0)//Y

    {

        TXData[BUTTON_PACKET_2] |= Y_MASK_ON;

    }else{

                TXData[BUTTON_PACKET_2] &= Y_MASK_OFF;

        }

    if((DNKEY)==0)//A

    {

        TXData[BUTTON_PACKET_2] |= A_MASK_ON;

    }else{

                TXData[BUTTON_PACKET_2] &= A_MASK_OFF;

                

        }

    

    if(LFKEY==0)

    {

        TXData[BUTTON_PACKET_2] |= X_MASK_ON;

    } else {

        TXData[BUTTON_PACKET_2] &= X_MASK_OFF;

    }

    if(RGKEY==0)

    {

        TXData[BUTTON_PACKET_2] |= B_MASK_ON;

    } else {

        TXData[BUTTON_PACKET_2] &= B_MASK_OFF;

    }

        

    if(BKKEY==0)//BUTTON_BACK

    {

        TXData[BUTTON_PACKET_1] |= BACK_MASK_ON;

    } else {

        TXData[BUTTON_PACKET_1] &= BACK_MASK_OFF;

    }

    if(MDKEY==0)//BUTTON_LB

    {

        TXData[BUTTON_PACKET_2] |= LB_MASK_ON;

    } else {

        TXData[BUTTON_PACKET_2] &= LB_MASK_OFF;

    }

    if(STKEY==0)//BUTTON_START

    {

        TXData[BUTTON_PACKET_1] |= START_MASK_ON;

    } else {

        TXData[BUTTON_PACKET_1] &= START_MASK_OFF;

    }        

    if(TBKEY==0)//BUTTON_RB

    {

        TXData[BUTTON_PACKET_2] |= RB_MASK_ON;

    } else {

        TXData[BUTTON_PACKET_2] &= RB_MASK_OFF;

    }        

    if(SW1!=0)

    {

        TXData[BUTTON_PACKET_2] |= **_MASK_ON;

    } else {

        TXData[BUTTON_PACKET_2] &= **_MASK_OFF;

    }

        TXData[LEFT_STICK_X_PACKET_LSB] = LOBYTE(Y);                // (CONFERIR)

        TXData[LEFT_STICK_X_PACKET_MSB] = HIBYTE(Y);

        //Left Stick Y Axis

        TXData[LEFT_STICK_Y_PACKET_LSB] = LOBYTE(X);

        TXData[LEFT_STICK_Y_PACKET_MSB] = HIBYTE(X);

        //Clear DPAD

        TXData[BUTTON_PACKET_1] &= DPAD_MASK_OFF;

        USBD_CUSTOM_HID_SendReport(&hUsbDeviceFS,(u8*)&TXData, 20);



}


这里我们直接参考了GitHub上大佬的。其中map函数的作用把12位AD值0~4095转成16位的0~65535。然后扫描一下按键,按要求赋值即可。我们这里实现了左摇杆,以及A、B、X、Y、LB、RB、START、BACK等按键。
---------------------
作者:lilijin1995
链接:https://bbs.21ic.com/icview-3274570-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值