"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?
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.
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.
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.
@ 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
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?
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.
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.
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.
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...
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...
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
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.
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.
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?
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...
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.
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?
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...
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.
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 */
}
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.