Forum: Mikrocontroller und Digitale Elektronik C: Touch-Region anhand Koordinaten ermitteln?


von Igor (Gast)


Lesenswert?

Von einem analogen Touchpanel erhält man nach Kalibrierung Koordinaten 
der Berührung. Nun möchte ich frei wählbare, rechteckige Regionen 
definieren, deren Berührung dann z.B. eine Funktion ausführt.
Die Frage ist: wie findet man die Region, in der der Berührpunkt liegt?
Nacheinander X-, dann dazugehörige Y-Bereiche prüfen? Oder alles in 
einer Bedingung wie
1
if( x > r1xmin && x < r1xmax && y > r1ymin && y < r1ymax )
2
  meinFunktion();

Oder mit dieser Bedingung und den X/Y-min/max-Werten aus einem Array 
iterieren, samt Funktionzeiger?

von Choose (Gast)


Lesenswert?

Ersteres ist besser...

von Easylife (Gast)


Lesenswert?

Choose schrieb:
> Ersteres ist besser...

Warum?

Ich denke das kommt sehr darauf an, wie viele dieser Regionen du hast, 
und ob die Regionen sich evtl. ändern (z.B. bei mehreren Menüseiten).

Dann würde ich es eher flexibel mit Koordinaten-Array und einer Schleife 
lösen.

Der Weg mit einzelnen Bedingungen bläht dir sonst ziemlich schnell den 
Code auf, und die Schleife kann durch aus auch schneller sein.
Denn die kannst du abbrechen, sobald die passende Region gefunden wurde.

von Karl H. (kbuchegg)


Lesenswert?

Ist aus Zeitsicht völlig egal.

Grundregel: alles was sich auf einer derartig banalen GUI Ebene abspielt 
(was also nicht mit aufwändigen Animationen oder dergleichen verknüpft 
ist), ist aus Rechnersicht nicht zeitkritisch. Ob die Reaktion auf die 
Berührung 1ms früher oder später erfolgt, kann kein menschlicher 
Benutzer je feststellen.

Viel wichtiger ist es, dass du dir den Code vernünftig und wartbar 
organisierst, so dass Änderungen (auch in der Menüstruktur) auch in ein 
paar Wochen noch einfach und fehlerfrei machbar sind und die Haptik in 
Ordnung ist.

> Dann würde ich es eher flexibel mit Koordinaten-Array und einer Schleife lösen.

Allerdings. Nur würde ich das gleich noch weiter treiben und für jeden 
Button eine schöne Struktur bauen. Die komplette Beschreibung ist dann 
ein Array derartiger Buttons. Denn dieses Beschreibung liefert mir dann 
gleich auch die Koordinaten um die für den Benutzer sichtbaren Bereiche 
am Display anzeigen zu können.
1
typedef void (*ButtonFnct)(void);
2
3
struct Button
4
{
5
  uint16_t   xMin;
6
  uint16_t   xMax;
7
  uint16_t   yMin;
8
  uint16_t   yMax;
9
  char*      Label;
10
  ButtonFnct Function;
11
};
12
13
struct Screen
14
{
15
  uint8_t        nrButtons;
16
  struct Button* Buttons;
17
};
18
19
void Help(void);
20
void DoIt1(void);
21
void Setup(void);
22
23
struct Button[] MainButtons =
24
{
25
  {  10, 100,  10,  40, "Help", Help },
26
  { 110, 200,  10,  40, "DoIt", DoIt1 },
27
  { 300, 390, 100, 130, "Setup", Setup ),
28
};
29
30
#define ARRAY_SIZE(x) (sizeof(x)/sizeof(*x))
31
32
struct Screen MainScreen =  { ARRAY_SIZE( MainButtons ), MainButtons };
33
34
void DrawScreen( struct Screen* screen )
35
{
36
  uint8_t i;
37
38
  for( i = 0; i < screen->nrButtons; i++ )
39
  {
40
    drawRectangle( screen->Buttons[i].xMin, screen->Buttons[i].xMax,
41
                   screen->Buttons[i].yMin, screen->Buttons[i].yMax );
42
    drawText( screen->Buttons[i].xMin + 5, screen->Buttons[i].yMax - 25,
43
              screen->Buttons[i].Label );
44
  }
45
}
46
47
void HandlePress( uint8_t x, uint8_t y )
48
{
49
  uint8_t i;
50
51
  for( i = 0; i < screen->nrButtons; i++ )
52
  {
53
    if( x >= screen->Buttons[i].xMin && x <= screen->Buttons[i].xMax &&
54
        y >= screen->Buttons[i].yMin && y <= screen->Buttons[i].yMax )
55
    {
56
      screen->Buttons[i].Function();
57
      return;
58
    }
59
  }
60
}
61
62
int main()
63
{
64
  DrawScreen( &MainScreen );
65
66
  ... komm irgendwie mit den Koordinaten hoch, gedrückte Koordinaten
67
  ... in pressX, pressY
68
69
  if( haveButtonPress )
70
    HandlePress( pressX, pressY );
71
}

