Hallihallo, Ich betreibe den Timer1 des ATmega88 im Compare1A-Modus, sprich, es wird eine ISR aufgerufen, sobald der Wert des Timers einen bestimmten Wert erreicht. Dieser Wert ist im Register OCR1A gespeichert. Danach zählt der Zähler wieder von Vorne (CTC mode). In der ISR wird ein Pin getoggelt - es entsteht ein Rechtecksignal. Im Programm verringere ich in einer Endlosschleife den Wert von OCR1A dauernd - die ISR wird immer früher angesprungen - die Frequenz steigt. (natürlich nur bis zu einem bestimmten Wert runter) Am Anfang läuft die Sache gut, die Frequenz nimmt zu. Doch plötzlich geht der Pin für ca. 3 Sekunden einfach nur auf High. Danach kommt das Rechtecksignal wieder, doch nun ist die Frequenz schon sehr hoch - Fazit: OCR1A wurde stetig verringert, doch irgendwann wurde die ISR nicht mehr angesprungen. Nach einer kurzen Weile kommt sie dann wieder... Beim Verringern von OCR1A schalte ich global alle Interrupts aus, wie es im DB empfohlen wird... Trotzdem spinnt da was... Was mache ich falsch/ Wieso gerät das Ding aus dem Tritt? Ändere ich den OCR1A-Wert IN der ISR, dann funktioniert die Sache tadellos, keine Aussetzer mehr... Ändere ich den Wert ausserhalb, dann spinnt es... - Was läuft da falsch? Herzlichen Gruss und gute N8 Mario
Mario wrote:
> Was mache ich falsch/ Wieso gerät das Ding aus dem Tritt?
Wie soll man das ohne Code sagen?
Hellsehen kann ich nicht.
Peter
Keeein Problem :)
1 | #include <avr/io.h> |
2 | #include <avr/interrupt.h> |
3 | #include <util/delay.h> |
4 | #include <stdlib.h> |
5 | #include <stdint.h> |
6 | //fosc = 20MHz
|
7 | |
8 | |
9 | volatile uint8_t commstep = 0; |
10 | |
11 | void peripheral_init(void) |
12 | {
|
13 | //Configure USART:
|
14 | UCSR0B = 0b00011000; //No Interrupts |
15 | UCSR0C = 0b00000110; // Asynchronuous, 8N1 |
16 | UBRR0 = 64; //Baud = 19200 |
17 | |
18 | //Configure Timer1: (COMPARE1A Interrupt)
|
19 | TCCR1B |= (1<<WGM12); //ctc mode |
20 | //TCCR1B |= (1<<CS12);//Prescale = 256
|
21 | TCNT1 = 0; |
22 | OCR1A = 255; |
23 | TIMSK1 |= (1<<OCIE1A);//Compare1a intrrupt enable |
24 | |
25 | //configure Timer0:
|
26 | TCCR0A |= (1<<WGM01); //ctc mode |
27 | TCNT0 = 0; |
28 | OCR0A = 100; |
29 | //TCCR0B |= ((1<<CS02) | (1<<CS00));//Prescaler = 1024
|
30 | TIMSK0 |= (1<<OCIE0A);//Output compare match A interrupt enable |
31 | }
|
32 | |
33 | void IO_init(void) |
34 | {
|
35 | SREG |= (1<<7); //Enable global interrupts |
36 | |
37 | DDRB |= (1<<0); //square-out |
38 | }
|
39 | |
40 | |
41 | ISR(TIMER1_COMPA_vect)//will be jumped into if timer1 compare match A is detected |
42 | {
|
43 | PORTB ^= (1<<0); |
44 | }
|
45 | |
46 | |
47 | |
48 | ISR(TIMER0_COMPA_vect)//Timer0 CompA ISR |
49 | {
|
50 | |
51 | SREG &= ~(1<<7); //disable global interrupts |
52 | OCR1A = OCR1A - 1; |
53 | if(OCR1A == 30) |
54 | OCR1A = 31; |
55 | SREG |= (1<<7);//enagle global interrupts |
56 | }
|
57 | |
58 | |
59 | int main(void) |
60 | {
|
61 | IO_init(); |
62 | peripheral_init(); |
63 | |
64 | TCCR1B |= (1<<CS12);//Prescale = 256, Start Timer1 |
65 | TCNT1 = 0; |
66 | |
67 | TCNT0 = 0; |
68 | TCCR0B |= ((1<<CS02) | (1<<CS00));//Prescaler = 1024 |
69 | |
70 | while(1) |
71 | {
|
72 | }
|
73 | |
74 | }
|
Wie man sieht wird der OCR1A-Wert in der ISR vom Timer0 runtergezählt. Ich kann OCR1A aber auch in der while(1)-Schleife dekrementieren, ohne den Timer0 zu gebrauchen - es kommt zum gleichen Aussetzer. Zum Teil gibt es sogar 2 solche Aussetzer. Vielen Dank! Mario
Edit: noch zu oben, habe den Code auf das Wesentliche runtergerafft - darum so unordentlich/überflüssige Sachen.
Mario wrote: > SREG |= (1<<7); //Enable global interrupts Gibt es als Inline-Funktion via avr/interrupt.h: sei(). > ISR(TIMER0_COMPA_vect)//Timer0 CompA ISR > { > SREG &= ~(1<<7); //disable global interrupts ... > SREG |= (1<<7);//enagle global interrupts > } Ist Unsinn, weil in ISRs Interrupts ohnehin ausgeschaltet sind.
Würde mich wundern, wenn dieser Code überhaupt irgendwelche Interrupts auslöst, weil beide Timer ausgeschaltet sind. Zudem passt er nicht zur Problembeschreibung oben, es sei denn es wäre der funktionierende Code. Denn hier wird ja OCR in der ISR modifiziert, nicht ausserhalb.
Nö, der Code tut... Die Timers laufen ja auch, werden in der main angeworfen. Dass das mit dem SREG in der ISR etwas doof ist ist mir bewusst, wollte es dennoch ausprobieren, da ich nicht mehr weiter wusste. Im Anhang noch das Original-Programm - wer es sich antun will - nur zu :) Es handelt sich um Experimente mit einem Brushless-Motor. In der Timer1-ISR wird kommutiert. Der Fehler ist nun, dass alle 6 Pins kurz auf High gehen, bevor sie mit der Arbeit fortfahren.
Mario wrote: > Nö, der Code tut... Die Timers laufen ja auch, werden in der main > angeworfen. Ok, ich hatte diese separate Taktsteuerung in main() übersehen.
Was ich ein bischen gewöhnungsbedürftig finde (ohne jetzt die Richtigkeit geprüft zu haben): Du schreibst öfters Sachen wie DDRB |= (1<<0); Das bedeutet, daß Du 1 um nichts nach links schieben willst. Kurzum: Das Ergebnis ist 1. Wenn das so sein soll, kann man das auch leserlicher schreiben. Ist aber sicher Ansichtssache.
Rein interessehalber: Welche Bedeutung hat ISR(TIMER0_COMPA_vect)//Timer0 CompA ISR++++++++++++++++++++++++++++++++++++++++ { OCR1A = OCR1A - 1; if(OCR1A == 30) OCR1A = 31; } So ändert sich OCR1A ja irgendwann in der ISR nur noch sehr schnell von 30 auf 31 innherhalb weniger Takte. Da der Timer (und eventuell darauf basierende Interrupts) ja unabhängig von ISRs weiterläuft, kann in dieser extrem kurzen Zeit etwas passieren. Ich hatte mal so ein Problem; seit dem mache ich keine so "scharfen" Vergleiche mehr mit ==, sondern wähle in diesem Fall lieber <=.
Lutz hat recht. Starte mal den Simulator und schau dem Timer zu. Dauert nicht lang, dann läuft TCNT1 über die vorgesehene Grenze hinaus. Abhilfe: Den Timer 1 an Stelle vom CTC Modus 4 im PWM Modus 15 betreiben, da in diesem Fall OCR1A doppelt gepuffert ist und das Problem somit nicht auftreten kann.
Der Fehler liegt hier:
1 | ISR(TIMER0_COMPA_vect)//Timer0 CompA |
2 | {
|
3 | OCR1A = OCR1A - 1; |
4 | if(OCR1A == 30) |
5 | OCR1A = 31; |
6 | }
|
Wenn Du OCR1A von 31 nach 30 wechselst und gleichzeitig TCNT1 nach 30 hochzählt, muß TCNT1 erstmal über 65535 rum, um wieder auf 30 zu kommen. Die Lösung wäre, daß T0 nur in eine Variable schreibt und erst der T1-Compareinterrupt diese Variable nach OCR1A lädt. Und in AVR-GCC enabled man Interrupts mit
1 | sei(); |
nicht mit schreiben des SREG. Peter
Peter Dannegger wrote: > Die Lösung wäre, daß T0 nur in eine Variable schreibt und erst der > T1-Compareinterrupt diese Variable nach OCR1A lädt. Und genau das macht der ansonsten dem CTC Modus recht ähnliche Fast-PWM Modus 15 ganz automatisch. Man muss die PWM-Ausgänge ja nicht einschalten.
Lutz wrote: > Wenn das so sein soll, kann man das auch leserlicher > schreiben. Ist aber sicher Ansichtssache. Ja, so ist es. Manche finden es aber lesbarer, wenn sie sofort das Bit (0..7) sehen, welches geändert wird. Ich schreibe daher auch immer 1<<0 .. 1<<7. Man kann aber auch schreiben: 1<<PB0 .. 1<<PB7. Peter
Huiii, Vielen Dank für die Antworten und Tipps! Werde das heute Abend mal ausprobieren. Sachen gibts, da kommt man ja nieee drauf :) Gruss und Danke Mario
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.