Forum: Mikrocontroller und Digitale Elektronik AVR: PORTB |= 0b00001111; atomar?


von michael (Gast)


Lesenswert?

Hallo,

ich möchte die unteren 4bit von PORTB als Datenbits für ein lcd (hd44780 
kompatibel) mit 4-bit Ansteuerung verwenden. Die oberen bit von PORTB 
sollen unabhängig davon als IO pins verwendet werden. Auf diese pins 
soll auch aus einer ISR zugegriffen werden.

Daher meine Fragen:
Sind Anweisungen wie PORTB |= 0b00001010; oder PORTB |= 1 << 3; atomar?

Falls nicht:
Können Probleme entstehen, wenn einer dieser Befehle durch eine ISR 
unterbrochen wird, in der ein Befehl wie PORTB |= 1 << 7; ausgeführt 
wird?

Vielen Dank für alle Antworten
Michael

von Andreas F. (aferber)


Lesenswert?

michael schrieb:
> Daher meine Fragen:
> Sind Anweisungen wie PORTB |= 0b00001010; oder PORTB |= 1 << 3; atomar?

Nein.

> Falls nicht:
> Können Probleme entstehen, wenn einer dieser Befehle durch eine ISR
> unterbrochen wird, in der ein Befehl wie PORTB |= 1 << 7; ausgeführt
> wird?

Ja. Der Effekt der ISR auf Pin 7 geht dann je nach genauem Timing 
verloren.

Andreas

von Rolf Magnus (Gast)


Lesenswert?

Andreas Ferber schrieb:
> michael schrieb:
>> Daher meine Fragen:
>> Sind Anweisungen wie PORTB |= 0b00001010; oder PORTB |= 1 << 3; atomar?
>
> Nein.

Naja, das zweite Beispiel schon.

von michael (Gast)


Lesenswert?

Hi,

wäre die einfachste Lösung für das Problem dann, den Befehl PORTB |= 
0b00001010; mit <util/atomic.h> atomar zu machen? Problematisch könnte 
hier sein, dass ich ähnliche Zugriffe auf PORTB oft brauche und zwischen 
den Befehlen häufig delays liegen, um das Timing des lcds einzuhalten.

Gibt es noch eine einfachere Lösung?

Vielen Dank,
Michael

von Rolf Magnus (Gast)


Lesenswert?

michael schrieb:

> Problematisch könnte
> hier sein, dass ich ähnliche Zugriffe auf PORTB oft brauche und zwischen
> den Befehlen häufig delays liegen, um das Timing des lcds einzuhalten.

Du solltest natürlich nur für einen einzelnen Zugriff die Interrupts 
ausschalten und nicht während des Delays. Schreib dir doch einfach eine 
kleine inline-Funktion, die du für den Zugriff auf den LCD-Teil des 
Ports aufrufst, und in der deaktivierst du die Interrupts.

von Andreas F. (aferber)


Lesenswert?

Rolf Magnus schrieb:
>>> Sind Anweisungen wie PORTB |= 0b00001010; oder PORTB |= 1 << 3; atomar?
>> Nein.
> Naja, das zweite Beispiel schon.

Nein. Bloss weil eine andere Konstante verwendet wird macht das das "|=" 
nicht atomar, es bleibt ein read-modify-write.

Andreas

von Rolf Magnus (Gast)


Lesenswert?

Andreas Ferber schrieb:
>>>> Sind Anweisungen wie PORTB |= 0b00001010; oder PORTB |= 1 << 3; atomar?
>>> Nein.
>> Naja, das zweite Beispiel schon.
>
> Nein. Bloss weil eine andere Konstante verwendet wird macht das das "|="
> nicht atomar, es bleibt ein read-modify-write.

... das der Prozessor mit einem einzelnen Assembler-Befehl, und damit 
atomar durchführen kann.

von Drachenbändiger (Gast)


Lesenswert?

Da wird wohl "sbi PORTB, 3" draus, also atomar!

von michael (Gast)


Lesenswert?

Hallo,

werde ich das Problem auch los, indem ich die Zugriffe auf PORTB aus der 
ISR über ein interrupt-Flag in die Hauptschleife verschiebe?

Vielen Dank,
Michael

von Rolf Magnus (Gast)


Lesenswert?

Ja.

von Andreas F. (aferber)


Lesenswert?

Rolf Magnus schrieb:
>> Nein. Bloss weil eine andere Konstante verwendet wird macht das das "|="
>> nicht atomar, es bleibt ein read-modify-write.
> ... das der Prozessor mit einem einzelnen Assembler-Befehl, und damit
> atomar durchführen kann.

In diesem speziellen Fall vielleicht, wenn die Optimierung beim 
Compiler richtig gesetzt ist. Und z.B. beim Zugriff auf PORTF beim 
ATmega128 fliegst du dann plötzlich auf die Schnauze, weil SBI damit 
nicht geht, dein Compiler dir das aber nicht sagt (warum auch, aus 
seiner Sicht ist nachwievor alles in bester Ordnung).

