Forum: Mikrocontroller und Digitale Elektronik ATmega32u2/32u4: USB HID descriptor für touchscreen unter Android


von Arno N. (jjuueerrggeenn)


Lesenswert?

Hallo.
Ich versuche seit einiger Zeit einen kleinen Touchscreen Monitor mit 
einem atmega32ux dazu zu bewegen, das Android auf ihn reagiert.
Leider bin ich mittlerweile so durcheinander gekommen, das ich erstmal 
keine Codeschnuipsel posten kann.
Hatte zwar schon einen 'Touch' bekommen, aber die Koordinaten werden 
nicht ünertragen.

Mein Grundgerüst war die LUFA Mouse Class Demo.
Hatte dann den Report geändert. Also die X/Y Koordinaten auf 16 Bit 
unsigned (uint16_t) sowie den Mouse-Descriptor geändert.

Das Gerät wird auch unter Windows (meist) einwandfrei erkannt, aber viel 
passiert trotzdem nicht.

Hat wer vielleicht schon ein ähnliches Problem gehabt und eine Lösung 
dafür?
Ich möchte nur absolute Koordinaten und (mindestens) einen Touch-Event 
übertragen.
Wie wäre egal. Kann auch eine Maus mit absoluten Koordinaten sein. Das 
funzt zwar unter Windows, aber nicht unter Android.

Verwendet wird die Android Version 4.2.2 (FInless 1.5) mit Kernel 
3.0.36+ auf einem Tropnsmart MK908 mit 2G/8G.

Die Touch Auswertung läuft auch halbwegs (Verbesserungspotential gibts 
ja immer). Es fehlt nur die Anbindung an den Android-Stick.

Zur Kontrolle nehme ich immer das HID Descriptor Tool von USB.org und 
erstelle auch damit den Descriptor.

Recherchen i Internet waren immer recht erfolglos.

Ich schau noch, ob ich den Descriptor mit Touch finde (muss von anderer 
Partition booten) und reiche den nach.

Viele Grüsse
Jürgen

von Arno N. (jjuueerrggeenn)


Angehängte Dateien:

Lesenswert?

Hier mal der Descriptor, bei dem es mit der geänderten
LUFA CLASS MOUSE DEMO
mit dem Touch funktioniert:
1
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
2
    0x09, 0x02,                    // USAGE (Mouse)
3
    0xa1, 0x01,                    // COLLECTION (Application)
4
    0x09, 0x01,                    //   USAGE (Pointer)
5
    0xa1, 0x00,                    //   COLLECTION (Physical)
6
    0x05, 0x0d,                    //     USAGE_PAGE (Digitizers)
7
    0x09, 0x33,                    //     USAGE (Touch)
8
    0x19, 0x01,                    //     USAGE_MINIMUM (Digitizer)
9
    0x29, 0x01,                    //     USAGE_MAXIMUM (Digitizer)
10
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
11
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
12
    0x95, 0x01,                    //     REPORT_COUNT (1)
13
    0x75, 0x01,                    //     REPORT_SIZE (1)
14
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
15
    0x95, 0x01,                    //     REPORT_COUNT (1)
16
    0x75, 0x07,                    //     REPORT_SIZE (7)
17
    0x81, 0x01,                    //     INPUT (Cnst,Ary,Abs)
18
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
19
    0x09, 0x30,                    //     USAGE (X)
20
    0x09, 0x31,                    //     USAGE (Y)
21
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
22
    0x26, 0xff, 0x03,              //     LOGICAL_MAXIMUM (1023)
23
    0x65, 0x00,                    //     UNIT (None)
24
    0x75, 0x10,                    //     REPORT_SIZE (16)
25
    0x95, 0x02,                    //     REPORT_COUNT (2)
26
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
27
    0xc0,                          //   END_COLLECTION
28
    0xc0                           // END_COLLECTION
