打印
[APM32E1]

【APM32E103xE测评】+USB键盘鼠标Gamepad手柄一体复合设备

[复制链接]
1586|35
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
继上次实现了USB的Gamepad手柄之后,见贴(https://bbs.21ic.com/icview-3220444-1-1.html)。本次实现了键盘鼠标手柄一体的复合设备。

效果如下:

板上的2个按键模拟了3个设备发送数据。

下面简单说下实现过程:
首先修改增加配置描述符:
const uint8_t g_hidConfigDescriptor[] = 
{
// configuration_descriptor hid_configuration_descriptor
   0x09,                               // Length
   0x02,                               // Type
   HID_CONFIG_DESCRIPTOR_SIZE&0xff,HID_CONFIG_DESCRIPTOR_SIZE>>8,// Totallength (= 9+9+9+7+7)
   0x03,                               // NumInterfaces          ?????
   0x01,                               // bConfigurationValue
   0x00,                               // iConfiguration ??????????
   0x80,                               // bmAttributes
   0xc0,                               // MaxPower (in 2mA units)

// interface_descriptor hid_interface_descriptor
   0x09,                               // bLength
   0x04,                               // bDescriptorType
   0x00,                               // bInterfaceNumber        ???,???????????
   0x00,                               // bAlternateSetting        ???????
   0x02,                               // bNumEndpoints                 ??????????,?0?????????0
   0x03,                               // bInterfaceClass (3 = HID)
   0x00,                               // bInterfaceSubClass
   0x00,                               // bInterfaceProcotol
   0x04,                               // iInterface Index of string descriptor
// class_descriptor hid_descriptor
   0x09,                               // bLength
   0x21,                               // bDescriptorType
   0x01,0x01,                          // bcdHID
   0x00,                               // bCountryCode
   0x01,                               // bNumDescriptors
   0x22,                               // bDescriptorType
   HID_GAMEPAD_REPORT_DESCRIPTOR_SIZE,0X00,                   // wItemLength (tot. len. of report descriptor)
// IN endpoint (mandatory for HID)
// endpoint1_descriptor hid_endpoint_in_descriptor
   0x07,                               // bLength
   0x05,                               // bDescriptorType
   0x81,                               // bEndpointAddress
   0x03,                               // bmAttributes        00-????,01-????,10-???,11-????
   0x40,0x00,                                    // MaxPacketSize (LITTLE ENDIAN)
   10,                               // bInterval
// OUT endpoint (optional for HID)
// endpoint1_descriptor hid_endpoint_out_descriptor
   0x07,                               // bLength
   0x05,                               // bDescriptorType
   0x01,                               // bEndpointAddress
   0x03,                               // bmAttributes
   0x40,0x00,                                   // MaxPacketSize (LITTLE ENDIAN)
   10,                               // bInterval


// interface_descriptor hid_interface_descriptor        222222222222222222222222222222
   0x09,                               // bLength
   0x04,                               // bDescriptorType
   0x01,                               // bInterfaceNumber        ???,???????????
   0x00,                               // bAlternateSetting        ???????
   0x02,                               // bNumEndpoints                 ??????????,?0?????????0
   0x03,                               // bInterfaceClass (3 = HID)
   0x01,                               // bInterfaceSubClass  0-nosub,1-bootclass,2-reserved
   0x01,                               // bInterfaceProcotol  0-No,1-keyboard,2-mouse,
   0x05,                               // iInterface Index of string descriptor
// class_descriptor hid_descriptor
   0x09,                               // bLength
   0x21,                               // bDescriptorType
   0x01,0x01,                          // bcdHID
   0x00,                               // bCountryCode
   0x01,                               // bNumDescriptors
   0x22,                               // bDescriptorType
   HID_KEYBOARD_REPORT_DESCRIPTOR_SIZE,0x00,// wItemLength (tot. len. of report descriptor)
// IN endpoint (mandatory for HID)
// endpoint1_descriptor hid_endpoint_in_descriptor
   0x07,                               // bLength
   0x05,                               // bDescriptorType
   0x82,                               // bEndpointAddress
   0x03,                               // bmAttributes        00-????,01-????,10-???,11-????
   0x40,0x00,                                    // MaxPacketSize (LITTLE ENDIAN)
   0x0a,                               // bInterval
// OUT endpoint (optional for HID)
// endpoint1_descriptor hid_endpoint_out_descriptor
   0x07,                               // bLength
   0x05,                               // bDescriptorType
   0x02,                               // bEndpointAddress
   0x03,                               // bmAttributes
   0x40,0x00,                                   // MaxPacketSize (LITTLE ENDIAN)
   0x0a,                                // bInterval

  /************** Descriptor of Mouse HID interface ****************/
  /* 09 */
  0x09,         /*bLength: Interface Descriptor size*/
  0x04,                        /*bDescriptorType: Interface descriptor type*/
  0x02,         /*bInterfaceNumber: Number of Interface*/
  0x00,         /*bAlternateSetting: Alternate setting*/
  0x01,         /*bNumEndpoints*/
  0x03,         /*bInterfaceClass: CUSTOM_HID*/
  0x01,         /*bInterfaceSubClass : 1=BOOT, 0=no boot*/
  0x02,         /*nInterfaceProtocol : 0=none, 1=keyboard, 2=mouse*/
  0X06,         /*iInterface: Index of string descriptor*/
  /******************** Descriptor of CUSTOM_HID *************************/
  /* 18 */
  0x09,         /*bLength: CUSTOM_HID Descriptor size*/
  0x21,                 /*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*/
  HID_MOUSE_REPORT_DESCRIPTOR_SIZE,0x00,/*wItemLength: Total length of Report descriptor*/
  
  /******************** Descriptor of Custom HID endpoints ********************/
  /* 27 */
  0x07,                  /*bLength: Endpoint Descriptor size*/
  0x05,                         /*bDescriptorType:*/  
  0x83,                     /*bEndpointAddress: Endpoint Address (IN)*/
  0x03,                  /*bmAttributes: Interrupt endpoint*/
  0x40,0x00,                /*wMaxPacketSize: 4 Byte max */  
  0x08,                  /*bInterval: Polling Interval (20 ms)*/
  /* 34 */

};
其次就是3个设备的报告描述符
/**
* [url=home.php?mod=space&uid=247401]@brief[/url]   HID report descriptor
*/
const uint8_t g_hidGamepadReportDescriptor[] =
{
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x05,                    // USAGE (Game Pad)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x09, 0x01,                    //   USAGE (Pointer)
    0xa1, 0x00,                    //   COLLECTION (Physical)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x09, 0x33,                    //     USAGE (RX)
    0x09, 0x34,                    //     USAGE (RY)
    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
    0x25, 0x7f,                        //     LOGICAL_MAXIMUM (127)       
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x04,                    //     REPORT_COUNT (4)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0xc0,                          //     END_COLLECTION
       
    0x09, 0x36,                    //   USAGE (Silder)
    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)
       
    0x09, 0x39,                    //   USAGE (Hat switch)
    0x15, 0x01,                    //   LOGICAL_MINIMUM (1)
    0x25, 0x08,                    //   LOGICAL_MAXIMUM (8)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x81, 0x42,                    //   INPUT (Data,Var,Abs,Null)
       
    0x05, 0x09,                    //   USAGE_PAGE (Button)
    0x19, 0x01,                    //   USAGE_MINIMUM (Button 1)
    0x29, 0x10,                    //   USAGE_MAXIMUM (Button 16)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)
    0x75, 0x01,                    //   REPORT_SIZE (1)
    0x95, 0x10,                    //   REPORT_COUNT (16)
    0x81, 0x02,                    //   INPUT (Data,Var,Abs)
   
    0x06, 0x00, 0xff,              //   USAGE_PAGE (Vendor Defined Page 1)
    0x09, 0x01,                    //   USAGE (Vendor Usage 1)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x26, 0xff, 0x00,              //   LOGICAL_MAXIMUM (255)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x95, 0x04,                    //   REPORT_COUNT (4)
    0x91, 0x02,                    //   OUTPUT (Data,Var,Abs)       
   
    0xc0                           // END_COLLECTION
};
const uint8_t g_hidKeyboardReportDescriptor[]=
{
    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)

    0x05, 0x07,                    //   USAGE_PAGE (Keyboard)
    0x19, 0x00,                    //   USAGE_MINIMUM (Reserved (no event indicated))
    0x29, 0xdd,                    //   USAGE_MAXIMUM (Keyboard Application)
    0x95, 0x06,                    //   REPORT_COUNT (6)
    0x75, 0x08,                    //   REPORT_SIZE (8)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0xdd,                    //   LOGICAL_MAXIMUM (164)       
    0x81, 0x00,                    //   INPUT (Data,Ary,Abs)

    0x05, 0x08,                    //   USAGE_PAGE (LEDs)
    0x19, 0x01,                    //   USAGE_MINIMUM (Num Lock)
    0x29, 0x05,                    //   USAGE_MAXIMUM (Kana)
    0x15, 0x00,                    //   LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //   LOGICAL_MAXIMUM (1)       
    0x95, 0x05,                    //   REPORT_COUNT (5)
    0x75, 0x01,                    //   REPORT_SIZE (1)       
    0x91, 0x02,                    //   OUTPUT (Data,Var,Abs)
    0x95, 0x01,                    //   REPORT_COUNT (1)
    0x75, 0x03,                    //   REPORT_SIZE (3)
    0x91, 0x03,                    //   OUTPUT (Cnst,Var,Abs)
       
    0xc0                           // END_COLLECTION
};
const uint8_t g_hidMouseReportDescriptor[]=
{
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
    0x09, 0x02,                    // USAGE (Mouse)
    0xa1, 0x01,                    // COLLECTION (Application)
    0x09, 0x01,                    //   USAGE (Pointer)
    0xa1, 0x00,                    //   COLLECTION (Physical)
       
    0x05, 0x09,                    //     USAGE_PAGE (Button)
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
    0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
    0x95, 0x03,                    //     REPORT_COUNT (3)
    0x75, 0x01,                    //     REPORT_SIZE (1)
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x75, 0x05,                    //     REPORT_SIZE (5)
    0x81, 0x01,                    //     INPUT (Cnst,Ary,Abs)
       
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
    0x09, 0x30,                    //     USAGE (X)
    0x09, 0x31,                    //     USAGE (Y)
    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x02,                    //     REPORT_COUNT (2)
    0x81, 0x06,                    //     INPUT (Data,Var,Rel)
       
    0x09, 0x38,                    //     USAGE (Wheel)
    0x15, 0x81,                    //     LOGICAL_MINIMUM (-127)
    0x25, 0x7f,                    //     LOGICAL_MAXIMUM (127)
    0x75, 0x08,                    //     REPORT_SIZE (8)
    0x95, 0x01,                    //     REPORT_COUNT (1)
    0x81, 0x06,                    //     INPUT (Data,Var,Rel)
       
    0xc0,                          //     END_COLLECTION
       
        0xc0
};
/** Device descriptor */
const USB_Descriptor_T s_deviceDescriptor = {g_hidDeviceDescriptor, HID_DEVICE_DESCRIPTOR_SIZE};
/** Config descriptor */
const USB_Descriptor_T s_configDescriptor = {g_hidConfigDescriptor, HID_CONFIG_DESCRIPTOR_SIZE};
/** String descriptor */
const USB_Descriptor_T s_stringDescriptor[] =
{
    {g_hidLandIDString,     HID_LANGID_STRING_SIZE},
    {g_hidVendorString,     HID_VENDOR_STRING_SIZE},
    {g_hidProductString,    HID_PRODUCT_STRING_SIZE},
    {g_hidSerialString,     HID_SERIAL_STRING_SIZE},
    {g_Class1StringDesc,    16},
    {g_Class2StringDesc,    18},
    {g_Class3StringDesc,    12},
};