Wenn man sowas verlässlich haben will, dann sollte man es als 
(Inline-)Assembler explizit hinschreiben. Sich darauf zu verlassen, dass 
der Compiler aus bestimmten Konstrukten bestimmten Maschinencode 
generiert, führt über kurz oder lang zu grauen Haaren.

Andreas

von Andreas S. (Firma: Schweigstill IT) (schweigstill) Benutzerseite


Lesenswert?

Rolf Magnus schrieb:
>> Nein. Bloss weil eine andere Konstante verwendet wird macht das das "|="
>> nicht atomar, es bleibt ein read-modify-write.
>
> ... das der Prozessor mit einem einzelnen Assembler-Befehl, und damit
> atomar durchführen kann.

Wie schon an anderer Stelle erwähnt, ist das eine viel zu gefährliche
Annahme.

Die Atomizität des Zugriffs folgt weder aus dem C-Sprachstandard noch
aus der Leistungsbeschreibung des eingesetzten Compilers.

von Walter (Gast)


Lesenswert?

michael schrieb:
> Hallo,
>
> werde ich das Problem auch los, indem ich die Zugriffe auf PORTB aus der
> ISR über ein interrupt-Flag in die Hauptschleife verschiebe?

verstehe ich jetzt nicht ganz,
wenn du den Zugriff in der ISR machst ist es doch egal ob atomar oder 
nicht,
da kann ja kein weiterer IRQ dazwischen kommen

von (prx) A. K. (prx)


Lesenswert?

Auf AVRs neuer als Mega8/Mega32 kann man die Toggle-Funktion von 
Schreibvorgängen nach PINx verwenden, um die unteren 4 Bits auf einen 
bestimmten Wert zu setzen, ohne dabei Änderungen an den oberen 4 Bits 
durch einen Interrupt-Handler in die Quere zu kommen:
   PINA = (PORTA & 0b00001111) ^ (data & 0b00001111);

von (prx) A. K. (prx)


Lesenswert?

PS: Diese Technik ist nur bezüglich der nicht betroffenen Bits atomar. 
Wenn die ISR auch die unteren Bits verändert, dann funktioniert dieses 
Verfahren nicht. Es ist also kein universeller atomarer Ersatz für 
SBI/CBI. Aber es ist unabhängig von der Adressierung des verwendeten 
Ports und faktisch unabhängig von Compiler und Optimierungsstufe.

von Abdul K. (ehydra) Benutzerseite


Lesenswert?

Andreas Ferber schrieb:
> Rolf Magnus schrieb:
>>>> Sind Anweisungen wie PORTB |= 0b00001010; oder PORTB |= 1 << 3; atomar?
>>> Nein.
>> Naja, das zweite Beispiel schon.
>
> Nein. Bloss weil eine andere Konstante verwendet wird macht das das "|="
> nicht atomar, es bleibt ein read-modify-write.
>

Die Sache ist noch viel schwieriger, denn auch die externe Hardware kann 
während dem -modify-, also dann wenn die CPU die Maskierung in der ALU 
berechnet, AUCH den Zustand ändern. Schreibt die CPU dann das Ergebnis 
heraus, sind zwar die neuen Bits richtig, aber die alten werden auch 
geschrieben, obwohl neuere vorliegen!

Es gibt wohl einige wenige Prozessoren, die Bit-Manipulation direkt 
unterstützen. Und hier sieht man, das C niemals für Hardware gedacht 
war.

Der Keil-Compiler unterstützt übrigens native Bits auch in C. Eine 
seltene Ausnahme. Der 8051 kann da übrigens an den Ports auch zur Falle 
werden.

Also unbedingt die Port-Kapitel im betreffenden Mikrocontroller-Manual 
durchackern und alles nietundnagelfest programmieren.

von Andreas F. (aferber)


Lesenswert?

Abdul K. schrieb:
> Die Sache ist noch viel schwieriger, denn auch die externe Hardware kann
> während dem -modify-, also dann wenn die CPU die Maskierung in der ALU
> berechnet, AUCH den Zustand ändern. Schreibt die CPU dann das Ergebnis
> heraus, sind zwar die neuen Bits richtig, aber die alten werden auch
> geschrieben, obwohl neuere vorliegen!

Nein. Das PORTx-Register ändert nur durch die CPU seinen Inhalt. Die 
alternativen Portfunktionen werden zwischen PORTx und dem Pin 
"eingeschleift".

Durch externe Ereignisse (und die genannten alternativen Portfunktionen) 
kann sich nur das PINx-Register ändern, dieses ist aber wiederum nicht 
durch die CPU schreibbar (die Toggle-Funktion bei neueren AVRs schreibt 
nicht wirklich in das PINx-Register).

