Forum: Mikrocontroller und Digitale Elektronik Warum signed int bei delay?


von Hans im Glück (Gast)


Lesenswert?

Hallo,

ich habe folgenden Code hier gefunden:
1
void delay( int16_t d )          // d = 0 ... 32000 ticks
2
{
3
  d += TCNT1;
4
  while( (TCNT1 - d) & 0x8000 ); // until positive
5
}
"Der Trick dabei ist, daß es überlauffest ist (Differenzen stimmen
immer)."

Normalerweise versucht man ja, signed zu vermeiden und nimmt unsigned. 
Das man hier bei signed nur Werte bis 32.767 als Parameter übergeben 
kann, ist klar.
Also warum werden hier signed-Werte genommen?

von Max H. (hartl192)


Lesenswert?

Wenn man das ganze für z.B. 40000 durchrechnet, dann ist (TCNT1 - d) % 
0x8000 schon beim ersten durchlauf false, es macht also keinen Sinn 
Zahlen > 32767 zuzulassen.

von Bernd K. (prof7bit)


Lesenswert?

Hans im Glück schrieb:
> Also warum werden hier signed-Werte genommen?

Wahrscheinlich weil es keinen Unterschied macht, vielleicht auch nur ein 
Versehen, eine Unachtsamkeit (ohne Folgen). Ich hätte hier einen 
unsigned genommen.

von Falk B. (falk)


Lesenswert?

Keine Ahnung, aber für eine Delay-Funktion würde ich schlicht die 
fertigen Routinen vom avr gcc nehmen, das sind ASM Makros. Die stimmen 
genau, wenn keine Interrupts laufen und brauchen auch keinen Timer. Die 
Funktion oben ist nicht sonderlich genau und IMO auch eher komisch.

von Max H. (hartl192)


Lesenswert?

Max H. schrieb:
> (TCNT1 - d) %
> 0x8000
Sry, Tippfehler, das '%' sollte ein '&'

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Max H. (hartl192)

>> (TCNT1 - d) %
>> 0x8000
>Sry, Tippfehler, das '%' sollte ein '&'

DOPPELFEHLER! Quelltexte NIE abtippen sondern IMMER Kopieren bzw. gleich 
die originalen Dateien als Anhang beifügen!

Siehe Netiquette

von Max H. (hartl192)


Lesenswert?

Falk Brunner schrieb:
> DOPPELFEHLER! Quelltexte NIE abtippen sondern IMMER Kopieren bzw. gleich
> die originalen Dateien als Anhang beifügen!
Soll ich also den Quelltest des TOs kopieren, eine *.c Datei mit dieser 
Zeile erstellen und meiner Antwort anhängen?

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

uint16_t wäre die bessere Wahl.

Für die Addition und die Subtraktion wird d sowieso implizit in unsigned
int konvertiert. Warum en Typ also nicht gleich unsigned machen?

Die Rückkonvertierung der unsigned int Summe (d+TCNT1) in int16_t (d)
ist implementation-defined, wenn sich der unsigned Wert nicht in signed
int (bzw. int16_t) darstellen lässt, was hier durchaus der Fall sein
kann. Der GCC liefert hier zwar das erwartete Ergebnis, sauberer ist es
aber, sich gar nicht erst auf solche implementation-defined Features zu
verlassen.

von c-hater (Gast)


Lesenswert?

Yalu X. schrieb:

> Die Rückkonvertierung der unsigned int Summe (d+TCNT1) in int16_t (d)
> ist implementation-defined, wenn sich der unsigned Wert nicht in signed
> int (bzw. int16_t) darstellen lässt, was hier durchaus der Fall sein
> kann. Der GCC liefert hier zwar das erwartete Ergebnis, sauberer ist es
> aber, sich gar nicht erst auf solche implementation-defined Features zu
> verlassen.

Noch viel besser ist es, einfach selbst genau das zu schreiben, was man 
erreichen will und sich nicht auf sich ständig ändernde 
Sprachdefinitionen und Compilerimplementierungen verlassen zu müssen.

