AnsweredAssumed Answered

Implement multi-touch USB HID for Windows 7

Question asked by chang.wook on Feb 6, 2013
Latest reply on Sep 9, 2014 by Chinzei.Tsuneo
Hello all,
I'm implementing multi-touch USB HID device for PC with a custom board using STM32F103 since several weeks and stucked at sending multi-touch information to a host PC.
My PC has a commercial multi-touch screen and Windows 7. I attach my device and it sends out two touch information. Using a USB analyzer, I can see that a report packet is correctly sent to my PC.
My problem is that even though my device reports two touches, PC recognizes only one touch - I checked it with MS Paint.

To make my firmware, I adapted ST's CustomHID and JoyStickMouse examples. And I used Ms's 'Sample Report Descriptor (Parallel/Hybrid Mode)' example in pp. 8-9 in MS Word document at http://msdn.microsoft.com/en-us/windows/hardware/gg487437.aspx as shown below

0x05, 0x0d,                         // USAGE_PAGE (Digitizers)
0x09, 0x04,                         // USAGE (Touch Screen)
0xa1, 0x01,                         // COLLECTION (Application)
0x85, REPORTID_MTOUCH,                 //   REPORT_ID (Touch)
0x09, 0x22,                         //   USAGE (Finger)
0xa1, 0x02,                         //     COLLECTION (Logical)
0x09, 0x42,                         //       USAGE (Tip Switch)
0x15, 0x00,                         //       LOGICAL_MINIMUM (0)
0x25, 0x01,                         //       LOGICAL_MAXIMUM (1)
0x75, 0x01,                         //       REPORT_SIZE (1)
0x95, 0x01,                         //       REPORT_COUNT (1)
0x81, 0x02,                         //       INPUT (Data,Var,Abs)
0x09, 0x32,                         //       USAGE (In Range)
0x81, 0x02,                         //       INPUT (Data,Var,Abs)
0x09, 0x47,                         //       USAGE (Touch Valid)
0x81, 0x02,                         //       INPUT (Data,Var,Abs)
0x95, 0x05,                         //       REPORT_COUNT (5)
0x81, 0x03,                         //       INPUT (Cnst,Ary,Abs)
0x75, 0x08,                         //       REPORT_SIZE (8)
0x09, 0x51,                         //       USAGE (Contact Identifier)
0x95, 0x01,                         //       REPORT_COUNT (1)
0x81, 0x02,                         //       INPUT (Data,Var,Abs)
0x05, 0x01,                         //       USAGE_PAGE (Generic Desk..
0x26, 0xff, 0x7f,                   //       LOGICAL_MAXIMUM (32767)
0x75, 0x10,                         //       REPORT_SIZE (16)
0x55, 0x00,                         //       UNIT_EXPONENT (0)
0x65, 0x00,                         //       UNIT (None)
0x09, 0x30,                         //       USAGE (X)
0x35, 0x00,                         //       PHYSICAL_MINIMUM (0)
0x46, 0x00, 0x00,                   //       PHYSICAL_MAXIMUM (0)
0x81, 0x02,                         //       INPUT (Data,Var,Abs)
0x09, 0x31,                         //       USAGE (Y)
0x46, 0x00, 0x00,                   //       PHYSICAL_MAXIMUM (0)
0x81, 0x02,                         //       INPUT (Data,Var,Abs)
0xc0,                               //    END_COLLECTION
0xa1, 0x02,                         //    COLLECTION (Logical)
0x05, 0x0d,                         //     USAGE_PAGE (Digitizers)
0x09, 0x42,                         //       USAGE (Tip Switch)
0x15, 0x00,                         //       LOGICAL_MINIMUM (0)
0x25, 0x01,                         //       LOGICAL_MAXIMUM (1)
0x75, 0x01,                         //       REPORT_SIZE (1)
0x95, 0x01,                         //       REPORT_COUNT (1)
0x81, 0x02,                         //       INPUT (Data,Var,Abs)
0x09, 0x32,                         //       USAGE (In Range)
0x81, 0x02,                         //       INPUT (Data,Var,Abs)
0x09, 0x47,                         //       USAGE (Touch Valid)
0x81, 0x02,                         //       INPUT (Data,Var,Abs)
0x95, 0x05,                         //       REPORT_COUNT (5)
0x81, 0x03,                         //       INPUT (Cnst,Ary,Abs)
0x75, 0x08,                         //       REPORT_SIZE (8)
0x09, 0x51,                         //       USAGE ( Cotact Identifier)
0x95, 0x01,                         //       REPORT_COUNT (1)
0x81, 0x02,                         //       INPUT (Data,Var,Abs)
0x05, 0x01,                         //       USAGE_PAGE (Generic Desk..
0x26, 0xff, 0x7f,                   //       LOGICAL_MAXIMUM (32767)
0x75, 0x10,                         //       REPORT_SIZE (16)
0x55, 0x00,                         //       UNIT_EXPONENT (0)
0x65, 0x00,                         //       UNIT (None)
0x09, 0x30,                         //       USAGE (X)
0x35, 0x00,                         //       PHYSICAL_MINIMUM (0)
0x46, 0x00, 0x00,                   //       PHYSICAL_MAXIMUM (0)
0x81, 0x02,                         //       INPUT (Data,Var,Abs)
0x09, 0x31,                         //       USAGE (Y)
0x46, 0x00, 0x00,                   //       PHYSICAL_MAXIMUM (0)
0x81, 0x02,                         //       INPUT (Data,Var,Abs)
0xc0,                               //    END_COLLECTION
0x05, 0x0d,                         //    USAGE_PAGE (Digitizers)
0x09, 0x54,                         //    USAGE (Contact Count)
0x95, 0x01,                         //    REPORT_COUNT (1)
0x75, 0x08,                         //    REPORT_SIZE (8)
0x15, 0x00,                         //    LOGICAL_MINIMUM (0)
0x25, 0x08,                         //    LOGICAL_MAXIMUM (8)
0x81, 0x02,                         //    INPUT (Data,Var,Abs)
0x09, 0x55,                         //    USAGE(Contact Count Maximum)
0xb1, 0x02,                         //    FEATURE (Data,Var,Abs)
0xc0,                               // END_COLLECTION