29
[/code>]
30
31
Datei für das HID Descriptor Tool von USB.org im Anhang.
32
33
Weiter habe och in der Mouse.c
34
den Teil der Button und Joystick Abfrage geändert, so dass sinnvolle Werte entstehen.
35
[code}
36
if (JoyStatus_LCL & JOY_UP)
37
{
38
  MouseReport->X = 100;
39
  MouseReport->Y = 100;
40
  MouseReport->Button |= (1 << 0);
41
}
42
43
if (JoyStatus_LCL & JOY_DOWN)
44
{
45
  MouseReport->X = 100;
46
  MouseReport->Y = 200;
47
  MouseReport->Button |= (1 << 0);
48
}
49
50
if (JoyStatus_LCL & JOY_LEFT)
51
{
52
  MouseReport->X = 200;
53
  MouseReport->Y = 100;
54
  MouseReport->Button |= (1 << 0);
55
}
56
57
if (JoyStatus_LCL & JOY_RIGHT)
58
{
59
  MouseReport->X = 200;
60
  MouseReport->Y = 200;
61
  MouseReport->Button |= (1 << 0);
62
}
63
64
if (JoyStatus_LCL & JOY_PRESS)
65
  MouseReport->Button |= (1 << 0);

Dann noch den Report angepasst:
1
typedef struct
2
{
3
  uint8_t Button; // unverändert
4
  uint16_t  X; // von int8_t auf uint16_t geändert
5
  uint16_t  Y; // von int8_t auf uint16_t geändert
6
} ATTR_PACKED USB_MouseReport_Data_t;

Wie gesagt:
Touch funktionert.
Anm einer freien auf dem Bildschrim kommt bei langem Touch der Dialog 
für den Hintergrund. Es lasen sich, mit der Maus vorher entsprechend 
positioniert, auch Apps starten.

von Arno N. (jjuueerrggeenn)


Angehängte Dateien:

Lesenswert?

Hier mal das gesamte Projekt (nach 'make clean') für AVR Studio 6.1

Das HID Descriptor Tool von USB.org gibt es hier:
http://www.usb.org/developers/hidpage/dt2_4.zip

von Arno N. (jjuueerrggeenn)


Lesenswert?

Als Grundlage bin ich hiervon ausgegangen:
http://source.android.com/devices/tech/input/touch-devices.html

Sorry für die vielen Antworten... Kommt halt kleckerweise...

von Michael D. (nospam2000)


Lesenswert?

Arno Nüm schrieb:
>  uint16_t  X; // von int8_t auf uint16_t geändert
>  uint16_t  Y; // von int8_t auf uint16_t geändert

Nach meinem Verständnis sind die logical values immer signed. Solange 
man nur Werte >=0 und <=32767 verwendet ist das aber egal. Ich hab bei 
mir die Werte min=-32768 und max=32768 verwendet um genauer 
positionieren zu könnnen (SubPixel Mouse Movement :-), ist aber 
vielleicht doch etwas übertrieben.

 >  0x05, 0x0d,                    //     USAGE_PAGE (Digitizers)
 >  0x09, 0x33,                    //     USAGE (Touch)

Ob man die Usage Digitizers:Touch oder Desktop:Mouse verwendet scheint 
keine große Rolle zu spielen, zumindest unter Windows und Mac OSX. Je 
nachdem welcher Treiber sich dafür zuständig fühlt kann es sich aber 
auswirken.

Windows hat die Einschränkung, dass man ohne eigenen Treiber auf den 
ersten Bildschirm beschränkt ist.

Bei MacOS gibt es 15% logischen Rand um den eigentliche Desktop (mit dem 
Standard-Treiber und Standard Settings), d.h. für min=0 und max=1023 
bewegt sich der Pointer nur zwischen den Werten 76 und 946, ansonsten 
klebt er am Rand.