Asm rules.

von Hans im Glück (Gast)


Lesenswert?

Max H. schrieb:
> Wenn man das ganze für z.B. 40000 durchrechnet, dann ist (TCNT1 - d) &
> 0x8000 schon beim ersten durchlauf false, es macht also keinen Sinn
> Zahlen > 32767 zuzulassen.
Genau. Hier hätte ich als Grund noch vermutet, daß zumindest der 
Compiler bei zur Compilezeit feststehenden Werte > 2^15 eine Warnung 
ausspucken kann. Bei Werten zur Laufzeit geht das natürlich nicht mehr.

Falk Brunner schrieb:
> Keine Ahnung, aber für eine Delay-Funktion würde ich schlicht die
> fertigen Routinen vom avr gcc nehmen, das sind ASM Makros.
Habe ich damals bei den AVR natürlich auch gemacht. Aber bei den 
Cortexen gibt es sowas oft leider nicht mitgeliefert. Und z.B. bei 
LPCOpen von NXP ist mit den StopWatch-Dingern das ganze schon von Haus 
aus fehleranfällig implementiert (Überlauf ist nicht berücksichtigt). 
Bei z.B.32 bit countern wohl eher seltener, aber eben da.

Yalu X. schrieb:
> Für die Addition und die Subtraktion wird d sowieso implizit in unsigned
> int konvertiert. Warum en Typ also nicht gleich unsigned machen?
Genau das hat mich zu der Frage geführt.
Aber da das ein Codeschnipsel von Peter Dannegger ist, Frage ich lieber 
nach. Denn in seinen Beispielen ist oft etwas, was man im ersten Moment 
übersieht.

von lightninglord (Gast)


Lesenswert?

Hans im Glück schrieb:
> Habe ich damals bei den AVR natürlich auch gemacht. Aber bei den
> Cortexen gibt es sowas oft leider nicht mitgeliefert. Und z.B. bei
> LPCOpen von NXP ist mit den StopWatch-Dingern das ganze schon von Haus
> aus fehleranfällig implementiert (Überlauf ist nicht berücksichtigt).
> Bei z.B.32 bit countern wohl eher seltener, aber eben da.

Dafür hast du bei den Cortexen ja meistens ein systick, der ja sowieso 
mitläuft...

von Michael S. (rbs_phoenix)


Lesenswert?

Yalu X. schrieb:
> Für die Addition und die Subtraktion wird d sowieso implizit in unsigned
> int konvertiert.

Wieso denn das? Wenn ich 50-d schreibe, aber d=-10 ist, erwarte ich als 
Ergebnis 60. Es sei denn, ich schreibe 50-(uint16_t)d. Oder hab ich da 
was falsch verstanden.

Ich finde allgemein bei delays signed Übergabewerte komisch. Man gibt ja 
i.d.R. die Wartedauer an. Die kann aber nie negativ sein, daher gleich 
den Fall ausschließen. Sonst müsste man noch auf negative Zahlen prüfen. 
Nicht das er bei delay_ms (-1) dann 65 Sekunden wartet...

von Yalu X. (yalu) (Moderator)


Lesenswert?

Michael Skropski schrieb:
> Wieso denn das? Wenn ich 50-d schreibe, aber d=-10 ist, erwarte ich als
> Ergebnis 60.

Das stimmt ja auch so. In diesem Fall sind beide Operanden signed int.
Damit wird auch die Berechnung in signed int ausgeführt.

Aber selbst wenn beide Operanden unsigned int wären, würde sich am
Ergebnis nichts ändern, denn
1
  (unsigned int)50 - (unsigned int)(-10)
2
= (unsigned int)(50 - 65526)
3
= (unsigned int)(-65476) 
4
= 60

von Karl H. (kbuchegg)


Lesenswert?

Michael Skropski schrieb:
> Yalu X. schrieb:
>> Für die Addition und die Subtraktion wird d sowieso implizit in unsigned
>> int konvertiert.
>
> Wieso denn das?