nur so als Skizze, wie ich das in etwa organisieren würde.
Sieht auf den ersten Blick viel aus, ist es aber nicht. Die Funktionen 
für zeichnen bzw. auswerten sind so schon mehr oder weniger fertig. Wenn 
ein neuer Screen dazu kommt, definiere ich ein neues Button Array und 
baue daraus eine neue Screen Struktur zusammen
1
struct Button SetupButtons[] =
2
{
3
  { ..... }
4
};
5
6
struct SetupScreen = { ARRAY_SIZE(SetupButtons), SetupButtons };

Kommt ein neuer Button irgendwo dazu, dann schreib ich ihn einfach ins 
Array mitsamt seinen Korrdinaten. Den Rest erledigt der Compiler bzw. 
die Funktionen, so dass eine leichte Erweiterbarkeit bzw. Änderbarkeit 
gegeben ist.

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

Karlheinz, du machst es komplizierter, als es ist. (Muß ich mal wieder 
von der Lernbetty zu reden anfangen?)

Irgendwann sollten auch C-Programmierer mal lernen, objektorientiert zu 
denken und zu programmieren. Da besteht dann ein Menü oder ein Screen 
aus Objekten, die zumindest 2 Methoden haben: eine, um sich selbst zu 
zeichnen und eine andere, um auf hereinkommende Events zu reagieren. In 
der letzteren muß bloß geprüft werden, ob die Touch- oder 
Maus-Koordinate innerhalb der eigenen Bounds liegt oder nicht. Wenn ja, 
kann drauf reagiert werden, wenn nicht, wird der Event einfach 
ignoriert. Sowas ist beliebig und einfach und übersichtlich erweiterbar. 
Selbst runde Bounds sind einfach machbar, per Abstand vom Mittelpunkt - 
ohne daß das übergeordnete programm sich um die Details des 
dargestellten Objekts kümmern müßte.

W.S.

von Karl H. (kbuchegg)


Lesenswert?

W.S. schrieb:
> Karlheinz, du machst es komplizierter, als es ist.

Wieviel soll ich denn den Amateuren noch vorkauen?

> Irgendwann sollten auch C-Programmierer mal lernen, objektorientiert zu
> denken und zu programmieren.

Keine Sorge. Ich arbeite seit 20 Jahren ojektorientiert. Ich weiss schon 
wie das geht und wie man das macht.
Aber die Vorschläge sollen ja auch Amateuere noch verstehen können, ohne 
dass ich sie gleich in die 'Geheimnisse' der Vererbung und in virtuelle 
Funktionen auch noch einführen muss. Mir reicht schon, wenn ich ihnen 
Strukturen nahelege und ihnen zeige, wie man damit umgeht. Die meisten 
hier haben doch schon Angst vor Arrays!

> ignoriert. Sowas ist beliebig und einfach und übersichtlich erweiterbar.

Na dann mach mal.
Wenn du bei weniger als doppelt sovielen Lines of Code landest, ziehe 
ich meinen Hut.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Ich würde einfach das Display in feste Tastenregionen aufteilen, z.B. 
4*5 und dann die Nummer der gedrückten Taste zurück geben.
Dann ist es auch einfacher, zu entprellen und die Flanke zu erkennen.