const USB_Descriptor_T s_reportDescriptor[] =
{
    {g_hidGamepadReportDescriptor,  HID_GAMEPAD_REPORT_DESCRIPTOR_SIZE},
    {g_hidKeyboardReportDescriptor, HID_KEYBOARD_REPORT_DESCRIPTOR_SIZE},
    {g_hidMouseReportDescriptor,    HID_MOUSE_REPORT_DESCRIPTOR_SIZE},
};
接着就是USB端点的初始化。本次要使用端点1,端点2的输入输出端点,端点3的输入端点。
初始化如下:
void HidMouse_Reset(void)
{
    uint8_t i;

    s_usbConfigStatus = 0;  //usb not config,disable send data
   
    USBD_SetBufferTable(USB_BUFFER_TABLE_ADDR);
    //endpoint 0 init
    USBD_ResetEPKind(USBD_EP_0);
   
    USBD_SetEPType(USBD_EP_0, USBD_EP_TYPE_CONTROL);
    USBD_SetEPTxStatus(USBD_EP_0, USBD_EP_STATUS_STALL);
    USBD_SetEPTxAddr(USBD_EP_0, USB_EP0_TX_ADDR);
   
    USBD_SetEPRxStatus(USBD_EP_0, USBD_EP_STATUS_VALID);   
    USBD_SetEPRxAddr(USBD_EP_0, USB_EP0_RX_ADDR);
    USBD_SetEPRxCnt(USBD_EP_0, USB_EP_PACKET_SIZE);

//ep1 gamepad
    USBD_SetEPType(USBD_EP_1, USBD_EP_TYPE_INTERRUPT);
    USBD_SetEPTxAddr(USBD_EP_1, USB_EP1_TX_ADDR);
    USBD_SetEPTxCnt(USBD_EP_1, 8);
    USBD_SetEPRxAddr(USBD_EP_1, USB_EP1_RX_ADDR);
    USBD_SetEPRxCnt(USBD_EP_1, 4);
    USBD_SetEPRxTxStatus(USBD_EP_1, USBD_EP_STATUS_VALID, USBD_EP_STATUS_VALID);
//EP2 keyboard
    USBD_SetEPType(USBD_EP_2, USBD_EP_TYPE_INTERRUPT);
    USBD_SetEPTxAddr(USBD_EP_2, USB_EP2_TX_ADDR);
    USBD_SetEPTxCnt(USBD_EP_2, 8);
    USBD_SetEPRxAddr(USBD_EP_2, USB_EP2_RX_ADDR);
    USBD_SetEPRxCnt(USBD_EP_2, 1);
    USBD_SetEPRxTxStatus(USBD_EP_2, USBD_EP_STATUS_VALID, USBD_EP_STATUS_VALID);
//EP3 mouse
    USBD_SetEPType(USBD_EP_3, USBD_EP_TYPE_INTERRUPT);
    USBD_SetEPTxAddr(USBD_EP_3, USB_EP3_TX_ADDR);
    USBD_SetEPTxCnt(USBD_EP_3, 4);
    USBD_SetEPRxAddr(USBD_EP_3, USB_EP3_RX_ADDR);
    USBD_SetEPRxCnt(USBD_EP_3, 8);
    USBD_SetEPRxTxStatus(USBD_EP_3,USBD_EP_STATUS_VALID, USBD_EP_STATUS_DISABLE );
   
    //endpoint addr
    for(i = 0; i < USB_EP_NUM; i++)
    {
        USBD_SetEpAddr((USBD_EP_T)i, i);
    }
   
    USBD_SetDeviceAddr(0);
    USBD_Enable();
}
下面说一下端点Buffer地址设置,这里容易搞错。
/** EP0 Tx address */
#define USB_EP0_TX_ADDR         (0X40)
/** EP0 Rx address */
#define USB_EP0_RX_ADDR         (0X80)
/** EP1 Tx address */
#define USB_EP1_TX_ADDR         (0XC0)
/** EP1 Rx address */
#define USB_EP1_RX_ADDR         (0XD0)
/** EP2 Tx address */
#define USB_EP2_TX_ADDR         (0XE0)
/** EP2 Rx address */
#define USB_EP2_RX_ADDR         (0XF0)
/** EP3 Tx address */
#define USB_EP3_TX_ADDR         (0X100)
/** EP3 Rx address */
#define USB_EP3_RX_ADDR         (0X110)


