Forum: Mikrocontroller und Digitale Elektronik PORT-Zustand abfrgen und zurück schreiben


von __Son´s B. (bersison)


Lesenswert?

Hallo.
Ich möchte den Zustand eines Ports in eine Variable speichern und später 
den Port auf den gespeicherten Zustand zurück setzen.

Folgende Idee funktioniert nicht;
main()
...
uint8_t Zustand = (PORTA & (1<<PA4)); // akt.Zustand speichen
...
PORTA & (1<<Zustand); // auf gespeicherten Zustand zurück setzen

von Alex R. (itaxel)


Lesenswert?

Probiers mal so:
1
uint8_t Zustand;
2
Zustand = PORTA;
3
4
...
5
6
PORTA = Zustand;

Gruß Alex

von Ralf G. (ralg)


Lesenswert?

__Son´s B. schrieb:
> Folgende Idee funktioniert nicht;
Nachvollziehbar!
Geh's mal gaaanz langsam im Simulator durch...

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?


von Felix C. (felix_c13)


Lesenswert?

Nicht

PORTA & (1<<Zustand);

sondern

//Bit ausmaskieren
uint8_t temp = PORTA & ~(1<<PA4);

PORTA = Zustand | temp;

von Felix C. (felix_c13)


Lesenswert?

Er meint wohl nur einen Pin, sonst macht das mit dem PA4 keinen Sinn.

von __Son´s B. (bersison)


Lesenswert?

Felix C. schrieb:
> Er meint wohl nur einen Pin, sonst macht das mit dem PA4 keinen Sinn.

Sorry, ja nur ein Pin PA4

von Oliver S. (oliverso)


Lesenswert?

Um welchen Controller geht's denn überhaupt?

Denn im Falle eines AVRs sind an die 100% aller Antworten bisher 
falsch...

Oliver

von __Son´s B. (bersison)


Lesenswert?

Oliver S. schrieb:
> Um welchen Controller geht's denn überhaupt?

ATTiny84

von Jonas B. (jibi)


Lesenswert?

PORTA geht nur für Ausgaben... Das Inputregister findest du aber mal 
selbst raus ;)

GRUß J

von __Son´s B. (bersison)


Lesenswert?

Jonas B. schrieb:
> PORTA geht nur für Ausgaben... Das Inputregister findest du aber mal
> selbst raus ;)

Wie beschrieben geht es um die Zustandsabfrage und dessen 
Widerherstellung des Ausgang PORTA/PA4

von komisch (Gast)


Lesenswert?

__Son´s B. schrieb:
> Widerherstellung
Also Invertierung? ;-) ;-) ;-)

von Felix C. (felix_c13)


Lesenswert?

__Son´s B. schrieb:
> Wie beschrieben geht es um die Zustandsabfrage und dessen
> Widerherstellung des Ausgang PORTA/PA4

Zustand kann alles mögliche eihentlich sein. INPUT/OUPUT, OUTPUT 
HIGH/LOW oder auch INPUT HIGH/LOW

aber ja eigentlich macht ja nur das zweite von allem Sinn, velleicht 
noch das erste. Gibt aber immer Leute die lausig gestellte Fragen 
nutzen, ihren Senfabzugeben und dann trotzdem nichts beitragen...

Oliver S. schrieb:
> Denn im Falle eines AVRs sind an die 100% aller Antworten bisher
> falsch...

von Stefan F. (Gast)


Lesenswert?

1
#define writeBit(port,bit,value) { if ((value)>0) (port) |= (1<<bit); else (port) &= ~(1<<bit); } 
2
#define readBit(port,bit) (((port) >> (bit)) & 1)
3
4
uint8_t backup=readBit(PORTA,4);
5
writeBit(PORTA,4,backup);

von Falk B. (falk)


Lesenswert?

Siehe Bitmanipulation.
1
uint8_t tmp;
2
3
tmp = PORTA;
4
5
..
6
7
PORTA = (PORTA & ~(1<<PA4) ) | (tmp & (1<<PA4));

von __Son´s B. (bersison)


Lesenswert?

Falk B. schrieb:
> tmp = PORTA;

Also des gesamten PORTA auslesen und speichern?

von Felix C. (felix_c13)


Lesenswert?

