Hallo zusammen,
Ich hätte da eine Frage, die wahrscheinlich schon tausend mal gestellt
wurde. Ich bin wahrscheinlich zu doof zum Suchen.
Ich arbeite mich grad in die Timer ein und will eigentlich nur delay
durch eine Timerabfrage ersetzten. Also ähnlich wie BlinkWithoutDelay
beim Arduino.
Momentan mache habe ich den Code so:
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
4
uint8_tschritt=0;//Schrittvariable
5
uint8_tsechzehn_ms=0;// alle 16ms ein Inkrement
6
uint8_tvergangene_zeit=0;// vergangene Zeit für schritt
7
8
intmain(void)
9
{
10
// Timer 0 konfigurieren -> Prescaler auf 1024
11
TCCR0B|=(1<<CS02)|(1<<CS00);
12
// Overflow Interrupt von Timer 0 einschalten
13
TIMSK0|=(1<<TOIE0);
14
// Global Interrupts aktivieren
15
sei();
16
17
18
while(1)
19
{
20
21
22
uint8_taktuelle_zeit=sechzehn_ms;
23
if(aktuelle_zeit-vergangene_zeit>=63)
24
{
25
vergangene_zeit=aktuelle_zeit;
26
27
//Schritt weiter schalten
28
if(schritt<2)
29
{
30
schritt++;
31
}else
32
{
33
schritt=0;
34
}
35
}
36
37
}
38
}
39
40
ISR(TIMER0_OVF_vect)
41
{
42
sechzehn_ms++;
43
}
Ich möchte im Prinzip nur ca alle Sekunde den Schritt um eins weiter
zählen oder eben auf 0 setzten. die Zeit muss nicht genau sein, daher
hab ich den Prescaler hoch gewählt.
Das ganze läuft einmal durch aber wenn die vergangene_zeit dann bei
einem zu hohen Wert steht, kann die Bedingung natürlich nicht mehr wahr
werden.
Wie umgeht man das? Mache ich das grundsätzlich falsch?
Gruß
Stefan
Samuel C. schrieb:> Indem du einen uint64_t für vergangene_zeit wählst. Dann hast du Ruhe
wie meinst du das? meine meine sechzehn_ms sind doch auch nur 8bit.
Selbst bei einem so großen wert wäre doch irgendwann das gleiche Problem
da oder?
1.) Mache sechzehn_ms volatile.
2.) Solche Zeitzähler soll man im Laufe des Programmes niemals ändern.
Die einzige Änderung darf das Inkrementieren in der ISR sein.
3.) Du musst subtrahieren, den Part hast du fast richtig gemacht.
Beispiel:
1
volatileuint8_tsechzehn_ms=0;
2
3
intmain(void)
4
{
5
// Hier den Timer starten
6
7
uint8_twarte_seit=sechzehn_ms;
8
while(1)
9
{
10
if(sechzehn_ms-warte_seit>=63)
11
{
12
warte_seit=sechzehn_ms;
13
// Tu etwas
14
}
15
}
16
}
17
18
ISR(TIMER0_OVF_vect)
19
{
20
sechzehn_ms++;
21
}
Dann funktioniert das sogar bei einem Timer-Überlauf korrekt. Wichtig
ist, dass beide Variablen unsigned Integer mit gleicher Größe sind.
Stefan H. schrieb:> Samuel C. schrieb:>> Indem du einen uint64_t für vergangene_zeit wählst. Dann hast du Ruhe>> wie meinst du das? meine meine sechzehn_ms sind doch auch nur 8bit.> Selbst bei einem so großen wert wäre doch irgendwann das gleiche Problem> da oder?
Dann mach auch sechzehn_ms zu uint64_t.
Wieviele Milliarden Jahre willst du das Ding denn betreiben?
Noch ein Nachtrag:
Wenn du mehr als 8 bits verwendest, musst du beim Lesen des Zählers
verhindern, dass er wöhrend dessen durch die ISR verändert wird. Zum
Beispiel so:
Stefanus F. schrieb:> Dann funktioniert das sogar bei einem Timer-Überlauf korrekt. Wichtig> ist, dass beide Variablen unsigned Integer mit gleicher Größe sind.
Danke für deine Antwort!
Leider funktioniert das wieder nicht. es läuft einmal durch, dann ist
wieder Schluss. Im Prinzip machst du ja fast das gleiche wie ich ganz am
anfang.
Stefan H. schrieb:> Im Prinzip machst du ja fast das gleiche wie ich ganz am> anfang.
Ja stimmt. Meine Aussage "fast richtig" war falsch, ich hatte mich
diesbezüglich verguckt.
Das müsste laufen. Zeige nochmal deinen aktuellen Quelltext (mit
volatile).
Stefanus F. schrieb:> Das müsste laufen. Zeige nochmal deinen aktuellen Quelltext (mit> volatile).
1
#include<avr/io.h>
2
#include<avr/interrupt.h>
3
4
//Delay Funktion
5
#define F_CPU 16000000UL // 16 MHz
6
#include<util/delay.h>
7
8
//Globale Variablen
9
uint8_tschritt=0;//Schrittvariable
10
volatileuint8_tsechzehn_ms=0;// alle 16ms ein Inkrement
11
uint8_tzeit_eins=0;// vergangene Zeit f�r schritt
12
13
14
intmain(void)
15
{
16
17
// Timer 0 konfigurieren -> Prescaler auf 1024
18
TCCR0B|=(1<<CS02)|(1<<CS00);
19
// Overflow Interrupt von Timer 0 einschalten
20
TIMSK0|=(1<<TOIE0);
21
// Global Interrupts aktivieren
22
sei();
23
24
25
warte_seit=sechzehn_ms;// müsste ich mir eigentlich sparen können, da sechzehn_ms==0
26
while(1)
27
{
28
29
//uint8_t aktuelle_zeit = sechzehn_ms;
30
if(sechzehn_ms-zeit_eins>=63)
31
{
32
zeit_eins=sechzehn_ms;
33
//Schritt weiter schalten
34
if(schritt<2)
35
{
36
schritt++;
37
}else
38
{
39
schritt=0;
40
}
41
}
42
43
44
45
}
46
}
47
48
ISR(TIMER0_OVF_vect)
49
{
50
sechzehn_ms++;
51
}
ich verwende dann den schritt um leds leuchten zu lassen... also immer
drei Farben abwechselnd. das sollte ja aber keinen Einfluss haben, da
ich eigentlich nur mit einer switch case schleife den schritt abfrage
Stefanus F. schrieb:> Ich raff' es nicht. Ich sehe den Fehler nicht!
Naja ich denke dass ich den fehler mache:
wenn sechzehn_ms gerade zb 250 ist und ich das dann in zeit_eins
abspeichere, dann kann bei der nächsten Rechnung ja nie mehr was
größeres als 5 raus kommen.
Also sechzehn_ms (= 255) - zeit_eins(250) = 5. Damit ist die Bedingung
nie erfüllt und ich komme nie mehr dazu den schritt weiter zu schalten.
Sehe ich das falsch?
wenn ich nun natürlich alles auf 32bit hoch nehme, dann dauert es ewig
bis es dazu kommt. aber irgendwann eben schon. das muss man doch
irgendwie umgehen können oder?
Wenn ich das so abfange, läuft zumindes irgenwie was weiter...
Samuel C. schrieb:> Indem du einen uint64_t wählst. Dann hast du Ruhe.
Einen kaputten Algorithmus durch größere Variablen etwas länger am Leben
zu halten, kann nicht die Lösung sein - allenfalls ein schlechter Work
around.
Wolfgang schrieb:> Einen kaputten Algorithmus durch größere Variablen etwas länger am Leben> zu halten, kann nicht die Lösung sein - allenfalls ein schlechter Work> around.
Deswegen frage ich ja, wie man das besser lösen könnte :)
Hast du da eine Idee?
Stefan H. schrieb:> Deswegen frage ich ja, wie man das besser lösen könnte :)
Mein Kommentar bezog sich auf den etwas cruden "Tip" von Samuel C.
> Hast du da eine Idee?
Ich würde das in dieser Art angehen:
1
volatiluint8_tsechzehn_ms=0;// alle 16ms ein Inkrement
Wolfgang schrieb:> Ich würde das in dieser Art angehen
Ja aber ich mache doch genau das, mit dem beschriebenen Problem.
in dem Moment wenn die Startzeit zu groß wird, kann die Bedingung ja
nicht mehr wahr werden oder?
Ich steh auf dem Schlauch
> wenn sechzehn_ms gerade zb 250 ist und ich das dann in zeit_eins> abspeichere, dann kann bei der nächsten Rechnung ja nie mehr was> größeres als 5 raus kommen.
Doch, wegen des Überlaufs: "negative" Ergebnisse sind bei unsigned sehr
groß.
Du hast allerdings zwei Probleme:
1) Alle Ausdrücke in C werden immer mit mindestens int-Größe
durchgeführt. Dein uint8_t wird also zu einem signed int. Bei a-b
wird, obwohl a und b uint8_t sind, signed gerechnet, evtl negativ und
damit kleiner als jeder positive Vergleichswert. Der Vergleich müsste
"if ((uint8_t)(a-b) > 63)" oder "if ((unsigned)a-b > 63)" lauten[1].
2) Deine Schleife macht nichts, außer die Variable "schritt" zu
verändern - da kann der Compiler ordentlich optimieren - evtl bleibt
nichts übrig ...
[1] Unterschied rausfinden "is left as an exercise for the reader" ;-)
Wolfgang schrieb:> Mein Kommentar bezog sich auf den etwas cruden "Tip" von Samuel C.
Krude seh ich ein. Aber löst das Problem (fast) ohne weiteres bis zum
Verlöschen unseres Zentralgestirns.
foobar schrieb:> 1) Alle Ausdrücke in C werden immer mit mindestens int-Größe> durchgeführt. Dein uint8_t wird also zu einem signed int. Bei a-b> wird, obwohl a und b uint8_t sind, signed gerechnet, evtl negativ und> damit kleiner als jeder positive Vergleichswert. Der Vergleich müsste> "if ((uint8_t)(a-b) > 63)" oder "if ((unsigned)a-b > 63)" lauten[1].>
okay. verstehe ich zwar im prinzip, aber mir ist nicht klar, warum dann
aus meinem uint8_t ein signed int wird. kannst du mir das vielleicht
noch erklären.
Das mit dem Überlauf verstehe ich. Stimmt eigentlich.
Ich habe die Variablen jetzt als unsigned int deklariert, dann geht es.
sorry, wenn ich das jetzt noch nicht ganz kapiere.
> 2) Deine Schleife macht nichts, außer die Variable "schritt" zu> verändern - da kann der Compiler ordentlich optimieren - evtl bleibt> nichts übrig ...
Ja ich mache schon noch was damit, dass habe ich raus geschnitten, weil
ich es nicht für wichtig hielt. im Prinzip eben nur drei Farben bei RGB
leds durch schalten. Dabei schreibe ich aber nicht in die verwendeten
Variablen rein, daher hab ichs weg gelassen.
Wenn du magst, kann ich den ganzen code rein tun, ist halt dann recht
lang.
Samuel C. schrieb:> Krude seh ich ein. Aber löst das Problem (fast) ohne weiteres bis zum> Verlöschen unseres Zentralgestirns.
Danke für den Lachanfall :)
wills halt so lernen, wie es sich gehört ;)
> aber mir ist nicht klar, warum dann aus meinem uint8_t ein signed int wird.
Ist halt eine Regel in C: wenn der kleinere Datentyp in ein "int" passt,
wird es ein "int". Google mal nach "C type promotion".
Lustig wird's z.B. bei "uint16_t": je nachdem, was für eine int-Größe
das System hat, wird es ein "signed int" (32-Bit-ints) oder ein
"unsigned int" (16-Bit-ints).
> Ja ich mache schon noch was damit, dass habe ich raus geschnitten,
Dann ist's ok - wollte nur drauf hinweisen.
Hey Stefan,
soweit ich das verstanden habe, willst du jedes Mal, wenn die Variable
sechzehn_ms 63 Schritte macht, den flag "Schritt" weiterschalten. Könnte
man nicht einfach 64 Schritte machen und eine Modulo-Operation nehmen?
Mit einer Potenz von 2 ist die Operation gar nicht mal so schwer.
Hallo,
ich verstehe noch nicht warum sechzehn und zeit_eins zu int wird, wenn
beide mit uint8_t deklariert sind. Wozu gibt man Datentypen an wenn sie
ignoriert werden. Zudem sehe ich wie sechzehn im Bytebereich sauber
überläuft und zeit_eins stehen bleibt.
Stefan H. schrieb:> //Globale Variablen> uint8_t schritt = 0; //Schrittvariable> volatile uint8_t sechzehn_ms = 0; // alle 16ms ein Inkrement> uint8_t zeit_eins = 0; // vergangene Zeit f�r schritt
... ich "hasse" redundanz!
- globale variables werden immer automatisch initialisiert mit zero.
- volatile ist hier auch redundant und der sinn dazu unsinnig erklärt
und die weit verbreite "unsitte" singel anweisungen e.g. bei if/else zu
klammern ist zu mindest hässlich/unästhetisch.
weg damit!
mt
In die etwas verzwickte Logik habe ich mich jetzt nicht rein gedacht.
Aber ginge das nicht:
#include <util/atomic.h>
// ...
while (1)
{
ATOMIC_BLOCK(ATOMIC_FORCEON)
{
if (sechzehn_ms >= 63)
{
sechzehn_ms=0;
++schritt;
}
}
if (schritt == 2)
schritt=0;
// .. schritt auswertung
} // end while
Stefan H. schrieb:> verstehe ich zwar im prinzip, aber mir ist nicht klar, warum dann> aus meinem uint8_t ein signed int wird. kannst du mir das vielleicht> noch erklären.
google mal int promotion rules!
> ich verstehe noch nicht warum sechzehn und zeit_eins zu int wird, wenn> beide mit uint8_t deklariert sind. Wozu gibt man Datentypen an wenn sie> ignoriert werden.
Weil in C immer mit mindestens int-Größe gerechnet wird. Die
abstrakte Maschine, die C beschreibt, kann keine Arithmethik mit Zahlen
kleiner als int. Datentypen kleiner als int (char/short) dienen nur als
"Speichergröße" - nur da existieren sie in der reduzierten Form. Dass
meist doch 8-Bit-Arithmetik rauskommt, ist dem Optimizer zu verdanken.
Das gleiche passiert übrigens auch bei Fließkommazahlen (immer double).
Ein weitere Grund für Datentypen ist die Fehlererkennung - man gibt dem
Compiler mehr Infos, mit denen er arbeiten kann. Wenn du das nicht
brauchst, geht zurück nach B oder gar BCPL ;-)
Teo D. schrieb:> ISR (TIMER0_OVF_vect)> static uint8_t sechzehn_ms=63;> {
ISR (TIMER0_OVF_vect) {
static uint8_t sechzehn_ms=63;
...
}
ansonsten alles viel besser als der mist vorher!
Hallo,
aha. Danke. Das Problem war mir bis jetzt in der Form nicht bewußt das
trotz Byte Angabe in int gewandelt wird. Das Literale wie 55+77 in int
gerechnet werden wußte ich dagegen. Nur hier gibt man keine Datentypen
an. Deswegen meine Verwunderung.
Auf jedenfall gut zu wissen und nochmal darauf hingewiesen zu werden.
Apollo M. schrieb:> - volatile ist hier auch redundant
Ist es nicht. Wann immer man eine Variable zwischen ISR und
Hauptschleife teilt, MUSS sie volatile sein, weil der Compiler sie sonst
wegoptimieren kann.
Teo und Apollo,
wenn man nur ein Einziges Timerintervall benötigt mag das so mehr Sinn
machen. Benötigt man mehrere verschiedene Zeitintervalle kommt man
glaube ich mit dem allgemeinen globalen Counter besser weg. Ansonsten
hätte man dann gefühlt Tausende globale Flags. Oder nicht?
Nop schrieb:> Ist es nicht. Wann immer man eine Variable zwischen ISR und> Hauptschleife teilt, MUSS sie volatile sein, weil der Compiler sie sonst> wegoptimieren kann.
so so ... dann google und lese nochmal.
use of volatile ...
wenn und nur wenn die variable sich zwischen zwei zugriffen ändern kann,
daher durch hw geändert wird, was ja der compiler sonst nicht wissen
kann und diese dann als konstante ansieht und e.g. in ein register
ablegt.
daher volatile erzwingt, dass die variable vor jeden zugriff immer neu
eingelesen wird!
... und wegoptimieren wäre auch unsinn, weil nicht weg sondern wird nur
einmal eingelesen.
mt
foobar schrieb:> Google mal nach "C type promotion".Apollo M. schrieb:> google mal int promotion rules!
Hab ich und ich wurde erleuchtet :) Danke für euren Hinweiß! Damit hat
sich meine eigentliche Frage erledigt.
Vielen Dank für eure Hilfe :) Immer wieder gut, wenn man dann weiter
kommt!
Apollo M. schrieb:> wenn und nur wenn die variable sich zwischen zwei zugriffen ändern kann
Das ist der Fall, nämlich zwischen zwei Durchläufen der while-Schleife
in Stefans main().
Da ISRs nie im Callpath stehen, kann der Compiler annehmen, die Variable
werde mit 0 initialisiert, nie verändert, und die ganze Auswertung in
main kann wegoptimiert werden.
Veit D. schrieb:> Teo und Apollo,>> wenn man nur ein Einziges Timerintervall benötigt mag das so mehr Sinn> machen. Benötigt man mehrere verschiedene Zeitintervalle kommt man> glaube ich mit dem allgemeinen globalen Counter besser weg. Ansonsten> hätte man dann gefühlt Tausende globale Flags. Oder nicht?
Ja genau. Ich will dann halt mehrere Zeiten abfragen, daher wollte ich
das eben so machen.
Apollo M. schrieb:> zur erleuchtung ...> https://barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword
Hast Du den Link eigentlich selber sinnerfassend gelesen?
Im Abschnitt "Proper Use of C's volatile Keyword" findet sich:
2. Global variables modified by an interrupt service routine
Das ist exakt die Situation in Stefans Code.
Nop schrieb:> Da ISRs nie im Callpath stehen, kann der Compiler annehmen, die Variable> werde mit 0 initialisiert, nie verändert, und die ganze Auswertung in> main kann wegoptimiert werden.
korrekt! und rückwärtsrolle ...
Hallo,
ich weiß auch nicht was es am "volatile sechzehn_ms" auszusetzen gibt.
Es ist absolut notwendig und korrekt. Wird eine Variable außerhalb des
eigentlichen Programmablaufes geändert, was in einer ISR der Fall ist,
dann muss diese volatile sein. Ist diese Variable zudem größer ein Byte,
dann muss der Zugriff im Programmablauf darauf atomar erfolgen.
Ansonsten könnte es passieren das low und high Byte nicht mehr
zusammenpassen, weil zwischendurch ein neuer Interrupt den Wert
verändert hat.
printf("3) %d - %d >= 63 is %d\n",jetzt,warte_seit,jetzt-warte_seit>=63);
12
printf("4) %d - %d >= 63 is %d\n",jetzt,warte_seit,jetzt-warte_seit>=(uint8_t)63);
13
printf("5) %d - %d >= 63 is %d\n",jetzt,warte_seit,(uint8_t)(jetzt-warte_seit)>=63);
14
}
Erzeugt unter Linux diesen Output:
1
sfrings@stefanpc:~/Test$ gcc test.c
2
sfrings@stefanpc:~/Test$ ./a.out
3
1) 1 - 192 = 65
4
2) 1 - 192 = -191
5
3) 1 - 192 >= 63 is 0
6
4) 1 - 192 >= 63 is 0
7
5) 1 - 192 >= 63 is 1
1) bestätigt, dass die Zeit-Differenz durch die Subtraktion trotz
Überlauf korrekt berechnet wird.
2) bestätigt, dass der Compiler automatisch mit mehr als 8bit rechnet,
wenn wir es ihm nicht anders vorgeben. (-191 ist größer als ein Byte).
3) bestätigt das eigentliche Problem des TO: Der if-Ausdruck
funktioniert nicht wie erwartet.
4) zeigt, dass es nichts nützt, das integer Literal 63 auf unit8_t zu
casten.
5) zeigt, dass es hilft, die Subtraktion auf uint8_t zu casten.
Veit D. schrieb:> Das Problem war mir bis jetzt in der Form nicht bewußt> Auf jedenfall gut zu wissen und nochmal darauf hingewiesen zu werden.
Geht mir genau so.
Ich habe das bisher immer nur mit mindestens 16bit Integer gemacht,
daher war ich mit diesem Problem bisher noch nie konfrontiert.
Stefanus F. schrieb:> Gerade mal auf dem PC ausprobiert:> #include <stdint.h>> #include <stdio.h>>> int main()> {> uint8_t jetzt=1;> uint8_t warte_seit=192;> uint8_t diff=jetzt-warte_seit;> printf("1) %d - %d = %d\n",jetzt,warte_seit,diff);> printf("2) %d - %d = %d\n",jetzt,warte_seit,jetzt-warte_seit);> printf("3) %d - %d >= 63 is %d\n",jetzt,warte_seit, jetzt-warte_seit>>= 63);> printf("4) %d - %d >= 63 is %d\n",jetzt,warte_seit, jetzt-warte_seit>>= (uint8_t)63);> printf("5) %d - %d >= 63 is %d\n",jetzt,warte_seit,> (uint8_t)(jetzt-warte_seit) >= 63);> }
Die nächste Fehlerquelle lauert dann im Format-String von printf - wenn
der nicht zum tatsächlichen (int), aber zum gewünschten (uint8_t)
Datentyp passt, kann die richtige Ausgabe herauskommen. Könnte zum
Beispiel passieren, wenn du %d durch %hhu ersetzt.
MfG, Arno
Stefanus F. schrieb:> Ich habe das bisher immer nur mit mindestens 16bit Integer gemacht,> daher war ich mit diesem Problem bisher noch nie konfrontiert.
... interessant, das steht fast in jeder docu über c oder specific
compiler drin, im sinne von vorsicht falle mit int promotion - wohl auch
so definiert im c-standard.
ist das bekannt ... struct/array init with zero? hatte ich noch nicht
auf meinen schirm oder wo im code gesehen.
void test() {
struct_t struct_test = {}; //initialize with zero
int array_test[42] = {};
...
}
attached gutes buch dazu aus meiner bibo, ... schnell zugreifen bevor
wieder weg!
Stefan H. schrieb:> ISR (TIMER0_OVF_vect)> {> sechzehn_ms++;> }>> Ich möchte im Prinzip nur ca alle Sekunde den Schritt um eins weiter> zählen
Also erst einmal: Was hat ein Timer (d.h. ein Hardware-Peripheriecore)
mit C zu tun? Antwort: garnichts.
Und dann:
Anstelle deiner wirklich extrem minimalistischen ISR solltest du dir
eine Art Systemuhr einrichten.
Also wäre es gut, wenn deine o.g. ISR in einem Takt aufgerufen würde,
der ein ganzzahliger Teil von 1 Sekunde ist. Zum Beispiel 10 ms oder 1
ms (für ganz Eilige), oder 1/2 1/4..1/128 Sekunde.
Dann zählst du in deiner ISR einfach eine globale Variable hoch - und
schon hast du die Systemzeit in Sekunden oder eben deinem gewählten
Takt. Und diese globale Variable darf jeder lesen, aber nicht
beschreiben.
Und wenn das Ganze mehr als nur 1 Tag laufen soll, dann mußt du eben in
der ISR auch zur Mitternacht den Tick nullen und den Datumzähler um 1
erhöhen.
Du scheinst mir nen AVR zu benutzen, also sollte deine ISR auch eine
Zeitausgabe beinhalten, etwa so:
1
globaleVars:
2
unsignedlongTime;
3
boolbitte;
4
unsignedlongda_isses;
5
6
ISR:...
7
++Time;
8
if(bitte)
9
{da_isses=Time;
10
bitte=false;
11
}
12
...ISRende
So, damit können andere Teile deiner Firmware die aktuelle Uhrzeit (in
Ticks) abholen, indem sie bitte = true setzen und warten, bis bitte
wieder false ist.
W.S.
Wer noch etwas üben möchte: Wo ist der Unterschied zwischen den drei
folgenden Varianten (sie erreichen das gewünschte Ziel auf
unterschiedliche Weise) und welche ist zu bevorzugen:
// uint8_t a, b;
1) if ((uint8_t)(a-b) > 63)
2) if ((unsigned)a-b > 63)
3) if (a-b > 63u)
W.S. schrieb:> Und wenn das Ganze mehr als nur 1 Tag laufen soll, dann mußt du eben in> der ISR auch zur Mitternacht den Tick nullen und den Datumzähler um 1> erhöhen.
Er hat das mit der Subtraktion prinzipiell richtig gemacht. Einfach die
Variable breiter machen halte ich für Pfusch, denn
a) Größere Variablen sind langsamer im Zugriff
b) Laufen auch irgendwann über
> indem sie bitte = true setzen und warten, bis bitte wieder false ist
Dann muss "bitte" und "da_isses" aber volatile sein ...
Die ursprüngliche Variante von Stefan ist schon OK und üblich - das
Linux-Kernel z.B. macht es genauso (der globale Zähler heißt da
"jiffies") und einige Systeme haben so einen Counter direkt in der
Hardware. Ja, man kann bei der Benutzung Fehler machen, insb wenn man
wrap-around und atomic-access berücksichtigen muß - im Linux-Kernel
haben sie deshalb irgendwann Macros eingeführt (time_after,
get_jiffies_64, etc).
foobar schrieb:> 1) if ((uint8_t)(a-b) > 63)> 2) if ((unsigned)a-b > 63)> 3) if (a-b > 63u)> Wo ist der Unterschied?
Werfen wir dazu einen Blick in den Assembler-Code, den der Compiler
generiert. Ich musste ein bisschen tricksen, damit der Optimizer den
wirkungslosen Code nicht komplett weg optimiert:
Wie wir sehen ist Variante 1 deutlich kürzer, da auf 8bit Berechnung
reduziert.
Die anderen beiden rechnen exakt gleich mit 16bit unsgined Integer.
Offenbar spielt es keine Rolle, ob die linke oder rechte Seite vom ">"
Operator den unsigned Integer vorgibt.
Apollo M. schrieb:>> attached gutes buch dazu aus meiner bibo, ... schnell zugreifen bevor> wieder weg!
Scheiß Buch wenn es im Index nicht einmal volatile gibt.
> Werfen wir dazu einen Blick in den Assembler-Code, den der Compiler> generiert.
Mir ging es eher um den konzeptuellen Unterschied: warum liefern diese
Varianten das gewünschte Ergebnis, wo findet welche Konvertierung statt,
mit welchem Typ werden die beiden Operationen durchgeführt, entstehen da
möglicherweise Probleme?
Welcher Kode daraus generiert wird, steht auf einem anderen Blatt - im
Optimalfall kommt bei allen drei Varianten das gleiche raus.
> Offenbar spielt es keine Rolle, ob die linke oder rechte Seite vom ">"> Operator den unsigned Integer vorgibt.
Korrekt. Der kleinere Operand wird immer zum größeren konvertiert
(wobei signed_int < unsigned_int < signed_long < unsigned_long).
foobar schrieb:> (wobei signed_int < unsigned_int < signed_long < unsigned_long).
Vorsicht. Wenn int und long die gleiche Breite haben stimmt das u.U.
nicht mehr.
Zum "atomic access" bei 16bit-Variablen auf einer 8-Bit-CPU kann man
auch ganz einfach ein sequential lock bei der Abfrage nutzen. Dann muß
man keine Interrupts abschalten:
Apollo M. schrieb:> volatile schrieb:>> Scheiß Buch wenn es im Index nicht einmal volatile gibt.>> für dich newbie noch eine "lesehilfe" und keinen mucks mehr!
Sie sollten die Bücher selbst lesen um volatile zu verstehen.
Codix schrieb:> if ( systick )> systick++;>> if ( TimeOut )> TimeOut--;
Kleiner Tipp:
1
systick++;
2
3
if(TimeOut&&(TimeOut>1))
4
TimeOut--;
Die Abfrage von systick kann man sich sparen.
TimeOut kann man zwar machen wie vorgeschlagen, ich bevorzuge meine
Lösung :)
Wenn TimeOut == 1, dann ist er abgelaufen. TimeOut == 0 ist
abgeschaltet.
So kann man in der Mainloop eine Aktion bei Timeout entweder permanent
ausführen oder, wenn TimeOut auf 0 gesetzt wird, nur ein Mal.
Bernhard R. schrieb:> if ( TimeOut && (TimeOut > 1))> TimeOut--;
... auch meine bevorzugte lsg!
ich würde nur noch die "klammerorgie" weglassen, da der rang der
operatoren das regelt, bei langen bedingungen wird das sonst schnell
unübersichtlich
if (TimeOut && TimeOut>1) TimeOut--;
Apollo M. schrieb:> attached gutes buch dazu aus meiner bibo, ... schnell zugreifen bevor> wieder weg!Apollo M. schrieb:> für dich newbie noch eine "lesehilfe" und keinen mucks mehr!
Es ist eine Urheberrechtsverletzung, so etwas einfach hier reinzusetzen.
Ich ermahne eindringlich, dieses zukünftig zu unterlassen. Nicht nur,
dass Andreas durch solche Aktionen rechtliche Schwierigkeiten bekommen
könnte, sondern auch Du. Hinzu kommt, dass solche "Aktionen" auch den
Ruf dieses Forums schädigen. Das ist hier keine Raubkopierer-Plattforum.
Bedenke auch, dass Du mit solchen Weitergaben von urheberrechtlich
geschütztem Material den Autor des Werkes nicht nur schädigst, sondern
auch den nötigen Respekt gegenüber seiner Arbeit vermissen lässt.
Ich appelliere daher eindringlich, so etwas grundsätzlich zu
unterlassen. Bei Wiederholung wird es zwangsläufig zu Gegenmaßnahmen
kommen müssen.