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.
>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
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.