Lies den Artikel Bitmanipulation

von Oliver S. (oliverso)


Lesenswert?

Falk B. schrieb:
> tmp = PORTA;

Jungs, jetzt wird's langsam peinlich...

Oliver

von __Son´s B. (bersison)


Lesenswert?

Oliver S. schrieb:
> Jungs, jetzt wird's langsam peinlich...

Nicht lästern, sondern Lösung vorschlagen! Wirkt qualifizierter!

von Mein grosses V. (vorbild)


Lesenswert?

Oliver S. schrieb:
> Jungs, jetzt wird's langsam peinlich...

Was ist dein Problem?


1
uint8_t tmp;
2
3
tmp = PORTA;
4
5
..
6
7
PORTA = (PORTA & ~(1<<PA4) ) | (tmp & (1<<PA4));

oder

1
uint8_t tmp;
2
3
tmp = PORTA  & (1<<PA4);
4
5
..
6
7
PORTA = (PORTA & ~(1<<PA4) ) | tmp;

Ist doch Jacke wie Hose.

von __Son´s B. (bersison)


Lesenswert?

DANK, für die einfache Lösung!

von Falk B. (falk)


Lesenswert?

@  __Son´s Bersi__ (bersison)

>Falk B. schrieb:
>> tmp = PORTA;

>Also des gesamten PORTA auslesen und speichern?

Sicher, das ist das einfachste. Du sparst keinen Speicher, wenn du nur 
ein Bit im Byte "nutzt".

von Oliver S. (oliverso)


Lesenswert?

Ich zitiere mal aus dem Tutorial:

"Dabei ist wichtig, zur Abfrage der Eingänge nicht etwa Portregister 
PORTx zu verwenden, sondern Eingangsregister PINx. Ansonsten liest man 
nicht den Zustand der Eingänge, sondern den Status der internen 
Pull-Up-Widerstände. Die Abfrage der Pinzustände über PORTx statt PINx 
ist ein häufiger Fehler beim AVR-"Erstkontakt"."

In diesem Sinne...

Oliver

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Oliver S. schrieb:
> Ich zitiere mal aus dem Tutorial:
>
> "Dabei ist wichtig, zur Abfrage der Eingänge nicht etwa Portregister
> PORTx zu verwenden, sondern Eingangsregister PINx. Ansonsten liest man
> nicht den Zustand der Eingänge, sondern den Status der internen
> Pull-Up-Widerstände. Die Abfrage der Pinzustände über PORTx statt PINx
> ist ein häufiger Fehler beim AVR-"Erstkontakt"."

Das ist schon korrekt, aber wie kann PA4 einmal Eingang und einmal 
Ausgang sein?

Von daher ist die Aufgabenstellung des TOs nicht ausreichend umrissen.

Was will er zwischenspeichern? Den Zustand eines Ausgangs oder den 
Zustand eines Eingangs?

Da ich nicht annehme, dass er den Pin mittendrin von der Richtung her 
umprogrammieren will, sondern sich den ehemaligen Zustand des Ausgangs 
PA4 merken will, ist die Verwendung von PORTx durchaus richtig. Ist das 
Ding nämlich auf Ausgang geschaltet, liest man nicht den Zustand der 
Pullups, sondern tatsächlich den aktuellen Wert des Ausgangs.

Von daher ist Dein Zitat aus dem Tutorial hier nicht passend. Das 
bezieht sich nämlich auf Eingänge.

: Bearbeitet durch Moderator
von Mein grosses V. (vorbild)


Lesenswert?

Oliver S. schrieb:
> In diesem Sinne...

... bist du der einzige, der das Problem nicht verstanden hat.

von Leo C. (rapid)


Lesenswert?

1
uint8_t tmp = PORTA & (1<<PA4);
2
3
...
4
5
PINA = (PORTA & (1<<PA4)) ^ tmp;

von Falk B. (falk)


Lesenswert?

@  Leo C. (rapid)
1
uint8_t tmp = PORTA & (1<<PA4);
2
3
...
4
5
PINA = (PORTA & (1<<PA4)) ^ tmp;

Naja, schöner Trick, aber das ist weder notwendig noch anfängertauglich. 
Das geht schon STARK in Richtung Obfuscation! Denn dazu muss man wissen, 
dass bein AVR ein Schreibzugriff auf PINx die Ausgangsbits toggelt . . .