von Igor (Gast)


Lesenswert?

Danke für die aufgezeigten Ansätze. Da lag ich ja gar nicht so falsch, 
aber natürlich passt ein Funktionszeiger nicht mit in ein 
Koordinaten-Array.. Die Lösung mit der Struktur ist übersichtlicher und 
wartbarer als mehrere Arrays.

Bei unterschiedlich großen Regionen ist eine feste 
Spalten/Zeilen-Aufteilung nicht so sinnvoll, da danach wieder eine 
Zusammenführung erfolgen müsste und auch die Regionengrößen Vielfache 
sein müssten.

Die Entprellung hätte ich "näher an der Leseroutine" angesetzt, also 
unabhängig von den Regionen. Die bekommen nur die "echten" 
Touchereigniskoordinaten.

Eine Optimierung durch Sortierung oder so [Pyramiden?] (falls überhaupt 
möglich) dürfte aber praktisch tatsächlich überflüssig sein. Evtuelle 
Möglichkeiten interessieren mich aber schon :-)

von Karl H. (kbuchegg)


Lesenswert?

Igor schrieb:

> Eine Optimierung durch Sortierung oder so [Pyramiden?] (falls überhaupt
> möglich) dürfte aber praktisch tatsächlich überflüssig sein.

Wie schon gesagt:
Wenn dein Programm innerhalb von 0.01 Sekunden auf einen Benutzeraktion 
reagiert, ist das mehr als genug und für einen Benutzer praktisch 
'sofort'. Für einen µC sind aber 0.01 Sekunden eine halbe Ewigkeit in 
der er zwischendurch noch 'teuflisch schwierige quadratische 
Gleichungen' (tm) zur eigenen Erbauung lösen kann.
Bei 16Mhz macht ein AVR in dieser Zeitspanne immerhin rund 140-tausend 
Instruktionen. Es gibt also keinen Grund sich vor 20*4 
Koordinatenvergleichen und ein bischen Steuerung drumherum zu fürchten. 
Mehr (und zwar richtig mehr) als 20 Bereiche wirst du hoffentlich ja nie 
auf einer Anzeigeseite haben. Da fängt es dann sowieso schon an 
unübersichtlich zu werden.

> Evtuelle
> Möglichkeiten interessieren mich aber schon :-)

Dann google mal nach "Quadtree".

: Bearbeitet durch User
von Igor (Gast)


Lesenswert?

Danke sehr. Ich glaube, ganz grob hatte ich das mit Pyramiden gemeint. 
Ist aber mit etwas Aufwand verbunden:

http://en.wikipedia.org/wiki/Quadtree

"Spacial partitioning", "collision detection"
http://blog.sklambert.com/html5-canvas-game-2d-collision-detection/

Beitrag "kollision in einem polygon finden"

von Mehmet K. (mkmk)


Lesenswert?

Karl Heinz schrieb:
> Na dann mach mal.
> Wenn du bei weniger als doppelt sovielen Lines of Code landest, ziehe
> ich meinen Hut.

Darf ich mich in die Schlange der Wartenden einreihen? Bin zwar kein 
Huttraeger, aber es heisst, dass ich sehr zackig mit den Absaetzen 
knallen und in perfekt preussischer Manier mich vor dem Gegenüber 
verneigen kann.

von Peter D. (peda)


Lesenswert?

Igor schrieb:
> Bei unterschiedlich großen Regionen ist eine feste
> Spalten/Zeilen-Aufteilung nicht so sinnvoll, da danach wieder eine
> Zusammenführung erfolgen müsste und auch die Regionengrößen Vielfache
> sein müssten.

Ich würde auch mal an den Benutzer denken.
Es ist für ihn einfacher zu lernen, wenn die Tasten immer an festen 
Positionen liegen und eine feste ergonomische Größe haben und nicht in 
jedem Menüpunkt lustig umher wandern.
Und Tasten an gleicher Position sollten auch in jedem Menü eine 
zumindest ähnliche Funktion haben.
Schau Dir mal kommerzielle Geräte an, wie die das machen.

