Timer Interrupt Anfängerprobleme

OP #4491205
Lesenswert?

Moin,

also erst mal Hallo zusammen.

Zu meinem Problem habe ich schon viel gegoogelt, aber so richtig komme 
ich da nicht weiter.

Ich möchte einen ganz einfachen Timer-Interrupt auslösen. Habe dazu auch 
das Datenblatt meines ATmega328 zur Hand. Der Code sieht so aus.
1
//ARDUINO UNO Board
2
//Atmega328P 16MHz
3
//Arduino IDE 1.6.6
4

5
void setup() {  
6
  //Portkonfiguration
7
  DDRB |= (1<<5);   //PB5 Ausgang   ArduinoUNO-PIN 13   Chip-PIN 19 (LED)
8
  DDRD |= (1<<6);   //PD6 Ausgang   ArduinoUNO-PIN 6    Chip-PIN 12 (OC0A)
9

10
  //Timerkonfiguration Timer0
11
  TCNT0 = 0;
12

13
  TCCR0A |= (1<<WGM01);   //CTC - Mode
14

15
  TCCR0B |= (1<<CS00) | (1<<CS02);  //PRESCALER 1/1024
16

17
  OCR0A = 250;   //Compare match value 250 (8-Bit-Register)
18
  
19
  TIMSK0 |= (1<<OCIE0A);  //Interrupt on compare match
20

21
  sei(); 
22
}
23

24
ISR (TIMER0_COMPA) {
25
  PORTD ^= (1<<6);
26
  PORTB |= (1<<5);
27
}
28

29
void loop() {
30

31
}

Der Timer soll einfach bis 250 hochlaufen und dann die entsprechende ISR 
aufrufen. Aber er springt nicht in die ISR rein.

Wenn ich den Timer mit 'Serial.println(TCNT0)' auslese, wird nur '0' 
angezeigt. Der Timer zählt nicht hoch.

Jemand 'ne Idee?

Gruß Maddin
OP #4491299
Lesenswert?

Also die void setup() wird schon automatisch gestartet. Die 
Portkonfiguraton wird ja ausgeführt. Kann man ja einfach testen.

Timer1 klappt auch nicht. Liefert die gleichen Symptome. Der Witz ist, 
wenn ich die CS-Bits und TIMSK0 nicht setze, also den ganzen Kram 
auskommentiere, dann zählt der Zähler wieder.

Im Datenblatt steht TIMER0_COMPA, habe es aber auch mit 
TIMER0_COMPA_vect probiert, weil ich dass in diversen Tutorials auch so 
gesehen habe.

Ich kann da quasi wild auf den Tasten klimpern, ohne Effekt.
#4491319
Lesenswert?

Martin K. schrieb:
> Der Witz ist,
> wenn ich die CS-Bits und TIMSK0 nicht setze, also den ganzen Kram
> auskommentiere, dann zählt der Zähler wieder

So witzig ist das gar nicht. Zum einen benutzt das Arduinosystem 
offenbar den Timer. Wurde jedenfalls oben so gesagt. Ich kenn das Ding 
nicht weiter. Also muß ich annehmen, daß das richtig ist. Zum anderen 
hast du einen falschen Interrut Vektor, genau genommen hast du damit 
keine ISR. Ohne ISR macht der Controller einen Reset, wenn er da 
reinspringen will.

Jetzt mach mal das hier:
1
void setup()
2
{  
3
  //Portkonfiguration
4
  DDRB |= (1<<5);   //PB5 Ausgang   ArduinoUNO-PIN 13   Chip-PIN 19 (LED)
5
  DDRD |= (1<<6);   //PD6 Ausgang   ArduinoUNO-PIN 6    Chip-PIN 12 (OC0A)
6

7
  TCCR2A |= (1<<WGM21);   //CTC - Mode
8
  TCCR2B |= (1<<CS22) | (1<<CS21) | (1<<CS20);  //PRESCALER 1/1024
9
  OCR2A = 250;   //Compare match value 250 (8-Bit-Register)
10
  TIMSK2 |= (1<<OCIE2A);  //Interrupt on compare match
11
  sei(); 
12
}
13

14
}
15

16
ISR (TIMER2_COMPA_vect) 
17
{
18
  PORTD ^= (1<<6);
19
  PORTB |= (1<<5);
20
}
21

