Hallo! Ein Interrupt setzt flag=1 => ok. In der while Schleife wird i hochgezählt und sollte Port B7 toggeln => nicht ok. Habe es testhalber mit einem anderen Interrupt probiert, in beiden Interrupts toggelt B7 korrekt. Beim Durchlesen der Forensuche bin ich leider nicht fündig geworden. Wie kann ich den Fehler weiter eingrenzen?
Du hast sowohl OC1A als auch OC1B interrupts aktiviert, jedoch keinen handler dafür.
korrekt. Will nachher in der while Schleife OCR1A und OCR1B Registerinhalte ändern und die beiden PWM-Kanäle des Timer1 nutzen. Es ist ein fehlerfrei kompilierbares Testprogramm für eine Attiny 2313, um den Fehler nachzuvollziehen. Aus diesem Grund sind Programmteile enthalten, die ich später ev. noch nutzen werde.
1 | ISR (TIMER1_CAPT_vect) { |
2 | |
3 | //PORTB ^= (1 << PB7);
|
4 | flag = 1; |
5 | |
6 | |
7 | }
|
Bei welcher Gelegenheit sollte den der Interrupt gerufen werden? Ich bin mir da nicht sicher, ob dass wirklich der gewünschte ist. Bei
1 | if (flag) { |
2 | flag = 0; |
kann es natürlich sein, dass Du ab- und zu ein "flag" verlierst. Welcher Prozessor wird den benutzt?
:
Bearbeitet durch User
Hallo Achim, danke für deine Antwort. Das Register ICR1 (InputCaptureRegister1) ist TOP im gewählten PWM-Modus. In der Interruptliste des Datenblatts habe ich den TIMER1_CAPT_vect gefunden. Wenn der Wert ICR1 verändert wird, ändert sich aliquot die Periodendauer der angezeigten Rechteckspannung am Oszi, PORTB ^= (1 << PB7) natürlich entkommentiert. Wenn PORTB ^= (1 << PB7) in der Schleife steht, zeigt das Oszi dieselbe Rechteckspannung. Controller ist der Attiny2313.
Schau mal
1 | http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html |
2 | |
3 | Catch-all interrupt vector |
4 | |
5 | If an unexpected interrupt occurs (interrupt is enabled and no handler is installed, which usually indicates a bug), then the default action is to reset the device by jumping to the reset vector. You can override this by supplying a function named BADISR_vect which should be defined with ISR() as such. (The name BADISR_vect is actually an alias for __vector_default. The latter must be used inside assembly code in case <avr/interrupt.h> is not included.) |
6 | |
7 | #include <avr/interrupt.h> |
8 | |
9 | ISR(BADISR_vect) |
10 | { |
11 | // user code here |
12 | } |
an. Wenn ich das richtig interpretiere, bekommst Du Resets für die Interrupts ohne ISR. Das war wohl auch der Hinweis von Walter.
ggggast schrieb: > korrekt. Nein. > Will nachher in der while Schleife OCR1A und OCR1B Registerinhalte > ändern und die beiden PWM-Kanäle des Timer1 nutzen. Man baut keine Sachen halb ein, die man später nutzen will. Das ist halbgar. > Es ist ein fehlerfrei kompilierbares Testprogramm für eine Attiny 2313, > um den Fehler nachzuvollziehen. Der Compiler ist böse. Der lässt dich mit deinen fehlenden und falschen ISRs voll auf die Schnauze fliegen. Wahrscheinlich grinst er sogar noch dabei. > Aus diesem Grund sind Programmteile enthalten, die ich später ev. noch > nutzen werde. S.o: halbgar! > In der Interruptliste des Datenblatts habe ich den TIMER1_CAPT_vect > gefunden. Schön. Da sind noch mehr drin. Bau die doch auch mit ein. Wenn du einen Interrupt möchtest, der bei TOP zuschlägt, also wenn TCNT1==ICR1, dann nimmst du den Overflow:
1 | ISR (TIMER0_OVF_vect) |
2 | {
|
3 | //PORTB ^= (1 << PB7);
|
4 | //b++;
|
5 | |
6 | //PORTB ^= (1 << PB7);
|
7 | flag = 1; |
8 | }
|
Siehe auch Datenblatt, Table 16-4, Mode 14, rechte Spalte. Und schalte die Interrupts, für die keine ISR vorhanden ist, ab. Auch wenn die später noch benutzt werden sollen. Oder benutz den Würgaround mit BADISR. mfg.
:
Bearbeitet durch User
Eigentlich nutzt er ja Timer 1, da ist TIMER0_OVF_vect u.U. nicht passend. In der Doku steht auch bei Fast PWm Mode (Figure 46)
1 | OCRnx/TOP Update and |
2 | TOVn Interrupt Flag Set and |
3 | OCnA Interrupt Flag Set |
4 | or ICFn Interrupt Flag Set |
5 | (Interrupt on TOP) |
Könnte also auch mit dem Capture Interrupt tun. P.S.: Atmel hat neue Dokus gemacht, ohne Kapitelnummern. Daher kann es sein, dass die "Figure" in den bisherigen Dokus auch anderst heißt.
Achim K. schrieb: > Eigentlich nutzt er ja Timer 1, da ist TIMER0_OVF_vect u.U. nicht > passend. Autsch. Ist mir beim Kopieren gar nicht aufgefallen. Dann natürlich so:
1 | ISR (TIMER1_OVF_vect) |
2 | {
|
3 | //PORTB ^= (1 << PB7);
|
4 | flag = 1; |
5 | }
|
> In der Doku steht auch bei Fast PWm Mode (Figure 46) > >
1 | > OCRnx/TOP Update and |
2 | > TOVn Interrupt Flag Set and |
3 | > OCnA Interrupt Flag Set |
4 | > or ICFn Interrupt Flag Set |
5 | > (Interrupt on TOP) |
6 | > |
> > Könnte also auch mit dem Capture Interrupt tun. Mag sein. Aber 2 IRQs für dieselbe Sache sind irgendwie unsinnig. Beim Overflow-Int stürzt der natürlich ohne ISR ab. mfg.
:
Bearbeitet durch User
Danke für die vielen Antworten! Habe nun die "halbgaren" Programmteile vom Attiny2313 entfernt und es funktioniert. Sowohl der Capture Interrupt als auch der Overflow Interrupt des Timer1 führen zum Programmaufruf alle 20ms und die Variable in der while Schleife wird raufgezählt =>ok. @ Thomas Eckmann: Dass der Capture Interrupt ein Fehlgriff ist, kann ich nicht nachvollziehen. Mit welcher Umgebung hast du den Code getestet? @all: Welche Stolpersteine gibt es sonst noch auf die Schnelle, auf die ich aufpassen sollte? mfg
Alle Variablen, die in Interrupt Routinen geändert werden und im Hauptprogram gelesen werden (oder auch umgekehrt) müssen volatile sein. Das hast Du ja schon richtig gemacht.
> @all: > Welche Stolpersteine gibt es sonst noch auf die Schnelle, auf die ich > aufpassen sollte? Ich nehme immer die Typen aus "<stdint.h>", dann weiß ich, wie viele Bytes es sind. Außerdem nehme ich natürlich auch immer den kleinsten Typ, in den die benötigten Werte passen. Also nicht
1 | volatile int flag = 0; |
sondern
1 | volatile int8_t flag = 0; |
Wenn man die 2 Byte Werte braucht und mit einem Interrupt teilt, dann reicht "volatile" oft nicht aus, man muss "cli(); .... ; sei();" (oder analoges) benutzen, sonst sieht man u.U. unerwartete "Geisterwerte". Das gilt auch für 16 Bit I/O SFRs, wenn man die 16 Bit Register oder einen Teil davon auch aus dem Interrupt nutzt. Und für "read and modify" gilt dies natürlich auch. Bei besonderen I/O Operationen mit "Zeitfenster" (z.B. Watchdog füttern, System Clock Prescaler ändern) sollte man die entsprechenden Funktionen verwenden. Wenn man es, aus welchem Grund auch immer, selber programmieren möchte, muss man inline Assembler nehmen. Da der CPU Core bei vielen Applikationen nur wartet, versuche ich möglichst mit "sleep" etwas Energie zu sparen. Man muss natürlich die Doku beachten, sonst schläft der Core vielleicht länger, als man wollte :-).
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.