Ich hab das letztens für den Arduino Leonardo (ATmega32U4) umgesetzt, 
d.h. den Core angepasst. Siehe auch Kommentare und Verweis auf den 
Pull-Request in Git: 
[https://github.com/arduino/Arduino/issues/1417#ref-commit-7b77d1e]

  Michael

von Arno N. (jjuueerrggeenn)


Lesenswert?

Hallo und danke für die Antwort.
Leider kann ich das mit einem Arduino Leonardo noch nicht tsten, da die 
grad erst bestellt wurden.
Habe sonst nur noch einen MattairTech MT-DB-U4 hier, der aber, trotz 
gewisser Konpatibilitäten, nicht so will. Mein XP muckt auch nach 
einiger Zeit...

Würde bei dem Porjekt auch gerne bei AVRStudio bleiben, auch, wenn ich 
schon eine ganze Kiste von Arduino Teilen habe. Di emeisten aber ohne 
Bootloader, da alles über AVRStudio...
Wobei ich mir ja zumindest mal den Descriptor auslesen könnte, wenns 
funzt. :D

von Michael D. (nospam2000)


Lesenswert?

Arno Nüm schrieb:
> Leider kann ich das mit einem Arduino Leonardo noch nicht tsten, da die
> grad erst bestellt wurden.

Das muss kein Arduino Board sein. Ich hab das auch hauptsächlich mit 
einem Teensy 1.0++ mit AT90USB646 und einem FLIP Bootloader gemacht. Es 
geht ja nicht um das Board oder den Bootloader, sondern nur um die USB 
HID Features.

> Habe sonst nur noch einen MattairTech MT-DB-U4 hier, der aber, trotz
> gewisser Konpatibilitäten, nicht so will. Mein XP muckt auch nach
> einiger Zeit...

Deine Report-Struktur kommt mir komisch vor:
1
typedef struct
2
{
3
  uint8_t Button; // unverändert
4
  uint16_t  X; // von int8_t auf uint16_t geändert
5
  uint16_t  Y; // von int8_t auf uint16_t geändert
6
} ATTR_PACKED USB_MouseReport_Data_t;

Der Compiler wird höchstwahrscheinlich zwischen 'Button' und 'X' ein 
Füllbyte einfügen. Das siehst du am einfachsten, wenn Du die erwartete 
Strukturgröße (5 Byte) mit der tatsächlichen (ich vermute: 6 Byte) 
vergleichst. Ich kann das heute zeitlich nicht mehr überprüfen.
Außerdem sollte man bei Netzwerk-Protokollen nicht auf die endianess der 
CPU verlassen (USB benötigt 'little endian') und die Bytes besser direkt 
dahin schreiben wo sie hin müssen.
Probier mal folgendes:
1
#define LSB(v) ((v >> 8) & 0xff)
2
#define MSB(v) (v & 0xff)
3
void FillUsbReport(uint8_t reportData[5], uint8_t buttons, int16_t x, int16_t y)
4
{
5
  reportData[0] = buttons;
6
  reportData[1] = LSB(x);
7
  reportData[2] = MSB(x);
8
  reportData[3] = LSB(y);
9
  reportData[4] = MSB(y);
10
}

Hier mal den Descriptor, den ich verwende:
1
#if 1
2
    0x05, 0x01,                    // USAGE_PAGE (Generic Desktop)
3
    0x09, 0x02,                    // USAGE (Mouse)
4
    0xa1, 0x01,                    // COLLECTION (Application)
5
    0x09, 0x01,                    //   USAGE (Pointer)
6
    0xa1, 0x00,                    //   COLLECTION (Physical)
7
#else
8
    0x05, 0x0D,                    // USAGE_PAGE (Digitizers)
9
    0x09, 0x01,                    // USAGE (Digitizer)
10
    0xa1, 0x01,                    // COLLECTION (Application)
11
    0x09, 0x14,                    //   USAGE (Stylus)
12
    0xa1, 0x02,                    //   COLLECTION (Logical)
13
#endif
14
    //0x85, HID_REPORTID_MOUSE_ABS,  //     REPORT_ID (5)
15
    0x05, 0x09,                    //     USAGE_PAGE (Button)
16
    0x19, 0x01,                    //     USAGE_MINIMUM (Button 1)
17
    0x29, 0x03,                    //     USAGE_MAXIMUM (Button 3)
18
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
19
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
20
    0x95, 0x03,                    //     REPORT_COUNT (3)
21
    0x75, 0x01,                    //     REPORT_SIZE (1)
22
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
23
    0x95, 0x01,                    //     REPORT_COUNT (1)
24
    0x75, 0x05,                    //     REPORT_SIZE (5)
25
    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)
26
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
27
    0x09, 0x30,                    //     USAGE (X)
28
    0x09, 0x31,                    //     USAGE (Y)
29
    0x09, 0x38,                    //     USAGE (Wheel)
30
    0x16, 0x00, 0x80,              //     LOGICAL_MINIMUM (-32768)
31
    0x26, 0xff, 0x7f,              //     LOGICAL_MAXIMUM (32767)
32
    0x36, 0x00, 0x80,              //     Physical minimum  = -37678
33
    0x46, 0xFF, 0x7f,              //     Physical maximum  =  32767
34
35
    //0x66, 0x00, 0x00               //     Unit (None) TODO: is this a good idea and is it correct?
36
    0x75, 0x10,                    //     REPORT_SIZE (16)
37
    0x95, 0x03,                    //     REPORT_COUNT (3)
