Forum: Compiler & IDEs Alternative zu PORTB.0


von Daniel (Gast)


Lesenswert?

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

von Johannes M. (johnny-m)


Lesenswert?

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.

von Daniel (Gast)


Lesenswert?

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);
}

von Matthias (Gast)


Lesenswert?

PINB = (PINB ^ (Variable<<PB2)) & (1<<PB2);

Vielleicht hilf das ja ;-)

von Daniel (Gast)


Lesenswert?

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

von Matthias (Gast)


Lesenswert?

for(int i = 18;i>=0;i--)
{
_delay_us(4);
 PINB = ( PINB ^ (data>>i) ) & ( 0x01 );
}

So sollte das gehen ;-)

von Bastler (Gast)


Lesenswert?

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

von Daniel (Gast)


Lesenswert?

@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

von Daniel (Gast)


Lesenswert?

@Bastler: Das ist das Problem, er weiß es nicht, aber der µC erfährt es 
zur Laufzeit ;)

von Matthias (Gast)


Lesenswert?

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?

von Daniel (Gast)


Lesenswert?

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;
}

von Daniel (Gast)


Lesenswert?

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

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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

von Johannes M. (johnny-m)


Lesenswert?

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.

von Matthias (Gast)


Lesenswert?

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

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von Matthias (Gast)


Lesenswert?

>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 ;-)

von Daniel (Gast)


Lesenswert?

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

von Johannes M. (johnny-m)


Lesenswert?

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)

von Stefan E. (sternst)


Lesenswert?

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?

von Johannes M. (johnny-m)


Lesenswert?

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
Noch kein Account? Hier anmelden.