von Stefan F. (Gast)


Lesenswert?

> Das geht schon STARK in Richtung Obfuscation

Hammer, wie kommt man aus solche Ideen? (Den C Code meine ich)

von Leo C. (rapid)


Lesenswert?

Falk B. schrieb:
> Naja, schöner Trick, aber das ist weder notwendig noch anfängertauglich.

Es ist kein Trick.

> Das geht schon STARK in Richtung Obfuscation!

Das ist Quatsch.

> Denn dazu muss man wissen,
> dass bein AVR ein Schreibzugriff auf PINx die Ausgangsbits toggelt . . .

Falls er es vorher noch nicht gewußt hat weiß er es ja jetzt.
Die Variante ist dann von Vorteil, wenn andere Bits auf dem gleichen 
Port durch Interrupts beeinflußt werden. Sie kommt ohne Interrupt-Sperre 
aus. Und weil gerade Anfänger sowas leicht übersehen, ist gerade diese 
Lösung anfängertauglich. ;-)

Stefan U. schrieb:
> Hammer, wie kommt man aus solche Ideen? (Den C Code meine ich)

Indem man ins Datenblatt schaut, und sich fragt, was der Hersteller sich 
wohl bei sowas gedacht hat.

von Mein grosses V. (vorbild)


Lesenswert?

Leo C. schrieb:
> Indem man ins Datenblatt schaut, und sich fragt, was der Hersteller sich
> wohl bei sowas gedacht hat.

Nur leider nicht das, was du dir dabei denkst.

1
   PINC = (PORTC & (1<<4)) ^ tmp;
2
 78c:  88 b1         in  r24, 0x08  ; 8
3
 78e:  80 71         andi  r24, 0x10  ; 16
4
 790:  8d 25         eor  r24, r13
5
 792:  86 b9         out  0x06, r24  ; 6
6
7
8
   PORTC = (PORTC & ~(1<<4) ) | tmp;
9
 794:  88 b1         in  r24, 0x08  ; 8
10
 796:  8f 7e         andi  r24, 0xEF  ; 239
11
 798:  8d 29         or  r24, r13
12
 79a:  88 b9         out  0x08, r24  ; 8

Ist wohl doch nicht so cool.

von Leo C. (rapid)


Lesenswert?

Ja, das war Mist. Dann halt so:
1
PINA = (tmp ^ PORTA) & (1<<PA4);

von Falk B. (falk)


Lesenswert?

@  Mein grosses Vorbild (vorbild)

>> Indem man ins Datenblatt schaut, und sich fragt, was der Hersteller sich
>> wohl bei sowas gedacht hat.

>Nur leider nicht das, was du dir dabei denkst.

Doch, das passt schon.

>   PINC = (PORTC & (1<<4)) ^ tmp;
> 78c:  88 b1         in  r24, 0x08  ; 8
> 78e:  80 71         andi  r24, 0x10  ; 16
> 790:  8d 25         eor  r24, r13
> 792:  86 b9         out  0x06, r24  ; 6

Ist korrekt übersetzt.

>   PORTC = (PORTC & ~(1<<4) ) | tmp;
> 794:  88 b1         in  r24, 0x08  ; 8
> 796:  8f 7e         andi  r24, 0xEF  ; 239
> 798:  8d 29         or  r24, r13
> 79a:  88 b9         out  0x08, r24  ; 8

Ist auch korrekt übersetzt.

Der Trick von Leo ist der. Er will das Pin in einer atomaren Operation 
auf den richtigen Zustand setzen. Das schafft er mit einem 
Schreibzugriff auf das Bit 4 im Register PINA. Wird vorher von PORTA 
eine 0 gelesen, das gespeicherte Bit in tmp aber auf 1 steht, dann 
ergibt das XOR eine 1. Die wird in Bit4 von PINA geschrieben und damit 
das Ausgangspin/bit in PORTA in den richtigen Zustand umgeschaltet. Das 
gleiche bei 1/0. Bei 0/0 bzw. 1/1 steht das Bit schon richtig und die 
XOR Operation ergibt 0, wodurch das Ausgangsbit auch nicht umgeschaltet 
wird.

