www.mikrocontroller.net

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


Autor: Mario (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Mario (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Keeein Problem :)

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
#include <stdint.h> 
//fosc = 20MHz


volatile uint8_t commstep = 0;

void peripheral_init(void)
{
    //Configure USART:
  UCSR0B = 0b00011000;  //No Interrupts
  UCSR0C = 0b00000110; // Asynchronuous, 8N1
  UBRR0 = 64; //Baud = 19200

    //Configure Timer1: (COMPARE1A Interrupt)
  TCCR1B |= (1<<WGM12); //ctc mode
  //TCCR1B |= (1<<CS12);//Prescale = 256
  TCNT1 = 0;
  OCR1A = 255; 
  TIMSK1 |= (1<<OCIE1A);//Compare1a intrrupt enable

    //configure Timer0:
  TCCR0A  |= (1<<WGM01); //ctc mode
  TCNT0 = 0;
  OCR0A = 100; 
  //TCCR0B |= ((1<<CS02) | (1<<CS00));//Prescaler = 1024
  TIMSK0 |= (1<<OCIE0A);//Output compare match A interrupt enable  
}

void IO_init(void)
{
  SREG |= (1<<7); //Enable global interrupts
         
         DDRB |= (1<<0); //square-out
}


ISR(TIMER1_COMPA_vect)//will be jumped into if timer1 compare match A is detected 
{
  PORTB ^= (1<<0);  
}



ISR(TIMER0_COMPA_vect)//Timer0 CompA ISR
{
  
         SREG &= ~(1<<7); //disable global interrupts
         OCR1A = OCR1A - 1;
  if(OCR1A == 30)
    OCR1A = 31;
         SREG |= (1<<7);//enagle global interrupts
} 


int main(void)
{
  IO_init();
  peripheral_init();
  
  TCCR1B |= (1<<CS12);//Prescale = 256, Start Timer1
  TCNT1 = 0;

  TCNT0 = 0;  
  TCCR0B |= ((1<<CS02) | (1<<CS00));//Prescaler = 1024
  
  while(1)
  {
  }
  
}


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

Autor: Mario (Gast)
Datum:

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

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Mario (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Lutz (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Lutz (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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 <=.

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Der Fehler liegt hier:
ISR(TIMER0_COMPA_vect)//Timer0 CompA
{
  OCR1A = OCR1A - 1;
  if(OCR1A == 30)
    OCR1A = 31;
}

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
 sei();
nicht mit schreiben des SREG.


Peter

Autor: Andreas K. (a-k)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Mario (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.