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
TIMSK0|=(1<<OCIE0A);//Output compare match A interrupt enable
31
}
32
33
voidIO_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
intmain(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
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.
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
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