Modernere CPUs ala Xmega und die allermeisten ARMs haben dafür explizite 
Register, womit man einzelne oder mehrere IOs atomar setzen, löschen 
oder umschalten kann, die heißen dann meist irgendwie _SET, _Clear, 
_Toggle. Dort würde man es genau so machen.

>Ist wohl doch nicht so cool.

Doch, aber den Trick muss man erst einmal durchschauen.

von Falk B. (falk)


Lesenswert?

@ Leo C. (rapid)


>Ja, das war Mist. Dann halt so:

>PINA = (tmp ^ PORTA) & (1<<PA4);

Ohh, der Meister solpert? Nein, das war schon korrekt.

1
uint8_t tmp = PORTA & (1<<PA4);
2
3
...
4
5
PINA = (PORTA & (1<<PA4)) ^ tmp;

Das ist vollkommen OK. Erst Bit4 allein maskieren, dann XOR-verknüpfen. 
Das ist vom Ergebnis isdentisch zu dem oben, WENN vorher in TMP alle 
anderen Bits maskiert wurden, was in deinem Beispiel der Fall ist.

Allerdings kann man mit der letzten Version eine Verknüpfung einsparen.

1
uint8_t tmp = PORTA;
2
3
...
4
5
PINA = (PORTA  ^ tmp) & (1<<PA4);

von Felix C. (felix_c13)


Lesenswert?

So oder so, sehr schön gecodet.

Falk B. schrieb:
> Modernere CPUs ala Xmega und die allermeisten ARMs haben dafür explizite
> Register, womit man einzelne oder mehrere IOs atomar setzen, löschen
> oder umschalten kann,

Nicht nur das, auch gibt auch noch für jedes Bit n eigenes Register --> 
Bit-Banding

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@  Felix C. (felix_c13)

>So oder so, sehr schön gecodet.

AHHH! Dafür ums schlechter gedenglischt!!!!

>> Modernere CPUs ala Xmega und die allermeisten ARMs haben dafür explizite
>> Register, womit man einzelne oder mehrere IOs atomar setzen, löschen
>> oder umschalten kann,

>Nicht nur das, auch gibt auch noch für jedes Bit n eigenes Register -->
>Bit-Banding

Die 32 Bit Dekadenz ;-)

Naja, mit einzelnen Set/Clear/Toggle Register braucht man das eigentlich 
nicht mehr, denn man spart dabei kaum Befehle (jaja, Konstante laden 
entfällt)

von Felix C. (felix_c13)


Lesenswert?

Falk B. schrieb:
> AHHH! Dafür ums schlechter gedenglischt!!!!

Falk B. schrieb:
> Die 32 Bit Dekadenz ;-)

Hahah, in Bestlaune so kurz vor Ostern? :D

Nene aber so war das nicht gemeint. AVR wird einem immer im Herzen 
bleiben wenn man damit mal angefangen hat, aber der Trend geht halt in 
Richtung Cortex-M.

von Mein grosses V. (vorbild)


Lesenswert?

1
   PORTC = (PORTC & ~(1<<4) ) | tmp;
2
 794:  88 b1         in  r24, 0x08  ; 8
3
 796:  8f 7e         andi  r24, 0xEF  ; 239
4
 798:  8d 29         or  r24, r13
5
 79a:  88 b9         out  0x08, r24  ; 8

Wenn zwischen in und out ein IRQ erfolgt und irgendein Bit im Port 
verändert wird, wird dieses beim out wieder zurück gesetzt. Also, nicht 
atomar.


1
   PINC = (PORTC & (1<<4)) ^ tmp;
2
 78c:  88 b1         in  r24, 0x08  ; 8
3
 78e:  80 71         andi  r24, 0x10  ; 16
4
 790:  8d 25         eor  r24, r13
5
 792:  86 b9         out  0x06, r24  ; 6
6
7
  PINC = (tmp ^ PORTC) & (1<<4);
8
 448:  88 b1         in  r24, 0x08  ; 8
9
 44a:  89 27         eor  r24, r25
10
 44c:  80 71         andi  r24, 0x10  ; 16
11
 44e:  86 b9         out  0x06, r24  ; 6