Erkennen kann man das alles in dem Schaltplan unter "Alternate Port 
Functions" im Datenblatt.

> Es gibt wohl einige wenige Prozessoren, die Bit-Manipulation direkt
> unterstützen. Und hier sieht man, das C niemals für Hardware gedacht
> war.

Wie sollte eine portable Programmiersprache auch auf Spezialitäten einer 
einzelnen Architektur eingehen?

Es ist aber nicht verboten, C entsprechend zu erweitern (siehe z.B. die 
MMX-Intrinsics beim x86-GCC), nur hat für AVR das eben niemand 
konsequent getan, und deshalb muss man mit Unzulänglichkeiten wie der 
Nicht-Atomizität (bzw. der Nicht-Garantie der Atomizität) bei 
Bitoperationen auf PORTx leben.

Programme, die solche Spracherweiterungen benutzen, sind dann natürlich 
nicht mehr portabel, aber das sind Mikrocontroller-Programme im 
allgemeinen sowieso nicht.

> Also unbedingt die Port-Kapitel im betreffenden Mikrocontroller-Manual
> durchackern und alles nietundnagelfest programmieren.

Ack. Die relevanten Kapitel des Manuals sollte man sowieso immer zuerst 
lesen, egal was man macht.

Andreas

von Abdul K. (ehydra) Benutzerseite


Lesenswert?

OK, ich bezog mich nicht auf AVR. Es war sehr allgemein gedacht. Und ich 
habe nur Erfahrung mit anderen Prozessoren, vor allem 8051, PSoC.

Und Sachen wie 8255, PCF8574 usw. gibts auch noch!

von Reinhard Kern (Gast)


Lesenswert?

Abdul K. schrieb:
> OK, ich bezog mich nicht auf AVR. Es war sehr allgemein gedacht. Und ich
> habe nur Erfahrung mit anderen Prozessoren, vor allem 8051, PSoC.
>
> Und Sachen wie 8255, PCF8574 usw. gibts auch noch!

Und bei denen steht in der Dokumentation auch ausdrücklich drin, dass 
ein Befehl Setbit irgendwas eben nicht ein einzelnes Bit setzt ohne 
Nebenwirkungen, sondern in Wirklichkeit ein Read-Modify-Write des ganzen 
Ports auslöst und daher z.B. die Möglichkeit besteht, einen 0-Eingang 
versehentlich "einzufrieren" (weil die 0 wieder zurückgeschrieben wird), 
je nachdem wie der Port im Detail aufgebaut ist.

Atomare Operationen gibt es nur in Assembler bzw. Maschinencode. Selbst 
innerhalb des gleichen Prozessors kann das von Port zu Port verschieden 
sein. Schliesslich SOLL C-Code ja hardware-unabhängig sein.

Dass eine ISR nicht durch eine weitere ISR unterbrochen werden kann, 
sollte man auch nicht so einfach annehmen. Eine ISR kann sich sogar 
selbst unterbrechen, aber dann ist in der Regel was falsch gelaufen.

Gruss Reinhard

von Abdul K. (ehydra) Benutzerseite


Lesenswert?

Ja ja ja Reinhard. Das sollte einfach nur explizit dargestellt werden. 
Ist nämlich ein fieser Fehler und damit schwer zu finden, wenn man das 
nicht in Erwägung zieht!

von Falk B. (falk)


Lesenswert?

Siehe Interrupt, dort steht das ALLES schon seit Ewigkeiten drin. . 
.

von Abdul K. (ehydra) Benutzerseite


Lesenswert?

Falk Brunner schrieb:
> Siehe Interrupt, dort steht das ALLES schon seit Ewigkeiten drin. .
> .


Ach Falk. ALLES das steht nur in der Bibel.
Du hast mehr drauf.

von Falk B. (falk)


Lesenswert?

@  Abdul K. (ehydra) Benutzerseite

>Ach Falk. ALLES das steht nur in der Bibel.
>Du hast mehr drauf.

Als die Bibel . . .?
Lass das mal nicht den großen Chef hören ;-)

MfG
Falk

P S Ich habe, im Gegensatz zu vielen anderen Forumsteilnehmern, keine 
Lust, 1001 mal die gleiche Geschichte zu den gleichen Problemen zu 
erzählen. Deshalb schreibe ich Artikel im Wiki und verweise auf sie. 
Willst du jedes Mal die UART/LCD/FUSE/INTERRUPT/WHATEVER Probleme der 
Leute haarklein neu "lösen"? Ich nicht.

von Abdul K. (ehydra) Benutzerseite


Lesenswert?

Falk, das stimmt.
Es ist nur leider so, daß Menschen zuviel gute Organisation oft 
ablehnen. Hat auch seinen Grund.

Das Schwein das nie einen Artikel verfaßte -
Abdul

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.