Forum: Mikrocontroller und Digitale Elektronik Variable per Timer zurücksetzen


von Lokus P. (derschatten)


Lesenswert?

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.

von Thomas E. (thomase)


Lesenswert?

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.

von Georg G. (df2au)


Lesenswert?

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?

von Lokus P. (derschatten)


Angehängte Dateien:

Lesenswert?

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.

von Lokus P. (derschatten)


Angehängte Dateien:

Lesenswert?

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.

von Stefan E. (sternst)


Lesenswert?

Manfred W. schrieb:
> Aber, das funkt nicht so wie ich will.

Da fehlt immer noch (mindestens) ein volatile.

von Lokus P. (derschatten)


Lesenswert?

und welches?
die Variable encoder?

Das wars aber auch nicht.

von Lokus P. (derschatten)


Lesenswert?

Ich habe eher die Vermutung das der aus der Zählerschleife nicht 
herauskommt.

von Lokus P. (derschatten)


Lesenswert?

Kann mir da keiner einen Tip geben?

von Lokus P. (derschatten)


Lesenswert?

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
}

von Karl H. (kbuchegg)


Lesenswert?

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.

von Lokus P. (derschatten)


Lesenswert?

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.

von Lokus P. (derschatten)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

Das hier
1
int8_t encoder          = 0;

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.

von Lokus P. (derschatten)


Angehängte Dateien:

Lesenswert?

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.

von Lokus P. (derschatten)


Lesenswert?

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:
1
encoder = encoderwert;

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?

von Lokus P. (derschatten)


Lesenswert?

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.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.