Weil TCNT1 unsigned ist.

von Karl H. (kbuchegg)


Lesenswert?

c-hater schrieb:
> Noch viel besser ist es, einfach selbst genau das zu schreiben, was man
> erreichen will und sich nicht auf sich ständig ändernde
> Sprachdefinitionen

ständig ändern?

Ganz ehrlich: Du hast ja einen Knall. So konservativ wie das C Standard 
Normungsgremium ist selten irgend eine andere Organisation.

Wenn man keine Ahnung hat, einfach mal ....
Dein unqualifiziertes C-Bashing geht mir ehrlich gesagt  schon ziemlich 
auf den Wecker. C hat seine Schwächen und Macken, keine Frage. Die will 
auch niemand schön reden. Aber das was du dauernd anführst gehört 
definitiv nicht dazu.

> Asm rules.

Komisch nur, dass unsere militanten Asm-über-alles Verfechter in Threads 
in denen es um Asm geht hauptsächlich eines tun: Durch Abwesenheit 
glänzen.

: Bearbeitet durch User
von Luigi (Gast)


Lesenswert?

Hans im Glück schrieb:
> Normalerweise versucht man ja, signed zu vermeiden und nimmt unsigned.

Achso? wo hast du das denn gelernt?

von Mark B. (markbrandis)


Lesenswert?

Karl Heinz schrieb:
> unqualifiziertes C-Bashing

Genau so ist es.

von ?!? (Gast)


Lesenswert?

Luigi schrieb:
> Hans im Glück schrieb:
>> Normalerweise versucht man ja, signed zu vermeiden und nimmt unsigned.
>
> Achso? wo hast du das denn gelernt?

Es ist einfach eine Sache des gesunden Menschenverstandes. Was macht 
eine negative Zeitangabe beim Delay für einen Sinn? Also ist es doch 
besser, signed zu vermeiden, oder nicht?

von c-hater (Gast)


Lesenswert?

Karl Heinz schrieb:

> Dein unqualifiziertes C-Bashing geht mir ehrlich gesagt  schon ziemlich
> auf den Wecker.

"Unqualifiziert"? Das nehme ich dir übel. Allerdings bereitet es mir 
andererseits auch sicher keine schlaflosen Nächte.

C-Bashing im Stil der Boulevard-Presse hingegen ist durchaus meine 
Absicht. Wie will man sonst in so einem C-Betonkopf-gesteuerten Forum 
Aufmerksamkeit erregen und Nachdenken auslösen?

> C hat seine Schwächen und Macken, keine Frage. Die will
> auch niemand schön reden.

Wenn das wirklich so wäre, würde es die persona "c-hater" hier in diesem 
Forum garnicht geben. Sie würde von ganz allein verschwinden bzw. wäre 
garnicht erst entstanden...

von Bernd M. (bernd_m)


Lesenswert?

>>Aufmerksamkeit erregen und Nachdenken auslösen?
Über ASM? LOL

von Michael S. (rbs_phoenix)


Angehängte Dateien:

Lesenswert?

c-hater schrieb:
> C-Bashing im Stil der Boulevard-Presse hingegen ist durchaus meine
> Absicht. Wie will man sonst in so einem C-Betonkopf-gesteuerten Forum
> Aufmerksamkeit erregen und Nachdenken auslösen?

Nur gehört es hier nicht hin. Es ist egal, ob ASM wirklich effektiver 
oder einfacher oder sonstwas ist. Auch wenn du ein ASM Programm 
schreiben könntest, dass einen C Compiler mit Abstand in den Schatten 
stellt. Es gehört hier nicht hin und denen es nicht egal ist, geht es 
auf die Nerven. Es wird keiner wegen dem bzw. den Posts von dir sagen 
"oh, der sagt ASM is cool und C stinkt, dann wechsle ich jetzt mal".
Genauso wie, wenn es in einer Frage um eine Funktion vom PIC geht und 
irgend einer dann schreibt "Nimm nen AVR, damit haste das Problem nicht 
und allg. is der viel toller".
Oder Vegetarier/Veganer, die jedem ihre Einstellung aufzwingen wollen 
und alle anderen sind Tierquälende Bastarde. (Bild)
Oder Hardcoregläubige, die einen um jeden Preis konvertieren wollen, als 
ob sie sonst in die Hölle kommen.

