Forum: Mikrocontroller und Digitale Elektronik zwei Bits beeinflussen - ich steh geradeauf dem Schlauch


von Mike (Gast)


Lesenswert?

Ich möchte mit meinem Atmel zwei PORT-Bits gleichzeitig beeinflussen (1 
Bit setzen und 1 Bit zurücksetzen) ohne die anderen sechs Bits zu 
verändern.

Ich weiß, dass ich mit &=~ ein Bit rücksetzen kann und mit |= eins 
setzen kann, aber wie kann ich das Gleichzeitig?
Ich brauche zwei Zustände: xxxx xx01 und xxxx xx10

Kann ich das in einem Befehl machen? Toggeln geht nicht, weil ich den 
Ausgangszustandnicht kenne und vorher sonst erst auslesen müsste.

Gesucht ist sowas wie <PortB 0x03> (hintere beiden ausgänge) = <0x01> 
bzw. <0x02> setzen.

Wisst ihr was ich meine?

von (prx) A. K. (prx)


Lesenswert?

Geht mit AVRs nicht, nicht ohne vorher irgendwas auszulesen.

von Peter II (Gast)


Lesenswert?

Mike schrieb:
> aber wie kann ich das Gleichzeitig?

ohne den port einzulesen, manipuliere und ausgeben geht es nicht.

Ich würde es nacheinander machen. Das ist die schnellste lösung, aber 
halt nicht gleichzeitig.

von spess53 (Gast)


Lesenswert?

Hi


>Kann ich das in einem Befehl machen? Toggeln geht nicht, weil ich den
>Ausgangszustandnicht kenne und vorher sonst erst auslesen müsste.

Kommt auf deinen AVR an. Neuere AVRs können Pins direkt durch Schreiben 
in das Pin-Register togglen.

MfG Spess

von (prx) A. K. (prx)


Lesenswert?

Aber auch dann muss er den vorherigen Zustand dieser beiden Pins kennen. 
Will er aber nicht.

von Mike (Gast)


Lesenswert?

OK, schon mal danke. Dann stehe ich ja doch nicht so auf dem Schlauch 
wie vermutet.

Wenn ich das jetzt nacheinander machen möchte (1 Bit setzen, 1 Bit 
zurücksetzen) kann ich das über ein #define hinbekommen?

so nach dem Motto:
1
#define Zustand1 (PORTC |= 0x01);PORTC &= ~0x02)
2
#define Zustand2 (PORTC |= 0x02);PORTC &= ~0x01)

von Matthias L. (Gast)


Lesenswert?

1
uint8_t u8Tmp;
2
3
...
4
u8Tmp =  PORTB;
5
u8Tmp &= ~(1<<PBx);
6
u8Tmp ¦=  (1<<PBy);
7
PORTB =  u8Tmp;

Hier wird zwar die Manipulation hintereinander gemacht, aber die 
Ausgänge schalten zeitgleich um.

von Mike (Gast)


Lesenswert?

zur Info:
die Ausgänge an sich brauchen nicht Zeitgleich Ihren Zustand ändern. Was 
ich meinte, ist: ich möchte in meinem Programmablauf die Ausgänge mit 
einem "Befehl" manipulieren und nicht immer zwei,drei Zeilen schreiben 
um Fehler zu verhindern.

von spess53 (Gast)


Lesenswert?

Hi

>Aber auch dann muss er den vorherigen Zustand dieser beiden Pins kennen.
>Will er aber nicht.

Wenn er immer beide gleichzeitig togglen will, braucht er nur einmal 
einen definierten Anfangszustand.

MfG Spess

von Peter II (Gast)


Lesenswert?

Matthias Lipinsky schrieb:
> Hier wird zwar die Manipulation hintereinander gemacht, aber die
> Ausgänge schalten zeitgleich um.

in der hoffnung das nicht eine ISR in der Zeit die ausgänge geändert 
hat, das ganze ist nicht meht atomar.


Man könnte aber den Port einlesen und sich danach eine Toggle-Maske 
bauen. Dann den Port Togglen. Das hätte den Vorteil das die anderen Pins 
nicht angefasst werden.

von (prx) A. K. (prx)


Lesenswert?