首先端点buffer地址为什么要从0x40开始,因为这个MCU有8个端点,每个端点又分输入输出。
而且每个端点有buffer起始地址和数据长度空间都占4字节。使用1个输入端点就要增加8字节,1个输出端点也要8字节。
本次使用了端点0,1,2,3。所以地址至少要7*8=56字节即0x38。后面地址就要根据每个端点的数据包长度来修改buffer地址。


最后就是按键模拟,及USB数据发送过程处理
void HidMouse_Proc(void)
{
        
    static  int8_t x = 0;
    static  int8_t y = 0;
    uint8_t buffer[8] = {0, 0, 0, 0};
    uint8_t buffer1[8] = {0, 0, 0, 0};
    uint8_t buffer2[4] = {0, 0, 0, 0};

    if(!s_usbConfigStatus)
    {
        return;
    }
    if(g_time_tick == 0)
        {
        g_time_tick = 10;
        /** Right key */
        if(!GPIO_ReadInputBit(GPIOA, GPIO_PIN_0))
        {
            GPIOE->ODATA ^= GPIO_PIN_5;
            x -= 10;
            buffer[5] = 3;
            buffer[6] |= 0x01;
            buffer1[2] = 0x20;
            buffer2[1] = -5;
        }else
        {
            buffer[6] &= ~0x01;
            buffer1[2] = 0;
            buffer2[1] = 0;
        }
      
        /** Left key */
        if(!GPIO_ReadInputBit(GPIOA, GPIO_PIN_1))
        {
            GPIOE->ODATA ^= GPIO_PIN_6;
            x += 10;
            buffer[5] = 7;
            buffer[6] |= 0x02;
            buffer1[3] = 0x21;
            buffer2[1] = 5;
        }else
        {
            buffer[6] &= ~0x02;
            buffer1[3] = 0;
        }
        
        buffer[0] = x;
        buffer[1] = y;

        if(s_statusEP_in[1])
        {
            s_statusEP_in[1] = 0; //send over flag
            USBD_WriteDataToEP(USBD_EP_1, buffer, sizeof(buffer));  //write data to buffer
            USBD_SetEPTxStatus(USBD_EP_1, USBD_EP_STATUS_VALID);    //send data valid
        }
        
        if(s_statusEP_in[2])
        {
            s_statusEP_in[2] = 0; //send over flag
            USBD_WriteDataToEP(USBD_EP_2, buffer1, sizeof(buffer1));  //write data to buffer
            USBD_SetEPTxStatus(USBD_EP_2, USBD_EP_STATUS_VALID);    //send data valid
        }
        
        if(s_statusEP_in[3])
        {
            s_statusEP_in[3] = 0; //send over flag
            USBD_WriteDataToEP(USBD_EP_3, buffer2, sizeof(buffer2));  //write data to buffer
            USBD_SetEPTxStatus(USBD_EP_3, USBD_EP_STATUS_VALID);    //send data valid
        }        
        
        }


}



