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.
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.
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
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?
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
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?
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
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.
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
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?
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?
das geht schon, aber ist ein bisschen umständlich (naja, vielleicht ansichtssache). damit wird dasselbe erreicht: PORTC = (PORTC & 0xfe) | (cmd >> 7);
Genau das habe ich gesucht. Jetzt klappts auch mit der Nachbarin ...
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.
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.
@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.
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.
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.
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?
@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
@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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.