mikrocontroller.net

Forum: Compiler & IDEs AVR - Bitverschiebung


Autor: Henning Achter (Firma: IAP) (wischmopp)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo zusammen.
Vorweg: Ich bin ziemlicher Anfänger, was das Programmieren mit Embedded 
Systems angeht.
Ich habe meinen Atmega32 über I2C mit einem PCF8574 verbunden. Hinter 
diesem sitzt eine Matrix Tastatur, die ich abfragen will. Ausgabe 
erfolgt auf LC-Display

Hier erstmal ein kleiner auszug aus dem Code:
int main (void)
{
  lcd_init();
  
  delay_s(1);
  
  lcd_on();
  
  twi_init(72);
  
    
  while(1)
  {
  
    for (int i=0; i<4; i++)
    }
    twi_m_start(32, TW_WRITE);
    twi_m_write(254 << i);
        
    twi_m_start(32, TW_READ);
    int j = twi_m_read();
    
    switch(j)
    {
      case 190:
        lcdxy(0,1);
        printf("1");
        break;
      
      case 222:
        lcdxy(0,1);
        printf("2");
        break;
        
      case 238:
        lcdxy(0,1);
        printf("3");
        break;  
      
      case 189:
        lcdxy(0,1);
        printf("4");
        break;
      
      case 221:
        lcdxy(0,1);
        printf("5");
        break;
        
      case 237:
        lcdxy(0,1);
        printf("6");
        break;
      
      case 187:
        lcdxy(0,1);
        printf("7");
        break;
      
      case 219:
        lcdxy(0,1);
        printf("8");
        break;
        
      case 235:
        lcdxy(0,1);
        printf("9");
        break;
    }
  }
      }
  


Ich muss im Prinzip 4x abfragen bis ich alle Reihen der Matrix einmal 
auf 0 gesetzt habe. Der Wert der bei tw_m_write() übergeben wird ist 
int. Ich habe das Problem, wenn ich die 254(1111 1110) nach 1 links << 
shifte, er von rechts wieder mit Nullen auffüllt (1111 1100). Konnte ich 
ja am PCF auch nachmessen. Ich möchte aber, dass es nach einem 254 << 1 
so aussieht: (1111 1101). Jetzt meine Frage, wie kann ich das 
realisieren? Wie lautet der Befehl dafür? Für Antworten wäre ich  sehr 
dankbar. Das, was ich suche, ist doch doch ein Rotate through Carry 
oder?

Gruß Henning

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mir ist davon ab noch ne Katastrophe aufgefallen:
twi_m_write(254 << i);

Des ist kacke, weil du um ne Variable schiebst (i), der AVR sowas aber 
nicht von Haus aus kann. Speicher lieber die 254 in ne Variable und 
schieb in jedem Durchlauf um eins nach links.

Autor: Stefan B. (stefan) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Henning Achter wrote:

> Das, was ich suche, ist doch doch ein Rotate through Carry
> oder?

Hast du schon im AVR 8-Bit Instruction Set 
(http://www.atmel.com/dyn/resources/prod_documents/...) 
nachgesehen, ob es sowas wie ein Rotate through Carry gibt? Mir fallen 
da die ROL und ROR Instruktionen auf...

Du könntest diese Befehle in (avr-gcc) C mit Inline Assembler machen.
http://www.roboternetz.de/wissen/index.php/Inline-...
http://www.nongnu.org/avr-libc/user-manual/inline_asm.html

Oder in C nachstellen, also aktuelles MostSignificantBit MSB in Variable 
carry merken, Shiften (<<) und carry als LeastSignificantBit LSB 
dazuodern.

Autor: Henning Achter (Firma: IAP) (wischmopp)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja der ROL steht für Rotate throug Carry. Nur habe ich im Stillen 
gehofft, dass ich das Problem mit C lösen kann. Ich kenne mich mit 
Assembler nicht wirklich gut aus. :)

Aber werd mir deine Links mal verinnerlichen. danke schonmal

@Sven pauli.

Die Variable i ist bei jeden Duchlauf eins größer und das klappt auch 
einwandfrei so, habs nachgemessen mit einem Oszi. Nur die 
Bitverschiebung ist keine zyklische, sondern eine arithmetische.

