ESP32S2 实现触摸屏

2022-04-168606
AI 快速预览详细 收起
本文介绍了如何在ESP32 S2上实现触摸屏。首先,需要一个正确的HID Descriptor来定义触摸屏幕的数据结构。接着,文章详细解释了数据结构中每个字节的意义,例如按下标志、按键编号、坐标等。此外,还强调了Get Report处理的重要性,这是使触摸屏正常工作的关键步骤。最后,提供了完整的代码示例,帮助读者快速实现触摸屏功能。


参照Teensy的触摸【参考1】,在 ESP32 S2 上实现了触摸屏。最关键的步骤有2个:
1.     正确的 HID Descriptor,下面是一个10指触摸的触摸屏幕的描述符
  1. static const uint8_t report_descriptor[] = { // 8 TouchData
  2.   0x05, 0x0D,
  3.   0x09, 0x04,
  4.   0xA1, 0x01,
  5.   0x09, 0x22,
  6.   0xA1, 0x02,
  7.   0x09, 0x42,
  8.   0x15, 0x00,
  9.   0x25, 0x01,
  10.   0x75, 0x01,
  11.   0x95, 0x01,
  12.   0x81, 0x02,
  13.   0x09, 0x30,
  14.   0x25, 0x7F,
  15.   0x75, 0x07,
  16.   0x95, 0x01,
  17.   0x81, 0x02,
  18.   0x09, 0x51,
  19.   0x26, 0xFF, 0x00,
  20.   0x75, 0x08,
  21.   0x95, 0x01,
  22.   0x81, 0x02,
  23.   0x05, 0x01,
  24.   0x09, 0x30,
  25.   0x09, 0x31,
  26.   0x26, 0xFF, 0x7F,
  27.   0x65, 0x00,
  28.   0x75, 0x10,
  29.   0x95, 0x02,
  30.   0x81, 0x02,
  31.   0xC0,
  32.   0x05, 0x0D,
  33.   0x27, 0xFF, 0xFF, 0x00, 0x00,
  34.   0x75, 0x10,
  35.   0x95, 0x01,
  36.   0x09, 0x56,
  37.   0x81, 0x02,
  38.   0x09, 0x54,
  39.   0x25, 0x0A,
  40.   0x75, 0x08,
  41.   0x95, 0x01,
  42.   0x81, 0x02,
  43.   0x05, 0x0D,
  44.   0x09, 0x55,
  45.   0x25, 0x0A,
  46.   0x75, 0x08,
  47.   0x95, 0x01,
  48.   0xB1, 0x02,
  49.   0xC0,
  50. };
复制代码

对应发送的数据结构是:
  1.     // touch report
  2.     //  0: on/off + pressure
  3.     //  1: contact id
  4.     //  2: X lsb
  5.     //  3: X msb
  6.     //  4: Y lsb
  7.     //  5: Y msb
  8.     //  6: scan time lsb
  9.     //  7: scan time msb
  10. //  8: contact count
复制代码
其中 Byte0 Bit0 是按下标志,一直为1Bit1-7 是按键压力;Byte1是按键编号,从 0-255,可以理解为手指编号,比如:右手食指按下,编号为0;右手中指按下,编号为1;右手抬起后再次按下,会重新分配一个编号。Byte2-3按键的X坐标;Byte4-5按键的Y坐标;Byte6-7是按压发生的事件,是以 100us为单位;Byte8是当前正在发生的按压事件中触摸点的数量。在【参考2】有一个例子:

ESP32S2 实现触摸屏图1
Table 7 Report Sequence for Two Contacts with Separated Lift(Two-Finger Hybrid)

  
   
