mikrocontroller.net

Forum: Compiler & IDEs Bitoperationen Zusammenfassen


Autor: tex (Gast)
Datum:

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

Autor: Marcus Maul (Gast)
Datum:

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

Autor: Daniel Jelkmann (Gast)
Datum:

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

Autor: tex (Gast)
Datum:

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

Autor: Daniel Jelkmann (Gast)
Datum:

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

Autor: tex (Gast)
Datum:

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

Autor: Daniel Jelkmann (Gast)
Datum:

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

Autor: tex (Gast)
Datum:

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

Autor: Daniel Jelkmann (Gast)
Datum:

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

Autor: tex (Gast)
Datum:

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

Autor: tex (Gast)
Datum:

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

Autor: Thomas (Gast)
Datum:

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

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

Autor: tex (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Genau das habe ich gesucht.
Jetzt klappts auch mit der Nachbarin ...

Autor: Andreas Kaiser (Gast)
Datum:

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

Autor: Stefan May (Gast)
Datum:

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

Autor: tex (Gast)
Datum:

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

Autor: A.K. (Gast)
Datum:

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

Autor: tex (Gast)
Datum:

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

Autor: Mirki (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der heisst "Brian W. Kernighan" und nicht Kernigan !!


mirki

Autor: tex (Gast)
Datum:

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

Autor: Mirki (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jetzt übertreibst Du aber :-)
alles klar dex ;-)

Autor: peter dannegger (Gast)
Datum:

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

Autor: tex (Gast)
Datum:

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

Autor: peter dannegger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

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.