Forum: Mikrocontroller und Digitale Elektronik Simpler Timer-CTC-Code funktioniert nicht


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von Michael N. (garril)


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

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Ich vermisse den Code, welcher den (CTC) Modus des Timers konfiguriert.

von Michael N. (garril)


Bewertung
0 lesenswert
nicht lesenswert
Bin leider seit 5 Jahren raus und tue mir gerade echt schwer. Dachte das 
macht der hier:
TCCR1B |= (1<<WGM12);
Oder verwechsle ich etwas?

von Stefan ⛄ F. (stefanus)


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

: Bearbeitet durch User
von Stefan ⛄ F. (stefanus)


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

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Vielleicht musst du das Compare-Flag bei der Initialisierung löschen:

TIFR1 |= (1<<OCF1A);

von K. S. (the_yrr)


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

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
K. S. schrieb:
> Keine Ahnung welcher das ist

Das ist der Timer 0

von Nachrechner (Gast)


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

von Nachrechner (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Nachrechner schrieb:
> (16MHz  1024  0xAAAA = 0.358 Hz)
--------^-----^------ da sollten Schrägstriche stehen
die das System als was anderes als Text interpretiert

von Nachrechner (Gast)


Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Stefanus F. schrieb:
> Versuche es mal mit:  TCCR1B = 0;

Ja die Register müssen gelöscht werden - da steht vorher was
drin was man nicht brauchen kann.

Mit der Original-Initialisierung wird kein Interrupt ausgelöst.
Hab mal eine kleine Demo angehängt.

Einfach mal den Seriellen Monitor benutzen.

von Michael N. (garril)


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

von Nachrechner (Gast)


Bewertung
0 lesenswert
nicht lesenswert
Michael N. schrieb:
> Weiß langsam nicht mehr weiter.

Lies noch den Beitrag vorher.

von Michael N. (garril)


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

von Stefan ⛄ F. (stefanus)


Bewertung
0 lesenswert
nicht lesenswert
Michael N. schrieb:
> In meinem Code fehlte aktuell nur wieder das   TCCR1A = 0;

Das wollte ich eigentlich auch empfehlen, hatte mich dabei aber 
vertippt. Mea culpa.

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]
  • [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.