www.mikrocontroller.net

Forum: Compiler & IDEs Encoder auswerten, Assembler Code unverständlich


Autor: Robert S. (razer) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo an alle,

Ich hab eine Routine für die Auswertung zweier Drehgeber (nach P. 
Danneger) geschrieben.
#define INC0_PHASE_A  (1<<PB4)
#define INC0_PHASE_B  (1<<PB5)
#define INC1_PHASE_A  (1<<PB6)
#define INC1_PHASE_B  (1<<PB7)

void decodeGrayCode(void)
{
  uint8_t inc0 = 0, inc1 = 0;
  static int8_t inc0_old = 0x01, inc1_old = 0x01;
  uint8_t tmp = ENCODER_PIN;
  
  if(tmp & 0x10)
    inc0 = 0x01;
    
  if(tmp & INC0_PHASE_B)
    inc0 ^= 0x03;
    
  if(tmp & INC1_PHASE_A)
    inc1 = 0x01;
  
  if(tmp & INC1_PHASE_B)
    inc1 ^= 0x03;
    
  inc0 -= inc0_old;
  inc1 -= inc1_old;
  
  if(inc0 & 0x01)
  {
    inc0_old += inc0;
    inc0_value += (inc0 & 0x02) - 1;
  }
  
  if(inc1 & 0x01)
  {
    inc1_old += inc1;
    inc1_value += (inc1 & 0x02) - 1;
  }
}

Nun hab ich mir das Assembler Listing angeschaut. Jedoch versteh ich da 
einen Teil nicht.
11c:  53 b1         in  r21, 0x03  ; 3
  
  if(tmp & INC0_PHASE_A)
 11e:  25 2f         mov  r18, r21
 120:  30 e0         ldi  r19, 0x00  ; 0
 122:  c9 01         movw  r24, r18
 124:  44 e0         ldi  r20, 0x04  ; 4
 126:  96 95         lsr  r25
 128:  87 95         ror  r24
 12a:  4a 95         dec  r20
 12c:  e1 f7         brne  .-8        ; 0x126 <decodeGrayCode+0xa>
 12e:  48 2f         mov  r20, r24
 130:  41 70         andi  r20, 0x01  ; 1
    inc0 = 0x01;
    
  if(tmp & INC0_PHASE_B)
 132:  55 ff         sbrs  r21, 5
 134:  02 c0         rjmp  .+4        ; 0x13a <decodeGrayCode+0x1e>
    inc0 ^= 0x03;
 136:  83 e0         ldi  r24, 0x03  ; 3
 138:  48 27         eor  r20, r24
 13a:  86 e0         ldi  r24, 0x06  ; 6
 13c:  36 95         lsr  r19
 13e:  27 95         ror  r18
 140:  8a 95         dec  r24
 142:  e1 f7         brne  .-8        ; 0x13c <decodeGrayCode+0x20>
 144:  92 2f         mov  r25, r18
 146:  91 70         andi  r25, 0x01  ; 1
    
  if(tmp & INC1_PHASE_A)
    inc1 = 0x01;
  
  if(tmp & INC1_PHASE_B)
 148:  57 ff         sbrs  r21, 7
 14a:  02 c0         rjmp  .+4        ; 0x150 <decodeGrayCode+0x34>
    inc1 ^= 0x03;
 14c:  83 e0         ldi  r24, 0x03  ; 3
 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

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe den C-Code mal auf das Wesentliche abgespeckt, um besser zu
erkenn, was der Compiler da für Unfug treibt:
#include <stdint.h>

uint8_t a;

void func(void) {
  uint8_t i = 0;
  
  if(a & 0x40)
    i = 0x01;
  a = i;
}

Hier ist der von GCC 4.2.3 erzeugte Assemblercode:
func:
/* prologue: frame size=0 */
/* prologue end (size=0) */
  lds r24,a
  clr r25
  clr __tmp_reg__
  lsl r24
  rol r25
  rol __tmp_reg__
  lsl r24
  rol r25
  rol __tmp_reg__
  mov r24,r25
  mov r25,__tmp_reg__
  andi r24,lo8(1)
  sts a,r24
/* epilogue: frame size=0 */
  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:
  if(tmp & INC0_PHASE_B)
    inc0 = 0x03;
    
  if(tmp & INC0_PHASE_A)
    inc0 ^= 0x01;
    
  if(tmp & INC1_PHASE_B)
    inc1 = 0x03;
    
  if(tmp & INC1_PHASE_A)
    inc1 ^= 0x01;

Optimal ist der erzeugte Code immer noch nicht, aber wenigstens
entfällt die wilde Bitschieberei.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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
#include "avr/io.h"

