Hi, Ich habe ein Programm für den ATMEGa8 und ich versuche es nach zu vollziehen. zum Beispiel: outb(MCUCR,(inp(MCUCR) | BV(SE)) & (~BV(SM0) | ~BV(SM1) | ~BV(SM2))); Im Endeffekt bedeutet diese Befehlzeile ddoch ass SE=1,SM0=0,SM1=0,SM2=0 wird (Sleepmode=IDLE). BV() und ~BV() schreibt dabei das Bit=1 bzw. 0. Wie funktioniert der bitweise Oder-Operator"|" diesem Zusammenhang und warum wird hier MCUCR erst gelesen? Und noch was anderes: DAs: DDRD=0x07 DDRB=0xc2 (Datenrichtungsregister) PORTD=0xff PORTB=0xff adressiert die POrts mit bestimmten Adressen. Kann diese Adressierung was mit dem Timer zu tun haben, das wenn er überläuft (255=0xff)sollen bei den Ports mit dem Programm weitergemacht werden, oder welche Bedeutung kann diese Adressierung haben? Hat einer nen Plan? mfg
natürlich vorausgesetzt GIE in SREG und die anderen ENABLE bits sind gleich 1.
> outb(MCUCR,(inp(MCUCR) | BV(SE)) & (~BV(SM0) | ~BV(SM1) | ~BV(SM2))); > Im Endeffekt bedeutet diese Befehlzeile ddoch ass SE=1,SM0=0,SM1=0,SM2=0 Mit dem ersten Teil hast du recht (SE=1) Der zweite Teil dürfte aber ein Denkfehler vom originalen Autor sein. Schaun wir mal warum: outp und inp sind alte Schreibweisen. Mit heutigem gcc würde man schreiben MCUCR |= (1 << SE); MCUCR &= ~(1 << SMO) | ~( 1 << SM1 ) | ~( 1 << SM2 ); Das setzen des SE Bits ist in Ordnung. Nur das Löschen geht so nicht. Zuallererst: In C arbeitet man mit Bytes. Auch die sog. Bitoperationen arbeiten immer mindestens mit Bytes. Wie löscht man ein bestimmtes Bit, sagen wir mal das SM0 Bit? Angenommen MCUCR enthällt den Wert 0b11111111. Also alle Bits gesetzt. Ich weiss jetzt nicht auswendig welches Bit das Bit SM0 ist, ich nehme einfach mal an es wäre das Bit 3. Um also Bit 3 zu löschen, braucht man eine Maske, so dass beim UND-en des Bytes mit dieser Maske alle Bits bis auf Bit3 unangetastet bleiben und Bit 3 mit Sicherheit auf 0 gesetzt wird. Die Maske muss dabei an allen Stellen bis auf Bit 3 eine 1 aufweisen, nur Bit 3 muss 0 sein. 0b11111111 original Wert 0b11110111 Maske UND ---------- 0b11110111 (Du wendest die Bit-Verknüpfung UND einfach auf jeweils übereinanderstehende Bits an. Es gibt keinen Übertrag oder sonst was. Einfach nur: 0 UND 0 gibt 0; 0 UND 1 gibt 0; 1 UND 0 gibt 0; 1 UND 1 gibt 1) Zur Kontrolle: Der Ausgangswert sei 0b01101001 0b01101001 original 0b11110111 Maske UND ---------- 0b01100001 Passt. Also das geht. Wie kommt man nun zu der Maske: SM0 hat den Wert 3. Also nehmen wir eine binäre 1 und schieben sie 3 mal nach links. 1 << SM0 macht genau das. Binär sieht das Ergebnis dann so aus: 0b00001000 Das sieht schon ganz gut aus. Allerdings müssen jetzt alle Bits invertiert werden. Aus 0 muss 1 werden, aus 1 muss 0 werden. Das ist der Job von ~ ~0b00001000 ergibt 0b11110111 Und das ist dann genau die Maske die wir brauchen. Soweit so gut. Aber warum klappt dann das trotzdem nicht? MCUCR &= ~(1 << SMO) | ~( 1 << SM1 ) | ~( 1 << SM2 ); Weil da nicht eine Maske benutzt wird, sondern deren 3. Und die einzelnen Masken werden bitweise ODER verknüpft. Probieren wir mal: SM0 sei Bit 3, SM1 sei Bit 4, SM2 sei BIT 5 Die Maske die wir im Endeffekt für den UND haben wollen muss also so aussehen: 0b11000111 Diese Maske, zusammen mit der UND Verknüpfung würde Bit 3, 4 und 5 auf 0 setzen. ~( 1 << SM0 ) = 0b11110111 ~( 1 << SM1 ) = 0b11101111 ODER ---------- 0b11111111 ~( 1 << SM2 ) = 0b11011111 ODER ---------- 0b11111111 Nicht ganz was wir wollen. Wo liegt der Denkfehler? Man kann schon ODER benutzen. Was wir aber verodern wollen, ist was anderes. Wir wollen ( 1 << SM0 ) = 0b00001000 ( 1 << SM1 ) = 0b00010000 ODER ---------- 0b00011000 ( 1 << SM2 ) = 0b00100000 ODER ---------- 0b00111000 Und erst diese Ergebnis wird negiert: ~0b00111000 -> 0b11000111 Und das ist dann die Maske die wir haben wollen Also muss letzteres lauten: MCUCR &= ~( (1 << SMO) | ( 1 << SM1 ) | ( 1 << SM2 ) ); Der Autor hat wahrscheinlich auf die De'Morgan Regel vergessen und ganz einfach die Klammern wie bei einer Multiplikation aufgelöst. Das geht aber nicht, denn nach De'Morgan gilt ~( A | B ) <==> ~A & ~B Man könnte obiges also auch so schreiben: MCUCR &= ~(1 << SMO) & ~( 1 << SM1 ) & ~( 1 << SM2 ) ); Machen wir wieder die Probe: ~( 1 << SM0 ) = 0b11110111 ~( 1 << SM1 ) = 0b11101111 UND ---------- 0b11100111 ~( 1 << SM2 ) = 0b11011111 UND ---------- 0b11000111 Auch das liefert die richtige Maske. Bei den Logikoperationen UND und ODER immer vorsichtig sein. Wir verwenden diese Operationen im täglichen Leben nämlich meistens falsch.
> Wie funktioniert der bitweise Oder-Operator"|" diesem Zusammenhang und > warum wird hier MCUCR erst gelesen? Wie ODER funktioniert sollte jetzt klar sein 0 ODER 0 ergibt 0; 0 ODER 1 ergibt 1; 1 ODER 0 ergibt 1; 1 ODER 1 ergibt 1 > warum wird hier MCUCR erst gelesen? Weil ja die restlichen Bits, also alle ausser SE, SM0, SM1 und SM2 so bleiben sollen wie sie gerade sind.
> DAs: > > DDRD=0x07 > DDRB=0xc2 (Datenrichtungsregister) > PORTD=0xff > PORTB=0xff > > adressiert die POrts mit bestimmten Adressen. Kann diese Adressierung > was mit dem Timer zu tun haben, das wenn er überläuft (255=0xff)sollen > bei den Ports mit dem Programm weitergemacht werden, oder welche > Bedeutung kann diese Adressierung haben? Nein. Kann es nicht. Das setzt einfach nur die Datenrichtung (also Eingang oder Ausgang) für die Ports D und B. DDRD = 0x07; ist dasselbe wie: DDRD = 0b00000111; Hier wird also vereinbart, dass die Bits 0, 1, 2 als Ausgang benutzt werden und alle anderen Bits als Eingang Das nachfolgende PORTD = 0xff; ist wieder äquivalent zu PORTD = 0b11111111; und hier gibt es einen kleinen Unterschied, je nachdem ob ein Bit als Eingang oder als Ausgang definiert wurde. Ist ein Bit als Ausgang, hier also die Bits 0, 1 und 2. so setzten die entsprechenden Bits beim PORT setzen, direkt den Ausgang am Chip. Hier werden also diese 3 Bits durch PORTD = 0b.....111; auf 1 gesetzt. Ist ein Port Pin auf Eingang geschaltet, so wird mit der zugehörigen Ausgabe auf PORT der zum PIN zugehörige Pull-Up Widerstand eingeschaltet bzw. ausgeschaltet PORTD = 0b11111...; schaltet also für die Bits 3, 4, 5, 6, 7 den PullUp Widerstand ein. Alles zusammen; DDRD = 0b00000111; // 0,1,2: Ausgang; 3,4,5,6,7: Eingang PORTD = 0b11111111; // 0,1,2: auf 1 setzen // 3,4,5,6,7: Pullup einschalten
Wow, du hast es drauf. Besser kann man es einfach nicht erklären. Nun habe ich noch ein Problem mit dem Programmablauf. Zunächst startet das Programm mit der Mainmode, danach werden die Interupts nach Priorität abgearbeitet. ////////// Programm Pseudocode: 1.Interrupt: ... enable ADC interrupts 2.Interrupt ... wähle der Reihe nach die Kanäle für ADC: wandele sie um 3.Interrupt ... sende zu UART Dann folgt die main mit: main { 1. Ports einstellen (die von oben) 2. Sleepmode=IDLE 3. ADC einstellen und anschalten 4. UART einstellen 5. Timer einstellen und starten dann werden die Interrupt noch mit outb(TIMSK, 1 << TOIE0) enabled. Dann wird noch GIE gesetzt und danach folgt: while(1) { sleep } } //////////// Das Hauptprogramm geht in IDLE mode Endlosschleife. Mit outb(TIMSK, 1 << TOIE0) setzt das TOIE0 Bit ; also wenn der Timer überläuft wird das Hauptprogramm unterbrochen und es wird die Interuptroutine höchster Priorität eingeleitet. 1.Befindet sich das Hauptprogramm zu diesem Zeitpunkt bereits in SLEEPmode? 2.Woher weiss dass Programm welcher Interrupt nach Overflow eingeleitet werden soll und welcher als nächstes? 3.WEchselt das Programm zwischen den Interrupts nochmal zurück ins Hauptprogramm? mfg
> Mit outb(TIMSK, 1 << TOIE0) setzt das TOIE0 Bit ; also wenn der Timer > überläuft wird das Hauptprogramm unterbrochen und es wird die > Interuptroutine höchster Priorität eingeleitet. > > 1.Befindet sich das Hauptprogramm zu diesem Zeitpunkt bereits in > SLEEPmode? Kann sein muss nicht sein. Das setzen des TOIE0 Bits gibt nur die Behandlung des Timer-Overflows durch eine Interrupt Funktion frei. Mehr nicht. Wann auch immer dieser Overflow erfolgt wird die entsprechende Funktion audgeführt. Es ist dabei unerheblich ob der µC dabei gerade schläft oder nicht. Wenn er schläft, wird er aufgeweckt und dir Overflow Funktion ausgeführt. Wenn er nicht schläft, wird die Funktion auch ausggeführt. Ob der µC also bereits schläft oder nicht, hängt davon ab wie lange der Timer gebraucht hat um in den SleepMode zu gelangen. > 2.Woher weiss dass Programm welcher Interrupt nach Overflow# > eingeleitet werden soll und welcher als nächstes? Die Hardware weiss ja welches Ereignis zum Interrupt geführt hat. Am Anfang des Programmes steht, für dich unsichtbar, eine Tabelle, in der für jedes mögliche Ereignis eingetragen wurde, welche Funktion dafür zuständig ist. Das macht der Compiler für dich. Wenn du eine Interrupt Service Funktion (ISR) schreibst, dann trägt der Compiler sie dort ein. Daher schauen ISR auf C Ebene ein klein wenig anders aus als normale Funktionen. Welcher als nächster. Die Regelung ist ganz einfach: Es kann immer nur eine ISR abgearbeitet werden. Tritt während der ISR ein neuer Interrupt auf, so wird mit der Behandlung dieses Interrupt gewartet bis der vorhergehende fertig ist. ISR können sich also gegenseitig nicht unterbrechen (stimmt nicht ganz). Wenn mehrere Interrupts liegen geblieben sind dann bestimmt die Reihung in der erwähnten Tabelle welcher zuerst abgearbeitet wird. > 3.WEchselt das Programm zwischen den Interrupts nochmal zurück ins > Hauptprogramm? Da bin ich mir nicht ganz sicher. Ich denke aber schon.
>> 3.WEchselt das Programm zwischen den Interrupts nochmal zurück ins >> Hauptprogramm? > Da bin ich mir nicht ganz sicher. Ich denke aber schon. Bei den AVRs ist es grundsätzlich so, dass, wenn mehrere Interrupt-Ereignisse gleichzeitig anstehen, nach der Abarbeitung einer Interrupt-Routine wieder zurück ins Hauptprogramm gesprungen und dort mindestens ein Befehl ausgeführt wird, bevor der nächste Interrupt (also der mit der nächst-niederen "Priorität") bearbeitet wird.
>Wenn er schläft, wird er >aufgeweckt und dir Overflow Funktion ausgeführt. In IDLemode wird nur die CPU angehalten. ISR, ADC, USART bleiben aktiv. 1. Angenommen der MCU befindet sich bereits in IDLEmode und dann läuft der timer ab,dann kann doch der Interrupt ausgeführt werden, wobei die CPU angehalten bleibt? 2. Wie übersetzt man 0x07,0xf2 in die Byteschreibweise 0b0000000? 3. Diese altmodischen ISRs werden verwendet: SIGNAL(SIG_OVERFLOW0) SIGNAL(SIG_ADC) SIGNAL(SIG_UART_DATA) Ist es der Fall, wenn das Timer enable bit ( auch GIE) gestetzt wurde und der Timer läuft über, dann wechselt das Hauptprogramm hwm (hardwaremäßig) zu SIGNAL(SIG_OVERFLOW0) und wenn in SIGNAL(SIG_OVERFLOW0) das ADC enable bit gesetzt wird, wechselt das Programm nach SIGNAL(SIG_OVERFLOW0) zurück in die Mainmode, dort wird ein Befehl ausgeführt und dann wird nach SIGNAL(SIG_ADC) gewechselt (hwm), usw. Haben diese SIGNALS(...) auch was mit INT0 bzw. INT1 von ISCx in MCUCR zu tun? mfg
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.