Forum: Mikrocontroller und Digitale Elektronik ATmega88: Timer1: OCR1A ändern - Fehlfunktion


von Mario (Gast)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Mario (Gast)


Lesenswert?

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

von Mario (Gast)


Lesenswert?

Edit: noch zu oben, habe den Code auf das Wesentliche runtergerafft - 
darum so unordentlich/überflüssige Sachen.

von Andreas K. (a-k)


Lesenswert?

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.

von Andreas K. (a-k)


Lesenswert?

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.

von Mario (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Andreas K. (a-k)


Lesenswert?

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.

von Lutz (Gast)


Lesenswert?

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.

von Lutz (Gast)


Lesenswert?

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 <=.

von Andreas K. (a-k)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Andreas K. (a-k)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Mario (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.