22
void loop()
23
{
24
}

Und dann berichtest du, ob das funktioniert.
OP #4491323
Lesenswert?

An sowas hatte ich auch schon gedacht. Hab aber keine Ahung, wie man das 
beeinflussen kann. Ich habe das ganze auch schon an einem zweiten 
Arduino UNO gestestet. Kommt genau das gleiche raus.

Was mir auch aufgefallen ist, dass der Timer1, der ja ein 16-Bit Timer 
ist, auch nur bis 255 zählt.

Wenn ich im Setup nicht konfiguriere (ausser einem Port z.B.), und 
einfach nur TCNT1 auslese, kommt der nicht über die 255 raus.

Ich habe sogar TCNT1L und TCNT1H einzeln ausgelesen. TCNT1H bleibt immer 
auf 0.

Aber immerhin läuft da was, wenn nichts konfiguriert ist.

Ich habe schon den Verdacht, dass das mit der Arduino IDE nicht geht, 
warum auch immer.

Ich habe es auch schon mit folgender Schreibweise in der Arduino IDE 
versucht:
1
#include <avr/io.h>
2
#include <avr/interrupt.h>
3

4
ISR (TIMER0_COMPA_vect) {
5
  PORTD ^= (1<<6);
6
  PORTB |= (1<<5);
7
}
8

9
int main(void) {
10
  //Portkonfiguration
11
  DDRB |= (1<<5);   //PB5 Ausgang   ArduinoUNO-PIN 13   Chip-PIN 19 (LED)
12
  DDRD |= (1<<6);   //PD6 Ausgang   ArduinoUNO-PIN 6    Chip-PIN 12 (OC0A)
13

14
  //Timerkonfiguration Timer0
15
  cli();  //Disable Interrupt
16

17
  TCNT0 = 0;
18

19
  TCCR0A |= (1<<WGM01);   //CTC - Mode
20

21
  TCCR0B |= (1<<CS00) | (1<<CS02);  //PRESCALER 1/1024
22

23
  OCR0A = 250;   //Compare match value 250 (8-Bit-Register)
24
  
25
  TIMSK0 |= (1<<OCIE0A);  //Interrupt on compare match
26

27
  sei();
28

29
  while(1) {
30
  }
31
}

Macht beim kompilieren keine Probleme, klappt aber trotzdem nicht.
#4491341
Lesenswert?

Martin K. schrieb:
> mit Timer 2 klappts.

Siehste. Timer 0 wird vom dann wohl vom Arduinosystem verwendet.

Und jetzt nochmal so:
1
void setup()
2
{  
3
  //Portkonfiguration
4
  DDRB |= (1<<5);   //PB5 Ausgang   ArduinoUNO-PIN 13   Chip-PIN 19 (LED)
5
  DDRD |= (1<<6);   //PD6 Ausgang   ArduinoUNO-PIN 6    Chip-PIN 12 (OC0A)
6

7
  TCCR1B |= (1<<CS12) | (1<<CS10) | (1<<WGM12);  //PRESCALER 1/1024
8
  OCR1A = 250;   //Compare match value 250 (8-Bit-Register)
9
  TIMSK1 |= (1<<OCIE1A);  //Interrupt on compare match
10
  sei(); 
11
}
12

13
}
14

15
ISR (TIMER1_COMPA_vect) 
16
{
17
  PORTD ^= (1<<6);
18
  PORTB |= (1<<5);
19
}
20

21
void loop()
22
{
23
}

Das geht nämlich auch mit Timer 1. Nur nicht mit Copy & Psste.
OP #4491348
Lesenswert?

@Mein grosses Vorbild
ne.. klappt leider nicht.

Aber so klappts mit Timer1
1
void setup()
2
{  
3
  //Portkonfiguration
4
  DDRB |= (1<<5);   //PB5 Ausgang   ArduinoUNO-PIN 13   Chip-PIN 19 (LED)
5
  DDRD |= (1<<6);   //PD6 Ausgang   ArduinoUNO-PIN 6    Chip-PIN 12 (OC0A)
6

7
  TCCR1A |= (1<<WGM11);   //CTC - Mode
8
  //TCCR1B |= (1<<CS12) | (1<<CS11) | (1<<CS10);  //PRESCALER 1/1024
9
  OCR1A = 250;   //Compare match value 250 (8-Bit-Register)
10
  TIMSK1 |= (1<<OCIE1A);  //Interrupt on compare match
11
  sei(); 
12
}
13

