Forum: Mikrocontroller und Digitale Elektronik Counter Register Überlauf


von stm32 (Gast)


Lesenswert?

Hallo Leute,

habe ein kleines Problem:

Ich habe hier einen Encoder, der über einen STM32 angesprochen wird. Der 
uC beschreibt automatisch ein Counter Register, je nach Zählrichtung. 
Das Register habe ich auf 20000 Werte begrenzt. Eine Umdrehung des 
Encoders bedeutet ebenfalls 20000 Counts. Ich möchte nun die absolute 
Anzahl der Counts anzeigen, dafür benötige ich eine Überlauferkennung.

Wenn ich das so nach dem Motto mache...
1
if(abs(naechsterWert - vorigerWert) > xxx)) ueberlauf();

...benötige ich aber eine Schwelle. Ich bin mir nicht im klaren, wie 
groß diese sein sollte. Wähle ich sie zu groß, erkennt sie keine 
Überläufe, zu klein werden welche dazugedichtet...

Der Encoder wird mit max 50u/s angetrieben, ein Timer, der das 
Counterregister ausließt hat eine Periodendauer von 6ms. Das bedeutet 
pro Timerintervall können max 6000 Counts entstanden sein.

Angenommen meine Werte befinden sich bei 10000 und 6ms später bei 16000 
Counts, dann ist alles gut, die Differenz beträgt 6000.
Kurz darauf kommt der Überlauf: 1. Wert bei 15k, der 2. 6ms später bei 
1k. Die Differenz beträgt jetzt |-4000|=4000. Wähle ich also 6000 als 
untere Grenze (darunter ist ein Überlauf), würde es hinkommen.

Dreht der Encoder aber langsamer, so werden natürlich auch die Counts 
zwischen zwei Abtastpunkten (6ms) weniger. Z.B. nur noch 1000 steps bei 
500rpm. Also wäre alles ein Überlauf :D

Das blöde: Es sollen sowohl positive als auch negative Überläufe erkannt 
werden.
Versteht ihr mein Problem? Wenn ja, wie löst man so etwas?

Danke & schöne Grüße!

von stm32 (Gast)


Lesenswert?

Ich nochmal: Werde es jetzt erst einmal per Overflow Interrupt lösen, 
der eine Flag setzt.

Aber wie geht es ohne Interrupt?

von Karl H. (kbuchegg)


Lesenswert?

stm32 schrieb:

> Aber wie geht es ohne Interrupt?


Du kennst die 'Richtung' in die sich der Zählerstand entwickeln müsste?
(zb wegen bekannter Drehrichtung eines Motor an dem der Encoder hängt)

Wenn der Counter eigentlich größer werden müsste.
   Wenn der neue Wert kleiner als der vorhergehende ist, gab es
   einen Überlauf. 19998 + 4 -> 2
     vorher: 19998
     jetzt:      2
      -> Überlauf, indem 4 Ticks aufgelaufen sind

Wenn der Counter eigentlich kleiner werden müsste
   Wenn der neue Wert größer als der vorhergehende ist, gab es
   einen Unterlauf.
      2 - 4 -> 19998
      vorher:     2
      jetzt:  19998
      -> Unterlauf, indem 4 Ticks in der anderen Richtung aufgelaufen
         sind




Du kennst die Drehrichtung nicht:
  zb. du weisst nur dass sich der Wert von 15k auf 1k verändert hat.

  dazu gibt es 2 Möglichkeiten
     entweder der Counter ist immer größer geworden und hat
     irgendwann mit einem Überlauf die 1k erreicht

     oder der Counter ist in der anderen Richtung sukzessive
     kleiner geworden, hat also tatsächlich
     15000, 14999, 14998, ..., 1002, 1001, 1000
     gezählt.

  Welche ist die richtige Lösung?
  Ist der Counter größer (+Überlauf) geworden, dann hat er
  1k - 15k (und weil das Ergebnis negtiv ist: + 20k): 6k Ticks
  zurückgelegt.   -> Ergebnis A

  Ist der Counter regulär kleiner geworden, dann hat er
  15k - 1k: 14k Ticks zurückgelegt -> Ergebnis B


  Da du aber weißt, das von einer Abfrage zur nächsten, der Counter
  sich maximal um 6k Einheiten verändern kann, KANN daher Ergebnis B
  nicht richtig sein. Der Counter kann sich physikalisch im
  betrachteten Zeitraum nicht um 14k Ticks verändert haben.
  Ergebnis A hingegen liegt mit 6k im Bereich des Möglichen.

  -> Strategie: rechne dir die Werte für beide Möglichkeiten aus
   und mach einen Plausibilitätscheck um zu entscheiden welcher
   von beiden der richtige ist.
   Note: Da Ergebnis A + Ergebnis B in Summe wieder die 20k ergeben
   müssen, und die 6k Schwelle kleiner als die Hälfte davon (10k) ist,
   ist es immer eindeutig welcher Fall vorliegt und man braucht auch
   nur einen der beiden Fälle durchrechnen. Ist der durchgerechnete
   Fall nicht möglich, kann es nur der andere sein.

