Moin moin,
Ich möchte mit folgendem C-Code-Schnipsel für ATMega8 ein Signal
generieren:
1 | ISR(TIMER0_OVF_vect)
|
2 | {
|
3 | uint16_t temp = outputValue; // global deklariert
|
4 | for(uint8_t i=16; i>0 i--)
|
5 | {
|
6 | if(temp & (1<<15))
|
7 | {
|
8 | SDA_PORT |= (1<<SDA_PIN);
|
9 | }
|
10 | else
|
11 | {
|
12 | SDA_PORT ~= (1<<SDA_PIN);
|
13 | }
|
14 | // toggle clock
|
15 | SCL_PORT &= ~(1<<SCL_PIN);
|
16 | SCL_PORT |= (1<<SCL_PIN);
|
17 |
|
18 | // next bit
|
19 | temp <<= 1;
|
20 | }
|
21 | }
|
Wird soweit mit -Os ganz nett übersetzt in sbi, cbi etc. Nur die
if-else-Abfrage mit den extrem kurzen Schleifenrümpfen (1
Assembler-Befehl) würde ich in Assembler schneller und vor allem immer
gleich schnell hinbekommen - das Ergebnis ist nämlich:
1 | sbrs r25,7
|
2 | rjmp .L6
|
3 | sbi 53-0x20,4
|
4 | rjmp .L7
|
5 | .L6:
|
6 | cbi 53-0x20,4
|
7 | .L7
|
Das dauert in Zyklen 2(sbrs)+1(sbi)+2(rjmp) für gesetzt,
1(sbrs)+2(rjmp)+1(cbi) für gelöscht.
Ich würde sowas schreiben wie:
1 | sbrs r25,7
|
2 | cbi 53-0x20,4
|
3 | sbrc r25,7
|
4 | sbi 53-0x20,4
|
Das dauert in Zyklen 2(sbrs)+1(sbrc)+1(sbi) für gesetzt,
1(sbrs)+1(cbi)+2(sbrc) für gelöscht - und etwas kleiner ist der Code
auch noch...
Ist das
a) ein Fall für inline-Assembler?
b) in neueren Versionen von avr-gcc besser geworden (aktuell hier
4.3.3)?
Oder gibt es eine geschicktere Variante, das in C zu schreiben? Zwei
getrennte if-Abfragen (einmal auf gesetzt, einmal auf gelöscht) habe ich
ausprobiert, kompiliert zu dem gleichen Code wie die Variante mit
if-else - obwohl eine einzelne if-Abfrage auf gelöscht den gleichen Code
erzeugt wie meine Assembler-Variante.
Oder verrechne ich mich hier gerade und der gcc-Code ist genauso schnell
wie meiner?
Hat ein Bugreport einen Sinn? Wenn ja, wo?
MfG, Heiko
P.S: Es kommt mir hier nicht nur auf den einen (halben) Zyklus an,
sondern viel mehr darauf, dass ich gerne high- und low-Bytes gleich lang
hätte...