Weshalb erzeugt diese Zeile: PORTB |= _BV(1)|_BV(2); 2Bytes mehr Code als diese 2 Zeilen: sbi(PORTB, 1); sbi(PORTB, 2); Für sbi verwende ich das define von <compat/deprected.h>
im ersten Code muß der Compiler den PORTB in ein Register laden, danach mit einem Wert odern und zurück in PORTB schreiben in reg, PORTB ldi temp, 3 or reg temp out PORTB, reg du schreibst ja im C soure auch PORTB = PORTB | (x); im zweiten Falle kann der Compiler einen speziellen ASM Opcode nutzen der immr nur 1 Bit eines Ports gleichzeitig löschen/setzen kann. Da dur nur 2 Bits auf diese weise änderst braucht man du 2 Befehle. Angenommen do wolltest PORTB |= _BV(1) | _BV(2) | _BV(3) | _BV(4) | _BV(5); setzen, dann wäre deine 2'te Methode um ein Takt langsammer. Gruß hagen
Bei mir benötigt aber z.B. auch: PORTB |= _BV(PB1)|_BV(PB2)|_BV(PB3)|_BV(PB4)|_BV(PB5); im Gegensatz zu: PORTB |= _BV(PB1)|_BV(PB2)|_BV(PB3)|_BV(PB4); 2 Bytes mehr...
Hi dein erster Fall dürfte eine fehlende Optimierung des GCC sein die eben nur zuschlägt wenn du zwei Bits setzt. Du kannst natürlich auch PORTB |= _BV(1); PORTB |= _BV(2); schreiben. Das sollte zum gleichen Code wie die sbi Makros führen. Den zweiten Fall das PORTB |= _BV(PB1)|_BV(PB2)|_BV(PB3)|_BV(PB4)|_BV(PB5); mehr Code erzeugt als PORTB |= _BV(PB1)|_BV(PB2)|_BV(PB3)|_BV(PB4); kann ich hier (WINAVR 20060125) nicht nachvollziehen. Matthias
> dein erster Fall dürfte eine fehlende Optimierung des GCC sein die eben > nur zuschlägt wenn du zwei Bits setzt. Die Optimierung kann er möglicherweise schon, aber PORTB ist volatile. Das heißt, das bei PORTB |= _BV(1) | BV(2); das Register nicht mehr als einmal gelesen/geschrieben werden darf.
Rolf Magnus wrote: >> dein erster Fall dürfte eine fehlende Optimierung des GCC sein die eben >> nur zuschlägt wenn du zwei Bits setzt. > > Die Optimierung kann er möglicherweise schon, aber PORTB ist volatile. > Das heißt, das bei > > PORTB |= _BV(1) | BV(2); > > das Register nicht mehr als einmal gelesen/geschrieben werden darf. Echt? Ist volatile derart definiert? Wo steht denn das? (ernstgemeinte Frage!) Mir war volatile nur als modifier bekannt das sich der Wert der Variable auch außerhalb des aktuellen Programmfluß ändern kann und der Compiler deshalb einige Optimierung nicht vornehmen darf. Von einem einzigen Zugriff ist mir nichts bekannt. PORTB |= _BV(1) | BV(2); ist eh nich atomar. Matthias
Ich sach ma, der Ausdruck rechts vom = muß immer zuerst berechnet werden, unabhängig vom volatile. Es ist durchaus ein sehr großer Unterschied, ob sich 2 Portpins gleichzeitig ändern oder nacheinander. Daher darf hier nichts dahingehend optimiert werden, daß einer erst verspätet schaltet. Z.B. ein SRG (74HC595) würde Dir da sehr böse sein. Peter
zu "volatile": <<This alters the translator to refetch the given identifier whenever it encounters an expression that includes the identifier. In addition, an object is marked as "volatile" must be stored at the point where an assignment to this object takes place.>> Logischerweise gilt das auch umgekehrt. Wenn genau eine Zuweisung dasteht, darf der Compiler nicht zwei daraus machen. Das ANSI-Komitee hatte dabei nicht nur Exceptions sondern ausdrücklich auch I/O-Ports im Auge. Und bei manchen I/O-Ports macht es nun einmal einen gewaltigen Unterschied, ob man ein- oder zweimal reinschreibt (z.B. UART-Datenregister).
@Peter: "Ich sach ma, der Ausdruck rechts vom = muß immer zuerst berechnet werden, unabhängig vom volatile." Nach dieser Definition wäre der Ausdruck a[i++] = b[i++]; wohldefiniert. Ist er aber nicht. Weder mit noch ohne "volatile". Eine Zuweisung ist kein "sequence point".
> Daher darf hier nichts dahingehend optimiert werden, daß einer erst > verspätet schaltet. Ja, genau daher sind die Portadressen ja auch als volatile definiert. Sonst dürfte der Compiler dort nämlich Dinge zusammenfassen.
@A.K. ich meinte natürlich nur diese Art Ausdrücke mit Operand= Bei c = a + b + c; ist die Reihenfolge undefiniert. Aber bei c += a + b; muß a + b immmer zuerst berechnet werden. Der Operand erbt quasi den Rang des = Zeichens. Peter
Peter Dannegger wrote: > Aber bei > > c += a + b; > > muß a + b immmer zuerst berechnet werden. Peter, deine Erfahrungen in allen Ehren, aber gelegentlich solltest du dich auch mit dem C-Standard auseinandersetzen. Nein, a + b muss keinesfalls zuerst berechnet werden. Der Compiler hat genauso gut das Recht, obige Anweisungen als c += a; c += b; zu implementieren, solange a, b und c wirklich einfache Variablen sind, die nicht "volatile" sind. Diffiziler wird es, wenn Funktionen im Spiel sind, aber selbst bei c += a() + b(); darf er das auch zu c += b(); c += a(); umsortieren, wenn ich mich nicht gerade gründlich irre, da die Reihenfolge der Bewertung der rechten Seite für den +-Operator nicht vorgeschrieben ist. (Andere Operatoren haben eine vorgeschriebene Reihenfolge, insbesondere ||, && und der Komma-Operator.) Ach so, das geht nur so, wenn es keine Chance gibt, dass "c" einen Einfluss auf die Funktionen a und b hat, also z. B. eine lokale Variable ist. Bei einer globalen Variablen wäre wohl nur: tmp = b(); tmp += a(); c += tmp; zulässig, d. h. jede der Funktionen a und b muss noch den vorherigen Wert von c sehen.
> wenn ich mich nicht gerade gründlich irre Du irrst nicht. In C gibt es ja dieses seltsame Konstrukt des 'sequence points'. Erst bei Erreichen eines Sequence Points müssen alle Aktionen abgeschlossen sein. Bis allerdings der sequence point erreicht ist, hat der Compiler alle Freiheiten Berechnungen in beliebiger Reihenfolge auszuführen, solange nur die Operatorreihenfolge (und die Ausnahmen die Jörg bereits angeführt hat) gewahrt bleibt. Ob der Compiler bei einer Zuweisung zuerst die rechte Seite auswertet (also den zugewiesenen Wert bestimmt) oder die linke Seite (also die Adressauswertung, wo der Wert abgespeichert werden soll), ist ganz alleine ihm überlassen. Der Standard macht da keine Vorschriften, = ist kein sequence point. > zulässig, d. h. jede der Funktionen a und b muss noch den vorherigen > Wert von c sehen. Das wäre mit allerdings neu, dass sich der C-Standard um sowas kümmert. Wenn du mit globalen Variablen in Funktionen arbeitest, dann liegt es am Programmierer sicherzustellen, dass die Funktionen auch in der richtigen Reihenfolge aufgerufen werden. Das mit 'muss den vorherigen Wert von c sehen' stimmt also so nicht. c = a() + b() Wenn es also wichtig ist, dass tatsächlich a() vor b() aufgerufen wird, dann kann man das nicht so schreiben, sondern muss explizit eine Reihenfolge erzwingen: c = a(); c += b(); Jetzt liegt zwischen dem Aufruf von a() und b() ein sequence point, der verhindert, dass der Compiler den Aufruf von b() vor dem Aufruf von a() machen kann. > Bei einer globalen Variablen wäre wohl nur: > > tmp = b(); > tmp += a(); > c += tmp; > > zulässig, Auch die andere Reihenfolge ist genauso zulässig. Genauso wie c += a(); c += b(); selbst wenn in a() und in b() c benutzt wird. Das ist das Problem des Programmierers sich darum zu kümmern. Niemand sagt, dass bei c += a + b; die += Zuweisung eine atomare Einheit sein muss und nicht auch auf Raten passieren kann. Die einzige Vorgabe die der Compiler hat: Bei erreichen des ;-sequence points muss die komplette Aktion abgeschlossen sein.
>> zulässig, d. h. jede der Funktionen a und b muss noch den vorherigen >> Wert von c sehen. >Das wäre mit allerdings neu, dass sich der C-Standard um sowas >kümmert." Ein Funktionsaufruf ist ein solcher "sequence point" und deshalb kümmert sich der Compiler darum. Nicht darum ob erst a() dann b() oder andersrum, aber sehr wohl darum, dass c erst nach beiden Aufrufen seinen Wert ändert.
> Ein Funktionsaufruf ist ein solcher "sequence point" und deshalb > kümmert sich der Compiler darum. Wo hatte ich nur meine Gedanken. War wohl schon spät.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.