mikrocontroller.net

Forum: Compiler & IDEs Alternative zu PORTB.0


Autor: Daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

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

Bewertung
0 lesenswert
nicht 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.

Autor: Daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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);
}

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
PINB = (PINB ^ (Variable<<PB2)) & (1<<PB2);

Vielleicht hilf das ja ;-)

Autor: Daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Matthias (Gast)
Datum:

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

So sollte das gehen ;-)

Autor: Bastler (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Daniel (Gast)
Datum:

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

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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;
}

Autor: Daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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:
if (var)
   PORTB |= (1 << PB2);
else
   PORTB &= ~(1 << PB2);

Erstens ist das nicht so schlimm wie man zu glauben geneigt ist.
Zweitens bleiben die Zugriffe atomar was sie bei
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).

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

Bewertung
0 lesenswert
nicht 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.

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Matthias (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 ;-)

Autor: Daniel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

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

Bewertung
0 lesenswert
nicht 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
PORTB = (PORTB & 0xFE) & (data & 0x01);
(Das letzte Bit von PORTB wird durch das letzte Bit von data ersetzt)

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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?

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

Bewertung
0 lesenswert
nicht 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.

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.