Forum: Compiler & IDEs Bitoperationen Zusammenfassen


von tex (Gast)


Lesenswert?

Hallo!
Ich versuche mich gerade an die Programmieurng von AVRs zu gewöhnen.
Um ein Bit zu setzen ist nach allem, was ich in diesem Forum lese, die
Form

DDRC |= (1<<DDC5);
DDRC |= (1<<DDC6);
DDRC |= (1<<DDC7);
anzuwenden,
was sich zusammenfassen läßt in

DDRC = (1<<DDC5)|(1<<DDC6)|(1<<DDC7);
(Warum darf da der senkrechte Strich vor dem = wegfallen?!)

Um eine Bit zu löschen wird

  PORTG &=  ~(1<<PG2);
  PORTG &=  ~(1<<PG1);

verwendet.
Wie muss die Zeile aussehen, wenn man es wie oben zusammenfassen
will?
Ich hab schon ein paar Sachen ausprobiert, aber der Compiler compiliert
da einfach alles, auch DDRC |= (1<<PG2) gibt keinen Fehler.

von Marcus Maul (Gast)


Lesenswert?

Hallo Tex,

DDRC = (1<<DDC5)|(1<<DDC6)|(1<<DDC7);
bedeutet: werte den Ausdruck (1<<DDC5)|(1<<DDC6)|(1<<DDC7) aus und
weise ihm DDRC zu.

DDRC |= (1<<DDC5)|(1<<DDC6)|(1<<DDC7);
bedeutet hingegen, werte den Ausdruck (1<<DDC5)|(1<<DDC6)|(1<<DDC7) aus
und oder verknüpfe ihn binär mit dem Inhalt von DDRC.
Sollte also DDRC jetzt von 0x0 abweichen, dann kann das Ergebniss auch
den Wert 0xff haben. Oben kann es nur den Wert: 0x70 haben.

 PORTG &=  ~(1<<PG2);
 PORTG &=  ~(1<<PG1);

läßt sich auch so schreiben:
PORTG &= ~((1 << PG2) | (1 << PG1));

Ergebniss ist das gleiche.

Gruß Marcus

PS: Schau Dir mal bitte ein gutes C, C++ Buch an.

von Daniel Jelkmann (Gast)


Lesenswert?

Moin,

DDRC |= (1<<DDC5);
DDRC |= (1<<DDC6);
DDRC |= (1<<DDC7);

ist genaugenommen nicht gleich

DDRC = (1<<DDC5)|(1<<DDC6)|(1<<DDC7);

Bei der oberen Schreibweise werden explizit nur die Bits für DDC5-7
gesetzt, die anderen Bits bleiben unverändert.
Unten hingegen werden nur die drei Bits gesetzt und dies wird dann dem
DDRC zugewiesen, also werden Bits, die vielleicht schon vorher gesetzt
waren, alle auf 0 gesetzt (ausser den dreien, die gesetzt werden
sollen). Wenn der senkrechte Strich (ein bitweises Oder) bei der
unteren Version dazukommt, dann ist es 100% äquivalent zu obiger
Schreibweise.

PORTG &=  ~(1<<PG2);
PORTG &=  ~(1<<PG1);

kann man zusammenfassen zu

PORTG &= ~( (1<<PG1) | (1<<PG2) );

Überleg Dir mal was da genau passiert, dann wird es hoffentlich
verständlich.

Bye
  Daniel Jelkmann

von tex (Gast)


Lesenswert?

OK Ich habe dann wohl ein Scheiß - C-Buch. Das klärt natürlich einiges.

es ist also

DDRC = (1<<DDC5)|(1<<DDC6)|(1<<DDC7);
nichts anderes als
DDRC = 0x70; ?

Kann ich, um DDC5-7 zu setzen und die übrigen unveränder zu lassen auch

DDRC |= 0x70 schreiben?

von Daniel Jelkmann (Gast)


Lesenswert?

Hi,

abhängig davon wie DDC5-7 definiert sind, ist es das gleiche.
Und mit DDRC |= 0x70 setzt Du dann genau die drei Bits, der Rest bleibt
unverändert.

Bye
  Daniel Jelkmann

von tex (Gast)


Lesenswert?

Dann kommt hier noch eine Frage. ch hoffe es geht, ohne einen neuen
Thread öffnen zu müssen

Ich versuche gerade einen LCD-Treiber umzuschreiben, der zwar nur 4 Bit
von Port C braucht, aber immer wieder mit Befehlen wie

PORTC = (cmd >> 7);

alle Bits auf C verbiegt, also auch 4 - 7, die er nicht braucht. Jetzt
habe ich schon alle die definirt gesetzt oder gelöscht wurden nach o.g.
Beispielen via und und Oder auf die relevanten Bits begrenzt, aber wie
geht das bei diesem hier? der kann ja nun 1 oder 0 sein. Brauche ich
jetzte eine If then else Anweisung, odergeht das auch einfacher?

von Daniel Jelkmann (Gast)


Lesenswert?

Hi,

ehrlich gesagt hab ich nicht ganz kapiert, was Du meinst.
Wenn Du nur die untersten 4 Bit von PORTC beschreiben willst, dann
kannst Du das so machen:
PORTC = x & 15;
In der 15 sind die untersten 4 Bit gesetzt und durch die
Und-Verknüpfung werden die obersten Bits rausgeschmissen.
Wenn Du die oberen 4 Bit der Variable x auf PORTC ausgeben willst, dann
geht das natürlich auch:
PORTC = (x >> 4) & 15;

Beantwortet das Deine Frage?

Bye
  Daniel Jelkmann

von tex (Gast)


Lesenswert?

Ich glaube nicht.
Also nochmal. Ich habe einen (juchhei) funktonstüchtigen LCD-Treiber
und ein Display, beides abgekupfert, weil ich selbst das nie auf die
reihe bekommen würde.
Jetzt hat der Programmiere des Displaytreibers etwas großzügig
programmiert und z.B.
PORTC = 0x02; eingegeben, um Bit 1 zu setzen und 0, 2 und 3 auf 0 zu
stellen. Natürlich hat er damit auch 4, 5, 6, und 7, auf null gestellt,
was ihn vermutlich nicht störte, weil er die Ports nicht belegt hat,
aber ich möchte sie gerne noch benutzen.
Also schreibe ich PORTC = 0x02 um in PORTC |= 0x02 um Bit 1 zu setzen
und PORTC &= ~ 0x02; um Bit 1 auf 0 zu bringen.
Das ist überschaubar, solange der Zustand, den das Bit bekommen soll
bekannt ist. Bei
 PORTC = (cmd >> 7)
Ist er aber in cmd eingeschlossen, d.h. es müsste dann
PORTC &=  ~(1<<PC1) heissen, wenn das Bit 0 währe, oder
PORTC |=  (1<<PC1) wenn es 1 ist.
Ich würde jetzt in Ermangelung einer besseren Idee eine If then else
Procedure schreiben. Ich denek aber, das es noch einen deutlich
besseren und eleganteren Weg gibt.

von Daniel Jelkmann (Gast)


Lesenswert?

Hi,

achso, jetzt habe ich glaube ich verstanden, was Du meinst :)
Auch wenn mir noch nicht klar ist wo das cmd herkommt und warum es um 7
Stellen geshiftet wird.

Um die oberen 4 Bit unverändert zu lassen, egal was in der Variablen x
steht, würde ich sowas versuchen:
PORTC = (PORTC & 240) | (x & 15);
Allerdings bin ich mir da gerade nicht so ganz sicher, ob das mit dem
PORTC auf der rechten Seite funktioniert. Sollte es eigentlich, da ein
PORTC |= y auch nichts anderes ist als PORTC = PORTC | y;
Du kannst es ja vielleicht mal ausprobieren. Die oberen 4 Bit sollten
eigentlich unverändert bleiben und die unteren 4 Bit werden von der
Variablen x genommen.

Bye
  Daniel Jelkmann

von tex (Gast)


Lesenswert?

Soweit ich das verstanden habe prüfen sie damit, ob cmd am Display
angekomen ist, weil sie es später an PC3 auslesen und vergleichen.
Deine Zeile verstehe ich noch nicht ganz, das muss ich mir ersta mal
durchrechnen. Gibt es vieleicht sowas wie schift mit und ohne Carry?
Man könnte dann PORTC auslesen, eins nach rechts schiften, so dass das
LSB hinten runterfällt, und dann mit Carry wieder nach links schiften
wowei das 7 fach geschiftete cmd das Carry ist und als LSB angehangen
wird?

von tex (Gast)


Lesenswert?

Noch eine Idee.
Würde

PORTC = ((PORTC >> 1)<<1)|(cmd >> 7);

funktionieren?
Was es tun soll ist den inhalt aus PORTC holen, ein nach rechts
schiften, damit das LSB runterfällt, ein nach links schiften, damit die
Bits wieder da sind wo sie hingehören und anschießend per oder mit dem
übrig gebliebenen Bit von cmd verknüpfen.
geht das?

von Thomas (Gast)


Lesenswert?

das geht schon, aber ist ein bisschen umständlich (naja, vielleicht
ansichtssache). damit wird dasselbe erreicht:

PORTC = (PORTC & 0xfe) | (cmd >> 7);

von tex (Gast)


Lesenswert?

Genau das habe ich gesucht.
Jetzt klappts auch mit der Nachbarin ...

von Andreas Kaiser (Gast)


Lesenswert?

Vorsicht beim Zusammenfassen von Bit-Operationen. Beim DDR in der
Initialisierung ist das i.A. ok, aber später kann es eine böse Falle
werden.

Einzelbit-Operationen werden i.A. auf jeweils einen einzelnen CBI/SBI
Befehl abgebildet. Die hier gezeigten Mehrfachbit-Operationen jedoch
nicht. Es kommt also z.B. IN  AND  OUT dabei heraus. Wird nun in
einer Interrupt-Routine der gleiche Port modifiziert, und da grad
mitten in der Sequenz drin, dann wird ebendiese Modifikation mit dem
OUT Befehl wieder rückgängig gemacht. Muss man also drum herum die
Interrupts abschalten. Umständlich und hässlich.

Und ebendies ist ein Grund, weshalb in leidlich portabel gedachten
Modulen solche Sequenzen von Einzelbit-Modifikationen vorkommen. Und
besser auch drin bleiben sollten, denn später übernimm man das Modul
irgendwo anders und denkt nicht dran. Merkt man ja auch nicht sofort.

NB: Da sind die AVRs den PICs gegenüber im Nachteil. Da gibt's AND/OR
zum Port hin, also IN/AND/OUT in einem 1 Befehl.

von Stefan May (Gast)


Lesenswert?

Hi Tex,

wenn ich Dir einen Buchtip geben darf:

Kernigan & Ritchie: Programmieren in C

ISBN ist 3-446-15497-3

Ist meiner Meinung nach das einzig brauchbare Buch für C. Kommt schnell
auf den Punkt und ist auch als Referenz zu gebrauchen. Außerdem haben
die beiden Autoren diese Programmiersprache verbockt, sollten es also
am besten wissen.

mfg, Stefan.

von tex (Gast)


Lesenswert?

@Andreas
Ich wünschte ich würde verstehen was Du mir mit dem Kommentar sagen
wolltest, denn es klingt als wäre es nicht unwichtig.

@Stefan
Danke, Werd ich mir mal anschauen.

von A.K. (Gast)


Lesenswert?

Du hast ein C-Statement "PORTB |= 3" (setze PB0,PB1), bestehend aus
den Assembler-Befehlen:
  IN  r24,PORTB
  ORI r24,3
  OUT PORTB,r24
Und es gibt eine Interrupt-Routine, die am gleichen Port über PB7 ein
Relais ansteuert.

Und wenn Du Pech hast läuft's dann so ab:

Anfangszustand: PB0=0, PB1=0, PB7=0. Relais aus.

Nach dem "IN" Befehl: PORTB unverändert

Nach dem ORI Befehl: PORTB unverändert, R24=3.

Jetzt kommt teuflischerweise die Interrupt-Routine dazwischen. Dort
steht "PORTB |= 0x80", also SBI PORTB,7. Stand jetzt ist:
PORTB==0x80, Relais kriegt Strom. So soll es sein.

Interrupt-Routine kehrt zurück, das Hauptprogramm läuft weiter und der
OUT-Befehl wird ausgeführt. Speichert aus R24 den Wert 3 nach PORTB.
Von dem zwischenzeitig gesetzten PB7 weiss er nichts, da der IN Befehl
den Zustand vor dem Interrupt gelesen hat. Ergebnis: PORTB==3, das
Relais war Microsekunden an, ist nun wieder aus.

Folge: in 99.9% der Fälle klappt alles, weil der Interrupt zu
günstigeren Zeiten kommt. Nur ganz selten mal "scheint irgendwie das
Relais zu klemmen".

Steuert man PB0,PB1 einzeln, benutzt der Compiler Einzelbitbefehle
(SBI), ergo entsteht das Problem nicht. Allerdings gilt das nur für
I/O-Ports unter Adresse 0x20, darüber gibt's genau das gleiche
Problem, also z.B. bei Mega64/128 und Ports F,G.

M.a.W: sobald man mit Interrupts arbeitet, gelten ähnliche Regeln wie
bei Multithreading in Windows/Linux/... Alles was gemeinsam genutzt
wird (und das gehört prinzipbedingt der ganze Port B), muss man wie ein
rohes Ei behandeln. Solche Fehler sind teuflisch selten, praktisch nicht
reproduzierbar, also arg schwer zu finden.

von tex (Gast)


Lesenswert?

OK!
Wenn die C-Programmierung ein Universum mit einem hellen Punkt in der
Mitte ist, bin ich an dem Ort, der am weitesten davon entfernt ist.
Ich muss eine DCF-77 Uhr am meinen mega128 hängen, weil ich zu blöd
bin, einen Timer mit Timerinterupt zu programmieren. Vermutlich kenne
ich die Lösung für die 1000 Uhr- und Kalender-Threads in diesem Forum,
aber ich werds nie rausfinden, weil ich es nicht programmieren kann.
Aber der Tip ist mit Sicherheit Gut. Ich werd versuchen ihn mir zu
merken.

von Mirki (Gast)


Lesenswert?

Der heisst "Brian W. Kernighan" und nicht Kernigan !!


mirki

von tex (Gast)


Lesenswert?

OH! Vielen Dank! Welch eine Überaus nützliche Information in diesem
Forum. Man möchte Sie um keinen Preis der Welt missen müssen!
Hallo Admin!
Gibt es eine Möglichkeit dieses Wissen der Nachwelt zu erhalten?

von Mirki (Gast)


Lesenswert?

Jetzt übertreibst Du aber :-)
alles klar dex ;-)

von peter dannegger (Gast)


Lesenswert?

@Tex

"Ich muss eine DCF-77 Uhr am meinen mega128 hängen, weil ich zu blöd
bin, einen Timer mit Timerinterupt zu programmieren."


Das eine geht nicht ohne das andere !


Der DCF-77 sendet auf Langwelle und kann damit leicht gestört werden
(Staubsauger, Fernseher, Gewitter usw.).

Eine DCF-77 Uhr besteht daher immer aus einer ganz normalen Uhr
(Timerinterrupt), die ab und zu durch das DCF-77 Signal synchronisiert
wird.


Wenn Du also nicht die Codebeispiele (z.B. von mir) nehmen willst, mußt
du trotzdem erstmal eine normale Uhr zum Laufen bringen und dann den
DCF-77 Teil hinzufügen.


Peter

von tex (Gast)


Lesenswert?

@peter
Na auf die Codebeispiele komme ich gerne noch mal zurück, schon weil
man was draus lernen kann. (Wenn Du mir sagt, wo ich sie finde)
Die Uhr ist schon fertig, so mit Decoder, Auswertung, eigener Timer.
Man  musste bloß die Serielle Schnittstelle auf 300 Baud eingestellt
bekommen und den Fehler in der Dokumentation finden.

von peter dannegger (Gast)


Lesenswert?


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.