程序:
游客,如果您要查看本帖隐藏内容请回复


使用特权

评论回复
沙发
Fanexs168| | 2022-5-25 16:49 | 只看该作者
Mark一下,后面自己抽空再搞搞~

使用特权

评论回复
板凳
olivem55arlowe| | 2022-5-28 12:44 | 只看该作者
支持otg吗?

使用特权

评论回复
地板
houjiakai| | 2022-5-28 13:12 | 只看该作者
支持和pc端的通信吗?

使用特权

评论回复
5
sesefadou| | 2022-5-28 13:32 | 只看该作者
USB速度可以到多少?

使用特权

评论回复
6
jonas222| | 2022-5-28 13:58 | 只看该作者
Gamepad手柄怎么驱动呢  

使用特权

评论回复
7
soodesyt| | 2022-5-28 14:47 | 只看该作者
买过一个最小板子,看着还不错的。   

使用特权

评论回复
8
geraldbetty| | 2022-5-28 15:26 | 只看该作者
这个支持usb吗?  

使用特权

评论回复
9
phoenixwhite| | 2022-5-28 16:34 | 只看该作者
可以读取ps2的键盘吗?

使用特权

评论回复
10
gxliu08| | 2022-5-28 21:19 | 只看该作者
感谢分享,学习学习