spess53 schrieb:
> Wenn er immer beide gleichzeitig togglen will, braucht er nur einmal
> einen definierten Anfangszustand.

Klar, aber er hatte ja ausdrücklich geschrieben, dass er aufgrund 
Unkenntnis des Zustands nicht toggeln will.

von Philipp F. (philipp5054)


Lesenswert?

Mike schrieb:
> zur Info:
>
> die Ausgänge an sich brauchen nicht Zeitgleich Ihren Zustand ändern. Was
>
> ich meinte, ist: ich möchte in meinem Programmablauf die Ausgänge mit
>
> einem "Befehl" manipulieren und nicht immer zwei,drei Zeilen schreiben
>
> um Fehler zu verhindern.

Warum machst du für die 3 Zeilen dann nicht einfach ein Macro?

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Hallo,

vielleicht habe ich das Problem auch falsch verstanden, aber es reichen 
doch zwei Befehle. Zum Beispiel:
1
sbi PORTB,0
2
cbi PORTB,1
Dadurch werden im Register PORTB das Bit 0 gesetzt und das Bit 1 
gelöscht. Die übrigen Bits bleiben unberührt.

von Falk B. (falk)


Lesenswert?

@  Markus W. (m-w)

>sbi PORTB,0
>cbi PORTB,1

>Dadurch werden im Register PORTB das Bit 0 gesetzt und das Bit 1
>gelöscht. Die übrigen Bits bleiben unberührt.

Aber nicht zeitgleich.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Falk Brunner schrieb:
> Aber nicht zeitgleich.

Korrekt. Allerdings nur ein oder zwei Takte entfernt.

von Falk B. (falk)


Lesenswert?

@  Markus W. (m-w)

>Korrekt. Allerdings nur ein oder zwei Takte entfernt.

Ob das wichtig ist, weiß nur der OP.

von (prx) A. K. (prx)


Lesenswert?


von Matthias L. (Gast)


Lesenswert?

>ich meinte, ist: ich möchte in meinem Programmablauf die Ausgänge mit
>einem "Befehl" manipulieren und nicht immer zwei,drei Zeilen schreiben
>um Fehler zu verhindern.


Ich weiss ja nicht, wie oft du in deinem Programm diese Port-änderungen 
durchführen musst, aber wenn es weniger als 42mal sind, hast du jetzt 
eben im Forum mehr blabla geschrieben, als diese ".. zwei, drei Zeilen 
.." an Tipparbeit sind...

von Mike (Gast)


Lesenswert?

Matthias Lipinsky schrieb:
> Ich weiss ja nicht, wie oft du in deinem Programm diese Port-änderungen
> durchführen musst, aber wenn es weniger als 42mal sind, hast du jetzt
> eben im Forum mehr blabla geschrieben, als diese ".. zwei, drei Zeilen
> .." an Tipparbeit sind...

1.: Dies diente lediglich der allgemeinen Information
2.: Das Wissen wird garantiert auch in Zukunft benötigt
3.: Flüchtigkeitsfehler, C&P-Fehler kennt wohl jeder
4.: Evtl. hat es auch einige Mitleser interessiert

Trotzdem bedanke ich mich bei allen Beteiligten für die Informationen 
und den Input, der zusätzlich gegeben wurde.

Vielen Dank

von Karl H. (kbuchegg)


Lesenswert?

Mike schrieb:

> 2.: Das Wissen wird garantiert auch in Zukunft benötigt

Fair enough.

Dann lass dir gesagt sein, dass du im Hinblick auf ...

> ich möchte in meinem Programmablauf die Ausgänge mit
> einem "Befehl" manipulieren und nicht immer zwei,drei Zeilen
> schreiben um Fehler zu verhindern.

... um derartige Makros einen Bogen machen solltest und das Instrument 
einer Funktion in Anspruch nehmen solltest.

Das ist einfach, banal und vermeidet Fehler ganz wunderbar.
1
void SignalToRed()
2
{
3
  LED_PORT ~= ( 1 << GREEN_LED );
4
  LED_PORT |= ( 1 << RED_LED );
5
}
6
7
void SignalToGreen()
8
{
9
  LED_PORT |= ( 1 << GREEN_LED );
10
  LED_PORT ~= ( 1 << RED_LED );
11
}


Dann hast du an der aufrufenden Stelle ebenfalls den Komfort eines 
'Befehls', den der Compiler (wenn es sich lohnt) inline expandieren kann 
(das kostet dir also keine Mehr-Laufzeit) und bist auf der sicheren 
Seite was Nebeneffekte angeht (was du bei Makros nicht unbedingt bist. 
Zumindest dann nicht, wenn man das Makro so schreibt wie du das getan 
hast).

Generell:
Benutze Makros um Konstanten zu ersetzen
Ab und an kann man auch mal einen einzigen Befehl in einem Makro 
verstecken. Sobald es aber etwas komplexer wird (und ja: 2 Befehle 
können schon komplex sein), bevorzuge lieber Funktionen.

von Matthias L. (Gast)


Lesenswert?

>das Instrument einer Funktion

Sehr gut formuliert ;-)


EDIT:

Das ist wohl ein Schnelligkeitsfehler:
1
 LED_PORT ~= ( 1 << GREEN_LED );

soll bestimmt so heissen:
1
 LED_PORT &= ~( 1 << GREEN_LED );

von Karl H. (kbuchegg)


Lesenswert?

Matthias Lipinsky schrieb:

> Das ist wohl ein Schnelligkeitsfehler:
>
1
>  LED_PORT ~= ( 1 << GREEN_LED );
2
>
>
> soll bestimmt so heissen:
>
1
>  LED_PORT &= ~( 1 << GREEN_LED );
2
>

Danke für die Korrektur. Ja, klar. War ein Schnelligkeitsfehler.

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Karl Heinz Buchegger schrieb:
1
>  LED_PORT |= ( 1 << RED_LED );

Ist eigentlich sichergestellt, dass diese Zeile in einen atomaren Befehl 
übersetzt wird? Wenn ich Pech habe, macht der Compiler sowas draus:

1
in r16,LED_PORT
2
ori r16,( 1 << RED_LED )
3
out LED_PORT,r16

Und das ist funktional doch etwas anderes als

1
sbi LED_PORT,RED_LED

Jedenfalls, wenn am gleichen Port auch Eingänge verarbeitet werden und 
das Programm Interrupts nutzt...

von (prx) A. K. (prx)


Lesenswert?

Markus W. schrieb:
> Ist eigentlich sichergestellt, dass diese Zeile in einen atomaren Befehl
> übersetzt wird?

Nein. Manche AVRs besitzen Ports ausserhalb des bitadressierbaren 
Bereichs.

von Christian K. (Firma: Atelier Klippel) (mamalala)


Lesenswert?

port = port ^ 0b00000011

Wenn port = 0b00000001 war, so wird es dann zu 0b00000010 und umgekehrt. 
nennt sich XOR.

Grüße,

Chris

von (prx) A. K. (prx)


Lesenswert?

Ist in dieser Form nicht atomar (sehr wohl aber bei "PIND = 3;") und 
benötigt Kenntnis des vorherigen Zustands - dies aber hatte er in seiner 
ursprünglichen Frage ausgeschlossen.

von spess53 (Gast)


Lesenswert?

Hi

>dies aber hatte er in seiner
>ursprünglichen Frage ausgeschlossen.

Mike schrieb:

>Ich möchte mit meinem Atmel zwei PORT-Bits gleichzeitig beeinflussen (1
>Bit setzen und 1 Bit zurücksetzen) ohne die anderen sechs Bits zu
>verändern.

Das ist bei mir togglen.

>Kann ich das in einem Befehl machen? Toggeln geht nicht, weil ich den
>Ausgangszustandnicht kenne und vorher sonst erst auslesen müsste.

Frage mich warum.

MfG Spess

von Sven P. (Gast)


Lesenswert?

spess53 schrieb:
>>Ich möchte mit meinem Atmel zwei PORT-Bits gleichzeitig beeinflussen (1
>>Bit setzen und 1 Bit zurücksetzen) ohne die anderen sechs Bits zu
>>verändern.
>
> Das ist bei mir togglen.
Ne, es ist nur dann togglen, wenn er weiß, dass das zu löschende Bit 
tatsächlich gesetzt ist und umgekehrt.

Die Instrktionen sbi/cbi arbeiten übrigens auch nicht ganz atomar. Die 
machen auch 'nur' Read-Modify-Write.

