Simpler Timer-CTC-Code funktioniert nicht

#5739420
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 :)
Gast #5739450
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;
Gast #5739461
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():
#5739467
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.
Gast #5739588
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.
Angehängte Dateien:
#5739592
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?
#5739601
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);

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren