Hallo und einen schönen guten Abend. (inzwischen Nacht)
Als kleine Vorwarnung, ich bin noch recht unerfahren in der C und µC
Programmierung, entsprechend kann der Code etwas abenteuerlich aussehen.
(Vorallem das Inkludieren..)
Programmiert wird in C mit dem GCC (4.8.1) im AVR Studio 6.2.
Der µC ist ein Atmega32 16PU, die Taster sind lowaktiv.
Mit dem Programm möchte ich Analogwerte aufnehmen, auf dem Display
anzeigen und später an einen PC/SD Karte schicken.
Aktuell bin ich dabei, die Menüstruktur aufzubauen und benute für jedes
Menü einen Zustand (Funktion) in einer FSM.
Dort sollen dann statische Texte (liegen noch im RAM) und dynamische
Werte angezeigt werden.
Die FSM basiert auf dem Code:
http://stackoverflow.com/questions/133214/is-there-a-typical-state-machine-implementation-pattern/9322528#9322528
Zum Entprellen der Tasten benutze ich den Code von PeDa.
Hier mal der grobe Aufbau des Menüs:
Das Menü beginnt mit der Funktion für das Startmenü.
Mit den Tasten Hoch(PC5)/Runter(PC4) wird vertikal navigiert, mit
Links(PC2)/Rechts(PC6) "kurz" wird horizontal navigiert, mit PC2/PC6
"lang" übernimmt man die in "Option x einstellen" gesetzten Werte bzw.
geht wieder eine Menüebene hoch.
Jetzt zum eigentlichen Problem:
Mit den Tasten Hoch(PC5)/Runter(PC4) kann ich mich problemlos
navigieren.
Wenn ich im "Startmenü" bin, kann ich zum Testen mit der Taste PC3
"kurz" die LED PB3 und mit PC3 "lang" die LED PB4 togglen.
Wenn ich jedoch in einem der anderen Menüs bin, reagieren die Ausgänge
PB3 und PB4 willkürlich auf einen kurzen Tastendruck von PC3. Ein
"langer" Tastendruck wird nicht mehr erkannt, sondern er geht als
"kurzer" durch.
Selbiges passiert auch mit den Tasten, die für die horizontale
Navigation zuständig sind.
Zum Eingrenzen des Problems:
Das Startmenü funktioniert einwandfrei, in allen anderen Menüs wird
nicht mehr richtig zwischen "get_key_shot" und "get_key_long"
unterschieden.
Leider sehe ich zurzeit nicht, woran das liegt.
[Mod: Quelltext auf Wunsch des TE entfernt, ist unter
Beitrag "Re: [C] GCC Atmega32, Menü über Funktionspointer, PeDa Entprellung, unterscheidet key_short/long nic"
zu finden.]
Hoffentlich habe ich nichts wichtiges vergessen. Wäre schön, wenn da
jemand einen Blick drauf werfen könnte und mir zeigen kann, was da
schief läuft.
Schöne Grüße
Natulo schrieb:> Hoffentlich habe ich nichts wichtiges vergessen.
Doch.
Du hast vergessen die "Wichtige Regeln - erst lesen, dann posten!"
zu lesen, zu verstehen und danach zu handeln.
Im Speziellen interpretiere ich deine Source als "längeren Sourcecode"
der "nicht im Text einzufügen ist, sondern als Dateianhang"
Das war natürlich blöd meinerseits. Ich habe meinen Beitrag mal
gemeldet, vielleicht kann ein Moderator bitte den Quelltext entfernen.
Ich habe die Dateien jetzt als Anhang angehängt.
Der betroffene Code steht in Main.c und Menu.c.
Schöne Grüße
Michael K. schrieb:> vielleicht kann ein Moderator bitte den Quelltext entfernen.
Erledigt, habe einen Link auf deinen neuen Beitrag stattdessen
hinterlegt.
Nochmal einen schönen guten Abend.
Ich habe vorhin die Funktion "menu_option_set_date" in der Menu.c mit
Leben gefüllt.
Beim Ausprobieren ist mir dann aufgefallen, dass innerhalb dieses Menüs,
ähnlich wie im "Startmenü", die Tastenerkennung problemlos funktioniert.
Mit der Ursprungsversion der Funktion, die ich im Beitrag weiter oben
angehängt hatte, funktioniert sie hingegen wie beschrieben nicht. (Es
wird nicht zwischen key_press_short und _long unterschieden, ein Druck
auf PC3 löst willkürlich mal PB3, mal PB4 aus.)
Ich habe mal die funktionsfähige und die "fehlerhafte" Funktion als .txt
angehängt.
Da mich das Verhalten stutzig gemacht hat, habe ich noch ein wenig
herumprobiert.
Im dem Code unten ist eine Stelle markiert. Wenn ich die sechs Zeilen
auskommentiere, tritt das Verhalten bei fast jedem Tastendruck von PC3
auf.
Wenn ich sie jetzt wieder aktiviere, tritt das Verhalten wesentlich
seltener auf. (nur noch alle >>100 Tastendrücke)
Und wie ich jetzt festgestellt habe, tritt das auch im "Startmenü" auf.
Nur nicht ganz so häufig wie in den anderen Menüs, bei denen noch recht
wenig Code drin steht.
Der Fehler wird also durch den Umfang des zu bearbeitenden Codes in den
einzelnen Programmpunkten beeinflusst.
1
voidmenu_option_set_date(state_t*currentState){
2
#define DAY 0
3
#define MONTH 1
4
#define YEAR 2
5
6
staticuint8_ttempDate[3];
7
8
staticuint8_tmode=DAY;
9
10
staticcharwriteOnce=1;
11
if(writeOnce){
12
lcd_clearf();
13
//lcd_clear();
14
lcd_setcursor(1,1);
15
lcd_string("option_set_date");
16
17
tempDate[DAY]=rtc_data[4];
18
tempDate[MONTH]=rtc_data[5];
19
tempDate[YEAR]=rtc_data[6];
20
21
/*lcd_setcursor(2,1);
22
lcd_zahl(tempDate[DAY],2,'m');
23
lcd_data('.');
24
lcd_zahl(tempDate[MONTH],2,'m');
25
lcd_data('.');
26
lcd_zahl(tempDate[YEAR],2,'m');*/
27
28
writeOnce--;
29
}
30
31
32
33
// vvv Das hier ist die besagte Stelle vvv
34
lcd_setcursor(2,1);
35
lcd_zahl(tempDate[DAY],2,'m');
36
lcd_data('.');
37
lcd_zahl(tempDate[MONTH],2,'m');
38
lcd_data('.');
39
lcd_zahl(tempDate[YEAR],2,'m');
40
// ^^^ Je mehr auskommentiert wird, umso häufiger tritt der Fehler auf ^^^
41
42
// Abspeichern der temporären Werte in RTC, bestätigt mit OK
Die long/short Erkennung benötigt, daß sie zur Unterscheidung
entsprechend häufig aufgerufen wird. Kann das durch Programmlaufzeiten
zu lange dauern, muß man es mit im Interrupt tun.
Dein Programm ist ziemlich verzwickt.
Ob das überhaupt so funktionieren kann, die Tastenabfragen alle zusammen
in einer Funktion zu machen und nur einer Variable zuzuweisen, kann ich
nicht überblicken. Zumindest können die if/else-Ketten bewirken, daß
Tasten längere Zeit nicht ausgewertet werden, weil sie weiter hinten in
der Kette stehen.
Ich habe in Menüs die Tasten immer direkt ausgewertet. Dann ist es auch
möglich, daß die selbe Taste in einem Kontext long/short bewirkt, in
einem anderen Kontext aber press/repeat.
Und generell sollten bei einem Kontextwechsel alle Tastenereignisse
gelöscht werden, damit nicht uralte Ereignisse etwas bewirken, die im
vorherigen Kontext nicht benutzt wurden:
Hallo peda und danke für die Antwort.
Peter D. schrieb:> Die long/short Erkennung benötigt, daß sie zur Unterscheidung> entsprechend häufig aufgerufen wird. Kann das durch Programmlaufzeiten> zu lange dauern, muß man es mit im Interrupt tun.
Meinst du damit, die Tasten innerhalb des Interrupts mit den
get_key_short/long abzufragen die Ergebnis der Abfrage dann im
restlichen Programm zu benutzen?
Und könntest du das mit dem löschen der Tastenereignisse bitte näher
ausführen?
Ich habe testweise die Tastenabfrage über die Funktion rausgenommen.
Also in der Main.c die Stelle
1
207//state.input = keyevent();
und in der Menu.c die komplette Funktion "keyevent".
Bis auf "Startmenü", "Optionen" und "Option Date/Set Date" habe ich die
anderen Menüfunktionen rausgenommen.
In den verbliebenen Menüs frage ich die Tasten jetzt nicht mehr über das
Struct ab, sondern direkt über deine Funktionen. (Siehe Anhang)
Am Tastenverhalten hat das jedoch nichts verändert, das Problem bleibt.
Lediglich ein neuer Nebeneffekt ist aufgetreten, den ich bisher so noch
nicht gesehen hatte. Das Menü besteht jetzt erstmal nur noch aus:
1
[ Optionen ] <-> [Option Datum] <-> [Setze Datum]
2
^
3
|
4
v
5
[ Start ]
Wenn ich jetzt z.B. in Start bin und die Taste runter drücke, bei der
eigentlich nichts passieren sollte, und danach die Taste rauf drücke,
sieht man wie sich das "Start" Menü neu aufbaut. Als ob nach dem Menü
noch ein "Schattenmenü" wäre, in etwa so:
1
^ Option < Taste runter führt jetzt wieder zu "Option", nicht zu "Start"
2
| Option < eigentliches Menü, Taste rauf führt zu "Schattenmenü", runter zu Start
get_key_short und get_key_long müssen in jedem Kontext aufgerufen
werden, sonst funktionieren sie nicht.
Man muß sie ja nicht auswerten. Allerdings stellt sich dann die Frage,
warum man überhaupt unterscheidet.
Generell würde ich sie nur sparsam einsetzen, da get_key_short erst auf
Loslassen reagiert, was nicht intuitiv ist.
Versuch mal Dein Menü nur mit press und repeat zu bauen.
So, nochmal einen schönen guten Abend.
Ich habe nochmal ein wenig rumgewühlt und ein wenig rumprobiert.
Dabei bin ich auf einen alten Beitrag von dir gestoßen:
Beitrag "Re: Universelle Tastenabfrage">Die Repeat-Maske darf daher nur die Pins beinhalten, die auch repeatet>werden sollen!
Also in einem kleinen Testprogramm etwas herumgespielt:
1
if(get_key_short(1<<PC0)){
2
PORTB^=(1<<PB6);
3
}elseif(get_key_long(1<<PC0)){
4
PORTB^=(1<<PB7);
5
}
6
7
if(get_key_long(1<<PC1)){
8
PORTB^=(1<<PB1);
9
zahl1++;
10
}
11
12
if(get_key_long(1<<PC2)){
13
PORTB^=(1<<PB2);
14
zahl2++;
15
}
16
17
if(get_key_short(1<<PC3)){
18
19
}elseif(get_key_long(1<<PC3)){
20
PORTB^=(1<<PB3);
21
zahl3++;
22
}
23
24
if(get_key_short(1<<PC4)){
25
26
}elseif(get_key_long(1<<PC4)){
27
PORTB^=(1<<PB4);
28
zahl4++;
29
}
Und dann zahl1 bis zahl4 auf dem Display ausgeben lassen.
In der debounce.h habe ich dann die Repeat_Mask geändert:
1
#define REPEAT_MASK ( 1<<PC0 | 1<<PC2 | 1<<PC4 )
Wie ich konsequenter Weise feststellen konnte:
Bei den Tasten, bei denen die Repeat_Mask nicht gesetzt war, tat sich
nichts. Da wo die Repeat_Mask gesetzt war, funktionierte die Erkennung
problemlos. Ich konnte jetzt auch keinen Unterschied zwischen der
alleinigen Abfrage von if(get_key_long) und der Kombination aus
if(get_key_short) else if (get_key_long) feststellen.
Dann habe ich nochmal in das andere Programm geguckt um zu sehen, was
ich da anders gemacht habe. Da ich mir nicht sicher war, für welche
Tasten ich die Repeat Funktion brauchen würde, hatte ich einfach
geschrieben:
1
#define REPEAT_MASK 0xff
Nach etwas Grübeln dämmerte es mir dann:
An Port C hängen von PC0 bis PC7 Taster, für die ich die internen
Pullups benutze. An PC0 und PC1 hängt aber auch das TWI, an dem die RTC
hängt, die ich jedoch erst später integriert habe.
Darum habe ich das DDRC und PORTC auch so gesetzt:
1
KEY_DDR=0x02;// PINC als Eingang, bis auf PC0 + PC1
2
KEY_PORT=0xfc;// Int. Pull-Ups am Eingang aktiviert, bis auf PC0 + PC1
Da ich aber nicht daran gedacht habe, die Repeat_Mask anzupassen, stand
die nach wie vor auf 0xff, hat sie also die ganze Zeit die Takt- und
Datenleitung des TWI mit abgefragt..
Das könnte dann auch erklären, wieso "volle" Menüfunktionen etwas besser
liefen, er hat schlicht seltener in der main mit RTC_read() die Daten
von der RTC abgefragt und somit seltener auf das TWI zugegriffen.
Da gibt es dann auch noch Verbesserungspotenzial in meinem Programm, die
RTC muss schließlich nicht mit Maximalgeschwindigkeit in der
Hauptschleife abgefragt werden.
Zur Problemlösung: Ich habe in der Debounce.h jetzt nur noch die Tasten
in die Repeat_Mask eingetragen, die ich für die Menünavigation /
Einstellungen auch mit solch einer Funktion verwenden werde.
Dann hätte ich noch eine Frage zu deinem Vorschlag, das Menü nur mit
press und repeat aufzubauen:
Wäre es möglich, mit dem repeat ein ähnliches Verhalten wie mit dem
key_long hinzubekommen?
Die Idee war eigentlich, z.B. beim Stellen einer Uhrzeit:
press up/down zählt die aktuelle Ziffer rauf/runter, rpt macht das dann
bei gedrückt halten automatisch, mit short left/right wollte ich die
aktuelle Stelle verändern und mit long left/right dann die Auswahl
bestätigen ('OK') bzw. in dem Schritt dann eine Menüebene nach oben
gehen.
Mit dem rpt könnte man auch eine kleine Verzögerung hinbekommen,
allerdings wird der Rückgabewert ja permanent geändert, was im
darüberliegenden Menüpunkt dann ebenfalls direkt ausgewertet werden
würde, so lange wie man den Knopf gedrückt hält.
Schöne Grüße