von Christian K. (Firma: Atelier Klippel) (mamalala)


Lesenswert?

Naja, ist halt immer die Frage wie atomar es nötig ist. Einzelne Bits 
nacheinander setzen/löschen sind ja ebenfalls mehrere Instruktionen. 
ursprünglich hiess es ja:

"Ich brauche zwei Zustände: xxxx xx01 und xxxx xx10"

Wenn man also einmalig bei Programmstart auf xxxx xx01 setzt, und 
anschliessend mit xxxx xx11 ein XOR macht, toggeln halt beide Bits, und 
das halt zur gleichen Zeit. Ich kenne mich mit AVR Assembler nicht so 
aus, aber bei einem PIC wäre das z.B.:

MOVLW 0x03
XORWF LATC, F, ACCESS

Also einmal die Maske in das Arbeitsregister laden, und dann mit einem 
einzigen Befehl den Ausgangsport verändern. Wäre also atomar was den 
reinen Port angeht. Die AVR werden doch sicherlich etwas vergleichbares 
haben?

Grüße,

Chris

von Markus W. (Firma: guloshop.de) (m-w)


Lesenswert?

Sven P. schrieb:
> Die Instrktionen sbi/cbi arbeiten übrigens auch nicht ganz atomar. Die
> machen auch 'nur' Read-Modify-Write.

Bezogen auf das ganze Byte? Das wär nicht gut... Woher hast du diese 
Information?

von Sven P. (Gast)


Lesenswert?

Christian Klippel schrieb:
> Die AVR werden doch sicherlich etwas vergleichbares
> haben?
Jein. Wie schon geschrieben: Bei den neueren wirkt ein Schreibzugriff 
aufs PIN-Register wie ein Umschießen im PORT-Register.

Ansonsten ist AVR halt RISC und Harvard. Da geht das Umschießen 
tatsächlich auch händisch nur als Read-Modify-Write. Setzen und Löschen 
dagegen geht (meistens) mit den cbi/sbi-Instruktionen. Umschießen aber 
eben nicht... mit o.g. Ausnahme.

von Sven P. (Gast)


Lesenswert?

Markus W. schrieb:
> Sven P. schrieb:
>> Die Instrktionen sbi/cbi arbeiten übrigens auch nicht ganz atomar. Die
>> machen auch 'nur' Read-Modify-Write.
>
> Bezogen auf das ganze Byte? Das wär nicht gut... Woher hast du diese
> Information?

Aus den Datenblättern. Das ist im Übrigen wohl der Grund, weshalb man 
Interruptflaggen durch Beschreiben mit einer '1' löscht. Andernfalls 
(lesen, ver-und-en, schreiben, händisch als auch mit cbi/sbi) könnte man 
versehentlich eine Flagge plattmachen, die zwischenzeitlich von der 
Hardware gesetzt wurde.

von spess53 (Gast)


Lesenswert?

Hi

>Ne, es ist nur dann togglen, wenn er weiß, dass das zu löschende Bit
>tatsächlich gesetzt ist und umgekehrt.

Bei einem XOR oder, wie vorgeschlagen, Togglen über PIN-Register muss 
man es nicht wissen.

MfG Spess

von Sven P. (Gast)


Lesenswert?

spess53 schrieb:
> Hi
>
>>Ne, es ist nur dann togglen, wenn er weiß, dass das zu löschende Bit
>>tatsächlich gesetzt ist und umgekehrt.
>
> Bei einem XOR oder, wie vorgeschlagen, Togglen über PIN-Register muss
> man es nicht wissen.
Er hat doch beschrieben, dass er die Zustände '...01' und '...10' 
braucht, aber nicht weiß, welche Bits aktuell gesetzt sind (nicht: 
welcher Zustand gerade vorherrscht). Daraus schließe ich, dass er auch 
von '...00' und '...11' in einen der beiden Zustände kommen will. Und 
das funktioniert mit Umschießen eben gerade nicht mehr.

von (prx) A. K. (prx)


Lesenswert?

spess53 schrieb:

> Bei einem XOR oder, wie vorgeschlagen, Togglen über PIN-Register muss
> man es nicht wissen.