According to the sample report descriptor, my codes at usb_desc.c are written as follows. In my implementation, I use endpoint 1 to send out multi-touch data.

/* USB Standard Device Descriptor */
const uint8_t CustomHID_DeviceDescriptor[CUSTOMHID_SIZ_DEVICE_DESC] =
  {
    0x12,                       /*bLength */
    USB_DEVICE_DESCRIPTOR_TYPE, /*bDescriptorType*/
    0x10,                       /*bcdUSB */
    0x01,
    0x00,                       /*bDeviceClass*/
    0x00,                       /*bDeviceSubClass*/
    0x00,                       /*bDeviceProtocol*/
    0x40,                       /*bMaxPacketSize 64*/
    0x83,                       /*idVendor (0x0483)*/
    0x04,
    0x30,                       /*idProduct = 0x5730*, temporal VID */
    0x57,
    0x10,                       /*bcdDevice rel. 1.10*/
    0x01,
    1,                          /*Index of string descriptor describing
                                                  manufacturer */
    2,                          /*Index of string descriptor describing
                                                 product*/
    3,                          /*Index of string descriptor describing the
                                                 device serial number */
    0x01                        /*bNumConfigurations*/
  }
  /* CustomHID_DeviceDescriptor */
 
 
/* USB Configuration Descriptor */
/*   All Descriptors (Configuration, Interface, Endpoint, Class, Vendor */
const uint8_t CustomHID_ConfigDescriptor[CUSTOMHID_SIZ_CONFIG_DESC] =
  {
    0x09, /* bLength: Configuation Descriptor size */
    USB_CONFIGURATION_DESCRIPTOR_TYPE, /* bDescriptorType: Configuration */
    CUSTOMHID_SIZ_CONFIG_DESC,
    /* 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_INTERFACE_DESCRIPTOR_TYPE,/*bDescriptorType: Interface descriptor type*/
    0x00,         /*bInterfaceNumber: Number of Interface*/
    0x00,         /*bAlternateSetting: Alternate setting*/
    0x01,         /* bNumEndpoints */
    0x03,         /*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 Custom HID HID ********************/
    /* 18 */
    0x09,         /*bLength: HID Descriptor size*/
    HID_DESCRIPTOR_TYPE, /*bDescriptorType: HID*/
    0x10,         /*bcdHID: HID Class Spec release number*/
    0x01,
    0x00,         /*bCountryCode: Hardware target country*/
    0x01,         /*bNumDescriptors: Number of HID class descriptors to follow*/
    0x22,         /*bDescriptorType*/
    CUSTOMHID_SIZ_REPORT_DESC,/* wItemLength: Total length of Report descriptor */
    0x00,
    /******************** Descriptor of Custom HID endpoints ******************/
    /* 27 */
    0x07,          /*bLength: Endpoint Descriptor size*/
    USB_ENDPOINT_DESCRIPTOR_TYPE, /*bDescriptorType:*/
 
    0x81,          /*bEndpointAddress: Endpoint Address (IN)*/
    0x03,          /*bmAttributes: Interrupt endpoint*/
    0x40,          /*wMaxPacketSize: 60 Byte max (report is 14 bytes) */
    0x00,
    0x08,          /*bInterval: Polling Interval (8 ms)*/
    /* 34 */
  }
  /* CustomHID_ConfigDescriptor */
 
 
