Simpler Timer-CTC-Code funktioniert nicht

OP #5739420
Habe hier ganz simplen Code (Timer1 im CTC Mode) für einen Arduino Uno 
(Atmega 328P). Leider kann ich durch Änderung von OCR1A nicht die 
Interrupt-Frequenz ändern - da läuft also etwas schief.

Ändere ich den Prescaler, so ändert sich auch wie erwartet die 
Interrupt-Frequenz.
Auch wird definitiv meine ISR aufgerufen.
Allerdings möchte ich eben die Interrupt-Häufigkeit durch das 
OCR1A-Register variabel anpassen können.

Kann mir jemand sagen was schief läuft?
1
int PinMotorRight=9;
2
void setup() {
3
  pinMode(PinMotorRight, OUTPUT); // den schalte ich später in der ISR
4
 
5
  // initialize timer1
6
  noInterrupts();           // disable all interrupts
7
 
8
  TCCR1B |= (1<<WGM12); // Clear on Compare Match
9
  OCR1A = 0xAAAA; // << Das hier scheint keinerlei Auswirkung zu haben
10
 
11
  // Compare Interrupt erlauben
12
  TIMSK1 |= (1<<OCIE1A);
13
 
14
  // Beispiel: Prescaler 1024
15
  TCCR1B |= (1<<CS10) | (1<<CS12);
16
 
17
  interrupts();             // enable all interrupts
18
}
19
 
20
ISR (TIMER1_COMPA_vect)        // interrupt service routine
21
{
22
  digitalWrite(PinMotorRight, digitalRead(PinMotorRight) ^ 1);
23
}

Danke für eure Hilfe :)
Gast
Michael N. schrieb:
> Bin leider seit 5 Jahren raus und tue mir gerade echt schwer.
> Dachte das
> macht der hier:
> TCCR1B |= (1<<WGM12);
> Oder verwechsle ich etwas?

Im Prinzip hast du Recht.

Der Timer wird über drei Register konfiguriert: TCCR1A, TCC1B und TIMSK. 
Du hast dich nur die zwei Bits gekümmert, die du gegenüber dem Default 
Zustand (nach Reset) ändern willst.

Verlasse dich besser nicht darauf, dass deren Bits alle den default 
Zustand haben, denn du hast da noch einen Bootloader und ein Arduino 
Framework, die beide möglicherweise dazwischen kommen. Ich fürchte, das 
ist dein Problem.

Versuche es mal mit:
1
  TCCR1B = 0;
2
  TCCR1B = (1<<WGM12) | (1<<CS10) | (1<<CS12);
3
  TIMSK1 = (1<<OCIE1A);
4
  OCR1A = 0xAAAA;
Gast
> digitalWrite(PinMotorRight, digitalRead(PinMotorRight) ^ 1);

Das kannst du übrigens vereinfachen:

PINB = (1<<PB1)

Siehe Datenblatt Kapitel 13.2.2:
"Writing a logic one to PINxn toggles the value of PORTxn, independent 
on the value of DDRxn. Note that the SBI instruction can be used to 
toggle one single bit in a port."

Das ist etwa 70x effizienter, als digitalWrite():
Stefanus F. schrieb:
> Verlasse dich besser nicht darauf, dass deren Bits alle den default
> Zustand haben, denn du hast da noch einen Bootloader und ein Arduino
> Framework, die beide möglicherweise dazwischen kommen. Ich fürchte, das
> ist dein Problem.

falls das noch nicht hilft, beim Arduino wird soweit ich weiß ein Timer 
intern für delay() oder so verwendet. Keine Ahnung welcher das ist, 
vllt. macht das auch Probleme.
Gast
Michael N. schrieb:
> OCR1A = 0xAAAA; // << Das hier scheint keinerlei Auswirkung zu haben

Nur mal so als Anhaltspunkt: bei einem Prescaler von 1024
und 0xAAAA als Compare Match Wert bekommst du nur ca alle
drei Sekunden einen Interupt.

(16MHz  1024  0xAAAA = 0.358 Hz)
OP
Stefanus F. schrieb:
> Versuche es mal mit:
>   TCCR1B = 0;
>   TCCR1B = (1<<WGM12) | (1<<CS10) | (1<<CS12);
>   TIMSK1 = (1<<OCIE1A);
>   OCR1A = 0xAAAA;

Selbes Problem: Unabhängig von OCR1A bekomme ich ca alle 16 ms eine 
Flanke.

Stefanus F. schrieb:
> Vielleicht musst du das Compare-Flag bei der Initialisierung löschen:
>
> TIFR1 |= (1<<OCF1A);

Ändert nichts...

Stefanus F. schrieb:
> Das ist etwa 70x effizienter, als digitalWrite():

Danke, ist ein super Tipp :) Werde ich umbauen sobald meine Interrupts 
laufen.


Weiß langsam nicht mehr weiter. Wie oben schon gesagt ist beim Arduino 
Timer0 schon im Einsatz. Daher nutze ich Timer1. Der Timer scheint 
irgendwie nicht im Compare-Modus zu arbeiten bzw sich nach dem Compare 
nicht auf 0 zurückzusetzen...

Reverse-Engineering: Der Uno läuft angeblich auf 16 MHz. Mit Prescaler 
1024 bekomme ich ca. alle 16 ms eine Flanke. Würde bedeuten, dass der 
Interrupt immer nach ca. 250 (wahrschenlich also 255 bzw 256) 
Zählschritten kommt. Timer1 ist aber ein 16 Bit Timer. Warum matcht er 
also (unabhängig von OCR1A) wenn ein Byte überläuft?
OP
Nachrechner schrieb:
> Hab mal eine kleine Demo angehängt.

Vielen, vielen Dank! Es geht :)

In meinem Code fehlte aktuell nur wieder das   TCCR1A = 0;

Für die Nachwelt. Folgender Code funktioniert nun:
1
  TCCR1A = 0;  // bei Arduino wichtig
2
  TCCR1B = 0;
3
  TCCR1B = (1<<WGM12) | (1<<CS10) | (1<<CS12);
4
  TIMSK1 = (1<<OCIE1A);
5
  OCR1A = 0x0002; 
6
  TIFR1 |= (1<<OCF1A);

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 7 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.