Forum: Mikrocontroller und Digitale Elektronik Arduino: Timer 1: Problem mit Output Compare Register


von Werner M. (wmauss)


Angehängte Dateien:

Lesenswert?

Hallo Zusammen,

ich habe den nachfolgenden Sketch für den Arduino Nano V3 programmiert.
Dabei dient das brücken gleichgerichtete und rechteckgewandelte 50Hz 
Netzspgssignal als Taktsignal.
Während eines Zykluses (der im Beispiel unten aus 10 Rechteckperioden 
besteht) bleibt das Ausgangssignal auf High bis die voreingestellte 
Anzahl von Halbwellen gezählt wurde, dann schaltet der µC den GPO auf 
Low.
Also quasi nichts anderes als eine PWM, die halt synchron zur 
Netzfrequenz läuft.

Grundsätzlich funktioniert das auch. Das einzige Problem besteht darin, 
daß der GPO frühestens nach zwei Taktzyklen auf Low geht, obwohl die 
Vergleichszahl in OCR1B nur um 1 größer ist als die über TCNT1 
definierte Startzahl des Zählers.
Ich hätte erwartet dass unter diesen Bedingungen der OCR1B Interrupt 
nach einem Taktzyklus kommt, sprich der GPO nach einem Takt auf Low 
wechselt.

Hat jemand eine Idee wieso das so ist und wie ich das korrigieren kann?

Vielen Dank und viele Grüße

Werner
1
#define osziPin 4            // Pin zum prüfen ob Wellenpaketsteuerung funzt
2
#define taktPin 5            // 100Hz Rechtecksignal Eingang 
3
4
void setup() 
5
{
6
  pinMode(osziPin, OUTPUT); // Pin an dem das Signal für die Wellenpaketsteuerung (WPS) ansteht
7
  pinMode(taktPin, INPUT);  // 100Hz Rechtecksignal aus Netzspg. erzeugt liegt hier an
8
9
  // Timer1: 
10
  noInterrupts();           // Alle Interrupts temporär abschalten
11
  // Es folgen Einstellungen der Timer/Counter Control Register
12
  TCCR1A = 0;               // Keine weitere Programmierung von TCCR1A -> Alle bits =0, kein PWM, keine IR Ausgabe über IO Pins 
13
  TCCR1B = 0;               // TCCR1B bestimmt die Taktquelle (int/ext.: Pin D5) und die Prescaler Einstellung 
14
  // Konfiguration über die drei Bits CS10..CS12=1. Bedeutet: 1. Kein prescaling, 2. ext. Taktquelle auf 3. steigende Flanke
15
  TCCR1B |= (1 << CS10);
16
  TCCR1B |= (1 << CS11);
17
  TCCR1B |= (1 << CS12);
18
19
  TIMSK1 |= (1 << OCIE1B); // Timer compare IR aktivieren
20
  TIMSK1 |= (1 << TOIE1);  // Overflow IR aktivieren
21
  
22
  TCNT1 = 65526;             // Register mit 65526 initialisieren, so dass 10 Zyklen bis Überlauf (65.536) verbleiben
23
  OCR1B = 65527;             // Output Compare Register B vorbelegen, so dass für Test 6 Zyklen bis Überlauf. Wert wird später dynamisch eingestellt.  
24
  digitalWrite(osziPin, HIGH);  // Zu Beginn wird WPS Steuerungssignal high gesetzt.  
25
  interrupts();             // Alle Interrupts einschalten
26
}
27
// Interrupt Behandlungsroutine für den Timer Overflow
28
ISR(TIMER1_OVF_vect)
29
{
30
  TCNT1 = 65526;             // Register wieder auf Startwert setzen (10 unter overflow)
31
  digitalWrite(osziPin, HIGH);    
32
}
33
34
// Interrupt Behandlungsroutine für den OCR1B compare match Fall
35
  ISR(TIMER1_COMPB_vect)
36
 {
37
  digitalWrite(osziPin, LOW);   
38
} 
39
40
void loop() {
41
  // put your main code here, to run repeatedly:
42
}

von S. Landolt (Gast)


Lesenswert?

> Hat jemand eine Idee wieso das so ist ...

"If TCNT equals OCR1x the comparator signals a match. A match will set 
the Output Compare Flag (OCF1x) at the next timer clock cycle."

> ... und wie ich das korrigieren kann?

Indem Timer1 auf einen PWM-Modus gesetzt wird?

von Werner M. (wmauss)


Lesenswert?

Hallo S. Landolt,

vielen Dank für den Hinweis, den habe ich offensichtlich überlesen.
Dann werde ich mir den PWM Mode mal näher anschauen. Ich bin bisher 
davon ausgegangen dass eine PWM Zyklusdauer von 1s zu niedrig sein wird. 
1s wäre nötig, um Sinuswellen im Nulldurchgang schalten und dabei eine 
Auflösung von 2% realisieren zu können (Wellenpaket Steuerung).
Allerdings habe ich das noch nicht konkret im Datenblatt nachgelesen, 
womöglich ist das kein Problem.

Grüße
Werner

von Dieter F. (Gast)


Lesenswert?

Werner M. schrieb:
> Dann werde ich mir den PWM Mode mal näher anschauen.

Schau Dir besser mal den externen Interrupt an :-)

von S. Landolt (Gast)


Lesenswert?

> Zyklusdauer von 1s

Bei diesen langen Zeiten könnte man es auch komplett 'von Hand' machen, 
also sogar ohne Timer (das war es vielleicht, was Dieter F. meinte), 
einfach eine Variable im Interrupt hochzählen und entsprechend schalten.
  Trotzdem würde ich einen PWM-Modus vorziehen (d.h. ganz ohne 
Interrupt), z.B. 14 mit Top=ICR1; allerdings erreicht man dabei nicht so 
ohne weiteres einen der Randwerte, es fehlt 0 % oder 100 % - kommt jetzt 
auf die genauen Anforderungen an.

von Werner M. (wmauss)


Lesenswert?

Danke S. Landolt.
Ich habe das Kapitel External Interrupts überflogen aber erst Dank 
Deines Hinweises verstehe ich was Dieter F. gemeint haben könnte.

Manuell programmieren hatte ich zuallererst vor, als ich noch keinen 
Arduino besaß und noch nicht ins Datenblatt geschaut habe. Jetzt ist mir 
der ATmega mit seinen Countern dafür eigentlich zu schade.

Eben ist mir zudem klar geworden dass die kleine Unschönheit von zwei 
Taktzyklen minimaler Einschaltdauer oberhalb vom Auszustand kein Problem 
ist.
Wegen des brücken gleichgerichteten Sinussignals mit anschließender 
Rechteckwandlung wird eine 50Hz Sinusschwingung ja von zwei 100Hz 
Rechteckperioden repräsentiert. Somit kann ich trotzdem minimal eine 
50Hz Periode schalten.

Ich möchte ein elektrisches Heizkabel steuern und dazu nur volle 
Sinuswellen nutzen (Phasenan-/-abschnitt finde ich old School:-), 
außerdem habe ich gelesen dass viele EVUn keine leistungsstarken 
Phasenanschniitssteuerungen mehr erlauben).
Eine Änderung in 1% Schritten ist dafür vermutlich eh over the Top.

Vielen Dank für Eure hilfreichen Hinweise!

Viele Grüße
Werner

von Dieter F. (Gast)


Lesenswert?

S. Landolt schrieb:
> das war es vielleicht, was Dieter F. meinte

Nach dem externen Interrupt x (interne 16 oder so MHz Takte / Prescaler 
z ) warten, bis y passiert.

von Dieter F. (Gast)


Lesenswert?

Werner M. schrieb:
> Eben ist mir zudem klar geworden dass die kleine Unschönheit von zwei
> Taktzyklen minimaler Einschaltdauer oberhalb vom Auszustand kein Problem
> ist.

Dein Fehler ist, das Du Dich auf den externen Takt (50 Hz ?) stützt um 
damit/danach (als Counter) zu schalten - ergo bist Du viel zu langsam.

von Werner M. (wmauss)


Lesenswert?

Du meinst, ich erwische den Nulldurchgang der Sinuswelle nicht mehr?
Das ist egal, dann erwische ich eben die nächste. Denselben Verzug gibt 
es ja auch beim nächsten schalten, so dass die Schaltvorgänge konstant 
versetzt sind.
Ich verwende ein Solid-State Relais mit Nullspannungsdetektor, das 
schaltet ohnehin nur im Nulldurchgang.

von S. Landolt (Gast)


Lesenswert?

> Solid-State Relais mit Nullspannungsdetektor

Also dann löst sich ja ohnehin alles in Wohlgefallen auf, dieser externe 
Netztakt wird überflüssig, einfach den Timer1 mit einer PWM laufen 
lassen.

von S. Landolt (Gast)


Lesenswert?

PS:
Keinerlei Interrupt; für eine Heizanwendung kann man den maximalen 
Vorteiler, d.h. 1024, nehmen.

von S. Landolt (Gast)


Lesenswert?

Der Arduino Nano läuft mit 16 MHz, oder? Also Vorschlag: Timer1 im Modus 
14, um auf die gewünschte Zeit von 1.0 s zu kommen, den Vorteiler auf 
1024 und ICR1 auf 15625-1 setzen (ich selbst würde 4.0 s vorziehen, 
folglich ICR1=62500-1).  Den Tastgrad, wie gehabt, mit OCR1A oder B 
einstellen, Ausgang ist dann OC1A oder B.

von Werner M. (wmauss)


Lesenswert?

Ähem, hüstel, ja, da lässt sich wenig dagegen einwenden. Genau genommen 
gar nichts.

Vielen Dank nochmal an Euch!

Viele Grüße
Werner

: Bearbeitet durch User
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.