Es sei denn Du willst ein Spiel programmieren: Suchen sie die Taste.

: Bearbeitet durch User
von Igor (Gast)


Lesenswert?

Peter Dannegger schrieb:
> Schau Dir mal kommerzielle Geräte an, wie die das machen.

Auch dort gibt es unterschiedlich große Tasten, die nicht alle in einem 
Raster angeordnet sind :-)

von Karl H. (kbuchegg)


Lesenswert?

Igor schrieb:
> Peter Dannegger schrieb:
>> Schau Dir mal kommerzielle Geräte an, wie die das machen.
>
> Auch dort gibt es unterschiedlich große Tasten, die nicht alle in einem
> Raster angeordnet sind :-)

Ein Raster ist allerdings meistens schon zu erkennen, aber Tasten sind 
oft unterschiedlich gross, je nach Wichtigkeit.
Man schaue einfach mal einer KellnerIn auf die seit einigen Jahren 
üblichen Bestellungs-Aufnehme-Terminals, mit denen sie auch die 
Abrechnung machen. Die Tasten sind bei weitem nicht alle gleich groß. 
Die Tasten für den Ziffernblock, mit dem sie Preise ausrechnen kann, 
sind meist größer. Genauso wie die + Taste, bzw. andere oft benötigte 
Waren.
Oder man denke einfach nur an den Ziffernblock auf einer PC Tastatur. 
Enter und + sind größer als die anderen.

von Nosnibor (Gast)


Lesenswert?

Hmm, auf meinem Ziffernblock sind 0, Enter und + zwar größer als die 
anderen Tasten, liegen aber im Raster, d.h. belegen den Platz von genau 
zwei Rasterkästchen.

Einen Touchscreen könnte man also in z.B. 5x4 Kästchen einteilen, und zu 
jedem Menü gehört dann eben ein Array mit 20 Einträgen, die den 
dazugehörigen "Tastencode" oder auch gleich einen Funktionspointer 
enthalten. Das wäre schön einfach für die Software, aber natürlich eine 
deutliche Einschränkung für's Design.

Unabhängig davon ist es natürlich immer sinnvoll, wenn Tasten mit der 
gleichen Bedeutung in jedem Menü an derselben Stelle liegen. Oder wenn 
das "Ja" bei der Rückfrage ("Wollen Sie wirklich...?") an einer 
anderen Stelle liegt als die Taste, die die Rückfrage verursacht hat.

von W.S. (Gast)


Lesenswert?

Karl Heinz schrieb:
> Na dann mach mal.
> Wenn du bei weniger als doppelt sovielen Lines of Code landest, ziehe
> ich meinen Hut.

Eigentlich sollte ich mich jetzt über dich ärgern. Aber ich tu's einfach 
nicht. So, bäh.

Die Grade an Übersichtlichkeit und Wartbarkeit und Buganfälligkeit sind 
nicht proportional zur Anzahl der dastehenden Zeilen. Deshalb ist es mir 
auch lieber, wenn die Dinge sauber voneinander getrennt werden: 
Typdefinitionen, Variablendeklarationen und Initialisierungen. Aber 
gerade bei C-Programmierern, die sich besonders schlau dünken, ist es 
Mode, all sowas in möglichst einen Rülps zusammenzuziehen. Ich denke 
mal, das war so ein bissel dein Hintergedanke.

Aber mal zu deinem Ansatz: Dein DrawScreen und HandlePress sind ziemlich 
albern, weil sie komplett vereinheitlichte innere "Objekte" 
voraussetzen. Entweder ist alles ein rechteckiger Button oder es geht 
garnix. Warst du früher beim Militär? Ein normales Menü hat deutlich 
mehr an inneren Unterschieden: Buttons, statischen Text, Icons, 
Fortschrittsbalken, und und und. Manches davon kann fokussiert werden, 
anderes nicht und die Draw-Methode unterscheidet sich von Objekt zu 
Objekt. Was soll da deine alberne DreawScreen Routine tun? Aufgebläht 
werden, bis du selbst die Übersicht verloren hast? Nein, das ist 
natürlich nicht der richtige Weg.

W.S.

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.