// Report descriptor is copied from pp. 8-9 of Word found at
#define REPORTID_PEN 0x01
const uint8_t CustomHID_ReportDescriptor[CUSTOMHID_SIZ_REPORT_DESC] =
{
    0x05, 0x0d,                         // USAGE_PAGE (Digitizers)
    0x09, 0x04,                         // USAGE (Touch Screen)
    0xa1, 0x01,                         // COLLECTION (Application)
    0x85, REPORTID_PEN,                 //   REPORT_ID (Touch)
    0x09, 0x22,                         //   USAGE (Finger)
    0xa1, 0x02,                         //     COLLECTION (Logical)
    0x09, 0x42,                         //       USAGE (Tip Switch)
    0x15, 0x00,                         //       LOGICAL_MINIMUM (0)
    0x25, 0x01,                         //       LOGICAL_MAXIMUM (1)
    0x75, 0x01,                         //       REPORT_SIZE (1)
    0x95, 0x01,                         //       REPORT_COUNT (1)
    0x81, 0x02,                         //       INPUT (Data,Var,Abs)
    0x09, 0x32,                         //       USAGE (In Range)
    0x81, 0x02,                         //       INPUT (Data,Var,Abs)
    0x09, 0x47,                         //       USAGE (Confidence)
    0x81, 0x02,                         //       INPUT (Data,Var,Abs)
    0x95, 0x05,                         //       REPORT_COUNT (5)
    0x81, 0x03,                         //       INPUT (Cnst,Ary,Abs)
    0x75, 0x08,                         //       REPORT_SIZE (8)
    0x09, 0x51,                         //       USAGE (Contact Identifier)
    0x95, 0x01,                         //       REPORT_COUNT (1)
    0x81, 0x02,                         //       INPUT (Data,Var,Abs)
    0x05, 0x01,                         //       USAGE_PAGE (Generic Desk..
    0x26, 0xff, 0x7f,                   //       LOGICAL_MAXIMUM (32767)
    0x75, 0x10,                         //       REPORT_SIZE (16)
    0x55, 0x00,                         //       UNIT_EXPONENT (0)
    0x65, 0x00,                         //       UNIT (None)
    0x09, 0x30,                         //       USAGE (X)
    0x35, 0x00,                         //       PHYSICAL_MINIMUM (0)
    0x46, 0x00, 0x00,                   //       PHYSICAL_MAXIMUM (0)
    0x81, 0x02,                         //       INPUT (Data,Var,Abs)
    0x09, 0x31,                         //       USAGE (Y)
    0x46, 0x00, 0x00,                   //       PHYSICAL_MAXIMUM (0)
    0x81, 0x02,                         //       INPUT (Data,Var,Abs)
    0xc0,                               //    END_COLLECTION
    0xa1, 0x02,                         //    COLLECTION (Logical)
    0x05, 0x0d,                         //     USAGE_PAGE (Digitizers)
    0x09, 0x42,                         //       USAGE (Tip Switch)
    0x15, 0x00,                         //       LOGICAL_MINIMUM (0)
    0x25, 0x01,                         //       LOGICAL_MAXIMUM (1)
    0x75, 0x01,                         //       REPORT_SIZE (1)
    0x95, 0x01,                         //       REPORT_COUNT (1)
    0x81, 0x02,                         //       INPUT (Data,Var,Abs)
    0x09, 0x32,                         //       USAGE (In Range)
    0x81, 0x02,                         //       INPUT (Data,Var,Abs)
    0x09, 0x47,                         //       USAGE (Confidence)
    0x81, 0x02,                         //       INPUT (Data,Var,Abs)
    0x95, 0x05,                         //       REPORT_COUNT (5)
    0x81, 0x03,                         //       INPUT (Cnst,Ary,Abs)
    0x75, 0x08,                         //       REPORT_SIZE (8)
    0x09, 0x51,                         //       USAGE (Contact Identifier)
    0x95, 0x01,                         //       REPORT_COUNT (1)
    0x81, 0x02,                         //       INPUT (Data,Var,Abs)
    0x05, 0x01,                         //       USAGE_PAGE (Generic Desk..
    0x26, 0xff, 0x7f,                   //       LOGICAL_MAXIMUM (32767)
    0x75, 0x10,                         //       REPORT_SIZE (16)
    0x55, 0x00,                         //       UNIT_EXPONENT (0)
    0x65, 0x00,                         //       UNIT (None)
    0x09, 0x30,                         //       USAGE (X)
    0x35, 0x00,                         //       PHYSICAL_MINIMUM (0)
    0x46, 0x00, 0x00,                   //       PHYSICAL_MAXIMUM (0)
    0x81, 0x02,                         //       INPUT (Data,Var,Abs)
    0x09, 0x31,                         //       USAGE (Y)
    0x46, 0x00, 0x00,                   //       PHYSICAL_MAXIMUM (0)
    0x81, 0x02,                         //       INPUT (Data,Var,Abs)
    0xc0,                               //    END_COLLECTION
    0x05, 0x0d,                         //    USAGE_PAGE (Digitizers)
    0x09, 0x54,                         //    USAGE (Contact Count)
    0x95, 0x01,                         //    REPORT_COUNT (1)
    0x75, 0x08,                         //    REPORT_SIZE (8)
    0x15, 0x00,                         //    LOGICAL_MINIMUM (0)
    0x25, 0x02,                         //    LOGICAL_MAXIMUM (2) (Origianlly 8)
    0x81, 0x02,                         //    INPUT (Data,Var,Abs)
    0x09, 0x55,                         //    USAGE(Contact Count Maximum)
    0xb1, 0x02,                         //    FEATURE (Data,Var,Abs) (Originally 8)
    0xc0                               // END_COLLECTION
};

