Hallo an alle,
Ich hab eine Routine für die Auswertung zweier Drehgeber (nach P.
Danneger) geschrieben.
1
#define INC0_PHASE_A (1<<PB4)
2
#define INC0_PHASE_B (1<<PB5)
3
#define INC1_PHASE_A (1<<PB6)
4
#define INC1_PHASE_B (1<<PB7)
5
6
voiddecodeGrayCode(void)
7
{
8
uint8_tinc0=0,inc1=0;
9
staticint8_tinc0_old=0x01,inc1_old=0x01;
10
uint8_ttmp=ENCODER_PIN;
11
12
if(tmp&0x10)
13
inc0=0x01;
14
15
if(tmp&INC0_PHASE_B)
16
inc0^=0x03;
17
18
if(tmp&INC1_PHASE_A)
19
inc1=0x01;
20
21
if(tmp&INC1_PHASE_B)
22
inc1^=0x03;
23
24
inc0-=inc0_old;
25
inc1-=inc1_old;
26
27
if(inc0&0x01)
28
{
29
inc0_old+=inc0;
30
inc0_value+=(inc0&0x02)-1;
31
}
32
33
if(inc1&0x01)
34
{
35
inc1_old+=inc1;
36
inc1_value+=(inc1&0x02)-1;
37
}
38
}
Nun hab ich mir das Assembler Listing angeschaut. Jedoch versteh ich da
einen Teil nicht.
1
11c: 53 b1 in r21, 0x03 ; 3
2
3
if(tmp & INC0_PHASE_A)
4
11e: 25 2f mov r18, r21
5
120: 30 e0 ldi r19, 0x00 ; 0
6
122: c9 01 movw r24, r18
7
124: 44 e0 ldi r20, 0x04 ; 4
8
126: 96 95 lsr r25
9
128: 87 95 ror r24
10
12a: 4a 95 dec r20
11
12c: e1 f7 brne .-8 ; 0x126 <decodeGrayCode+0xa>
12
12e: 48 2f mov r20, r24
13
130: 41 70 andi r20, 0x01 ; 1
14
inc0 = 0x01;
15
16
if(tmp & INC0_PHASE_B)
17
132: 55 ff sbrs r21, 5
18
134: 02 c0 rjmp .+4 ; 0x13a <decodeGrayCode+0x1e>
19
inc0 ^= 0x03;
20
136: 83 e0 ldi r24, 0x03 ; 3
21
138: 48 27 eor r20, r24
22
13a: 86 e0 ldi r24, 0x06 ; 6
23
13c: 36 95 lsr r19
24
13e: 27 95 ror r18
25
140: 8a 95 dec r24
26
142: e1 f7 brne .-8 ; 0x13c <decodeGrayCode+0x20>
27
144: 92 2f mov r25, r18
28
146: 91 70 andi r25, 0x01 ; 1
29
30
if(tmp & INC1_PHASE_A)
31
inc1 = 0x01;
32
33
if(tmp & INC1_PHASE_B)
34
148: 57 ff sbrs r21, 7
35
14a: 02 c0 rjmp .+4 ; 0x150 <decodeGrayCode+0x34>
36
inc1 ^= 0x03;
37
14c: 83 e0 ldi r24, 0x03 ; 3
38
14e: 98 27 eor r25, r24
Für beide Encoder wird die Auswertung der Phase A sehr kompliziert
übersetzt. Woran liegt das?
Da INC0_PHASE_A eine Konstante ist, sollte er nu das eine Bit prüfen,
wie das bei der Phase B gemacht wird.
Ich verwende WinAVR2008610.
Ich hoffe ihr könnt mir helfen.
Danke im Voraus
Gruß Robert
Ich habe den C-Code mal auf das Wesentliche abgespeckt, um besser zu
erkenn, was der Compiler da für Unfug treibt:
1
#include<stdint.h>
2
3
uint8_ta;
4
5
voidfunc(void){
6
uint8_ti=0;
7
8
if(a&0x40)
9
i=0x01;
10
a=i;
11
}
Hier ist der von GCC 4.2.3 erzeugte Assemblercode:
1
func:
2
/* prologue: frame size=0 */
3
/* prologue end (size=0) */
4
lds r24,a
5
clr r25
6
clr __tmp_reg__
7
lsl r24
8
rol r25
9
rol __tmp_reg__
10
lsl r24
11
rol r25
12
rol __tmp_reg__
13
mov r24,r25
14
mov r25,__tmp_reg__
15
andi r24,lo8(1)
16
sts a,r24
17
/* epilogue: frame size=0 */
18
ret
Der von GCC 4.3.0 generierte Code sieht gleich aus, lediglich 'clr
r25' wird durch 'ldi r25,lo8(0)' ersetzt.
Wie man sieht, hat der Compiler Code erzeugt, der das Eregbnis a
direkt, also ohne Verzweigungen, berechnet. Das ist auch sinnvoll auf
Prozessoren mit schnellen Shiftern und vergleichsweise langsamen
Sprüngen, also bspw. bei PC-Prozessoren. Beim AVR ist das aber genau
anders herum, zumal die Schieberei über drei Register mit
anschließendem Zurückkopieren ziemlich umständlich ist.
Möglicherweise gibt es irgendeine Compileroption, mit der dieses
"Rechnen-statt-Springen"-Feature abgeschaltet werden kann, ich habe
sie allerdings weder im GCC-Manual noch per Google-Suche gefunden.
Vielleicht hat ja jemand anderes mehr Glück bei der Suche.
Als Workaround kannst du die ersten vier If-Anweisungen etwas
umstellen, so dass den Variablen inc0 und inc1 keine Werte direkt
zugewiesen werden, in denen nur ein einzelnes Bit gesetzt ist:
1
if(tmp&INC0_PHASE_B)
2
inc0=0x03;
3
4
if(tmp&INC0_PHASE_A)
5
inc0^=0x01;
6
7
if(tmp&INC1_PHASE_B)
8
inc1=0x03;
9
10
if(tmp&INC1_PHASE_A)
11
inc1^=0x01;
Optimal ist der erzeugte Code immer noch nicht, aber wenigstens
entfällt die wilde Bitschieberei.
Alos für mich sieht das mal wieder nach einer Verschlimmbesserung des
AVR GCC aus. Mit meinem Alteisen von 2006 kommt ganz normaler Assembler
raus.
Optimierung -Os
> Optimierung -Os
Ups, da fällt mir ein, ich habe das kleine Beispiel in meinem letzten
Post mit -O2 kompiliert. Mit -Os wird der Code zwar 4 Worte kürzer,
dafür braucht er etwa doppelt so viele Zyklen, weil jetzt statt ein
24-Bit-Wort zweimal nach links zu schieben ein 16 Bit-Wort in einer
Schleife um 6 Bits nach rechts geschoben wird. Das Assemblerlisting
von Robert sieht übrigens auch nach -Os aus.