Report
   
   
1
   
   
2
   
   
3
   
   
4
   
   
5
   
   
6
   
   
7
   
   
8
   
   
9
   
   
10
   
   
11
   
  
Contact Count
  
  
2
  
  
2
  
  
2
  
  
2
  
  
2
  
  
2
  
  
1
  
  
1
  
  
1
  
  
1
  
  
1
  
  
Contact 1 Tip Switch
  
  
1
  
  
1
  
  
1
  
  
1
  
  
1
  
  
0
  
  
NR
  
  
NR
  
  
NR
  
  
NR
  
  
NR
  
  
Contact 1 X,Y
  
  
X₁,Y₁
  
  
X₂,Y₂
  
  
X₃,Y₃
  
  
X₄,Y₄
  
  
X₅,Y₅
  
  
X₅,Y₅
  
  
NR
  
  
NR
  
  
NR
  
  
NR
  
  
NR
  
  
Contact 2 Tip Switch
  
  
1
  
  
1
  
  
1
  
  
1
  
  
1
  
  
1
  
  
1
  
  
1
  
  
1
  
  
1
  
  
0
  
  
Contact 2 X,Y
  
  
X₁,Y₁
  
  
X₂,Y₂
  
  
X₃,Y₃
  
  
X₄,Y₄
  
  
X₅,Y₅
  
  
X₆,Y₆
  
  
X₇,Y₇
  
  
X₈,Y₈
  
  
X₉,Y₉
  
  
X₁₀,Y₁₀
  
  
X₁₀,Y₁₀
  

  
图中是2个手指
图中是2个手指进行触摸的例子,R1 会分别报告手指1和2移动的信息,同时 Byte8 的 Contract Count 会等于2;R6的时候,因为手指1已经抬起,所以Contract Count会变成1.
.另外一个重要的,容易被忽视的要求:Get Report 的处理。即使上面的描述符正确报告,然后数据也正常发送到Windows中,你的触摸屏依然无法正常工作,原因就是缺少了对Get Report的处理。更糟糕的是:你无法使用 USBlyzer 这样的工具抓到 Teensy 中的数据。
ESP32S2 实现触摸屏图2
Teensy 例子中上位机发送 GET_REPORT 收到返回值0x0a
如果不在代码中特别处理,对于这个命令会 STALL
ESP32S2 实现触摸屏图3
关于这个 COMMAND 的含义,目前没搞清楚【参考3】