von chick (Gast)


Lesenswert?

stm32 schrieb:
>Kurz darauf kommt der Überlauf: 1. Wert bei 15k, der 2. 6ms später bei
>1k. Die Differenz beträgt jetzt |-4000|=4000

Das ist ja wohl komplett falsch gerechnet.


Auch ist der Überlauf nicht durch einen Wert kleiner als irgendwas 
gekennzeichnet, sondern größer als irgendwas.

von stm32 (Gast)


Lesenswert?

Danke für deine Antwort!

Karl Heinz Buchegger schrieb:
> Du kennst die 'Richtung' in die sich der Zählerstand entwickeln müsste?
> (zb wegen bekannter Drehrichtung eines Motor an dem der Encoder hängt)

Leider nein, da schnelle Änderungen, fällt somit raus.

Es gibt zwar ein direction Bit, aber ich habe Angst das zu benutzen, 
weil der Encoder sehr hochauflösend ist und die Richtungsänderung 
ziemlich schnell sein kann.

Karl Heinz Buchegger schrieb:
> Du kennst die Drehrichtung nicht:
>   zb. du weisst nur dass sich der Wert von 15k auf 1k verändert hat.

Das schon eher (wenn man z.B. von 3000rpm ausgeht)

Karl Heinz Buchegger schrieb:
> -> Strategie: rechne dir die Werte für beide Möglichkeiten aus
>    und mach einen Plausibilitätscheck.

So in der Richtung wollte ich das ja ausprobieren, kam aber auf keinen 
grünen Ast ;)

Dein Beispiel klingt aber logisch. Ich muss das nur noch in Befehle 
verpacken, werde ich morgen machen.

Dankeschön =)

von stm32 (Gast)


Lesenswert?

chick schrieb:
> Das ist ja wohl komplett falsch gerechnet.
>
>
> Auch ist der Überlauf nicht durch einen Wert kleiner als irgendwas
> gekennzeichnet, sondern größer als irgendwas.

Aber das is das, was ich oben stehen hatte ;) Schlicht und ergreifend 
die Differenz. Dass die nicht stimmt ist ja mehr oder weniger das 
Problem ;)

von Karl H. (kbuchegg)


Lesenswert?

stm32 schrieb:

> Dein Beispiel klingt aber logisch.

Sowas (Überlauf/Unterlauf) kann man sich oft ganz leicht mit einer Uhr 
und einem Sekundenzeiger klar machen.

  erste Zeit:  Sekundenzeiger auf 56
  zweite Zeit: Sekundenzeiger auf 2

Du weisst nicht, ob es sich um eine normale Uhr oder um eine 
rückwärtslaufende Eieruhr handelt.

Aber du weißt, dass der Vorgang (Gewicht, das vom Dach fällt) nicht 
länger als 10 Sekunden gedauert haben kann.

Also wird es wohl eine normale Uhr gewesen sein, bei der der Zeiger über 
60 drüber gelaufen ist.
Denn im anderen Fall hätte der Sekundenzeiger 54 Sekunden gebraucht um 
von der 56 auf die 2 zu kommen. Und das widerspricht dem Grundwissen, 
dass der Vorgang spätestens nach 10 Sekunden vorbei ist.


Dein Glück ist diese Aussage
> Der Encoder wird mit max 50u/s angetrieben, ein Timer, der
> das Counterregister ausließt hat eine Periodendauer von 6ms.
> Das bedeutet pro Timerintervall können max 6000 Counts entstanden
> sein.
Die ist der Schlüssel zur Lösung des Gordischen Knotens.

von Karl H. (kbuchegg)


Lesenswert?

stm32 schrieb:
> chick schrieb:
>> Das ist ja wohl komplett falsch gerechnet.
>>
>>
>> Auch ist der Überlauf nicht durch einen Wert kleiner als irgendwas
>> gekennzeichnet, sondern größer als irgendwas.
>
> Aber das is das, was ich oben stehen hatte ;) Schlicht und ergreifend
> die Differenz. Dass die nicht stimmt ist ja mehr oder weniger das
> Problem ;)

Das Problem ist, dass du dich von den Zahlenwerten hast täuschen lassen.
Eine Differenz errechnet sich immer aus Ende minus Anfang. Immer!

   1k - 15k

