Hallo!
Ich möchte einen Drehencoder in ein bestehendes Projekt implementieren.
Dieser soll dazu dienen durch ein Menü zu navigieren. Bisher läuft dies
über Taster. Der Encoder funktioniert auch so, wie er soll. Ich benötige
jedoch Hilfe die Taster in der Menüsteuerung gegen den Drehencoder zu
ersetzen. Vermutlich ist es keine große Sache, komme aber trotzdem nicht
drauf :-)
Was für mich besonders interessant war:
- das Tutorial Drehgeber
- dieser Thread: Beitrag "Encoder mit ATMEGA8"Setup
- ATmega8 auf einem STK500 (Takt: 1MHz)
- LCD (HD 48870) auf Steckbrett an Port C
- Drehencoder ALPS STEC12E auf Steckbrett an Port D (Pin 6 und 7)
- Taster auf dem STK (auch auf dem Steckbrett möglich)
Code
Die einzelnen Code-Teile findet ihr im Anhang. Mein Programm habe ich
auf das nötigste beschränkt. Und ja, ich muss an einigen Stellen noch
aufräumen ;-). Zum Code:
- main: Meine Hauptdatei, sie enthält vor allem die Initialisierung des
Timer1 (hat im Beispielprojekt keine Funktion mehr), Initialisierung des
Timer0 (für das Entprellen), die Hauptschleife sowie einen
auskommentierten Codeblock für das Testen des Drehencoders. Die
eigentlichen Funktionen standen hier, sind aber für die Bedienung nicht
relevant.
- debounce: Code aus dem Artikel Entprellung von Peter Dannegger
- encoder: Code aus dem Artikel Drehgeber für Encoder mit wackligen
Rastpunkten (auch von Peter Dannegger?). Portiert auf den ATmega8 und
Umbau auf die Verwendung von __flash statt PROGMEM.
- lcd_menu: Die Menüerstellung und -steuerung. Basiert auf dem Code von
http://projects.higaski.at/.
- lcd-routines: Code aus dem Artikel
AVR-GCC-Tutorial/LCD-Ansteuerung mit Änderungen für das Menüsystem
und um Stings aus dem Flash ausgeben zu können (Menü soll als nächstes
auf die Nutzung von __flash umgebaut werden).
Problem
In der lcd_menu.c werden die Taster für die Navigation definiert:
1
#define UP_KEY get_key_press (1<<1)
2
#define DOWN_KEY get_key_press (1<<0)
3
#define ENTER_KEY get_key_press (1<<2)
Im Prinzip müsste man diese Definitionen für den Encoder umschreiben.
Ich habe es erst einmal versucht direkt in der Funktion umzuschreiben.
So sieht die Navigation in der lcd_menu.c aus:
1
void browse_menu(void)
2
{
3
do
4
{ show_menu();
5
6
if (UP_KEY)
7
{lcd_clear();
8
selected = menu[selected].up;
9
}
10
11
if (DOWN_KEY)
12
{lcd_clear();
13
selected = menu[selected].down;
14
}
15
16
if (ENTER_KEY)
17
{lcd_clear();
18
if (menu[selected].fp != 0)
19
menu[selected].fp();
20
21
selected = menu[selected].enter;
22
}
23
24
DELAY_MS(50);
25
26
}while(1);
27
}
Ich habe hier folgendes versucht:
1
void browse_menu(void)
2
{
3
do
4
{ show_menu();
5
6
val += encode_read();
7
8
if (val > last_val)
9
{last_val = val;
10
lcd_clear();
11
selected = menu[selected].up;
12
}
13
14
if (val < last_val)
15
{last_val = val;
16
lcd_clear();
17
selected = menu[selected].down;
18
}
19
20
if (ENTER_KEY)
21
{lcd_clear();
22
if (menu[selected].fp != 0)
23
menu[selected].fp();
24
25
selected = menu[selected].enter;
26
}
27
28
DELAY_MS(50);
29
30
}while(1);
31
}
Das brachte mich allerdings nicht an das erhoffte Ziel. Im Prinzip habe
ich versucht meinen Code-Block zum Test des Encoders aus der main auf
die Menüsteuerung zu übertragen. Hier noch der genannte Codeblock aus
der main:
1
int32_t val = 0;
2
3
encode_init();
4
sei();
5
6
char Buffer[30];
7
8
while(1)
9
{
10
11
static int32_t last_val = 256;
12
val += encode_read();
13
14
if (val != last_val)
15
{
16
last_val = val;
17
ltoa(val, Buffer, 10); // convert int into string
18
lcd_clear(); // clear display home cursor
19
lcd_string(Buffer); // put converted string to display
20
}
21
}
Die Variablen val, last_val und Buffer habe ich beim Umbauen der
Menüsteuerung in die encoder-Dateien übernommen und in lcd_menu.c die
encoder.h eingebunden. Damit alle Änderungen klar sind, habe ich noch
meinen Umbauversuch (= die encoder_edit.h, encoder_edit.c und die
lcd_menu_edit.c) mit angehängt.
Ich freue mich auf eure Hilfe!
Gruß Max
P.S.: Der Vollständigkeit halber habe ich die defines in lcd_menu nicht
entfernt. So kann es von anderen noch leichter weiter verwendet werden
:-)
P.P.S.: Falls entgegen meiner Erwartung ein Schaltplan benötigt werden
sollte, reiche ich diesen natürlich nach!
Max B. schrieb:> Das brachte mich allerdings nicht an das erhoffte Ziel.
Sieht aber im Prinzip nicht so schlecht aus.
Da val nur größer werden kann, wenn aus encode_read etwas positives
rauskommt, bzw. kleiner werden kann, wenn encode_read etwas negatives
liefert, könnte man auch auf die ganze aufsummiererei verzichten und
sich ganz einfach den Rückgabewert von encode_read ansehen. Ist er
ungleich 0 und positiv, hat der Benutzer in die eine Richtung gedreht.
Ist er ungleich 0 und negativ dann in die andere Richtung.
Aber im Grunde spricht auch nichts gegen dein Prinzip.
hast du denn schon kontrolliert, ob an dieser Stelle aus dem encode_read
überhaupt etwa ungleich 0 geliefert wird. Sprich: Ob der Encode in
deinem richtigen Programm korrekt funktioniert? Das wäre wichtig, denn
manchmal macht man bei der Übertragung von funktionierendem Code aus
einem Testprogramm in die Produktionsversion blöde Fehler bzw. übersieht
eine Wechselwirkung mit dem restlichen Code.
Karl Heinz schrieb:> Aber im Grunde spricht auch nichts gegen dein Prinzip.
Da war ich zu vorschnell.
Doch, es spricht was dagegen.
Wozu sich 32 Bit Arithmetik einhandeln, wenn es gar nicht notwendig ist.
Zum anderen hat man irgendwann einen Überlauf. Gut. Bei 32 Bit kann ein
Benutzer schon ganz schön kurbeln, bis es so weit ist, aber prinzipiell
.... Geht man einfach nach dem Vorzeichen einer nicht-0 Antwort von
encode_read dann lösen sich beide Probleme ganz von alleine in Luft auf
und einfacher wird der Code auch noch.
Das wichtigste (und peinlichste) vorweg:
Karl Heinz schrieb:> mir fehlt in deiner main() irgendwie der Aufruf von encode_init().> Seh ich ihn nur nicht oder hast du den tatsächlich vergessen?
Das war es! Das ist heute schon das zweite mal, dass mich sowas verrückt
macht... Ist Programmieren nicht ein schönes Hobby? ;-)
Naja, ich möchte ja auch mal für Erheiterung sorgen können :-)
Karl Heinz schrieb:> hast du denn schon kontrolliert, ob an dieser Stelle aus dem encode_read> überhaupt etwa ungleich 0 geliefert wird. Sprich: Ob der Encode in> deinem richtigen Programm korrekt funktioniert?
Ja, mit dem auskommentierten Codeblock in der main. Zuerst gab es da
auch die von dir angesprochenen (unvorhergesehenen) Probleme bzgl.
Konflikte mit bestehendem Code. Aber, wie gesagt, in diesem
auskommentierten Codeblock findet sich auch das "vergessene"
encode_init().
Karl Heinz schrieb:> Da val nur größer werden kann, wenn aus encode_read etwas positives> rauskommt, bzw. kleiner werden kann, wenn encode_read etwas negatives> liefert, könnte man auch auf die ganze aufsummiererei verzichten und> sich ganz einfach den Rückgabewert von encode_read ansehen. Ist er> ungleich 0 und positiv, hat der Benutzer in die eine Richtung gedreht.> Ist er ungleich 0 und negativ dann in die andere Richtung.Karl Heinz schrieb:> Aber im Grunde spricht auch nichts gegen dein Prinzip.Karl Heinz schrieb:> Da war ich zu vorschnell.> Doch, es spricht was dagegen.> Wozu sich 32 Bit Arithmetik einhandeln, wenn es gar nicht notwendig ist.> Zum anderen hat man irgendwann einen Überlauf. Gut. Bei 32 Bit kann ein> Benutzer schon ganz schön kurbeln, bis es so weit ist, aber prinzipiell> .... Geht man einfach nach dem Vorzeichen einer nicht-0 Antwort von> encode_read dann lösen sich beide Probleme ganz von alleine in Luft auf> und einfacher wird der Code auch noch.
Stimmt, das werde ich mal mit auf meine Verbesserungsliste setzen.
Danke für die schnelle Hilfe und den Hinweis ala "Da fehlt ein ;" nach
5h Suchen (im übertragenen Sinne).
Gruß Max
edit: Jetzt werde ich das mal in die defines für die "KEY'S" übertragen
Max B. schrieb:> Danke für die schnelle Hilfe und den Hinweis ala "Da fehlt ein ;" nach> 5h Suchen (im übertragenen Sinne).
Gerne. An dem Punkt war wohl jeder schon mal. Selber sieht man solche
Sachen nicht.
Karl Heinz schrieb:> Selber sieht man solche> Sachen nicht.
Ganz genau! Seit dem ich mit meiner Freundin zusammen wohne gibt es den
Mitbewohner nicht mehr, der genau solche Dinge dann sehen konnte :-P
Gruß Max
Einmal habe ich doch noch Tomaten auf den Augen:
Wenn ich encode_init() nicht vergesse, läuft mein Encoder auch. Was dann
jedoch nicht mehr klappt sind Tastereingaben. Was übersehe ich?
Gruß Max
Du benötigst gar keine 2 Timer, um Taster und Drehgeber abzufragen, das
kann ein Timer ganz alleine. Da die Wiederholrate für den Encoder etwa
10 mal höher sein sollte, lässt du im Timerinterrupt die Buttonroutine
eben nur alle 10 mal durchlaufen, z.B.:
1
ISR(TIMER1_OVF1_vect){
2
int8_tenc_new,diff;
3
staticuint8_tct0,ct1,rpt,btimer;
4
uint8_ti;
5
6
// rotary handling
7
enc_new=0;
8
if(PHASE_A)
9
enc_new=3;
10
if(PHASE_B)
11
enc_new^=1;// convert gray to binary
12
diff=enc_last-enc_new;// difference last - new
13
if(diff&1){// bit 0 = value (1)
14
enc_last=enc_new;// store new as next last
15
enc_delta+=((diff&2)-1);// bit 1 = direction (+/-)
16
}
17
btimer++;
18
if(btimer>9){
19
// button handling
20
i=key_state^~ROTARY_PIN;
21
ct0=~(ct0&i);
22
ct1=ct0^(ct1&i);
23
i&=ct0&ct1;
24
key_state^=i;
25
key_press|=key_state&i;
26
if((key_state&REPEAT_MASK)==0)// check repeat function
Matthias Sch. schrieb:> Du benötigst gar keine 2 Timer, um Taster und Drehgeber abzufragen, das> kann ein Timer ganz alleine.
Guter Hinweis! So wird der Code einfacher und ich habe noch einen Timer
frei :-)
Dennoch würde ich gerne wissen, warum es mit dem aktuellen Code nicht
funktioniert.
Gruß Max
Karl Heinz schrieb:> Denk nicht so sehr daran, was es mit dem einen angegebenen Bit macht,> sondern daran, was es mit den ANDEREN Bits macht
Hehe, ich dachte, der TE hätte meinen 'Wink mit dem Zaunpfahl' (nur
einen Timer) schon verstanden - dann fällt das mit dem TIMSK erstmal
nicht auf. Nur, wenn dann doch noch ein Timer für andere Sachen eröffnet
wird, is' wieder Banane...
Karl Heinz schrieb:> Denk nicht so sehr daran, was es mit dem einen angegebenen Bit macht,> sondern daran, was es mit den ANDEREN Bits macht.Matthias Sch. schrieb:> Hehe, ich dachte, der TE hätte meinen 'Wink mit dem Zaunpfahl' (nur> einen Timer) schon verstanden - dann fällt das mit dem TIMSK erstmal> nicht auf.
Ich habe das Problem hiermit gelöst ;-)