38
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
39
    0xc0,                          //   END_COLLECTION
40
    0xc0,                          // END_COLLECTION


Wenn Du aus dem #if 0 ein #if 1 machst, wird anstatt der Maus ein 
Digitizer emuliert.

Die ReportID brauchst du wohl nicht, wenn du nicht unterschiedliche 
Reports pro Endpoint schicken willst

Ich habe 3 Maus-Buttons, eine x-, y- und eine wheel-Achse definiert.

 Michael

von Arno N. (jjuueerrggeenn)


Lesenswert?

Soooooo...

Ansatzweise habe ich es!

Descriptor für 800x480:
1
    0x05, 0x0d,                    // USAGE_PAGE (Digitizers)
2
    0x09, 0x01,                    // USAGE (Digitizer)
3
    0xa1, 0x01,                    // COLLECTION (Application)
4
    0x09, 0x04,                    //   USAGE (Touch Screen)
5
    0xa1, 0x00,                    //   COLLECTION (Physical)
6
    0x05, 0x0d,                    //     USAGE_PAGE (Digitizers)
7
    0x09, 0x33,                    //     USAGE (Touch)
8
    0x09, 0x32,                    //     USAGE (In Range)
9
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
10
    0x25, 0x01,                    //     LOGICAL_MAXIMUM (1)
11
    0x75, 0x01,                    //     REPORT_SIZE (1)
12
    0x95, 0x02,                    //     REPORT_COUNT (2)
13
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
14
    0x75, 0x01,                    //     REPORT_SIZE (1)
15
    0x95, 0x06,                    //     REPORT_COUNT (6)
16
    0x81, 0x03,                    //     INPUT (Cnst,Var,Abs)
17
    0x05, 0x01,                    //     USAGE_PAGE (Generic Desktop)
18
    0x09, 0x30,                    //     USAGE (X)
19
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
20
    0x26, 0x20, 0x03,              //     LOGICAL_MAXIMUM (800)
21
    0x75, 0x10,                    //     REPORT_SIZE (16)
22
    0x95, 0x01,                    //     REPORT_COUNT (1)
23
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
24
    0x09, 0x31,                    //     USAGE (Y)
25
    0x15, 0x00,                    //     LOGICAL_MINIMUM (0)
26
    0x26, 0xe0, 0x01,              //     LOGICAL_MAXIMUM (480)
27
    0x75, 0x10,                    //     REPORT_SIZE (16)
28
    0x95, 0x01,                    //     REPORT_COUNT (1)
29
    0x81, 0x02,                    //     INPUT (Data,Var,Abs)
30
    0xc0,                          //     END_COLLECTION
31
    0xc0                           // END_COLLECTION

Touch funzt, Position funzt.
Der Rest vom COde (weiter oben) ist unverändert.
Naja, die Auswertung der Eingänge hat sich ein wenig geändert und die 
Auswahl des Wertes für den/die Button(s) habe ich auf ein #define 
umgelegt, um das ewige Editieren zu umgehen.

von Arno N. (jjuueerrggeenn)


Lesenswert?

@Michael

Vielen Dank für Deine Mühe. Werde mir das auf alle Fälle auch mal 
genauer anschauen.
Das mit den Descriptoren habe ich mittlerweile wohl geschnallt. 
Zumindest den Aufbau. Ob sie funktionieren ist was anderes...

Das mit den Füllbits habe ich auch schon gelernt.
1
    0x75, 0x01,                    //     REPORT_SIZE (1)
2
    0x95, 0x06,                    //     REPORT_COUNT (6)
Damit kommt man dann auf glatte Bytes (oder so - mir felhen manchmal die 
Fachausdrücke).

Das ganze aufzudröselön ist auch keine schlechte Idee.
Da merkt man, das Du 'etwas' mehr drauf hast als ich.

Werde dann mal anfangen, die Panelauswertung mit dem USB Kram zu 
verbinden.

Der (über)nächste Schritt ist dann, die Daten aus dem Navianschluss 
meines Alpine Radios auszuwerten und einen H/V -> Composite-Sync Adapter 
sowie das Signal des HDMI -> VGA Wandlers einzuspeisen.

Und schon bin ich der erste mit 'nem Auroradio, Android 4.2 und Quadcore 
CPU/GPU!

von Souffleur (Gast)


Lesenswert?

> Damit kommt man dann auf glatte Bytes

