Forum: Mikrocontroller und Digitale Elektronik LCD Menü schreiben


von Stefanie (Gast)


Lesenswert?

Hallo,
ich habe ein 2x16 LCD-Display und 5 Taster zum Steuern und möchte dafür 
ein Menü schreiben.
Wenn ich den mittleren Taster drücke, so wird ein Interrupt ausgelöst 
und in der Interruptfkt wird die Funktion "show_menu" aufgerufen, die 
die ersten beiden Zeilen meines Menüs auf dem Display anzeigt.

Nun habe ich den anderen Tastern auch noch Funktionen zugewiesen und 
zwar
-Scroll UP
-Scroll DOWN
-Enter + Cursor RIGHT
-Cursor LEFT

Die Abfrage, was der Anwender tun möchte wollte ich so lösen 
(for-Schleife zum Entprellen):
  if(DOWN){
    for(i=0;i<255;i++){}
    if(DOWN){
      menue_scroll_down();
    }
  }

  if(UP){
    for(i=0;i<255;i++){}
    if(UP){
      menue_scroll_up();
    }
  }

  if(ENTER){
    for(i=0;i<255;i++){}
    if(ENTER){
      subMenue_Enter();
    }
  }


Mein Problem ist es nun, wie ich auf die Eingabe des Anwenders warte. 
Ich habe schon alles in eine for-Schleife gesteckt, die hochzählt, aber 
das geht viel zu schnell und funktioniert nicht.

Wie löst man den soetwas professionell? Ich meine, der Mikrocontroller 
soll doch auch nicht die ganze Zeit warten, bis vielleicht irgendwann 
der Anwender eine Eingabe macht.

von Karl H. (kbuchegg)


Lesenswert?

> Ich meine, der Mikrocontroller
> soll doch auch nicht die ganze Zeit warten, bis vielleicht irgendwann
> der Anwender eine Eingabe macht.

Doch. Genau das soll er.

Schau mal hier.

http://www.mikrocontroller.net/articles/Entprellung#Komfortroutine_.28C_f.C3.BCr_AVR.29

Die Funktionen sind perfekt geeignet um Eingaben
damit zu machen.

von Stefanie (Gast)


Lesenswert?

Danke für das Tasten entprellen, aber ich habe mich woll etwas ungünstig 
ausgedrückt.

Wenn nun mein externer Interrupt kommt und das Display das Menü anzeigt, 
dann möchte ich ja wissen, ob der Anwender UP, DOWN oder ENTER drückt.

Wie mache ich das? Wenn ich die if-Abfragen in die Interruptroutine 
schreibe,  dann funktioniert es ja nicht, da ja kein Mensch mit dem 
nächsten Takt schon eine Taste gedrückt hat.

Jetzt verständlich? Mir geht es nicht um das Warten bei der 
Tastenentprellung, sondern um das Warten auf die Eingabe des Anwenders.

DANKE!

von bla (Gast)


Lesenswert?

du brauchst irgendwo eine Endlosschleife in der du die Tasten 
abfragst(das ändert nichts an der Tatsache das die Entprellroutinen bei 
deiner Anwendung sinnvoll wären).
In diesem Fall müsstest du dir in der Schleife in einem Flag merken,dass 
Menu gedrückt wurde und entsprechend bei weiteren Eingaben vorgehen.

Gruß

von Karl H. (kbuchegg)


Lesenswert?

Stefanie wrote:
> Danke für das Tasten entprellen, aber ich habe mich woll etwas ungünstig
> ausgedrückt.
>
> Wenn nun mein externer Interrupt kommt und das Display das Menü anzeigt,
> dann möchte ich ja wissen, ob der Anwender UP, DOWN oder ENTER drückt.
>
> Wie mache ich das? Wenn ich die if-Abfragen in die Interruptroutine
> schreibe,  dann funktioniert es ja nicht, da ja kein Mensch mit dem
> nächsten Takt schon eine Taste gedrückt hat.
>
> Jetzt verständlich? Mir geht es nicht um das Warten bei der
> Tastenentprellung, sondern um das Warten auf die Eingabe des Anwenders.
>

Schau dir das Demo zu der Entprellroutine an!
Da ist alles drinn was du für deine Anwendung
brauchst.

> dann funktioniert es ja nicht, da ja kein Mensch mit dem
> nächsten Takt schon eine Taste gedrückt hat.

Bruacht er auch nicht.
Das zentrale Element ist die Endlosschleife. In der Endlos-
schleife fragst du ständig eine Taste nach der anderen ab,
ob sie gedrückt wurde. Wenn ja, machst du die Aktion

Um das Erkennen des Drückens der Tasten kümmern sich die
Entprellroutinenen von PeDa. Darum, dass jedes Tastendruck (selbst
wenn sie eine halbe Stunde gedrückt wird) nur einmal erkannt
wird, kümmern sich die Entprellroutinen von PeDa. Um den
Autorepeat (wenn eine Taste länger gedrückt wird, melden die
Funktionen regelmässig, dass die Taste schon wieder gedrückt
wurde) kümmern sich die Routinen von PeDa.

Die paar Funktionen können alles, was du brauchst um damit
eine Tastenauswertung zu machen. Und im Demo wird auch gezeigt
wie das geht.

Also schaus dir an.

von Karl H. (kbuchegg)


Lesenswert?

Und das ...

> Wenn ich den mittleren Taster drücke, so wird ein Interrupt
> ausgelöst und in der Interruptfkt wird die Funktion "show_menu"
> aufgerufen

.. solltest du gar nicht machen. Tasten legt man auf keinen
Interrupt.

Deine Haupschleife ist mit den Entprellroutinen so simpel
wie:
1
  unsigned char MenuOnScreen;
2
3
  ....
4
5
  MenuOnScreen = 0;
6
7
  while( 1 ) {
8
9
    if( get_key_press( 1<<KEY0 )) {
10
      MenuOnScreen = 1 - MenuOnScreen;    // Menu anzeigen/verbergen
11
12
      if( MenuOnScreen ) {
13
        // das Menue anzeigen
14
      }
15
      else {
16
        // das LCD löschen
17
      }
18
    }
19
20
    if( MenuOnScreen ) {      // wenn das Menue aktiv ist, werden
21
                              // auch die anderen Tasten ausgewertet
22
23
      if( get_key_press( 1<<KEY1 )) {   // Cursor links
24
         // Cursor nach links
25
      }
26
 
27
      if( get_key_press( 1<<KEY2 )) {   // Cursor rechts
28
         // Cursor nach rechts
29
      }
30
31
      if( get_key_press( 1<<KEY3 )) {   // Cursor up
32
         // Cursor nach oben
33
      }
34
35
      if( get_key_press( 1<<KEY1 )) {   // Cursor down
36
         // Cursor nach unten
37
      }
38
    }
39
  }




von Stefanie (Gast)


Lesenswert?

OK, danke nun verstehe es auch ich!

Nur, warum legt man auf Tasten keinen Interrupt? Ich wollte es so 
machen, damit die ganze Zeit etwas anderes getan werden kann und wenn 
der Interrupt kommt, dann soll sich überhaupt erst mal ums Display 
gekümmert werden.

Nun gut, wenn ich nun die obere Routine verwende, kann ich dann die 
if-Schleifen die z.B. Temperaturen überprüfen, mit da rein schreiben.

NOCHMAL VIELEN DANK!!

von bla (Gast)


Lesenswert?

die paar Abfragen in der Schleife kosten "keine" Zeit. Der Controller 
kann auch so noch jede menge andere Sachen machen, die du dann in die 
Endlosschleife integriegen musst.

von Ray (Gast)


Lesenswert?

Hallo,

> Nur, warum legt man auf Tasten keinen Interrupt?
Weil Du dann das ganze Prellen Deiner Tasten als Interruptaufrufe 
bekommst und das können je nach Kontaktart ziemlich viele sein. Besser 
ist es einen Timer-Interrupt wie im Beispiel von PeDa zu nehmen, da Du 
dann in festen Zeitabständen den Zustand der Tasten abfrägst und das 
ganze Prellen der Tasten ausgeblendet wird.

> Nun gut, wenn ich nun die obere Routine verwende, kann ich dann die
> if-Schleifen die z.B. Temperaturen überprüfen, mit da rein schreiben.

Wenn Du obiges Beispiel übernimmst, musst Du alles was Du tun willst in 
die while Schleife reinpacken, da diese endlos läuft und nie mehr 
(regulär) verlassen wird. Evtl. kannst Du auch Sachen (z.B. Temperatur 
prüfen) in den Timerinterrupt verlagern. Dies ist für Routinen 
interesant, die nur selten, aber trotzdem regelmässig ausgeführt werden 
sollen. Aber beachten, dass diese Programmteile nicht zu aufwendig sind 
-> Also ein paar if-Abfragen und Zuweisungen ja, Ausgaben auf die RS232 
nein.

Gruß
Ray

von Stefanie (Gast)


Lesenswert?

Sorry, wenn ich nochmal störe.

Ich habe die Entprellfunktion des obigen Links auf meinen Controller 
gespielt, um die Funktion get_key_press() zu testen.
Es funktioniert alles soweit ganz gut, nur nicht, wenn ich zweimal 
hintereinander die gleiche Taste drücken möchte. Auch wenn ich 
dazwischen lange warte. Warum funktioniert das nicht? Ich bräuchte das 
für meine Anwendung!

DANKE

von Karl H. (kbuchegg)


Lesenswert?

Zeig mal dein Programm.
An den Funktionen liegt es nicht, die hab ich selbst im
Einsatz und gehen tadellos.

von Stefanie (Gast)


Angehängte Dateien:

Lesenswert?

Danke, das wäre super. Ich habe einen Atmega128 mit 14.7456MHz Quarz.

Diese Definitionen stehen in dem Header-File
#define UP    (PINA & 1<<PINA0)
#define DOWN  (PINA & 1<<PINA3)
#define LEFT  (PINA & 1<<PINA2)
#define SHOW  (PIND & 1<<PIND2)
#define ENTER (PINA & 1<<PINA1)  /* RIGHT */