Bit 4 wird zwischen in und out nach einem IRQ geändert. War es zuvor 1 
und soll gemäß tmp auf 0 gesetzt werden, muß es getoggelt werden. Wird 
es aber durch den dazwischen funkenden Interrupt auf 0 gesetzt, dürfte 
es jetzt nicht mehr getoggelt werden. Zur Entscheidung wird aber immer 
noch der ursprüngliche Wert(1) verwendet. Also, auch nicht atomar. 
Vielleicht ein bißchen atomarer als oben, aber ...

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@Mein grosses Vorbild (vorbild)


>ursprüngliche Wert verwendet. Also, auch nicht atomar. Vielleicht ein
>bißchen atomarer als oben, aber ...

Das ist ja ganz schöne Atom- ähhh, Haarspalterei!

Nein, das atomar bezieht sich auf alle anderen Bits ausser Bit4! Und 
diesbezüglich ist der Zugriff echt atomar! Wenn der Interrupt in der 
Zwischenzeit Bit4 verändert ist das Gesamtkonzept fragwürdig.

: Bearbeitet durch User
von Mein grosses V. (vorbild)


Lesenswert?

Falk B. schrieb:
> Nein, das atomar bezieht sich auf alle anderen Bits ausser Bit4!

Nun komm, sind wir hier im Häkelbüdelclub?

Entweder es ist atomar oder es ist nicht atomar. Dazwischen ist Raum für 
gar nichts.

Falk B. schrieb:
> Wenn der Interrupt in der
> Zwischenzeit Bit4 verändert ist das Gesamtkonzept fragwürdig.

Passiert bei asynchronen Signalen. Sperrt man den Interrupt, setzt sich 
der Interrupt durch. Lässt man ihn zwischendurch reinfunken, erhält man 
zu 50% einen Wert, den keiner will.

Zum Gesamtkonzept kann ich nichts sagen. Ist wahrscheinlich auch nur 
akademisch.

von Falk B. (falk)


Lesenswert?

@ Mein grosses Vorbild (vorbild)

>> Nein, das atomar bezieht sich auf alle anderen Bits ausser Bit4!

>Nun komm, sind wir hier im Häkelbüdelclub?

Ich zumindest nicht. ;-)

>Entweder es ist atomar oder es ist nicht atomar. Dazwischen ist Raum für
>gar nichts.

Nö.

Beispiel 1.

Bit4 wird von Funktionen in der Hauptschleife verwendet, andere Bits am 
Port von Interrupts. Damit das reibungsfrei klappt, müssen die Zugriffe 
atomar sein. Entweder mit Interruptsperre oder den genannten 
_SET/_CLEAR/_TOGGLE Registern, je nach Verfügbarkeit.

Beispiel 2.

Man will/muss mehrere Flags in ein Byte packen, warum auch immer. Um 
diese einzeln und atomar setzen zu können, kann man beim AVR bzw. 
ATXmega Register im bitadressierbaren Bereich nutzen (Beim ATXmega sind 
sie sogar exclusiv dafür gedacht!) und mittels sbi/cbi sbis/sbic 
einzelne Bits in Registern atomar lesen und schreiben, ohne 
Interuptsperre.

Das Thema atomar bezieht sich nicht ausschließlich auf Int/Word/Long 
whatever Variablen, die als Ganzes benutzt werden.

von Mein grosses V. (vorbild)


Lesenswert?

Falk B. schrieb:
> Nö.

Doch.

Falk B. schrieb:
> Entweder mit Interruptsperre oder den genannten
> _SET/_CLEAR/_TOGGLE Registern, je nach Verfügbarkeit.

Es steht aber nur ein TOGGLE zur Verfügung. Diese Operation ist relativ. 
Nämlich abhängig vom momentanen Zustand. Wenn das Bit eingelesen wurde, 
wird eine Entscheidung getroffen, ob das Bit getoggelt werden muß oder 
nicht. Ändert sich dieses Bit währenddessen, ist diese getroffene 
Entscheidung falsch. Damit ist diese Operation nicht per se atomar.

Falk B. schrieb:
> Das Thema atomar bezieht sich nicht ausschließlich auf Int/Word/Long
> whatever Variablen, die als Ganzes benutzt werden.

Was willst du mir damit sagen? Geht es darum in diesem Thread? Ich denke 
nicht.

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.