Forum: Mikrocontroller und Digitale Elektronik Abstand zweier Zahlen in 16-Bit (vgl. Galoisfeld)


von Alexander I. (daedalus)


Lesenswert?

Hallo,

ich habe ein Problem, das sicherlich schonmal gelöst wurde. Folgendes:

Ein µC hat eine Timer-gesteuerte Endlosschleife die in 100µs-Zyklen 
arbeitet. Dabei verfügt der Knirps auch über eine 16-Bit Systemzeit die 
alle 100µs um 1 inkrementiert und bei 65535 wieder bei 0 anfängt, 
abgebildet in einem uint16.

Das ermöglicht mir einen Zeitraum von 6,55 Sekunden abzuprüfen, was 
ausreichend ist.

Auf dem µC läuft auch ein kleines serielles Protokoll. Selbiges muß auch 
Timeouts beherrschen um z.B. den Zusammenhang der hereintröpfelnden 
Bytes zu erkennen.

Jetzt ergibt sich folgendes Problem:
Zum Zeitpunkt 65530 wird eine Funktion aufgerufen. Diese setzt einen 
Timeout von 10 Zyklen, also bei 65540! Da wir hier aber nur 16 Bit 
haben, wird der Wert "5" abgespeichert. Soweit so gut.

Im Zeitpunkt 65534 findet eine Überprüfung statt, ob der Timeout schon 
erreicht wurde ... in einem unendlichen Zahlenraum würde das problemlos 
mit der Abfrage
1
if(SystemZeit>Timeout)
2
{
3
 // Timeout erreicht.
4
}
5
else
6
{
7
 // Timeout noch nicht abgelaufen.
8
}

funktionieren ... In diesem speziellen Fall ergibt sich aber das 
Problem, dass Timeout=5 ist und SystemZeit=65534! Obwohl ja der Timeout 
noch nicht abgelaufen ist, trifft die If-Bedingung fehlerhafterweise zu.

Im Endeffekt handelt es sich dabei doch nur um den Abstand in positiver 
Zählweise in einem endlichen Zahlenraum (Galois Feld)? Ich kann mir 
nicht vorstellen, dass ich hier auf weiter Flur allein bin mit dieser 
Problematik. Gibt's da eine geschmeidige Standardlösung für?

Achso und bevor der Vorschlag kommt: ein Long oder ähnlicher Typecast 
kann ich mir aufgrund sehr begrenzter Ressourcen nicht leisten. Ich 
werde auch mehrere von einander unabhängige Timeouts brauchen, d.h. ein 
"vertrimmen" der SystemZeit wie man es halt grade bräuchte (z.B. 
rücksetzen auf 0) ist auch nicht drin...

Vielen Dank.

von olykar (Gast)


Lesenswert?

Hallo,

Frage: wann wird die "Systemzeit > Timeout"- Bedingung abgefragt? Ist es 
sichergestellt, dass nach jedem Interrupt ausgeführt wird, dann würde 
ich "Systemzeit == Timeout" abfragen, der Übertrag wird dann 
uninteressant.
mfG Oswald

von Jens S, (Gast)


Lesenswert?

wenn du einfach die abfrage machst
1
if(SystemZeit != Timeout)
 dann müsste es doch gehen oder?

Die Systemzeit fängt ja beim überlauf auch von 0 wieder an und dann geht 
das doch.

von Martin (Gast)


Lesenswert?

Hallo Alex,

vielleicht so etwas:

if (systemzeit - timeout > 0) {
   // Timeout erreicht
}

Das funktioniert aber nur bis zu Verzögerungszeiten von 32767 Schritten 
korrekt.

Gruß,
Martin

von Alexander I. (daedalus)


Lesenswert?

Hallo,

es kann leider nicht auf "==" und "!=" zurückgegriffen werden, da die 
Tasks priorisiert sind und es so auch mal sein kann, dass einer erst 
einen Zyklus später dran kommt. Der µC hat noch ein paar mehr Aufgaben 
zu erledigen.

Der Vorschlag von Martin scheint mir eine Option zu sein.

von Gast (Gast)


Lesenswert?

timeout = systemzeit;

...

if ( abs(systemzeit - timeout) > 0)
{
 ...
}

von Klaus (Gast)


Lesenswert?

Also wenn mich nicht alles täuscht, hast du den Preis für die simplste 
Frage gewonnen =)

Ich würde den Timeout einfach als Countdown speichern. Also z. B. 5 
Zyklen eine 5 speichern und in jedem Zyklus um ein decrementieren. Dann 
auf 0 überprüfen.

von Peter D. (peda)


Lesenswert?

