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


von Michael N. (garril)


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. (Gast)


Lesenswert?

Ich vermisse den Code, welcher den (CTC) Modus des Timers konfiguriert.

von Michael N. (garril)


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. (Gast)


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;

von Stefan F. (Gast)


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. (Gast)


Lesenswert?

Vielleicht musst du das Compare-Flag bei der Initialisierung löschen:

TIFR1 |= (1<<OCF1A);

von K. S. (the_yrr)


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. (Gast)


Lesenswert?

K. S. schrieb:
> Keine Ahnung welcher das ist

Das ist der Timer 0

von Nachrechner (Gast)


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)


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:

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)


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)


Lesenswert?

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

Lies noch den Beitrag vorher.

von Michael N. (garril)


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. (Gast)


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.

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.