14
ISR (TIMER1_COMPA_vect) 
15
{
16
  PORTD ^= (1<<6);
17
  PORTB |= (1<<5);
18
}
19

20
void loop()
21
{
22
}

Die CS-Bits auskommentiert, und geht.

Schon seltsam...
#4491353
Lesenswert?

Martin K. schrieb:
> Also wenn ich das jetzt mit TIMER1 mache und die CS-Bits nicht setze,
> klappt es jetzt auch. Allerdings habe ich jetzt kein 50% Rechteck,
> sondern irgendwas im Verhältnis 5:1 oder so.
>
> Egal. Klappt auf jeden Fall.

Nein, da muß 1:1 rauskommen. Entweder es geht oder es geht nicht.

Geht ist 100%, <100% ist geht nicht.

Geben wir dem Arduino die Schuld. Initialisiere den Timer mal so:
1
  TCCR1B = (1<<CS12) | (1<<CS10) | (1<<WGM12);  //PRESCALER 1/1024
2
  OCR1A = 250;   //Compare match value 250 (8-Bit-Register)
3
  TIMSK1 = (1<<OCIE1A);  //Interrupt on compare match
OP #4491355
Lesenswert?

Ja.. ich werd das jetzt mal Stück für Stück durchkauen.

Eines weiss ich schon. Es muss auf jeden Fall

ISR (TIMER1_COMPA_vect)

lauten.

ISR (TIMER1_COMPA)

geht nicht.

Aber das hat jetzt mit den CS-Bits nichts zu tun.

Ist aber dann schon ein Fehler im Datenblatt. Da steht
ISR (TIMER1_COMPA).
#4491380
Lesenswert?

Martin K. schrieb:
> TCCR1A |= (1<<WGM11);   //CTC - Mode
>   //TCCR1B |= (1<<CS12) | (1<<CS11) | (1<<CS10);  //PRESCALER 1/1024
>   OCR1A = 250;   //Compare match value 250 (8-Bit-Register)
>   TIMSK1 |= (1<<OCIE1A);  //Interrupt on compare match
>   sei();

Was ist denn das? Das habe ich dir nicht so geschrieben!
Die Schuldzuweisung an den Arduino ziehe zurück.

Das kann nicht funktionieren. Programmieren ist kein Copy & Paste. Außer 
wenn du mein Konfigurationsbeispiel so nimmst, wie es ist.

Also, kopier das so rein, wie es ist. Dann funktioniert das auch.

Und dann guckst du dir die drei verschiedenen Konfigurationen an, 
findest die Unterschiede und siehst im Datenblatt nach, warum das so 
konfiguriert sein muß und nicht anders.

Martin K. schrieb:
> Es scheint wohl eine Rolle zu spielen, wie man den Timer initialisiert,
> also die Reihenfolge.

Nein, das ist vollkommen egal.
Gast #4491384
Lesenswert?

Das Arduino Zeugs benutzt die Timer für die Arduino Funktionen. Für 
millis() und delay() z.B. wird der Timer0 verwendet.

Probiers mal mit dem Tip:
chris schrieb:
> Setz doch einfach mal am Anfang von setup() alle Timerregister auf
> default Werte zurück (also auf 0).

Dann funktionieren nur, je nach Timer, einige Arduino Funktionen nicht 
mehr.
OP #4491390
Lesenswert?

Also die letzte Variante geht doch nicht so gut.
1
  TCCR1B = (1<<CS12) | (1<<CS10) | (1<<WGM12);  //PRESCALER 1/1024
2
  OCR1A = 250;   //Compare match value 250 (8-Bit-Register)
3
  TIMSK1 = (1<<OCIE1A);  //Interrupt on compare match

Da kann ich beim OCR1A einstellen was ich will. Da ändert sich nichts. 
Kann die Zeile auch komplett auskommentieren. Hab immer das gleiche 
Rechtecksignal. Läuft dann wohl im Normalmode und nicht im CTC Mode.

Das hier geht
1
  TCCR1A |= (1<<WGM11);   //CTC - Mode
2
  //TCCR1B |= (1<<CS12) | (1<<CS11) | (1<<CS10);  //PRESCALER 1/1024
3
  OCR1A = 500;   //Compare match value 250 (8-Bit-Register)
4
  TIMSK1 |= (1<<OCIE1A);  //Interrupt on compare match

Und der OCR1A - Wert beeinflusst auch das Signal. Was ja auch plausibel 
ist.
OP #4491395
Lesenswert?

ArduinoArduino schrieb:
> Dann funktionieren nur, je nach Timer, einige Arduino Funktionen nicht
> mehr.

Ja... da hab ich mal was zu gelesen. Man kann auch auf Kosten einer 
Arduinofunktionen mit anderen Einstellungen die PWM-Frequenz verändern. 
Bzw. für verschiedene PWM-AUsgänge unterschiedliche Frequenzen verwenden

Da gibts nen haufen Zeug zu lernen. Aber bin schon mal froh, dass das 
hier geht.
#4491402
Lesenswert?

Martin K. schrieb:
> Da kann ich beim OCR1A einstellen was ich will. Da ändert sich nichts.
> Kann die Zeile auch komplett auskommentieren. Hab immer das gleiche
> Rechtecksignal. Läuft dann wohl im Normalmode und nicht im CTC Mode.

Wahrscheinlich deshalb nicht, weil noch irgendwas im TCCR1A-Register 
steht.

Also:
1
TCCR1A = 0;

Das zweite ist irgendein PWM-Mode. Und das:

TCCR1B |= (1<<CS12) | (1<<CS11) | (1<<CS10);  //PRESCALER 1/1024

funktioniert nicht, weil das External Clock ist.

Ja, man muß alle Register zuweisen  bzw. die nicht verwendeten löschen.
OP #4491412
Lesenswert?

Ja stimmt. Da muss ich das WGM-Bit setzen, sonst klappt es nicht.

Also TCCR1A |= (1<<WGM11); muss bleiben, sonst kein CTC-Mode. Steht auch 
so im Datenblatt.

Aber die CS-Bits brauch ich nicht setzen. Wenn ich mich da an das 
Datenblatt halte, klappt es nicht.

Ich denke, ich werde mich mal näher mit dem Bootloader befassen. Habe so 
das Gefühl, dass die Einstellungen, die da gemacht werden, auch was 
damit zu tun haben, was geht oder nicht geht. Hab schon gelesen, das der 
öfter mal Probleme macht.
OP #4491420
Lesenswert?

Hier mal paar Angaben aus dem Datenblatt.


The Timer/Counter can be clocked by an internal or an external clock 
source. The clock source is selected by
the Clock Select logic which is controlled by the Clock Select (CS12:0) 
bits located in the Timer/Counter control
Register B (TCCR1B). For details on clock sources and prescaler, see 
”Timer/Counter0 and Timer/Counter1
Prescalers” on page 138.


Depending on the mode of operation used, the counter is cleared, 
incremented, or decremented at each timer
clock (clkT1). The clkT1 can be generated from an external or internal 
clock source, selected by the Clock Select
bits (CS12:0). When no clock source is selected (CS12:0 = 0) the timer 
is stopped. However, the TCNT1 value
can be accessed by the CPU, independent of whether clkT1 is present or 
not. A CPU write overrides (has priority
over) all counter clear or count operations.
Angehängte Dateien:
#4491470
Lesenswert?

Martin K. schrieb:
> Also TCCR1A |= (1<<WGM11); muss bleiben, sonst kein CTC-Mode. Steht auch
> so im Datenblatt.

Nein, das steht nicht im Datenblatt. TCCR1A muß 0 sein.

Martin K. schrieb:
> Aber die CS-Bits brauch ich nicht setzen. Wenn ich mich da an das
> Datenblatt halte, klappt es nicht.

Doch. Nur wenn du dich ans Datenblatt hältst.


TCCR1B |= (1<<CS12) | (1<<CS10);

Das ist anders als beim Timer 2.
OP #4491535
Lesenswert?

Naja, der Link...

F. F. schrieb:
> Timer und PWM:
> https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM

... ist schon hilfreich. Statt CTC wird hier Fast PWM genommen, und 
damit komme ich schon gut weiter. Zumindest habe ich jetzt sinnvollen 
Einfluss auf den OCRx und der Prescaler macht auch was er soll. Auch 
wenn die Ergebnisse hier auf dem Oszi noch nicht immer plausibel sind. 
Aber es geht voran.
OP #4494702
Lesenswert?

So... das Problem ist gelöst.

Ich hab mal die Register für Timer1 unmittelbar nach dem Reset 
ausgelesen, also noch bevor irgendwas programmiert wird. Da kommt dann 
das hier raus.

Timer1-Register nach RESET
==========================

TCCR1A-Register
---------------
COM1A1  COM1A0  COM1B1  COM1B0  WGM11  WGM10
0  0  0  0  0  1

TCCR1B-Register
---------------
ICNC1  ICES1  WGM13  WGM12  CS12  CS11  CS10
0  0  0  0  0  1  1

TCCR1C-Register
---------------
FOC1A  FOC1B
0  0

TIMSK1-Register
---------------
ICIE1  OCIE1B  OCIE1A  TOIE1
0  0  0  0

Wenn im Register TCCR1B CS11 und CS10 gesetzt sind, bedeutet das 
Prescaler = 1/64. Für Prescaler 1/1024 müsseten die Bits do gesetzt 
werden.

CS12=1, CS11=0, CS10=0

Wenn ich aber nur das CS12-Bit auf 1 setze, die anderen Bits aber nicht 
lösche, dann kommt da 1 1 1 raus. Und da heist es im Datenblatt:

"External clock source on T1 pin. Clock on rising edge."

Gleiches Problem für den TCCR1A-Register. Da steht nacht dem Start das 
Bit WGM10 auf 1. Wenn ich jetzt das WGM12-Bit für den CTC-Modus auf 1 
setze ohne WGM10 zu löschen, dann habe ich einen unerlaubten Zustand. 
Dieser Modus ist laut Datenblatt als "Reserved" deklariert.

Damit ist dann auch klar, warum der CTC-Mode nicht geklappt hat.

Vorher alles löschen, dann Bits richtig setzen, klappt.

Fall gelöst.
Gast #4494797
Lesenswert?

> Ich hab mal die Register für Timer1 unmittelbar
> nach dem Reset ausgelesen
Eben nicht. Nach dem Reset stehen die Defaultwerte, wie sie im 
Datenblatt nachzulesen sind, drin.

> also noch bevor irgendwas programmiert wird.
Davor befindet sich im vorliegenden Fall die Arduino-Ebene, und genau 
das war hier das Problem; eine korrekte, also vollständige 
Initialisierung löst es.
#4494817
Lesenswert?

Martin K. schrieb:
> Ich hab mal die Register für Timer1 unmittelbar nach dem Reset
> ausgelesen, also noch bevor irgendwas programmiert wird. Da kommt dann
> das hier raus.
> ....

Nach Datenblatt müsste aber in diesen Registern nach Reset bei allen 
Bits 0 drinstehen ("Initial Value").
Also entweder
- läuft da doch ein Stück Programm, das hier was geändert hat, z.B. das 
Arduino-"Betriebssystem", das Du nicht siehst,
- hat kein HW-Reset stattgefunden (SW-Reset, also Sprung nach Adresse 0, 
macht keinen vollständigen Reset),
- hast Du was Falsches ausgelesen ;-(

Gruß Dietrich
OP #4495675
Lesenswert?

Es steht eben nicht von Anfang an überall 0, so wie im Datenblatt 
angegeben. Deshalb ja auch das Problem. Das liegt daran, dass der 
Bootloader ein paar Dinge konfiguriert. Der Bootloader richtet eine 
PWM-Funktion ein, und zwar eben genau mit den Registern, die nach dem 
Start/Reset auf 1 gesetzt werden. Wenn ich die lösche, und den CTC-Mode 
aktiviere, steht mir PWM an dem entsprechenden Ports auch nicht mehr zur 
Verfügung, welche mit dem entsprechenden Timer arbeiten.

Das gilt für Timer0 und für Timer1 in der Ausgangskonfiguration.

Der Bootloader hat mich hier in die Irre geführt.

Wenn ich nach dem Start alle Bit des Timers lösche und dann so setze, 
wie es im Datenblatt steht, dann klappts ja auch.

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