Du brauchst eine zusätzliche Variable:
1
startzeit = systemzeit;
2
// ...
3
if( (systemzeit - startzeit) > timeout ){
4
// timeout handler
5
}


Peter

von Matthias L. (Gast)


Lesenswert?

>if (systemzeit - timeout > 0) {
>   // Timeout erreicht


Das ist ncihit ganz korrekt.

Ich mache solche Abfragen immer so:
SystemZeit sei der Zeitstempel, der im Zeitraster um eins erhöht wird.
1
if ( zeit soll beginnen )   Zeitstempel = SystemZeit;
2
...
3
if (   ( SystemZeit - Zeitstempel ) 
4
     > ( TimeOut                  )  )
5
{
6
...

Das funktioniert super.


Du brauchst somit für jedes zeitliche Ereignis eine ZeitStempel-Variable 
und kann denselbsen Systemzeit für alle verwenden...

von Stefan K. (_sk_)


Lesenswert?

Es gibt 2 Lösungsansätze:

1. Wenn Du Timeouts > 3,2s brauchst:
------------------------------------
Merk Dir beim Start des Timeouts einen Timestamp:
1
uint16_t Systemzeit;
2
uint16_t Timestamp;
3
uint16_t Timeout;
4
...
5
void set_timeout (uint16_t my_Timeout){
6
  sei();
7
  Timestamp = Systemzeit;
8
  cli();
9
  Timeout = my_Timeout;
10
}
Zum Test, ob der Timeout abgelaufen ist, rechnest Du dann:
1
  sei();
2
  my_Systemzeit = Systemzeit);
3
  cli();
4
  if (Timeout <= (Systemzeit - Timestamp){
5
    // Timeout erreicht
6
  }
Angenommen, beim Start ist die Systemzeit 00FFh:
Dann ist 10 Ticks später die Systemzeit 109h.
Die Rechnung 109h - 000ffh ergibt im 16-Bit-Zahlenraum 0Ah (10dez).

Angenommen, beim Start ist die Systemzeit 0ffffh:
Dann ist 10 Ticks später die Systemzeit 09h.
Die Rechnung 09h - 0ffffh ergibt im 16-Bit-Zahlenraum 0Ah (10dez).

Bei der Berechnung wird ein Überlauf also durch die 16-Bit-Begrenzung 
abgeschnitten, das Ergebnis ist automatisch immer, wie von Dir 
gewünscht.

Nachteil an dieser Methode:
Du musst mit 2 16-Bit-Variablen arbeiten (und den Speicherplatz 
vorhalten).


2. Wenn 3,2s Timeout ausreichen:
--------------------------------
1
uint16_t Systemzeit;
2
uint16_t Timeout;
3
...
4
void set_timeout (uint16_t my_Timeout){
5
  sei();
6
  Timeout = Systemzeit + my_Timeout;
7
  cli();
8
}
1
  sei();
2
  int16_t Timediff = Systemzeit - Timeout;
3
  cli();
4
  if ( Timediff >= 0){
5
    // Timeout erreicht
6
  }

Nicht vergessen:
Ich gehe mal davon aus, dass die Systemzeit im IR hochgezählt wird. Weil 
Zugriffe auf die Systemzeit nicht atomar sind (2 8-Bit Zugriffe), muss 
in main() per sei() / cli() der IR kurzfristig gesperrt werden.

Viele Grüße, Stefan

von Stefan K. (_sk_)


Lesenswert?

oh Mann ... und ich gewinne den Preis für die langsamste Antwort ...

von Jens S, (Gast)


Lesenswert?

Re: Abstand zweier Zahlen in 16-Bit (vgl. Galoisfeld)
Autor: Klaus (Gast)
Datum: 01.12.2008 11:20

Also wenn mich nicht alles täuscht, hast du den Preis für die simplste
Frage gewonnen =)

Ich würde den Timeout einfach als Countdown speichern. Also z. B. 5
Zyklen eine 5 speichern und in jedem Zyklus um ein decrementieren. Dann
auf 0 überprüfen.



also da gibts wirklich noch simplere fragen, ich fand die frage super, 
aber deine antwort würde ich mit zu den schlechtesten zählen. Wenn man 
die Zeit aus einem Timerregister liest und quasi mit Ticks arbeitet 
bringt deine Variante nichts.





Autor: Stefan Kleinwort (sk)
Datum: 01.12.2008 11:28

oh Mann ... und ich gewinne den Preis für die langsamste Antwort ...


Dafür war es auch eine sehr schöne und ausführliche Antwort die doch 
alle Fragen gut klärt :)

von Alexander I. (daedalus)


Lesenswert?

Vielen Dank für die zahlreichen Rückmeldungenen. Ich denke ich werde die 
1. Variante von SK realisieren.

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.