Aber es ist egal, wieviel Leute wie oft was sagen: Ich bleib bei C, bei 
PIC, bei Fisch&Fleisch und ich glaube an keinen Gott.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

c-hater schrieb:
> Noch viel besser ist es, einfach selbst genau das zu schreiben, was man
> erreichen will und sich nicht auf sich ständig ändernde
> Sprachdefinitionen und Compilerimplementierungen verlassen zu müssen.

In den gut 25 Jahren, seit denen die Sprachdefinition inzwischen genormt 
ist, hat sie sich ganze zweimal geändert. Das im Computerbereich als 
"ständig" zu bezeichnen grenzt schon arg an Wahnvorstellungen.
Compiler ändern sich und haben System- und Compiler-spezifische 
Eigenschaften - Assembler auch.

c-hater schrieb:
> Karl Heinz schrieb:
>
>> Dein unqualifiziertes C-Bashing geht mir ehrlich gesagt  schon ziemlich
>> auf den Wecker.
>
> "Unqualifiziert"? Das nehme ich dir übel.

Damit hat er aber recht. In der Regel plapperst du irgendeinen Blödsinn 
raus, der keinen Bezug zur gestellten Frage hat und niemandem 
weiterhilft. Dein Posting hier ist ein gutes Beispiel dafür. Ich habe 
immer noch nicht verstanden, warum du solche Threads überhaupt öffnest, 
wenn dich das Thema eigentlich gar nicht interessiert. Den Inhalt lesen 
tust du offenbar sowieso nicht, da deine "Antworten" sich eigentlich 
immer auf Fragen beziehen, die keiner gestellt hat.

> C-Bashing im Stil der Boulevard-Presse hingegen ist durchaus meine
> Absicht.

Dann tu das bitte wo anders. Hier geht es darum, jemandem, der eine 
Frage hat, zu helfen und nicht auf Bild-Niveau große Sprüche zu klopfen.

> Wie will man sonst in so einem C-Betonkopf-gesteuerten Forum
> Aufmerksamkeit erregen und Nachdenken auslösen?

Das einzige, was du damit tust, ist andere nerven und dich unbeliebt 
machen. Was versprichst du dir eigentlich davon?

von Nosnibor (Gast)


Lesenswert?

Das signed Argument soll wohl den Nutzer daran erinnern, daß er keine 
Werte größer als 32767 einsetzen darf; für die funktioniert der 
Algorithmus nämlich nicht. Für negative Werte auch nicht, aber darauf 
kommt der Nutzer wahrscheinlich von alleine.

Für die Berechnungen ist aber unsigned besser, soweit ich weiß. Für 
signed ist das Verhalten bei Überlauf nicht definiert. Das war bisher 
egal, weil es sich bei praktisch allen Compilern für alle gängigen 
Prozessoren wie erwartet verhalten hat, aber moderne Compiler suchen 
gezielt nach Code, der undefiniertes Verhalten provoziert, und 
optimieren den weg. Hier könnte so ein Compiler z.B. eine Abfrage 
erzeugen, ob bei d += TCNT1 ein Überlauf auftritt, und dann sofort 
zurückkehren. Geniale Optimierung, weil die Funktion dann in einigen 
Fällen schneller fertig wird...

von Hans im Glück (Gast)


Lesenswert?

Uff! Also irgendeinen Krieg oder Streit wollte ich nun ganz gewiß nicht 
vom Zaun brechen, sondern nur was dazulernen!!!

Meine Frage wurde aber auf jeden Fall für mich beantwortet; Vielen Dank 
dafür.

von pc_oldie (Gast)


Lesenswert?

