Hallo,
ich habe hier folgenden kurzen code-ausschnitt.
ich möchte damit einfach ein bussignal auswerten.
wenn ich mir den testpin (hier portd pd0) am oszi anschaue muss ich
feststellen dass dessen frequenz zunimmt, ich habe testweise auch schon
mal recvcount=20 initialisiert um das phänomen genauer zu untersuchen,
auch hier scheint es so dass ich je öfter hintereinander ich _delay_us
aufrufe, umso kürzer wartet es,.... kann doch nicht sein, oder?
wenn diese empfangsroutine dann nochmal von neuem aufgerufen wird ist
beim ersten bit wieder alles in ordnung, aber um so mehr es gegen das
achte bit geht um so kürzer wartet das delay,...
ich bin ratlos, aber vll kann mir jemand von euch weiterhelfen.
lg
Arthur Dent schrieb:> ich bin ratlos, aber vll kann mir jemand von euch weiterhelfen.
ja, doku lese hilft.
_delay_us();
darf nur mit konstanten und mit optimerung verwendet werden.
Peter II schrieb:> ja, doku lese hilft.>> _delay_us();>> darf nur mit konstanten und mit optimerung verwendet werden.
-os hab ich als optimierungsstufe gewählt,
und im code weiter oben (ziehmlich am anfang um genau zu sein ;)) steht
folgendes:
1
#define MyDelayValue 8
2
#define MyDelayValueAndAHalf 14
jaja ich weis, 8*1,5 = nicht 14 ;)
aber diese werte hab ich experimentell ermittelt ;)
Arthur Dent schrieb:> und im code weiter oben (ziehmlich am anfang um genau zu sein ;)) steht> folgendes:> #define MyDelayValue 8> #define MyDelayValueAndAHalf 14
ok das hätte nich nicht erwartet, weil man define im üblichen GROSS
schreibt. Dann ist das nicht das Problem.
(kommt aber auch nur weil man nicht alles wichtige sieht)
Arthur Dent schrieb:> -os hab ich als optimierungsstufe gewählt,
Das ist keine Optimierungsstufe, sondern es setzt die Ausgabedatei auf
s. Siehe Dokumentation zu -o
Compilierfähiger Code wäre schon manchmal schön ... Angabe des
genauen Controllers und der benutzten Taktfrequenz ebenfalls.
OK, ich habe deinen Code mal wie folgt ergänzt:
(Ja, ich weiß, hab' vergessen, Recv zu initialisieren. Spielt aber
fürs Prinzip jetzt keine Geige.)
Wenn ich das jetzt für einen ATmega8 compiliere (GCC 4.5.1), bekomme
ich:
1
.global Rx
2
.type Rx, @function
3
Rx:
4
/* prologue: function */
5
/* frame size = 0 */
6
/* stack size = 0 */
7
.L__stack_usage = 0
8
.L2:
9
sbic 48-32,0
10
rjmp .L2
11
ldi r20,lo8(37)
12
1:dec r20
13
brne 1b
14
nop
15
ldi r25,lo8(8)
16
ldi r22,lo8(2)
17
ldi r18,lo8(1)
18
ldi r19,hi8(1)
19
.L3:
20
subi r25,lo8(-(-1))
21
ldi r20,lo8(21)
22
1:dec r20
23
brne 1b
24
nop
25
in r20,50-32
26
eor r20,r22
27
out 50-32,r20
28
in r20,48-32
29
movw r20,r18
30
mov r0,r25
31
rjmp 2f
32
1: lsl r20
33
rol r21
34
2: dec r0
35
brpl 1b
36
or r24,r20
37
tst r25
38
brne .L3
39
/* epilogue start */
40
ret
Wie du sehen kanst, sind deine Ausdrücke der Form (1 << RecvCount)
die Übeltäter. Da der AVR keinen Schiebebefehl für mehrere Bits
hat, muss die variable Verschiebung durch eine Schleife von LSL/ROL
implementiert werden. Je nachdem, wie groß dein RecvCount gerade
ist, nimmt diese Schleife eine unterschiedliche Zeit in Anspruch.
Das gesamte Timing des restlichen "Overheads" geht halt im Vergleich
zu deinen _delay_us() bereits heftig mit ein (mit 1 MHz CPU-Takt
könnte man die Sache bereits komplett in die Tonne drücken).
Wenn du mit -O3 compilieren lässt, entrollt der Compiler die
Schleife, und du bist die Abhängigkeit vom Durchlaufzähler los:
1
.global Rx
2
.type Rx, @function
3
Rx:
4
/* prologue: function */
5
/* frame size = 0 */
6
/* stack size = 0 */
7
.L__stack_usage = 0
8
.L2:
9
sbic 48-32,0
10
rjmp .L2
11
ldi r24,lo8(37)
12
1:dec r24
13
brne 1b
14
nop
15
ldi r25,lo8(21)
16
1:dec r25
17
brne 1b
18
nop
19
in r25,50-32
20
ldi r24,lo8(2)
21
eor r25,r24
22
out 50-32,r25
23
in r25,48-32
24
ldi r25,lo8(21)
25
1:dec r25
26
brne 1b
27
nop
28
in r25,50-32
29
eor r25,r24
30
out 50-32,r25
31
in r25,48-32
32
ldi r25,lo8(21)
33
1:dec r25
34
brne 1b
35
nop
36
in r25,50-32
37
eor r25,r24
38
out 50-32,r25
39
in r25,48-32
40
ldi r25,lo8(21)
41
1:dec r25
42
brne 1b
43
nop
44
in r25,50-32
45
eor r25,r24
46
out 50-32,r25
47
in r25,48-32
48
ldi r25,lo8(21)
49
1:dec r25
50
brne 1b
51
nop
52
in r25,50-32
53
eor r25,r24
54
out 50-32,r25
55
in r25,48-32
56
ldi r25,lo8(21)
57
1:dec r25
58
brne 1b
59
nop
60
in r25,50-32
61
eor r25,r24
62
out 50-32,r25
63
in r25,48-32
64
ldi r25,lo8(21)
65
1:dec r25
66
brne 1b
67
nop
68
in r25,50-32
69
eor r25,r24
70
out 50-32,r25
71
in r25,48-32
72
ldi r25,lo8(21)
73
1:dec r25
74
brne 1b
75
nop
76
in r25,50-32
77
eor r25,r24
78
out 50-32,r25
79
in r24,48-32
80
ldi r24,lo8(-1)
81
/* epilogue start */
82
ret
Du kannst aber auch deinen Quellcode ein wenig umschreiben und
die Bitmaske als Variable mitführen, um dem Compiler zu helfen:
Der sich ergebende Code (-Os) ist nicht nur invariabel von der Zahl
der Schleifendurchläufe, sondern auch noch kompakter:
1
.global Rx
2
.type Rx, @function
3
Rx:
4
/* prologue: function */
5
/* frame size = 0 */
6
/* stack size = 0 */
7
.L__stack_usage = 0
8
.L2:
9
sbic 48-32,0
10
rjmp .L2
11
ldi r19,lo8(37)
12
1:dec r19
13
brne 1b
14
nop
15
ldi r18,lo8(8)
16
ldi r25,lo8(-128)
17
ldi r24,lo8(0)
18
ldi r20,lo8(2)
19
.L3:
20
ldi r19,lo8(21)
21
1:dec r19
22
brne 1b
23
nop
24
in r19,50-32
25
eor r19,r20
26
out 50-32,r19
27
in r19,48-32
28
or r24,r25
29
lsr r25
30
subi r18,lo8(-(-1))
31
brne .L3
32
/* epilogue start */
33
ret
Allerdings wird das Timing mit der nächsten Version des Compilers
trotzdem wahrscheinlich geringfügig anders sein. Eigentlich sollte
man derartige Dinge wohl daher in Assembler schreiben (auch wenn
ich davon sonst nicht der große Protagonist bin ;) und dann die
Tatkzyklen gleich mit der Hand auszählen.
Delays können garnicht den Anspruch erheben, genau zu sein. Sie können
ja nicht wissen, welche Zeit der Code davor und danach benötigt.
Z.B. braucht ein einzelner Befehl schon 1µs bei 1MHz und eine C-Zeile
besteht fast nie aus nur einem Befehl, eher sinds 10 .. 20.
Und Interrupthandler addieren sich auch auf die Delayzeit auf.
Delays sollen nur nicht kürzer sein, als angegeben. Z.B. wenn man ein
Byte an ein LCD schickt, muß man ~50µs warten. Wenns länger dauert,
juckt das niemanden.
Je länger ein Delay, umso geringer wird natürlich der Einfluß der
Codelaufzeit auf die Gesamtdauer.
Solls genau sein, muß man einen Timer nehmen.
Peter
Jörg Wunsch schrieb:> Eigentlich sollte man derartige Dinge wohl daher in Assembler> schreiben (auch wenn ich davon sonst nicht der große Protagonist> bin ;) und dann die Taktzyklen gleich mit der Hand auszählen.
Geht zB mit so einer Instruktionsfolge:
Johann L. schrieb:> Jörg Wunsch schrieb:>>> Eigentlich sollte man derartige Dinge wohl daher in Assembler>> schreiben (auch wenn ich davon sonst nicht der große Protagonist>> bin ;) und dann die Taktzyklen gleich mit der Hand auszählen.>> Geht zB mit so einer Instruktionsfolge:
Nein, ich meinte nicht den Shift, sondern die gesamte Schleife,
die ja offenbar zeitkritisch ist.
ok, vielen vielen dank,...
so was ähnliches hab ich in assembler schon rumliegen, das soll aber
eben jetzt c werden (wenn möglich) warum?,... hmmm eigentlich nur weils
halt noch mehr "state of the art" ist als asm ;) UND weil wir in letzter
zeit immer öffter änbderungen oder spezielitäten hatten die sich in c
dann besser anpassen/ändern lassen als in asm. momentan soll wieder eine
änderung rein, und beim erneuten einlesen ins asm hab ich dann lieber
angefangen das in c zu machen.
ich glaube mit den ansätzen kann ich heut gut arbeiten!!
vielen dank!
lg
Verzögerungsschleifen programmier ich lieber selbst wenn Sie genau sein
sollen.
Zum testen am besten auf einen OSI an einem pin ausgeben
(Rechtecksignal) dann kann man das exakt messen.
Wenn es nicht Programmlastig werden soll die Delay Scheife über
Timerinteruupt triggern.
Frank schrieb:> Verzögerungsschleifen programmier ich lieber selbst wenn Sie genau sein> sollen.>> Zum testen am besten auf einen OSI an einem pin ausgeben> (Rechtecksignal) dann kann man das exakt messen.
Und das machst du wirklich nach jedem mal kompilieren erneut, und
stellst die delays neu ein? Kann ich kaum glauben.
Arthur Dent schrieb:> so was ähnliches hab ich in assembler schon rumliegen, das soll aber> eben jetzt c werden (wenn möglich) warum?,... hmmm eigentlich nur weils> halt noch mehr "state of the art" ist als asm ;) UND weil wir in letzter> zeit immer öffter änbderungen oder spezielitäten hatten die sich in c> dann besser anpassen/ändern lassen als in asm.
Du sollst ja deshalb auch nicht das ganze Projekt in Assembler
schreiben, sondern nur die eine, zeitkritische Funktion. Diese
lässt sich ja dann als separates Objekt zu einem ansonsten in
C geschriebenen Projekt dazu linken.
Frank schrieb:> Verzögerungsschleifen programmier ich lieber selbst wenn Sie genau sein> sollen.
Wird nicht mehr und nicht minder genau als _delay_us(). Sieht mir
eher danach aus, als wüsstest du überhaupt nicht, wie diese
Verzögerungsschleifen tatsächlich implmentiert sind.
> Zum testen am besten auf einen OSI an einem pin ausgeben> (Rechtecksignal) dann kann man das exakt messen.
Genau das hat Arthur ja versucht. Hat nur nicht funktioniert. Hast
du dir den Rest seines Problems denn überhaupt angesehen?