Autor: Sven P. (haku) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Henning Achter wrote:
> @Sven pauli.
>
> Die Variable i ist bei jeden Duchlauf eins größer und das klappt auch
> einwandfrei so, habs nachgemessen mit einem Oszi. Nur die
> Bitverschiebung ist keine zyklische, sondern eine arithmetische.

Du verstehst das falsch. Natürlich funktioniert das, aber schau dir mal 
an, welchen riesen Code der Compiler draus macht.

Wenn du immer nur um 1 (eben um eine Konstante) schiebst, gibt das im 
Code dann genau eine "lsl"-Instruktion. Wenn du um "i" schiebst, gibts 
im Code ne Schleife, die "i"-Mal ausgeführt wird, und das ist total 
ineffizient und in deinem Fall auch unnötig.

Autor: Henning Achter (Firma: IAP) (wischmopp)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ok, das hast du natürlich recht. Darüber hab ich auch ehrlich gesagt 
nicht nachgedacht. Ich wollte, dass es zumindest erstmal läuft und mich 
dann um die Effienz kümmern. Habs aber nun geändert. danke :)

Autor: Jörg X. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Eine leserliche C-Variante wäre sowas:
mask = 0x01
//...
for (int i=0; i<4; i++)
{
  //...
  twi_m_write( ~mask); ("~" bzw "com" geht in einem Asm-Befehl)
  // ...
  mask <<=1;
//...
}
Außerdem sind die ganzen Dezimalzahlen sehr verwirrend, ich würde auf 
Jeden Fall die I2C-Adresse 'rausziehen' ("#define PCF8574 0x20" o.ä.) 
und die case's zumindest in hex darstellen (0xbe, 0xde, 0xee...) oder 
sogar binär...
(und den "case default:" einbauen).

hth. Jörg

Autor: Henning Achter (Firma: IAP) (wischmopp)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ich bin nochmal erstmal danke für die zahlreichen Antworten.

Habe es mal mit asm volatile versucht. hier mal der code nur als 
Prinzip.
int main (void)
{
  lcd_init();
  
  lcd_on();
  
  unsigned int i = 254;
  
  printf("%i", i);
  
  for (int j=0; j<3; j++)
  {
    delay_s(2);
  
    dspclr();

    asm volatile ("rol %0":"=r" (i));
  
    printf("%i", i);
  }
}


Als Ergebnis erhalte ich jedoch meiner Meinung nach immernoch eine 
logische Bitverschiebung.
Beispiel 254 (1111 1110)
1. Shift 1111 1100 und nicht 1111 1101
2: Shift 1111 1000 und nicht 1111 1011
usw.

Stimmt da etwas mit der Syntax nicht in dem asm Befehl?

Danke im Voraus.

Gruß Henning

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
rol verschiebt den Wert im Register um eine Stelle nach links. Dabei 
wird das, was grad (in diesem Fall wohl eher zufällig) im Carry-Flag 
steht, von hinten in das Register reingeschoben und das MSB wird ins 
Carry-Flag geschoben. Es ist sehr unwahrscheinlich, dass nach einem 
printf-Aufruf im Carry für den nächsten Schiebevorgang noch der alte 
Zustand steht...

Ich würde es ohne das Inline-Assembler-Zeugs machen. Das Carry-Flag kann 
man in C auch abfragen. Ist ein recht gewöhnliches Bit im SREG.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Stimmt da etwas mit der Syntax nicht in dem asm Befehl?

Nein, was nicht stimmt, ist dein Verständnis von "rotate through carry". 
Was links raus fällt landet im Carry, und rechts wird nachgeschoben, was 
vorher im Carry war.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
variable = (variable << 1) | (SREG & 1);
könnte klappen. Wäre die einfachste und kürzeste Methode.

Aber besser ins Listing schauen, ob der Compiler nicht noch irgendeinen 
Carry-Flag-beeinflussenden Befehl dareinschiebt.

Wenn Du auf Nummer sicher gehen willst, dann besser so:
variable = (variable << 1) | (variable & 0x80) ? 1 : 0;
...oder so ähnlich.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Je nachdem wie zeitkritisch das Ganze ist, könnte man
auch eine reine C-Lösung ins Auge fassen:
   tmp = i & 0x80;
   i <<= 1;
   if( tmp )
     i |= 0x01;

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johannes M. wrote:
>
> variable = (variable << 1) | (SREG & 1);
> 
> könnte klappen.