Bei Timern hier ist die Verwendung von signed int bewusst gewählt 
worden:

Ich habe die aktuelle Zeit time und den Zeitpunkt event jeweils als 
signed int (sint16, sint32, sint64 möglich, aber beide GLEICHER Typ und 
in 2er-Komplement dargestellt).

Bei den Rechenoperation wird bewusst bei Überlauf KEIN Event erzeugt 
(nötigenfalls abschalten falls die benutzte Architektur das sonst macht)

Damit gilt dann einfach:  bool expired = ((event - time) <= 0);
Ist auf Assembler-Ebene nur ein subtract und ein conditional branch. 
Einfacher geht nicht.

Bedingung hier ist, dass der Event nicht weiter als die HALBE 
turnaround-time in der Zukunft liegen kann ( bei sint16 also <=32767)

Gesetzt wird der Timer auch einfach durch ein signed add auf die 
Momentanzeit:  event = time + delay;

Man braucht hier KEINE if/else Kaskaden wie sie bei Verwendung von 
unsigned int nötig sind um einen Timer zu beliebiger Zeit zu starten.
Ich habe da einige wirklich grausige Negativ-Beispiele in meinem 
Berufsleben erlebt, die teilweise >100 Zeilen Code, mehrmalige Bugfixes 
nötig geworden, massive Jitter im Laufzeitbedarf.

Einziger Vorteil bei der signed implementierung: man kommt doppelt so 
weit in die Zukunft. Sollte das nötig sein, ist es aber sinnvoller von 
16 auf 32 (oder 64) Bit Breite zu gehen.

Die Variable time kann bei den meisten Architekturen direkt aus einem 
Hardware-Register implementiert werden.


Hier mein Beispiel:
extern sint32 systime;  /* Plaziert auf einen free running timer mit 
gewünschter Taktung, z.b. 1Tick/100uSec */
#define START_TIMER(x,delay)  (x = systime + delay)
#define MS2TIMERTICK(x) (x*10)  /* Passend zum Timer, hier 
millisekunde*/
#define TIMER_EXPIRED(x)  ((x-systime)<0)

/* Zwei Timer überlappend implementiert */
sint32 sekunden_Blinker;
sint32 abschalt_timer;

void demo (void)
{
  init_hardware(); /* was sonst noch nötig ist */
  START_TIMER(sekunden_Blinker,MS2TIMERTICK(1000);
  START_TIMER(abschalt_timer,MS2TIMERTICK(100000);

  while (!TIMER_EXPIRED(abschalt_timer))
  {
    if (TIMER_EXPIRED(sekunden_blinker))
    {
      sekunden_blinker += MS2TIMERTICK(1000);  /* Eine Sekunde weiter */
      lass_es_blinken();
    }
    tu_sonst_was();
  }
  /* Nach 100 Sekunden ist dei Demo vorbei */
}

von Yalu X. (yalu) (Moderator)


Lesenswert?

pc_oldie schrieb:
> Ich habe die aktuelle Zeit time und den Zeitpunkt event jeweils als
> signed int (sint16, sint32, sint64 möglich, aber beide GLEICHER Typ und
> in 2er-Komplement dargestellt).

Im konkreten Fall steht die aktuelle Zeit in TCNT1, und das ist
vorzeichenlos (uint16_t).

> Bei den Rechenoperation wird bewusst bei Überlauf KEIN Event erzeugt

Gerade weil bewusst mit Überläufen gerechnet wird, muss mit unsigned
gerechnet werden, da der C-Standard das Verhalten bei Überläufen nur für
unsigned-Arithmetik spezifiziert.

> Man braucht hier KEINE if/else Kaskaden wie sie bei Verwendung von
> unsigned int nötig sind um einen Timer zu beliebiger Zeit zu starten.

Die braucht man auch bei unsigned nicht. Man muss lediglich TCNT-d < 0
(in signed-Arithmetik) durch TCNT-d >= 0x8000 oder (TCNT1-d) & 0x8000
(in unsigned-Arithmetik) ersetzen.

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.