Padding ist das Stichwort.

von Elmü M. (elmue)


Lesenswert?

Hallo

Auf der Suche nach einem Touch Screen HID device descriptor hat mich 
Google hierher gebracht und ich habe beide oben geposteten Descriptoren 
getestet.

Mein Vorhaben war einen universalen HID Descriptor zu finden der auf 
ALLEN Betriebssystemen funktioniert.

Die beiden Deskriptoren von Arno und Michael oben taugen dafür beide 
nicht.

Das Probem mit USAGE_PAGE (Digitizers) ist z.B. daß Windows XP diese 
nicht unterstützt.

Der Touchscreen von Michael verwendet DREI buttons!
Kein realer Touchscreen hat 3 Finger oder 3 Stifte.
Mit solchen selbstgebauten Deskriptoren hat man vielleicht Glück auf 
seinem Heimcomputer, aber sobald man damit in die weite Welt geht, wird 
man unweigerlich Betriebssysteme finden die das nicht akzeptieren. Ein 
Treiber für einen Touchscreen ist nicht für 3 Buttons ausgelegt.

Der Linux X11 Server ist voller Bugs und hat schon Probleme mit einer 
Maus die absolute Koordinaten sendet!

Ich bin deshalb auf Nummer Sicher gegangen und habe einen echten 
Touchscreen von ELO Touchsystems geklont.

Wer an einem universellen Touchscreen für alle Betriebssystem 
interessiert ist, der lese meinen Artikel auf Codeproject:
http://www.codeproject.com/Articles/1001891/A-USB-HID-Keyboard-Mouse-Touchscreen-emulator-with

von Michael D. (nospam2000)


Lesenswert?

Elmü Meister schrieb:
> Der Touchscreen von Michael verwendet DREI buttons!
> Kein realer Touchscreen hat 3 Finger oder 3 Stifte.
> Mit solchen selbstgebauten Deskriptoren hat man vielleicht Glück auf
> seinem Heimcomputer, aber sobald man damit in die weite Welt geht, wird
> man unweigerlich Betriebssysteme finden die das nicht akzeptieren. Ein
> Treiber für einen Touchscreen ist nicht für 3 Buttons ausgelegt.

Die Idee hinter HID ist, dass es keine spezifischen Treiber für Mäuse, 
Tastaturen, Touchscreens, etc. mehr gibt sondern genau einen HID Treiber 
der das alles kann.

Man definiert und implementiert gegen die HID Spec, welche keine 
Einschränkungen bei der Anzahl der Buttons macht und nicht gegen die 
Implementierung irgendeines 3rd Party Treiberherstellers welcher 
implizite Annahmen in seinem Code macht.

Der Stift für den Digitizer meines alten Notebooks Toshiba M200 hatte 3 
physikalische Buttons: links, rechts, radierer
Auf wie viele HID Buttons diese abgebildet wurden habe ich nicht 
geprüft.


> Mein Vorhaben war einen universalen HID Descriptor zu finden der auf ALLEN 
Betriebssystemen funktioniert.
> Die beiden Deskriptoren von Arno und Michael oben taugen dafür beide nicht.

Kannst du das mal etwas genauer ausführen, damit ich meinen Descriptor 
korrigieren kann?

  Michael

: Bearbeitet durch User
von Elmü M. (elmue)


Lesenswert?

Hallo Michael

Wie gesagt: Es kann sein dass bei dir dein Descriptor funktioniert.

Aber auf Linux ist das alles sehr buggy implementiert und wenn du willst 
dass dein Descriptor auch auf Linux sicher funktioniert empfehle ich dir 
auf Nummer sicher zu gehen.

> Kannst du das mal etwas genauer ausführen,
> damit ich meinen Descriptor korrigieren kann?

Offenbar hast du meinen Artikel auf Codeproject nicht gelesen ?

Auch Microsoft macht seine Anforderungen an Touchscreens die von deinem 
nicht erfüllt werden:
https://msdn.microsoft.com/en-us/library/windows/hardware/ff553737%28v=vs.85%29.aspx

Du mußt einen TipSwitch und InRange definieren.

Aber wenn du diesen Descriptor benutzt wird er auf Windows XP nicht 
funktionieren.

Deshalb empfehle ich dir meinen (von ELO kopierten) zu nehmen, den ich 
bereits auf etlichen Betriebssystemen getestet habe.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.