And in hw_config.c, I added a function MT_Send() which is invoked when a button on my board is pressesd. MT_Send() creates 14 bytes HID report for two virtual touches moving diagonally across the screen. But as I mentioned before, my PC only gets the first touch. I compared the report of my device with that of another multi-touch screen, and confirmed that the packet structure is same. Of course, I can see two touch drawing on MS Paint if I use the multi-touch screen.... I've spent several days for this issue, but have no idea yet. I'm just suspicious that I need to write some codes for the feature value in report descriptor.

It would be great if someone had an idea that could help me.

Thanks in advance...

void MT_Send(void)
{
    // Information of virtual two touches.
    // X & Y: 0 ~ 32767
    int X1 = 15000;
    int Y1 = 15000;
    int X2 = 15000;
    int Y2 = 16000;
    int step = 50;
    uint8_t nTouch = 0x02;
    uint8_t id1 = 0x01;
    uint8_t id2 = 0x02;
     
    // Touch status
    // .......x: TIP
    // ......x.: InRange
    // .....x..: Confidence
    uint8_t touchStart = 0x00;
    uint8_t touchDown = 0x07; // 0 0 0 0 0 1 1 1
    uint8_t touchUp = 0x04; // 0 0 0 0 0 1 0 0
 
    // 2 touches start.
    SendHIDReport(touchStart, id1, X1, Y1,
        touchStart, id1, X2, Y2,   
        nTouch);
 
    // 2 touches move diagonally.
    for (int i = 0; i < 50; i++)
    {
        SendHIDReport(touchDown, id1, X1, Y1,
            touchDown, id2, X2, Y2,
            nTouch);
        X1 += step;
        Y1 += step;
        X2 += step;
        Y2 += step;
    }
     
    // 2 touches pause.
    SendHIDReport(touchUp, id1, X1, Y1,
        touchUp, id2, X2, Y2,
        nTouch);
     
    // 2 touches move diagonally.
    for (int i = 0; i < 50; i++)
    {
        SendHIDReport(touchDown, id1, X1, Y1,
            touchDown, id2, X2, Y2,
            nTouch);
        X1 += step;
        Y1 += step;
        X2 += step;
        Y2 += step;
    }
     
    // 2 touches up.
    SendHIDReport(touchUp, id1, X1, Y1,
    touchUp, id2, X2, Y2,
    nTouch);
}
 