und da kommt ganz sicher nicht -4k raus. Schon eher -14k. UNd weil es 
sich um unsigned Arithmetik handelt, kann das nicht negativ sein, 
sondern muss mit +20k beaufchlagt werden um wieder im erlaubten Bereich 
0 bis 20k zu sein.

Wieder: einfach mal mit einer Uhr und einem Sekundenzeiger klarmachen.
Oder auch mit Winkeln am Winkelmesser. 20° minus 40° ergeben -20°. Um in 
den erlaubten Bereich 0 bis 360 zu kommen, müssen da noch 360 dazu. -20 
plus 360 macht 340. Also: 20° minus 40° ergeben 340°. Und natürlich auch 
umgekehrt: 340° plus 40° ergeben 20°

Ein Absolutwert hingegen hat da nichts verloren.

von stm32 (Gast)


Lesenswert?

Ist mir vollkommen klar, habe mir den Sachverhalt hier per 
Sägezahnfunktion dargestellt. Das Problem war es bisher das in 
Anweisungen zu verpacken :-(


Trotzdem natürlich danke für die weitere Ausführung!

Mir kommt gerade noch eine Frage hoch: Ist es überhaupt sinnvoll, den 
absoluten Winkel benutzen zu wollen? Unter Umständen braucht es dafür 
große Variablen, die auch überlaufen können. Mit einem int32 z.B. kann 
ich ca 30 Min auf voller Drehzahl den Winkel loggen (pos + neg).
Ich möchte eigentlich eine Positionsregelung machen. Ist es da 
möglicherweise sinniger, die Anzahl der Überläufe zu zählen und dann per 
modulo Operation den Winkel herauszufinden? Kam mir gerade nur so in den 
Kopf ;)

Und dann noch was zur Laufzeit:
Eine Flag im Interrupt setzen geht schnell denke ich mal. Eine 
verschachtelte If-Konstruktion ist bestimmt länger.
Aber ein Interrupt "stört" auch gleichzeitig die anderen Abläufe. Ich 
habe z.B. einen wichtigen Timer, der das Counter register abtastet. 
Diesen habe ich natürlich hoch priorisiert. Dann sollte das kein Problem 
sein oder?

Die selbe Frage bezüglich der Modulo Operation: Wie viel langsamer ist 
die im Vergleich zum einfachen addieren der Winkel?

Danke euch!

von stm32 (Gast)


Lesenswert?

Oh, da kam schon wieder was neues..

Karl Heinz Buchegger schrieb:
> Dein Glück ist diese Aussage
>> Der Encoder wird mit max 50u/s angetrieben, ein Timer, der
>> das Counterregister ausließt hat eine Periodendauer von 6ms.
>> Das bedeutet pro Timerintervall können max 6000 Counts entstanden
>> sein.
> Die ist der Schlüssel zur Lösung des Gordischen Knotens.

Das hab' ich so festgelegt, sonst käme mir auch der Herr Shannon in die 
Quere ;)

Karl Heinz Buchegger schrieb:
> Das Problem ist, dass du dich von den Zahlenwerten hast täuschen lassen.
> Eine Differenz errechnet sich immer aus Ende minus Anfang. Immer!
>
>    1k - 15k
>
> und da kommt ganz sicher nicht -4k raus. Schon eher -14k. UNd weil es
> sich um unsigned Arithmetik handelt, kann das nicht negativ sein,
> sondern muss mit +20k beaufchlagt werden um wieder im erlaubten Bereich
> 0 bis 20k zu sein.

Oh man. Damit muss ich meine Aussage von oben revidieren, hatte noch 
nicht klick gemacht beim Sägezahn ;) Habe in der Tat die falschen Werte 
verwendet. Statt 15k nämlich 5k, was die Differenz von 15k zu 20k ist.

Dankeschön

von Karl H. (kbuchegg)


Lesenswert?

stm32 schrieb:

> verschachtelte If-Konstruktion ist bestimmt länger.

Programmiers aus. Das hört sich jetzt nur kompliziert an. Im Code ist 
das dann viel einfacher.

von pwm (Gast)


Lesenswert?

So, heute nochmal drüber geguckt.

Fand' die Interrupt Lösung eigentlich cool, aber habe dann festgestellt, 
dass sich mei einem Ripple in 0° das übliche Problem ergibt.

Daher also 6000 als Schwelle verwendet und läuft. Hab' ich übrigens 
vorher auch schon ausprobiert; Es lief nur nicht, weil ich Idiot den 
falschen Header für die abs() Funktion includiert habe.

Jetzt läufts, besten Dank!

von pwm (Gast)


Lesenswert?

Edit: Falscher Benutzername; wird Zeit sich anzumelden ;)

von Karl H. (kbuchegg)


Lesenswert?