对于我们来说,只要有一个返回值就能让它工作正常。最终一个可以工作的代码如下:
  1. #include "USB.h"
  2. #include "USBHID.h"
  3. USBHID HID;
  4. static const uint8_t report_descriptor[] = { // 8 TouchData
  5.   0x05, 0x0D,
  6.   0x09, 0x04,
  7.   0xA1, 0x01,
  8.   0x09, 0x22,
  9.   0xA1, 0x02,
  10.   0x09, 0x42,
  11.   0x15, 0x00,
  12.   0x25, 0x01,
  13.   0x75, 0x01,
  14.   0x95, 0x01,
  15.   0x81, 0x02,
  16.   0x09, 0x30,
  17.   0x25, 0x7F,
  18.   0x75, 0x07,
  19.   0x95, 0x01,
  20.   0x81, 0x02,
  21.   0x09, 0x51,
  22.   0x26, 0xFF, 0x00,
  23.   0x75, 0x08,
  24.   0x95, 0x01,
  25.   0x81, 0x02,
  26.   0x05, 0x01,
  27.   0x09, 0x30,
  28.   0x09, 0x31,
  29.   0x26, 0xFF, 0x7F,
  30.   0x65, 0x00,
  31.   0x75, 0x10,
  32.   0x95, 0x02,
  33.   0x81, 0x02,
  34.   0xC0,
  35.   0x05, 0x0D,
  36.   0x27, 0xFF, 0xFF, 0x00, 0x00,
  37.   0x75, 0x10,
  38.   0x95, 0x01,
  39.   0x09, 0x56,
  40.   0x81, 0x02,
  41.   0x09, 0x54,
  42.   0x25, 0x0A,
  43.   0x75, 0x08,
  44.   0x95, 0x01,
  45.   0x81, 0x02,
  46.   0x05, 0x0D,
  47.   0x09, 0x55,
  48.   0x25, 0x0A,
  49.   0x75, 0x08,
  50.   0x95, 0x01,
  51.   0xB1, 0x02,
  52.   0xC0,
  53. };
  54. class CustomHIDDevice: public USBHIDDevice {
  55.   public:
  56.     CustomHIDDevice(void) {
  57.       static bool initialized = false;
  58.       if (!initialized) {
  59.         initialized = true;
  60.         HID.addDevice(this, sizeof(report_descriptor));
  61.       }
  62.     }
  63.     uint16_t _onGetFeature(uint8_t report_id, uint8_t* buffer, uint16_t len)
  64.       {
  65.         buffer[0]=0x0A;
  66.         return 0x01;
  67.       }
  68.     void begin(void) {
  69.       HID.begin();
  70.     }
  71.     uint16_t _onGetDescriptor(uint8_t* buffer) {
  72.       memcpy(buffer, report_descriptor, sizeof(report_descriptor));
  73.       return sizeof(report_descriptor);
  74.     }
  75.     bool send(uint8_t * value) {
  76.       return HID.SendReport(0, value, 9);
  77.     }
  78. };
  79. CustomHIDDevice Device;
  80. const int buttonPin = 0;
  81. int previousButtonState = HIGH;
  82. uint8_t TouchData[9];
  83. void setup() {
  84.   Serial.begin(115200);
  85.   Serial.setDebugOutput(true);
  86.   Device.begin();
  87.   USB.begin();
  88. }
  89. void loop() {
  90.   if (HID.ready()) {
  91.     Serial.println("Finger");
  92.     // touch report
  93.     //  0: on/off + pressure
  94.     //  1: contact id
  95.     //  2: X lsb
  96.     //  3: X msb
  97.     //  4: Y lsb
  98.     //  5: Y msb
  99.     //  6: scan time lsb
  100.     //  7: scan time msb
  101.     //  8: contact count
  102.     for (int i=0;i<200;i+=100) {
  103.     TouchData[0] = 0x81; TouchData[1] = 0x08;
  104.     TouchData[2] = (16000)&0xFF; TouchData[3] = ((16000)>>8)&0xFF;
  105.     TouchData[4] = (4000+i)&0xFF; TouchData[5] = ((4000+i)>>8)&0xFF;
  106.     TouchData[6] = (millis()*10)&0xFF; TouchData[7] = (millis()*10>>8)&0xFF;
  107.     TouchData[8] = 0x01;
  108.     Device.send(TouchData);
  109.     delay(10);
  110.     TouchData[0] = 0x81; TouchData[1] = 0x08;
  111.     TouchData[2] = (16000)&0xFF; TouchData[3] = ((16000)>>8)&0xFF;
  112.     TouchData[4] = (4000+i)&0xFF; TouchData[5] = ((4000+i)>>8)&0xFF;
  113.     TouchData[6] = (millis()*10)&0xFF; TouchData[7] = (millis()*10>>8)&0xFF;
  114.     TouchData[8] = 0x01;
  115.     delay(10);
  116.     }
  117.     //每隔10秒
  118.     delay(5000);
  119.   }
  120. }
复制代码