Streng genommen hast du keine Garantie, dass der Compiler das
so umsetzt wie du es erwartest. Der Compiler darf durchaus
(SREG & 1 ) berechnen noch ehe er Code für (variable << 1)
generiert.

Dann schiebst du wieder irgendein Bit hinein

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger wrote:
> Streng genommen hast du keine Garantie, dass der Compiler das
> so umsetzt wie du es erwartest. Der Compiler darf durchaus
> (SREG & 1 ) berechnen noch ehe er Code für (variable << 1)
> generiert.
>
> Dann schiebst du wieder irgendein Bit hinein
Das ist natürlich richtig. Also besser in zwei Schritten. Und in jedem 
Fall ins .lss schauen, was der wirklich draus macht.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Johannes M. wrote:
>
> variable = (variable << 1) | (variable & 0x80) ? 1 : 0;
> 
> ...oder so ähnlich.
Oder:
variable = (variable << 1) | ((variable & 0x80) && 1);

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger wrote:
> Johannes M. wrote:
>>
>> variable = (variable << 1) | (SREG & 1);
>> 
>> könnte klappen.
>
> Streng genommen hast du keine Garantie, dass der Compiler das
> so umsetzt wie du es erwartest. Der Compiler darf durchaus
> (SREG & 1 ) berechnen noch ehe er Code für (variable << 1)
> generiert.
>
> Dann schiebst du wieder irgendein Bit hinein

In diesem Fall holt er zumindest vorher das SREG, klappt also nicht:
+00000074:   B78F        IN      R24,0x3F         In from I/O location
+00000075:   0F99        LSL     R25              Logical Shift Left
+00000076:   7081        ANDI    R24,0x01         Logical AND with immediate
+00000077:   2B98        OR      R25,R24          Logical OR

Autor: Henning Achter (Firma: IAP) (wischmopp)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank erstmal,
tmp = i & 0x80;
   i <<= 1;
   if( tmp )
     i |= 0x01;
und
variable = (variable << 1) | ((variable & 0x80) && 1);

funktionieren.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan Ernst wrote:

> In diesem Fall holt er zumindest vorher das SREG, klappt also nicht:
>
> +00000074:   B78F        IN      R24,0x3F         In from I/O location
> +00000075:   0F99        LSL     R25              Logical Shift Left
> +00000076:   7081        ANDI    R24,0x01         Logical AND with
> immediate
> +00000077:   2B98        OR      R25,R24          Logical OR
> 

Wie bekommt man so ausführliche Assembler Listings mit dem AVR-GCC? ;)

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Simon K. wrote:

> Wie bekommt man so ausführliche Assembler Listings mit dem AVR-GCC? ;)

Das stammt aus dem Disassembler-Fenster des AVR-Studio-Debuggers. ;-)

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan Ernst wrote:

> Das stammt aus dem Disassembler-Fenster des AVR-Studio-Debuggers. ;-)

Besonders ,,nützlich'' finde ich immer die letzte Spalte...  Sie hätten
den Platz wirklich sinnvoller nutzen können, bspw. um für Adressen die
symbolischen Namen anzuzeigen (sofern bekannt) oder bei relativen
Sprüngen das Sprungziel auszurechnen.  Wer nicht weiß, dass "ANDI"
ein "Logical AND with" ist, dem wird auch der Kommentar beim Lesen
kaum noch helfen.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg Wunsch wrote:

> Besonders ,,nützlich'' finde ich immer die letzte Spalte...

Ja, ist reichlich überflüssig.
Aber das lss-File hat auch so seine Macken. Im konkreten Fall steht da:
  e8:  8f b7         in  r24, 0x3f  ; 63
  ea:  99 0f         add  r25, r25
  ec:  81 70         andi  r24, 0x01  ; 1
  ee:  98 2b         or  r25, r24
Ich finde "lsl r25" lesbarer als "add r25,r25" (ist aber wohl 
Geschmackssache).

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Stefan Ernst wrote:

> Ich finde "lsl r25" lesbarer als "add r25,r25" (ist aber wohl
> Geschmackssache).

Tja, das ist wohl das Dilemma eines Disassemblers, welchen von zwei
Mnemonics es sich für ein und denselben Opcode aussuchen darf...
Letztlich ist wohl das, was man gerade lesbarer findet, vom Kontext
abhängig.

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.