>  Ich möchte nun die absolute Anzahl der Counts anzeigen, dafür
> benötige ich eine Überlauferkennung.

Wenn ich mich jetzt gedanklich nicht zu sehr verfranst habe, dann ist 
das hier ....
1
    ....
2
3
    diff = naechsterWert - vorigerWert;
4
    if( diff < 0 )
5
      diff += 20000;
6
7
    if( diff < 10000 )
8
      total += diff
9
    else
10
      total -= 20000 - diff;
11
12
    vorigerWert = naechsterWert;
13
14
    ....

.... die komplette Aufsummierung der Tick Counts auf eine laufende 
Summe.
Komplett ohne Interrupts oder auch nur die Anzahl der Overflows kennen 
zu müssen. Ich sagte doch: Es erklärt sich nur schlecht, der Code selbst 
ist ganz simpel.


Fall 1:  Drehrichtung positiv, ohne Überlauf

    vorigerWert   sei 8000
    naechsterWert sei 12000

   -> also sollten 4000 Ticks addiert werden. Schaun ma mal
1
    diff = naechsterWert - vorigerWert;           +4000   (12 - 8)
2
    if( diff < 0 )                                nein
3
      diff += 20000;
4
5
    if( diff < 10000 )                            Ja
6
      total += diff                               4000 addieren
7
    else
8
      total -= 20000 - diff;
    *** Passt ***


Fall 2:  Drehrichtung positiv, mit Überlauf

    vorigerWert   sei 18000
    naechsterWert sei  2000

   -> also sollten 4000 Ticks addiert werden. Schaun ma mal
1
    diff = naechsterWert - vorigerWert;          -16000    (2 - 18)
2
    if( diff < 0 )                                ja
3
      diff += 20000;                              4000
4
5
    if( diff < 10000 )                            Ja
6
      total += diff                               4000 addieren
7
    else
8
      total -= 20000 - diff;
    *** Passt ***

Fall 3:  Drehrichtung negativ, ohne Unterlauf

    vorigerWert   sei 12000
    naechsterWert sei  8000

   -> also sollten 4000 Ticks subtrahiert werden. Schaun ma mal
1
    diff = naechsterWert - vorigerWert;          -4000   (8 - 12)
2
    if( diff < 0 )                                ja
3
      diff += 20000;                              16000
4
5
    if( diff < 10000 )                            Nein
6
      total += diff                               
7
    else
8
      total -= 20000 - diff;                      4000 subtrahieren (20 - 16)
    *** Passt ***


Fall 4:  Drehrichtung negativ, mit Unterlauf

    vorigerWert   sei  2000
    naechsterWert sei 18000

   -> also sollten 4000 Ticks subtrahiert werden. Schaun ma mal
1
    diff = naechsterWert - vorigerWert;          +16000   (18 - 2)
2
    if( diff < 0 )                                Nein
3
      diff += 20000;                              
4
5
    if( diff < 10000 )                            Nein
6
      total += diff                               
7
    else
8
      total -= 20000 - diff;                      4000 subtrahieren (20 - 16)
    *** Passt ***

: Wiederhergestellt durch User
von pwm (Gast)


Lesenswert?

Wow, so eine ausführliche Antwort hatte ich gar nicht mehr erwartet, ich 
hatte es folgendermaßen gelöst (oder hab' ich da was vergessen?):
1
  if(abs(actualPositionCount - lastPositionCount) > sprungSchwelle)    // überlauf erkannt,z.B.: 1.Wert=15k, 2.Wert=1k -> 1-15=-14. überlauf -> Oder: 1.=10k, 2.=16k -> 2.-1.=6k -> passt
2
  {                               // Am Sägezahn vorstellen, oder an Uhr, oder am Winkel
3
     if (actualPositionCount > lastPositionCount)      // 6000 kommt von 6ms Timer @ 3000rpm und 10k steps/u
4
   {
5
     actualPosition+=(actualPositionCount-lastPositionCount-20000);   // echte Winkelposition aus Counter Value berechnen, underflow
6
   }
7
   else if (actualPositionCount < lastPositionCount)  
8
   {
9
      actualPosition+=(actualPositionCount-lastPositionCount+20000);   // overflow
10
   }
11
   }
12
   else
13
    actualPosition+=(actualPositionCount-lastPositionCount);
14
15
   lastPositionCount = actualPositionCount;

Habe ich da Möglichkeiten nicht mit abgedeckt?

von Karl H. (kbuchegg)


Lesenswert?

pwm schrieb:

> Habe ich da Möglichkeiten nicht mit abgedeckt?


Probiers aus.
Es gibt 4 Fälle, spiel sie durch
(Oder schreib dir schnell am PC ein Programm, welches das für dich tut)

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.