再复杂一点,做一个画圆的:

  1. #include "USB.h"
  2. #include "USBHID.h"
  3. USBHID HID;
  4. int STARTX=16000;
  5. int STARTY=12000;
  6. int STARTR=2000;
  7. static const uint8_t report_descriptor[] = { // 8 TouchData
  8.   0x05, 0x0D,
  9.   0x09, 0x04,
  10.   0xA1, 0x01,
  11.   0x09, 0x22,
  12.   0xA1, 0x02,
  13.   0x09, 0x42,
  14.   0x15, 0x00,
  15.   0x25, 0x01,
  16.   0x75, 0x01,
  17.   0x95, 0x01,
  18.   0x81, 0x02,
  19.   0x09, 0x30,
  20.   0x25, 0x7F,
  21.   0x75, 0x07,
  22.   0x95, 0x01,
  23.   0x81, 0x02,
  24.   0x09, 0x51,
  25.   0x26, 0xFF, 0x00,
  26.   0x75, 0x08,
  27.   0x95, 0x01,
  28.   0x81, 0x02,
  29.   0x05, 0x01,
  30.   0x09, 0x30,
  31.   0x09, 0x31,
  32.   0x26, 0xFF, 0x7F,
  33.   0x65, 0x00,
  34.   0x75, 0x10,
  35.   0x95, 0x02,
  36.   0x81, 0x02,
  37.   0xC0,
  38.   0x05, 0x0D,
  39.   0x27, 0xFF, 0xFF, 0x00, 0x00,
  40.   0x75, 0x10,
  41.   0x95, 0x01,
  42.   0x09, 0x56,
  43.   0x81, 0x02,
  44.   0x09, 0x54,
  45.   0x25, 0x0A,
  46.   0x75, 0x08,
  47.   0x95, 0x01,
  48.   0x81, 0x02,
  49.   0x05, 0x0D,
  50.   0x09, 0x55,
  51.   0x25, 0x0A,
  52.   0x75, 0x08,
  53.   0x95, 0x01,
  54.   0xB1, 0x02,
  55.   0xC0,
  56. };
  57. class CustomHIDDevice: public USBHIDDevice {
  58.   public:
  59.     CustomHIDDevice(void) {
  60.       static bool initialized = false;
  61.       if (!initialized) {
  62.         initialized = true;
  63.         HID.addDevice(this, sizeof(report_descriptor));
  64.       }
  65.     }
  66.     uint16_t _onGetFeature(uint8_t report_id, uint8_t* buffer, uint16_t len)
  67.       {
  68.         buffer[0]=0x0a;
  69.         return 1;
  70.       }
  71.     void begin(void) {
  72.       HID.begin();
  73.     }
  74.     uint16_t _onGetDescriptor(uint8_t* buffer) {
  75.       memcpy(buffer, report_descriptor, sizeof(report_descriptor));
  76.       return sizeof(report_descriptor);
  77.     }
  78.     bool send(uint8_t * value) {
  79.       return HID.SendReport(0, value, 9);
  80.     }
  81. };
  82. CustomHIDDevice Device;
  83. const int buttonPin = 0;
  84. int previousButtonState = HIGH;
  85. uint8_t TouchData[9];
  86. void setup() {
  87.   Serial.begin(115200);
  88.   Serial.setDebugOutput(true);
  89.   Device.begin();
  90.   USB.begin();
  91. }
  92. void loop() {
  93.   if (HID.ready()) {
  94.     Serial.println("Finger");
  95.     // touch report
  96.     //  0: on/off + pressure
  97.     //  1: contact id
  98.     //  2: X lsb
  99.     //  3: X msb
  100.     //  4: Y lsb
  101.     //  5: Y msb
  102.     //  6: scan time lsb
  103.     //  7: scan time msb
  104.     //  8: contact count
  105.     for (int i=0;i<101;i++) {
  106.     TouchData[0] = 0x81; TouchData[1] = 0x08;
  107.     TouchData[2] = ((int)(STARTX+STARTR*sin(2*PI*i/100)))&0xFF;
  108.     TouchData[3] = ((int)(STARTX+STARTR*sin(2*PI*i/100)))>>8&0xFF;
  109.     TouchData[4] = ((int)(STARTY+STARTR*cos(2*PI*i/100)))&0xFF;
  110.     TouchData[5] = ((int)(STARTY+STARTR*cos(2*PI*i/100)))>>8&0xFF;
  111.     TouchData[6] = (millis()*10)&0xFF; TouchData[7] = (millis()*10>>8)&0xFF;
  112.     TouchData[8] = 0x01;
  113.     Device.send(TouchData);
  114.     delay(10);
  115.     }
  116.     //每隔10秒
  117.     delay(5000);
  118.     STARTX=STARTX+300;
  119.     STARTY=STARTY+300;
  120.    
  121.   }
  122. }
复制代码
测试结果:

ESP32S2 实现触摸屏图4
视频:
就是这样,不要问这个能干啥, 还没想好。

参考:

创作许可协议

本项目采用 None(不开放任何权利,保留所有权利) 进行许可。

