Hallo, ich betreibe einen ATmega16 mit einem STK500 und verwende AVRStudio 4 und AVRgcc. Ich habe dabei folgendes Problem: Ich möchte genau ein Bit von PORTB editieren. Das Problem dabei ist, ich weiß nicht, ob dieses Bit zu 0 oder zu 1 gesetzt werden soll, da es in einer Variablen steht. Der Weg über PORTB != (<variable> << PB2); funktioniert also leider nicht, da ich if-abfragen gerne vermeiden würde. In früheren avrgcc Versionen gab es hierfür eine Möglichkeit über den Befehl PORTB.2 = <variable>;, der aber leider rausgeflogen ist, weil er wohl nicht c konform war... Gibt es irgend eine Alternative, um das Problem zu lösen? Gruß, Daniel
Daniel wrote: > In früheren avrgcc Versionen gab es hierfür eine Möglichkeit über den > Befehl PORTB.2 = <variable>;, der aber leider rausgeflogen ist, weil er > wohl nicht c konform war... War das tatsächlich mal standardmäßig drin? Sollte mich wundern, oder ist schon lange lange her. Es gibt in C den ^-Operator. Der macht ein (bitweises) Exklusiv-ODER, also genau das, was Du brauchst... Sind aber eigentlich absolute C-Grundlagen (zumindest die Operatoren sollte man eigentlich kennen...) EDIT: Sehe grad, dass Du offensichtlich doch was anderes meinst. Einzelne Bits werden mit & bzw. | (UND mit dem Komplement der Bitmaske zum löschen, ODER mit der Bitmaske zum setzen) verändert. Siehe dazu auch Bitmanipulation. Sehr empfehlenswerter Artikel. AVR-GCC-Tutorial ist auch immer wieder lesenswert, wenn man solche Fragen hat. Das mit dem EXOR ist dann angesagt, wenn man ein Bit oder mehrere davon unabhängig von ihrem ursprünglichen Wert ändern möchte.
Hallo, danke für die Antwort, der XOR Operator ist mir bekannt ;) Leider hiflt er mir aber nicht weiter... Nehmen wir an, im Register PORTB steht 0b0000 0001 und ich möchte an die letzte Stelle eine 0 setzen. Wenn ich jetzt 0000 0001 XOR 0000 0000 berechnen lasse, bleibt es bei 0000 0001. Oder habe ich mich da verkalkuliert? Nochmal genauer: Ich kenne weder den Inhalt von PORTB, noch weiß ich, ob ich eine 0 oder eine 1 eintragen möchte. Das einzige, was ich weiß ist, an welcher Stelle ich den Wert eintragen will. Letztendlich soll das ganze so aussehen, dass ich eine uint8_t variable habe und ich diese Variable seriell über einen Pin ausgebe. Sprich, nach alter Schreibweise so: uint8_t data; //Variable mit beliebigem Inhalt for(int i = 18;i>=0;i--) { _delay_us(4); PORTB.2 = (data>>i)&(0x01); }
>> Sehe grad, dass Du offensichtlich doch was anderes meinst. Einzelne Bits werden mit & bzw. | (UND mit dem Komplement der Bitmaske zum löschen, ODER mit der Bitmaske zum setzen) verändert. Siehe dazu auch Bitmanipulation. Sehr empfehlenswerter Artikel. AVR-GCC-Tutorial ist auch immer wieder lesenswert, wenn man solche Fragen hat. >> Richtig, das würde funktionieren, wenn ich wüsste, ob ich eine 1 oder eine 0 setzen möchte.(Wie du sagtest, entweder |oder &) Aber wie gesagt, ich würde gerne if-Abfragen vermeiden, das das ganze evtl. ein wenig zeitkritisch werden könnte.
for(int i = 18;i>=0;i--) { _delay_us(4); PINB = ( PINB ^ (data>>i) ) & ( 0x01 ); } So sollte das gehen ;-)
>Richtig, das würde funktionieren, wenn ich wüsste, ob ich eine 1 oder >eine 0 setzen möchte. Wenn du es nicht weisst... Wie soll es wohl der Compiler wissen?
@Matthias: ich fürchte leider, das wird auch nicht funktionieren... Das führt leider auf das Problem, was ich oben bereits erwähnt habe, dass die XOR verknüpfung versagt, wenn ich eine 1 in PORTB stehen habe und sie durch eine 0 ersetzen möchte, das XOR lässt die 1 einfach stehen
@Bastler: Das ist das Problem, er weiß es nicht, aber der µC erfährt es zur Laufzeit ;)
Eine schön optimierte Version wäre dann: for(int i = 18;i>=0;i--) { _delay_us(4); PINB = ( PINB ^ data ) & ( 0x01 ); data >>= 1; } Da der Atmega nur eine "Einfach" Schiebeoperation hat und keine n-Shift. Durch das XOR wird ein Unterschied zw. dem Pinzustand und der Variablen festgestellt. Also wenn im untersten Bit eine 1 Steht und am Port im untersten Bit eine 0, dann Kommt im untersten Bit eine 1 raus. Die wird dann durch das & ausgeschnitten (man will ja nur das unterste Bit beeinflussen). Laut Atmel Datenblatt toggelt man einen Pin durch schreiben einer '1' in das PINx Register. Also wird der neue Zustand gesetzt. Oder muss es mit einem Taktzyklus gehen?
Hmm, das mit dem toggeln durch setzen von PIN wusste ich nicht... Welchen Pin würde der von die gepostete Code den setzen? Ich habs grade mal per copy/paste reingesetzt und ausprobiert, aber auf dem Oszi hat sich nichts getan. Hier der ganze Code: #define F_CPU 16000000UL #include <avr/io.h> #include <stdint.h> #include <util/delay.h> int main() { DDRB = 0xff; PORTB =0x00; uint8_t data; data=0xff; for(int i = 8;i>=0;i--) { _delay_us(4); PINB = ( PINB ^ data ) & ( 0x01 ); data >>= 1; } while(1) { } return 0; }
Hab grade mal folgenden Code auf einem Mega16 und einem Mega32 getestet: #define F_CPU 16000000UL #include <avr/io.h> #include <stdint.h> #include <util/delay.h> int main() { DDRB = 0xff; PORTB = 0x00; uint8_t data; data=0xff; while(1) { PINB=0xff; _delay_us(100); } return 0; } Leider hat sich beide Male nichts getan. Kann es sein, dass die beiden Controller das mit dem Toggeln durch Schreiben in PINB nicht können? Im Datenblatt steht bei beiden, dass PINB reine read-Register sind. Gruß, Daniel
Daniel wrote: > Hallo, > > ich betreibe einen ATmega16 mit einem STK500 und verwende AVRStudio 4 > und AVRgcc. > > Ich habe dabei folgendes Problem: Ich möchte genau ein Bit von PORTB > editieren. Das Problem dabei ist, ich weiß nicht, ob dieses Bit zu 0 > oder zu 1 gesetzt werden soll, da es in einer Variablen steht. Der Weg > über PORTB != (<variable> << PB2); funktioniert also leider nicht, da > ich if-abfragen gerne vermeiden würde. Ich würde allerdings eine if-Abfrage vorziehen:
1 | if (var) |
2 | PORTB |= (1 << PB2); |
3 | else
|
4 | PORTB &= ~(1 << PB2); |
Erstens ist das nicht so schlimm wie man zu glauben geneigt ist. Zweitens bleiben die Zugriffe atomar was sie bei
1 | PORTB = (PORTB & ~(1 << PB2)) | (var ? (1 << PB2) : 0); |
nicht mehr sind. Zudem steckt auch bei Version 2 eine Bedingung/Abfrage drinne, und wenn du anfangen musst, Variablen zu schieben, wird's noch ineffizienter (wenn auch evtl. hüppscher im C-Code).
Daniel wrote: > Hmm, das mit dem toggeln durch setzen von PIN wusste ich nicht... > [...] > Hab grade mal folgenden Code auf einem Mega16 und einem Mega32 getestet: Das mit dem Toggeln über das PIN-Register geht nur bei neueren AVRs. ATMega16 und 32 hatten das noch nicht.
>Erstens ist das nicht so schlimm wie man zu glauben geneigt ist. >Zweitens bleiben die Zugriffe atomar was sie bei FALSCH! Der Compiler macht das nur dann atomar, wenn sich der Port im Adressbereich von CBI und SBI (Set/Clear Bit in IO Register) befindet (falls der Compiler das kann)! Andernfalls wird ein Read-Modify-Write generiert. Und das ist sicher nicht atomar!
Matthias wrote: >>Erstens ist das nicht so schlimm wie man zu glauben geneigt ist. >>Zweitens bleiben die Zugriffe atomar was sie bei > FALSCH! Es geht um avr-gcc und PORTB von ATmega16, welcher im I/O-Bereich liegt.
>Das mit dem Toggeln über das PIN-Register geht nur bei neueren AVRs. >ATMega16 und 32 hatten das noch nicht. Hoppla. Hatte ich wohl was falsches im Hinterkopf. Aber wir haben hier auch ziemlich viele AVR Tapen im Einsatz. Da kann man schon mal den Überblick verlieren ;-)
Hi, viele Dank für eure Antworten, Matthias hat mich mit der PINB Idee auf den richtigen Pfad gebracht, ich toggele das Bit nur nicht über PINB sondern über ein simples XOR, sprich PORTB ^= ( PINB ^ data ) & ( 0x01 ); Gruß, Daniel
Daniel wrote:
> PORTB ^= ( PINB ^ data ) & ( 0x01 );
Oh, da wäre ich vorsichtig! Je nachdem, was an dem Port alles dranhängt,
kannste damit auch sehr interessante, jedoch i.d.R. unerwünschte
Nebeneffekte erzielen! Du liest den Zustand der Pins ein, der nur bei
Ausgängen i.d.R. dem Zustand der Treiberstufe entspricht (also dem
Zustand des PORTx-Registers). Wenn an dem Port aber auch Eingänge sind,
dann ist das nicht mehr gegeben! U.U. schaltest Du damit lustig die
Pull-Ups von den Eingängen hin und her.
Du willst doch vermutlich das letzte Bit manipulieren, ohne die anderen
Bits in PORTB zu verändern, oder sehe ich das falsch? Und das letzte Bit
in data ist dasjenige, das an den Port ausgegeben werden soll? Dann
ist es eher
1 | PORTB = (PORTB & 0xFE) & (data & 0x01); |
(Das letzte Bit von PORTB wird durch das letzte Bit von data ersetzt)
Johannes M. wrote: > U.U. schaltest Du damit lustig die > Pull-Ups von den Eingängen hin und her. Hast du das "& ( 0x01 )" übersehen? Oder das "^=" hinter PORTB?
Stefan Ernst wrote:
> Hast du das "& ( 0x01 )" übersehen? Oder das "^=" hinter PORTB?
Hmmm, so wird es wohl gewesen sein...
Wieauchimmer, bei solchen Sachen sollte man trotzdem immer PORTx
einlesen, damit man sich nix Falsches angewöhnt.
Bin allerdings immer noch nicht ganz sicher, dass ich hundertprozentig
verstanden habe, was Daniel wirklich will.
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.