#define ENCODER_PIN PINA
#define INC0_PHASE_A  (1<<PB4)
#define INC0_PHASE_B  (1<<PB5)
#define INC1_PHASE_A  (1<<PB6)
#define INC1_PHASE_B  (1<<PB7)

uint8_t inc0_value, inc1_value;

void decodeGrayCode(void)
{
  uint8_t inc0 = 0, inc1 = 0;
  static int8_t inc0_old = 0x01, inc1_old = 0x01;
  uint8_t tmp = ENCODER_PIN;
  
  if(tmp & 0x10)
    inc0 = 0x01;
    
  if(tmp & INC0_PHASE_B)
    inc0 ^= 0x03;
    
  if(tmp & INC1_PHASE_A)
    inc1 = 0x01;
  
  if(tmp & INC1_PHASE_B)
    inc1 ^= 0x03;
    
  inc0 -= inc0_old;
  inc1 -= inc1_old;
  
  if(inc0 & 0x01)
  {
    inc0_old += inc0;
    inc0_value += (inc0 & 0x02) - 1;
  }
  
  if(inc1 & 0x01)
  {
    inc1_old += inc1;
    inc1_value += (inc1 & 0x02) - 1;
  }
}

int main(void) {}
void decodeGrayCode(void)
{
  uint8_t inc0 = 0, inc1 = 0;
  8e:  50 e0         ldi  r21, 0x00  ; 0
  90:  45 2f         mov  r20, r21
  static int8_t inc0_old = 0x01, inc1_old = 0x01;
  uint8_t tmp = ENCODER_PIN;
  92:  99 b3         in  r25, 0x19  ; 25
  
  if(tmp & 0x10)
  94:  29 2f         mov  r18, r25
  96:  33 27         eor  r19, r19
  98:  24 fd         sbrc  r18, 4
    inc0 = 0x01;
  9a:  41 e0         ldi  r20, 0x01  ; 1
    
  if(tmp & INC0_PHASE_B)
  9c:  25 ff         sbrs  r18, 5
  9e:  02 c0         rjmp  .+4        ; 0xa4 <decodeGrayCode+0x16>
    inc0 ^= 0x03;6 
  a0:  83 e0         ldi  r24, 0x03  ; 3
  a2:  48 27         eor  r20, r24
    
  if(tmp & INC1_PHASE_A)
  a4:  26 fd         sbrc  r18, 6
    inc1 = 0x01;
  a6:  51 e0         ldi  r21, 0x01  ; 1
  
  if(tmp & INC1_PHASE_B)
  a8:  97 ff         sbrs  r25, 7
  aa:  02 c0         rjmp  .+4        ; 0xb0 <decodeGrayCode+0x22>
    inc1 ^= 0x03;
  ac:  83 e0         ldi  r24, 0x03  ; 3
  ae:  58 27         eor  r21, r24
    
  inc0 -= inc0_old;
  b0:  80 91 60 00   lds  r24, 0x0060
  b4:  48 1b         sub  r20, r24
  inc1 -= inc1_old;
  b6:  90 91 61 00   lds  r25, 0x0061
  ba:  59 1b         sub  r21, r25
  
  if(inc0 & 0x01)
  bc:  40 ff         sbrs  r20, 0
  be:  0a c0         rjmp  .+20       ; 0xd4 <decodeGrayCode+0x46>
  {
    inc0_old += inc0;
  c0:  84 0f         add  r24, r20
  c2:  80 93 60 00   sts  0x0060, r24
    inc0_value += (inc0 & 0x02) - 1;
  c6:  42 70         andi  r20, 0x02  ; 2
  c8:  80 91 62 00   lds  r24, 0x0062
  cc:  84 0f         add  r24, r20
  ce:  81 50         subi  r24, 0x01  ; 1
  d0:  80 93 62 00   sts  0x0062, r24
  }
  
  if(inc1 & 0x01)
  d4:  50 ff         sbrs  r21, 0
  d6:  0a c0         rjmp  .+20       ; 0xec <decodeGrayCode+0x5e>
  {
    inc1_old += inc1;
  d8:  95 0f         add  r25, r21
  da:  90 93 61 00   sts  0x0061, r25
    inc1_value += (inc1 & 0x02) - 1;
  de:  52 70         andi  r21, 0x02  ; 2
  e0:  80 91 63 00   lds  r24, 0x0063
  e4:  85 0f         add  r24, r21
  e6:  81 50         subi  r24, 0x01  ; 1
  e8:  80 93 63 00   sts  0x0063, r24
  ec:  08 95         ret

MfG
Falk

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> 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.

Autor: Robert S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja ich hab Optimierung Os. Ich hab bereits mit der WinAVR Version 
20071221 getestet. Die liefert jedoch den gleichen Code...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.