评论(3)
wukonglaoshi
wukonglaoshi
看了你上面描述还有下面的评论,修改你提供的第一个例程进行2个触控点的测试,在win10系统上始终没有正确反馈,不知道是什么原因?一旦设置成2个触控点,就无法反馈,测试代码只改了下面,不知道哪里不对?
void loop() {
if (HID.ready()) {
Serial.println("Finger");
// touch report
// 0: on/off + pressure
// 1: contact id
// 2: X lsb
// 3: X msb
// 4: Y lsb
// 5: Y msb
// 6: scan time lsb
// 7: scan time msb
// 8: contact count
for (int i = 0; i < 16000; i += 100) {
int scan_time = millis() * 10;
TouchData[0] = 0x81; TouchData[1] = 0x01;//触控点1
TouchData[2] = (12000) & 0xFF; TouchData[3] = ((12000) >> 8) & 0xFF;
TouchData[4] = (4000 + i) & 0xFF; TouchData[5] = ((4000 + i) >> 8) & 0xFF;
TouchData[6] = (scan_time) & 0xFF; TouchData[7] = (scan_time >> 8) & 0xFF;//使用相同时间标签与使用不同时间标签
TouchData[8] = 0x02;//修改成2个触控点
Device.send(TouchData);
delay(10);
scan_time = millis() * 10;
TouchData[0] = 0x81; TouchData[1] = 0x02;//触控点2
TouchData[2] = (16000) & 0xFF; TouchData[3] = ((16000) >> 8) & 0xFF;
TouchData[4] = (4000 + i) & 0xFF; TouchData[5] = ((4000 + i) >> 8) & 0xFF;
TouchData[6] = (scan_time) & 0xFF; TouchData[7] = (scan_time >> 8) & 0xFF;//使用相同时间标签与使用不同时间标签
TouchData[8] = 0x02;//修改成2个触控点
Device.send(TouchData);
delay(10);
}
//每隔10秒
delay(5000);
}
}
wukonglaoshi
wukonglaoshi
看了你上面描述还有下面的评论,修改你提供的第一个例程进行2个触控点的测试,在win10系统上始终没有正确反馈,不知道是什么原因?一旦设置成2个触控点,就无法反馈,测试代码只改了下面,不知道哪里不对?
void loop() {
if (HID.ready()) {
Serial.println("Finger");
// touch report
// 0: on/off + pressure
// 1: contact id
// 2: X lsb
// 3: X msb
// 4: Y lsb
// 5: Y msb
// 6: scan time lsb
// 7: scan time msb
// 8: contact count
for (int i = 0; i < 16000; i += 100) {
int scan_time = millis() * 10;
TouchData[0] = 0x81; TouchData[1] = 0x01;//触控点1
TouchData[2] = (12000) & 0xFF; TouchData[3] = ((12000) >> 8) & 0xFF;
TouchData[4] = (4000 + i) & 0xFF; TouchData[5] = ((4000 + i) >> 8) & 0xFF;
TouchData[6] = (scan_time) & 0xFF; TouchData[7] = (scan_time >> 8) & 0xFF;//使用相同时间标签与使用不同时间标签
TouchData[8] = 0x02;//修改成2个触控点
Device.send(TouchData);
delay(10);
scan_time = millis() * 10;
TouchData[0] = 0x81; TouchData[1] = 0x02;//触控点2
TouchData[2] = (16000) & 0xFF; TouchData[3] = ((16000) >> 8) & 0xFF;
TouchData[4] = (4000 + i) & 0xFF; TouchData[5] = ((4000 + i) >> 8) & 0xFF;
TouchData[6] = (scan_time) & 0xFF; TouchData[7] = (scan_time >> 8) & 0xFF;//使用相同时间标签与使用不同时间标签
TouchData[8] = 0x02;//修改成2个触控点
Device.send(TouchData);
delay(10);
}
//每隔10秒
delay(5000);
}
}
天意岸
天意岸
我想问下哈,如果是多点触控,比如两点TouchData数组相当于加长一倍么?那关于最后一位表示按压事件中触摸点的数量,应该全都设置成2么(相当于数组中第8和第17个数就是2)?那这样我看最开始的十点数据也不太对啊
zoologist
zoologist
作者
回复天意岸
这个应该是 ESP32 S2自带的,不是第三方的
天意岸
天意岸回复zoologist
哦哦,我明白了,那最后一个问题就是前面两个include文件怎么搞得,我觉得你这个还是很有用的,目前我想用视觉做虚拟触屏,现在坐标可以返回了就差触屏实现了
共3条回复,已加载2条,点击查看更多共3条回复,已加载全部
- 没有更多了 -