Wie gesagt, manchmal funktioniert der Tastendruck richtig, aber ich kann 
nie zweimal hintereinander eine Taste drücken.

von Zoch (Gast)


Lesenswert?

Mein Ansatz : im Main eine Statusmaschine. Mit dem Timer werden die 
Tasten entprellt und des Status des Menues veraendert. Gewartet wird 
nirgends. Die Statusmaschine verarbeitet Events und produziert Actions.

Z.

von Karl H. (kbuchegg)


Lesenswert?

Deine Interrupt Overflow Zeiten sind etwa um einen
Faktor 3.6 zu lang. Warum hast du denn die Berechnung

  TCNT0 = (u8)(s16)-(XTAL / 1024 * 10e-3 + 0.5);  // preload for 10ms

rausgeschmissen?

Aber das kanns eigentlich nicht sein.

Ich hab jetzt keinen AVR hier, auf der ich dein Pgm mal
laufen lassen könnte. Ich werd am Abend mal dein
Pgm auf meinen Mega16 (hab leider keinen 128er) spielen
und schauen ob man da was sieht.

So offensichtlich fällt mir nichts auf. Das ist das
Pgm aus dem Artikel ergänzt um LCD Funktionen und
angepassten Clock Settings für den Mega128.

Tut mir leid, dass ich dir im Moment nicht weiter
helfen kann.

von Karl H. (kbuchegg)


Lesenswert?

> aber ich kann nie zweimal hintereinander eine Taste drücken.

Auch wenn du dir zwischen den Tastendrücken betont viel Zeit
lässt (sagen wir mal so 1 bis 2 Sekunden)?

Wie gesagt: Deine Overflow Zeiten stimmen nicht. Anstatt 10ms
liegt dein Overflow Intervall bei ca 35ms.

von Stefanie (Gast)


Lesenswert?

OK, ich glaube ich habe den Fehler.
Meine Tasten ziehen den Pegel auf GND, sobald sie gedrückt sind. Ich 
glaube bei den oben beschriebenen Entprellroutinen ist der Pegel auf VCC 
bei Tastendruck.

Ich hab versucht die Routinen umzuschreiben, aber irgendwie versteh ich 
es nicht ganz. Kann mir bitte wer helfen?

von Karl H. (kbuchegg)


Lesenswert?

Das stimmt schon, die Taster muessen nach GND schalten.

Bin grade erst nach Hause gekommen.
Ich schau mal in dein Pgm.

von Karl H. (kbuchegg)


Lesenswert?

Die Lösung ist ganz einfach. Ärgert mich jetzt ein bischen,
eigentlich hätte ich das sehen müssen.

Dein Port mit den Tastern ist zwar default mässig auf
Input. Du hast aber vergessen die Pullup Widerstände
einzuschalten:

int main()
{
  PORTA = 0xFF;



Und der Spuk hat ein Ende.

Ändere bitte die Berechnung des Reload Wertes in der
Interrupt Routine wieder zurück.

von Karl H. (kbuchegg)


Lesenswert?

Ich kann dir nicht mal einen Vorwurf machen.
Die Pullups (bzw. ein Hinweis darauf) werden im
Originalcode vom Artikel auch nicht eingeschaltet.

PeDas Einverständnis mal vorausgesetzt, hab ich den
dazu notwendigen Code hinzugefügt.

Ich hab auch die SIGNAL gegen ISR ausgetauscht.
PeDa hast du ein Problem damit, wenn ich die u8 gegen
uint8_t austausche und so den Code noch etwas mehr
modernisiere?

von Stefanie (Gast)


Lesenswert?

Bin erst jetzt zum Testen gekommen, aber es funktioniert super!!!!

Vielen, vielen Dank!!!!

von Stefanie (Gast)


Lesenswert?

Hallo,
es hat jetzt zwar nichts direkt mit dem Tastenentprellen zu tun, sondern 
ich glaube mehr mit dem Aufbau eines C-Projekts, aber ich hab halt eben 
Probleme damit:

Ich verwende in der Main-Fkt die Fkt get_ket_press() und auch in der 
Datei menue.c

Nun habe ich in der tasten.t die Funktion:
extern uint8_t  get_key_press( uint8_t key_mask );
deklariert und in tasten.c definiert (mit der zugehörigen 
Interruptfunktion).

Komischerweise verwende ich in der main.c auch funktionen, die ich 
woanders definiert habe und die funktionieren bestens.

von Karl H. (kbuchegg)


Lesenswert?

Was ist deine Frage?

von Stefanie (Gast)


Lesenswert?

warum die Fehlermeldung in der main.c und in der menue.c lautet:
undefined reference to 'get_ket_press'
obwohl ich die tasten.h überall eingebunden habe

von Karl H. (kbuchegg)


Lesenswert?

Dazu müssten wir schon den Source Code sehen.

von Olli (Gast)


Lesenswert?

get_ket_press != get_key_press

von Stefanie (Gast)


Lesenswert?

Sorry, ichnehme alles zurück! Habe vergessen das neue File ins makefile 
mit einzutragen!
Tja, Anfängerin...

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.