Ich möchte gerne mittels Timer eine Variable setzen.
Die Schaltung wird mit einem Drehencoder betrieben.
(Der Code entspricht dem Beispiel ->
http://www.mikrocontroller.net/articles/Drehgeber)
Das sieht in etwa so aus:
1 | ISR (TIM0_COMPA_vect)
| 2 | {
| 3 | if(timeCount > 0)
| 4 | {
| 5 | timeCount--;
| 6 | if(timeCount == 0)
| 7 | {
| 8 | val = valwert;
| 9 | }
| 10 | }
| 11 | }
| 12 |
| 13 | int8_t encode_read(void)
| 14 | {
| 15 | int8_t val;
| 16 |
| 17 | cli();
| 18 | val = enc_delta;
| 19 | enc_delta = val & 1;
| 20 | sei();
| 21 | return val >> 1;
| 22 | }
| 23 |
| 24 | int8_t encode_read_timeout(void)
| 25 | {
| 26 | int8_t tmp = encode_read();
| 27 |
| 28 | if(tmp != valwert)
| 29 | timeCount = 3000;
| 30 | return tmp;
| 31 | }
| 32 |
| 33 | int main(void)
| 34 | {
| 35 | ioinit(); // PORT setzen
| 36 | eeprom_var(); // EEPROM-Werte auslesen
| 37 | reset(); // C64 Reset durchführen
| 38 | encode_init(); // ENCODER initialisieren
| 39 | sei(); // Interrupt aktivieren
| 40 |
| 41 | for(;;)
| 42 | {
| 43 | val += encode_read_timeout(); // ENCODER-Wert auslesen
| 44 |
| 45 | if (val > 3) val = 3; // ENCODER-Wert auf 3 limitieren
| 46 | if (val < 0) val = 0;
| 47 |
| 48 | switch(val)
| 49 | {
| 50 | case 0:
| 51 | AUSGANG_PORT |= led_setzen[val]; // Aktiven LED-PIN setzen
| 52 | AUSGANG_PORT &= ~(led_loeschen[val]); // Inaktive LED-PIN löschen
| 53 | if (get_key_short(TASTER)) // bei kurzem Tastendruck
| 54 | {
| 55 | kernal_id(CBM);
| 56 | valwert = val;
| 57 | reset();
| 58 | }
| 59 | if(get_key_long(TASTER)) // bei langem Tastendruck
| 60 | {
| 61 | kernal_id(CBM);
| 62 | eeprom_write_byte(&eeprom_kernalid, CBM); // KERNAL-Wert in EEPROM speichern
| 63 | eeprom_write_byte(&eeprom_encoderid, val); // ENCODER-Wert in EEPROM speichern
| 64 | reset();
| 65 | }
| 66 | break;
| 67 | case 1:
| 68 | AUSGANG_PORT |= led_setzen[val];
| 69 | AUSGANG_PORT &= ~(led_loeschen[val]);
| 70 | if (get_key_short(TASTER))
| 71 | {
| 72 | kernal_id(SJIFFY);
| 73 | valwert = val;
| 74 | reset();
| 75 | }
| 76 | if(get_key_long(TASTER))
| 77 | {
| 78 | kernal_id(SJIFFY);
| 79 | eeprom_write_byte(&eeprom_kernalid, SJIFFY);
| 80 | eeprom_write_byte(&eeprom_encoderid, val);
| 81 | reset();
| 82 | }
| 83 | break;
| 84 | case 2:
| 85 | AUSGANG_PORT |= led_setzen[val];
| 86 | AUSGANG_PORT &= ~(led_loeschen[val]);
| 87 | if (get_key_short(TASTER))
| 88 | {
| 89 | kernal_id(SPEED);
| 90 | valwert = val;
| 91 | reset();
| 92 | }
| 93 | if(get_key_long(TASTER))
| 94 | {
| 95 | kernal_id(SPEED);
| 96 | eeprom_write_byte(&eeprom_kernalid, SPEED);
| 97 | eeprom_write_byte(&eeprom_encoderid, val);
| 98 | reset();
| 99 | }
| 100 | break;
| 101 | case 3:
| 102 | AUSGANG_PORT |= led_setzen[val];
| 103 | AUSGANG_PORT &= ~(led_loeschen[val]);
| 104 | if (get_key_short(TASTER))
| 105 | {
| 106 | kernal_id(DOLPHIN);
| 107 | valwert = val;
| 108 | reset();
| 109 | }
| 110 | if(get_key_long(TASTER))
| 111 | {
| 112 | kernal_id(DOLPHIN);
| 113 | eeprom_write_byte(&eeprom_kernalid, DOLPHIN);
| 114 | eeprom_write_byte(&eeprom_encoderid, val);
| 115 | reset();
| 116 | }
| 117 | break;
| 118 | }
| 119 | }
| 120 | }
|
Das funktioniert soweit, das der Timer reagiert wenn der Encoder den
Wert 0 hat. Also wenn ich bei CASE 0 die taste betätige und anschließen
mit dem Encoder die Werte erhöhe, springt dieser nach etwa 3 Sekunden
wieder auf CASE 0. Bei CASE 1,2,3 tut er das allerdings nicht. Da bleibt
er auf dem aktuellen Wert.
Manfred W. schrieb:
> Das sieht in etwa so aus:
Schön. Und wie sieht der gesamte Code aus?
Wie soll man dir helfen, wenn du etwas wesentliches, nämlich die
Deklaration der Variablen unterschlägst?
1 | ISR (TIM0_COMPA_vect)
| 2 | {
| 3 | if(timeCount > 0)
| 4 | {
| 5 | timeCount--;
| 6 | if(timeCount == 0)
| 7 | {
| 8 | val = valwert;
| 9 | }
| 10 | }
| 11 | }
| 12 |
| 13 | int8_t encode_read(void)
| 14 | {
| 15 | int8_t val;
| 16 |
| 17 | cli();
| 18 | val = enc_delta;
| 19 | enc_delta = val & 1;
| 20 | sei();
| 21 | return val >> 1;
| 22 | }
|
Hier passt mit val etwas schon mal ganz gewaltig nicht, also gevaltig.
mfg.
Mach mal val volatile. Zugriffe auf eine Variable, die vom Interrupt
verändert wird, werden sonst seltsam optimiert. Außerdem sollte val
global sichtbar sein.
Gleiches gilt für timeCount.
Und es ist gefährlich, eine globale Variable mit gleichem Namen noch
einmal lokal in einer Funktion zu nutzen. Da sollte man genau wissen,
was man tut.
Compiliert das Programm wirklich ohne Warnungen?
Compiliert fehlerfrei.
Ich häng mal den kompletten Code an.
Abgesehen davon habe ich gemerkt das die Auswertung noch einen anderen
Fehler hat.
ich limitiere ja den Encoderwert im Hauptprogram von 0-3.
encode_read_timeout prüft aber den Wert schon vorher.
Das bedeutet wenn ich schneller drehe läuft der Timer gar nicht an, weil
der Encoder auf einem Wert springt der nicht abgefragt wird in der
Switch-Schleife.
Ich müßte das also komplett ummoddeln.
Ich habe jetzt mal die Variablen eindeutig gemacht.
Das mit der limiterung des Encoder-Wertes weiß ich noch nicht wie ich
das am besten löse.
Aber wie siehts mit dem Timer aus.
Also meine Überlegung war wie folgt:
1.) Beim drücken einer taste wird der aktuelle Encoderwert in die
Variable "encoderwert" gespeichert.
2.) "encode_read_timeout()" überprüft un ob beim Drehen sich der
aktuelle Encoderwert vom gespeicherten unterscheidet.
3.) Ist das der Fall läuft der Timer 300 rückwerts.
4.) Wird 0 erreicht erhält der Encoder den gespeicherten Wert (encoder =
encoderwert;), was zur Folge hat das CASE das entsprechende Programm
aufruft.
Aber, das funkt nicht so wie ich will.
Manfred W. schrieb:
> Aber, das funkt nicht so wie ich will.
Da fehlt immer noch (mindestens) ein volatile.
und welches?
die Variable encoder?
Das wars aber auch nicht.
Ich habe eher die Vermutung das der aus der Zählerschleife nicht
herauskommt.
Kann mir da keiner einen Tip geben?
Warum funktioniert das nur wenn die Variable "encoderwert" 0 ist ?
Wenn die Variable größer 0 ist läuft die IF-Schleife permanent und
erreicht somit nie das Ende des Zählers "timeCount"
1 | int8_t encode_read_timeout(void)
| 2 | {
| 3 | int8_t tmp = encode_read();
| 4 |
| 5 | if(tmp != encoderwert)
| 6 | timeCount = 3000;
| 7 | return tmp;
| 8 | }
|
Das ergibt keinen Sinn
1 | int8_t encode_read_timeout(void)
| 2 | {
| 3 | int8_t tmp = encode_read();
| 4 |
| 5 | if(tmp != encoderwert)
| 6 | timeCount = 3000;
| 7 | return tmp;
| 8 | }
| 9 |
| 10 | ...
| 11 |
| 12 | encoder += encode_read_timeout(); // ENCODER-Wert auslesen
| 13 |
| 14 | if (encoder > 3) encoder = 3; // ENCODER-Wert auf 3 limitieren
| 15 | if (encoder < 0) encoder = 0;
| 16 |
| 17 | switch(encoder)
| 18 | {
| 19 | case 0:
| 20 | AUSGANG_PORT |= led_setzen[encoder]; // Aktiven LED-PIN setzen
| 21 | AUSGANG_PORT &= ~(led_loeschen[encoder]); // Inaktive LED-PIN löschen
| 22 | if (get_key_short(TASTER)) // bei kurzem Tastendruck
| 23 | {
| 24 | kernal_id(CBM);
| 25 | encoderwert = encoder;
| 26 | ....
|
encoderwert ist also offensichtlich eine Zahl von 0 bis 4, eine
'Einstellung' die sich aus der Aufsummierung der Drehbewegungen des
Encoders ergibt.
In der Funktion encode_read_timeout kriegst du aber einen Wert von
encode_read. Dieser Wert ist aber NICHT die Stellung des Encoders,
sondern ein Mass dafür um wieviele Einheiten sich der Encoder seit der
letzten Abfrage gedreht hat.
Du vergleichst hier Äpfel mit Birnen. Auf der einen Seite benutzt du
einen Wert der die tatsächliche Stellung des Encoders wiederspiegelt,
auf der anderen Seite eine Veränderung das Wertes. Das passt nicht
zusammen.
Und PS: es gibt keine If-Schleifen. Eine Schleife ist ein
Programmierkonstrukt, welches gegebenenfalls wiederholt wird, entweder
bedingt oder unbedingt. Aber zentrales Element einer Schleife ist die
Wiederholung. Ein If ist einfach nur die Auswahl von 2 Möglichkeiten und
hat als solches mit einer Schleife überhaupt nichts zu tun.
jetzt wo dus schreibst...
Die Encoderdrehung muß natürlich mit dem Wert 0 verglichen werden, alles
andere ergibt keinen Sinn.
1 | int8_t encode_read_timeout(void)
| 2 | {
| 3 | int8_t tmp = encode_read();
| 4 |
| 5 | if(tmp != 0)
| 6 | timeCount = 3000;
| 7 | return tmp;
| 8 | }
|
Allerdings reagiert das noch etwas seltsam.
Wenn ich den Encoder schneller drehe, wird anscheinend keine Drehung
registriert.
OK, damit läuft es besser:
1 | if (encoder > 2) encoder = 3; // ENCODER-Wert auf 3 limitieren
| 2 | if (encoder < 1) encoder = 0;
|
Kann es sein das diese Abfrage auch zu langsam ist für einen Encoder?
Kann ich den Encoder auf eine andere Art limitieren? Am besten bevor er
gedreht wird.
An dem if iegt es aber anscheinend nicht.
Sondern das die Abfrage ob der Encoder gedreht wurde nicht immer
reagiert.
Das macht aber jetzt keinen Unterschied ob ich schnell oder langsam
drehe.
Lokus Pokus schrieb:
> OK, damit läuft es besser:
>
> 1 | > if (encoder > 2) encoder = 3; // ENCODER-Wert auf 3
| 2 | > limitieren
| 3 | > if (encoder < 1) encoder = 0;
| 4 | >
|
> Kann es sein das diese Abfrage auch zu langsam ist für einen Encoder?
Denke ich eigentlich nicht.
Wie macht sich das 'Symptom' bemerkbar?
Ist eigentlich das hier 1 | if(timeCount > 0)
| 2 | {
| 3 | timeCount--;
| 4 | if(timeCount == 0)
| 5 | {
| 6 | encoder = encoderwert;
| 7 | }
| 8 | }
|
immer noch in der ISR?
Denn was das hier in Summe bewirkt (zumindest hab ich mir das im Kopf so
zusammengereimt) ist: wird der Encoder gedreht, hat man nach Stillstand
des Encoders 3 Sekunden (wenn die Timereinstellung stimmt), um die
Verdrehung mit einem Tastendruck zu bestätigen. Bestätigt man nicht in
der Zeit, dann wird die Auswahl wieder zurückgesetzt.
Wenn das so beabsichtigt ist, dann ist es ok.
Das hier
sollte ein 1 | volatile int8_t encoder = 0;
|
sein, wenn der Wert sowohl in der ISR als auch in der Hauptschleife
verändert werden soll und sich die beiden ganz sicher nicht ins Gehege
kommen sollen.
Ist bereits gesetzt.
Genauso ist es gedacht.
Wird in 3 Sekunden keine Taste betätigt springt der Encoder (die LED)
auf den ursprünglichen Wert. Es soll somit der Wert der mit der Taste
bestätigt wird, fixiert werden.
Also das Symptom ist folgendes:
Ich stelle den Encoder auf 0 und drücke die Taste. Der Wert 0 wird in
Variable "encoderwert" gespeichert.
Drehe dann den Encoder in eine Richtung 2 oder 3 Stellen weiter wandert
die LED und bleibt dort stehen. Der Zähler springt somit nicht an.
Drehe ich den Encoder einen schritt zurück oder vor arbeitet der Zähler
wieder und der Encoder (die LED) springt auf 0.
Manchmal ist jedoch ein vor und zurück erforderlich damit der Zähler
wieder läuft.
Das dubiose dabei ist, es passiert nicht immer.
Und es ist unabhängig davon auf welcher Position der Encoder beim start
steht. Also wenn ich den Wert 1 fixiere, passiert das gleiche.
ich gehe mal davon aus das der Encoder selbst richtig zählt, sonst würde
auch die entsprechende LED nicht leuchten.
Ich habe nun folgendes Versucht um sichtbar zu machen ob die Routine
überhaupt eine Drehung des Encoders erkennt.
Dazu habe ich mir bei
1 | if(tmp != 0)
| 2 | {
| 3 | timeCount = 3000;
| 4 | eeprom_write_byte(&eeprom_testid, tmp);
| 5 | }
|
wie man sieht die Variable ins EEPROM geschrieben und anschließend
ausgelesen.
Der Wert ergibt 1. Also wird die Drehung korrekt erkannt.
Weiter gehts zum Timer:
1 | if(timeCount > 0)
| 2 | {
| 3 | timeCount--;
| 4 | if(timeCount == 0)
| 5 | {
| 6 | eeprom_write_byte(&eeprom_test2id, timeCount);
| 7 | encoder = encoderwert;
| 8 | }
| 9 | }
|
Hier ebenfalls zur Kontrolle ob dieser überhaupt läuft, den Wert ins
EEPROM geschrieben (Zuvor den Wert test2id natürlich auf einen höheren
Wert gesetzt)
Und auch hier passt noch alles. Der Timer zählt bis 0.
Das Problem muß also meiner Meinung nach zwischen:
und
1 | for(;;)
| 2 | {
| 3 | ADCSRA &= !(1<<ADEN); // ADC dekativieren
| 4 | encoder += encode_read_timeout(); // ENCODER-Wert auslesen
|
liegen.
Hier wird dem Encoder mehr oder weniger ein Wert aufgedrängt. Könnte das
dass Problem sein?
Ich habe das jetzt mal so gelöst:
1 | if(timeCount > 0)
| 2 | {
| 3 | timeCount--;
| 4 | }
| 5 |
| 6 | for(;;)
| 7 | {
| 8 | // ADCSRA &= !(1<<ADEN); // ADC dekativieren
| 9 |
| 10 | encoder += encode_read_timeout(); // ENCODER-Wert auslesen
| 11 | if (timeCount == 0) encoder = encoderwert;
| 12 | if (encoder < 1) encoder = 0; // ENCODER-Wert darf nicht kleiner als 0
| 13 | if (encoder > 2) encoder = 3; // und größer als 3 werden
|
Sieht zwar nicht schön aus, dürfte aber funktionieren. Zumindest sind
einige Drehtests problemlos gelaufen.
Vielleicht könnte man das noch eleganter lösen, wenn jemand eine Idee
hat.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|