Na dann verrate uns mal, wie du in einer solchen Operation Bit 0 auf 0 
und Bit 1 auf 1 setzt, wenn du den bisherigen Zustand dieser beiden Bits 
nicht kennst und ihn nicht auslesen darfst.

von spess53 (Gast)


Lesenswert?

Hi

>Er hat doch beschrieben, dass er die Zustände '...01' und '...10'
>braucht, aber nicht weiß, welche Bits aktuell gesetzt sind (nicht:
>welcher Zustand gerade vorherrscht). Daraus schließe ich, dass er auch
>von '...00' und '...11' in einen der beiden Zustände kommen will. Und
>das funktioniert mit Umschießen eben gerade nicht mehr.

Vielleicht sollte Mike das mal klären. Denn ich glaube das ist der 
Knackpunkt. Persönlich kann ich das nämlich nicht so herauslesen.

MfG Spess

von (prx) A. K. (prx)


Lesenswert?

Sven P. schrieb:
> von '...00' und '...11' in einen der beiden Zustände kommen will.

Unnötig. Es reicht, wenn er nicht weiss, ob es aktuell 01 oder 10 ist, 
er aber hinterher 01 haben will. Das geht nicht in einer Operation, ohne 
vorher auszulesen. Mit auslesen geht es aber (echt) atomar bezogen auf 
die übrigen Bits mit
  PINx = (PORTx & 0b11) ^ 0b01;
wenn der AVR diese PINx Toggle-Operation versteht. Da er aber mit zwei 
Bit-Operationen leben kann ist auch das unnötig komplex. Vorteil dieser 
Variante ist freilich, dass der neue Wert nicht konstant sein muss.

von (prx) A. K. (prx)


Lesenswert?

PS: Universell ausgedrückt wäre das also:
1
#define CAT(a,b) a##b
2
#define AssignPins(port,mask,value) CAT(PIN,port) = (CAT(PORT,port) & (mask)) ^ (value)
3
4
AssignPins(D,0b11,val);
Resultat:
1
PIND = (PORTD & (0b11)) ^ (val);

Oder für Freunde abartiger Syntax:
1
#define CAT(a,b) a##b
2
#define AssignPins(port,mask) CAT(PIN,port) = (CAT(PORT,port) & (mask)) ^
3
4
AssignPins(D,0b11) val;

von Mike (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Dann hast du an der aufrufenden Stelle ebenfalls den Komfort eines
> 'Befehls', den der Compiler (wenn es sich lohnt) inline expandieren kann
> (das kostet dir also keine Mehr-Laufzeit) und bist auf der sicheren
> Seite was Nebeneffekte angeht (was du bei Makros nicht unbedingt bist.
> Zumindest dann nicht, wenn man das Makro so schreibt wie du das getan
> hast).
>
> Generell:
> Benutze Makros um Konstanten zu ersetzen
> Ab und an kann man auch mal einen einzigen Befehl in einem Makro
> verstecken. Sobald es aber etwas komplexer wird (und ja: 2 Befehle
> können schon komplex sein), bevorzuge lieber Funktionen.

Vielen Dank für diese zusammenfassende Ausführung.

spess53 schrieb:
>>Er hat doch beschrieben, dass er die Zustände '...01' und '...10'
>>braucht, aber nicht weiß, welche Bits aktuell gesetzt sind (nicht:
>>welcher Zustand gerade vorherrscht). Daraus schließe ich, dass er auch
>>von '...00' und '...11' in einen der beiden Zustände kommen will. Und
>>das funktioniert mit Umschießen eben gerade nicht mehr.
>
> Vielleicht sollte Mike das mal klären.

Ja, es ist richtig.
Ich stoße aktiv die Zustände xx01 und xx10 an. Generell kann es aber 
vorkommen, dass der Ausgangszustand auch schon mal xx00 ist. Daher kann 
ich auch nicht einfach toggeln. xx11 sollte bei mir auf keinen Fall 
vorkommen!

von Sven P. (Gast)


Lesenswert?

A. K. schrieb:
> Mit auslesen geht es aber (echt) atomar bezogen auf
> die übrigen Bits mit
>   PINx = (PORTx & 0b11) ^ 0b01;
> wenn der AVR diese PINx Toggle-Operation versteht.
Das ist wirklich elegant, gefällt mir.

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.