void SendHIDReport(uint8_t state1, uint8_t id1, uint32_t x1, uint32_t y1,
                        uint8_t state2, uint8_t id2, uint32_t x2, uint32_t y2,
                        uint8_t numTouch)
{
    uint8_t buffer[14];
    uint8_t reportID = 0x01;
     
    // Report ID
    buffer[0] = reportID;
 
    // 1st touch
    buffer[1] = state1;
    buffer[2] = id1;
    buffer[3] = (uint8_t)(x1 & 0x00FF);
    buffer[4] = (uint8_t)((x1 & 0xFF00) >> 8);
    buffer[5] = (uint8_t)(y1 & 0x00FF);
    buffer[6] = (uint8_t)((y1 & 0xFF00) >> 8);
 
    // 2nd touch
    buffer[7] = state2;
    buffer[8] = id2;
    buffer[9] = (uint8_t)(x2 & 0x00FF);
    buffer[10] = (uint8_t)((x2 & 0xFF00) >> 8);
    buffer[11] = (uint8_t)(y2 & 0x00FF);
    buffer[12] = (uint8_t)((y2 & 0xFF00) >> 8);
 
    // Number of touches   
    buffer[13] = numTouch;
     
    // Reset transfer status. This value is set to 1 in EP1_OUT_Callback().
    PrevXferComplete = 0;
     
    // Wait until the data transmission is finished
    while (GetEPTxStatus(ENDP1) == EP_TX_VALID); //0x30
     
    // Send report
    USB_SIL_Write(EP1_IN, buffer, 14);
    SetEPTxValid(ENDP1);
     
    /* Debug message via USART */
    printf("SendHIDReport\n");
}

Outcomes