使用特权

评论回复
11
发条陈| | 2022-6-28 22:49 | 只看该作者
支持

使用特权

评论回复
12
everyrobin| | 2022-7-9 13:22 | 只看该作者
这个hid设备吗?  

使用特权

评论回复
13
burgessmaggie| | 2022-7-9 13:41 | 只看该作者
资料很棒,感谢分享。  

使用特权

评论回复
14
nomomy| | 2022-7-9 14:20 | 只看该作者
不知道刷新的速度怎么样   

使用特权

评论回复
15
mickit| | 2022-7-9 15:20 | 只看该作者
这个驱动怎么样   

使用特权

评论回复
16
gaokosen| | 2022-9-15 16:43 | 只看该作者
mark 参考

使用特权

评论回复
17
海洋无限| | 2022-9-16 12:44 | 只看该作者
求教,这个评测的板子能拿到

使用特权

评论回复
18
Stahan| | 2022-10-4 20:12 | 只看该作者
支持pc通信吗?

使用特权

评论回复
19
luobeihai| | 2022-12-10 00:18 | 只看该作者
需要回复才能看,回复学习一下,感谢分享!

使用特权

评论回复
20
Undshing| | 2023-1-3 15:36 | 只看该作者
刷新的速度怎么样  

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

115

主题

4395

帖子

25

粉丝