Forum: Compiler & IDEs Timer 1 OCR1A und OCR1B gleichzeitig verwenden


von Philipp P. (putzer_philipp)


Lesenswert?

Hallo Allerseits,

ich zerbrech mir grad den Kopf über einer ziemlich einfachen Aufgabe. 
Ich möchte gerne mit dem Timer 1 zwei Interrupts auslösen, einmal bei 
OCR1A match und einmal mit OCR1B match. Mit OCR1A hauts einwandfrei hin, 
nur mit B geht gar nix. Zum messen toggle ich zwei verschieden Ports in 
den ISRs. Was mir aber schon nicht komisch vorkommt, dass es im DB unter 
den Modi nur einen gibt der bis OCR1A zählt, und nix mit OCR1B?

mein Code: (Mega8 16MHz)

TCCR1B|= (1<<CS11)|(1<<WGM12);  //Systemtakt durch 8 teilen
OCR1A = 1000;
OCR1B = 10000;
TIMSK|=(1<<OCIE1A)|(1<<OCIE1B);

DDR_OUT|=(1<<CLK)|(1<<ENABLE)|(1<<DIRECTION);

sei();
...

ISR(TIMER1_COMPA_vect)
{    PORT_OUT^=(1<<CLK);
}

ISR(TIMER1_COMPB_vect)
{    PORT_OUT^=(1<<ENABLE);
}

Der A läuft, nur der B tut gar nix, außer ich setze beide OCRs auf den 
gleichen Wert? Komischerweise kommt beim B auch etwas raus, wenn ich den 
A auf 10000 und B auf 1000 setze???

Kann mir jemand sagen, wo mein Denkfehler liegt?

Danke
Philipp

von Justus S. (jussa)


Lesenswert?

Wenn ich mich nicht irre, betreibst du den Timer im "Clear Timer on 
Compare Match"-Modus...

von Stefan B. (Gast)


Lesenswert?

Hatte ich auch schon mal. Ich bin davon ausgegangen: Im DB nicht für 
OCR1B beschrieben, also geht CTC dafür nicht.

von Stefan E. (sternst)


Lesenswert?

Philipp Putzer schrieb:

> Kann mir jemand sagen, wo mein Denkfehler liegt?

Überlege mal, wie der CTC-Mode konkret mit einem Compare-Match 
funktioniert. Und dann versuchst du mal, es auf einen Timer und zwei 
Compare-Matches zu erweitern. Dann wirst du es schon merken.

von Philipp P. (putzer_philipp)


Lesenswert?

hh!, genau, das isses! Man klar. Der zählt mir am Ende immer nur bis zum 
OCR1A, oder bis zu dem niedrigeren halt!

Mal schaun was sich da machen lässt!

von Justus S. (jussa)


Lesenswert?

Philipp Putzer schrieb:
> oder bis zu dem niedrigeren halt!

laut Datenblatt ist der TOP-Wert im CTC-Modus immer OCR1A

von Stefan W. (swessels)


Lesenswert?

Moin,

kleiner Denkanstoss:

Nimm FastPWM mit TOP in ICR1.

Gruß,
Stefan

von Christian B. (luckyfu)


Lesenswert?

nimm doch den "Mode 12" (Datenblatt S. 99) WGM13 und WGM 12 = 1, WGM 11 
und WGM 10 = 0
mit ICR1 = x kannst du für x deinen CTC wert eingeben und hast für den 
fall daß OCR1A und OCR1B < ICR1 beide comparewerte zur verfügung.

von Philipp P. (putzer_philipp)


Lesenswert?

ok, das werd ich mal versuchen. Momentan verwende ich den Timer 1 und 
den Timer 2 im CTC Modus. Nur wenn ich das Programm erweitere und noch 
mehr timer brauchen sollte kann ich auf den ICR vom T1 zurückgreifen und 
kann den T2 für sonstwas verwenden

Vielen Dank für die Hilfe!

Gruß
Philipp

von Peter D. (peda)


Lesenswert?

Das mit dem ICR1 ist Quatsch, damit sind die beiden Compares nicht 
unabhängig.

Du mußt in den Interrupts einfach nur OCR1A/B += Intervall_A/B; machen 
und aus die Maus.
Der Timer muß durchlaufen ohne Clear on Compare, sonst geht die Addition 
nicht.

Und den Overflow- und den Capture-Interrupt kannst Du dann auch noch 
uneingeschränkt benutzen.


Peter

von Christian B. (luckyfu)


Lesenswert?

Peter Dannegger schrieb:
> Das mit dem ICR1 ist Quatsch, damit sind die beiden Compares nicht
> unabhängig.
>
> Peter

ich will dir nicht gern wiedersprechen, ich tus aber dennoch, da ich das 
genau in der kombination bei mir im aktuellen projekt problemlos laufen 
habe:

(ist allerdings bei mir auf basis atmega1280, sollte aber egal sein, da 
der 16 bit timer ja gleich aufgebaut ist, hier mal meine codeschnipsel:

initialisierung:
// Timer 5 CTC Mode aktivieren +  Vorteiler (256) setzen
TCCR5B |= (1<<WGM52) | (1<<WGM53)| (1<<CS52);
// Vergleichswert setzen
ICR5 = 26473;    //39062 Schritte ->ca. 1s (39062*25.6µs)
// Interrupt wird durch Compare Match (B und C) ausgelöst
TIMSK5 = (1<<OCIE5B) | (1<<OCIE5C);
//ISR wird ausgelöst


und hier die zugehörigen ISR:

ISR(TIMER5_COMPB_vect)
{
  if(Display_Verzoegerung_Temp >= Display_Verzoegerung)
  {
    // Display wird abgedunkelt auf Schlafmodus (Pin PH3!)
    Displaybeleuchtung = 0;
    Display_Verzoegerung_Temp = 0;  //Variable rücksetzen

  }
  else if(Displaybeleuchtung == 1)
  {
    Display_Verzoegerung_Temp ++;  //Wenn Display nicht im Schlafmodus 
-> Variable Hochzählen, sonst nichts tun
  }
  else
  {
    OCR5B = ICR5 + 10;   //Comparewert auf einen Wert jenseits des 
Zählbereichs legen und damit Interrupt stoppen (Alternative zum 
Demaskieren)
  }
}
ISR(TIMER5_COMPC_vect)
{
  if(Hauslicht_Aktiv == 2)
  {
    if(Hauslicht_Zeit_Temp >= Hauslicht_Zeit)
    {
      //Hauslicht wieder abschalten
      Hauslicht_Aktiv = 3;
      Hauslicht_Zeit_Temp = 0;
    }
    else
    {
      Hauslicht_Zeit_Temp ++;
    }
  }
  else
  {
    OCR5C = ICR5 + 10; //Comparewert auf einen Wert jenseits des 
Zählbereichs legen und damit Interrupt stoppen (Alternative zum 
Demaskieren)
  }

}

sinn des ganzen ist bei mir: einen 1 sec timer im hintergrund laufen zu 
haben. wenn ich dann eine der beiden aktionen ausführen will starte ich 
diese mit den folgenden zeilen im hauptprogramm:

OCR5B = TCNT5;

oder

OCR5C = TCNT5;


da der code bei mir funktioniert gehe ich davon aus, daß er richtig ist. 
ich habe somit (bei meinem controller) die möglichkeit 3 "virtuelle" 
timer mit der selben zeitbasis zu starten welche aber unabhängig 
voneinander laufen. so wie ichs verstanden habe will er das so machen. 
wenn er nat. COMPA 10 mal häufiger als COMPB ausführen möchte geht dies 
so nicht direkt.


ich habe den jeweiligen interrupt hier gestoppt, indem ich den 
comparewert über den CTC wert lege. das hat den hintergrund, daß ich 
eben nur die eine zuweisung benötige um den timer wieder zu starten und 
nicht den interrupt noch maskieren muss.  aber das nur am rande, den 
Compare A habe ich noch frei, da ich ihn derzeit noch nicht benötige.

von Karl H. (kbuchegg)


Lesenswert?

Christian B. schrieb:
> Peter Dannegger schrieb:
>> Das mit dem ICR1 ist Quatsch, damit sind die beiden Compares nicht
>> unabhängig.
>>
>> Peter
>
> ich will dir nicht gern wiedersprechen, ich tus aber dennoch, da ich das
> genau in der kombination bei mir im aktuellen projekt problemlos laufen
> habe:

Du hast für alle deine Interrupts dieselbe Zeitbasis (1 Sekunde) und 
wozu du in deiner Anwendung einen Compare-Match benötigst, ist mir 
ehrlich gesagt schleierhaft. Deine 2 virtuellen 1-Sekunden Timer hättest 
du auch ganz bequem in eine einzige ISR packen können. Aber gut, so 
kannst du durch den Wert im OCR Register die Funktionalität ein bzw. 
ausschalten. Du benutzt sie Quasi als Flag, der tatsächliche OCR Wert 
ist mehr oder weniger uninteressant. Hauptsache im ICR1 Bereich bzw. 
ausserhalb.

Aber versuch doch mal das ganze so aufzusetzen, dass der Compare Match A 
alle 300 Take und der Compare Match B alle 500 Takte kommt. Da wirst du 
Schiffbruch erleiden :-) Das geht nur so, wie Peter das vorschlägt.

von Christian B. (luckyfu)


Lesenswert?

das mag sein, aber wie ich bereits schrieb: ich habs so verstanden, daß 
er eine zeitbasis für beide verwenden will. andernfalls sieht die sache 
natürlich auch anders aus.

wobei, wenn mans geschickt anstellt geht auch das, die endaktion die die 
beiden compares bei mir ausführen wird einmal nach ca 20 sek (B) und 
einmal nach ca, 1,5 min (C) ausgeführt. beide werte sind aber 
sekundengenau variabel einstellbar bis reichlich 4 minuten.

von Karl H. (kbuchegg)


Lesenswert?

Christian B. schrieb:
> das mag sein, aber wie ich bereits schrieb: ich habs so verstanden, daß
> er eine zeitbasis für beide verwenden will.

Das geht leider aus dem Ursprungsposting nicht klar hervor.
Soll es etwas mehr PWM artiges sein oder sollen die Comparematch 
tatsächliche Taktzähler sein.

Ersteres geht mit dem Copmare Match Registern wenn man darauf achtet, 
dass der Endwert des Timers nicht von einem der beiden abhängt, also so 
wie du das hast.
Letzteres geht aber nur mit der vom Peter vorgeschlagenen Methode 
vernünftig. Allerdings kann man auch ersteres damit machen :-)

> wobei, wenn mans geschickt anstellt geht auch das,
> die endaktion die die beiden compares bei mir ausführen
> wird einmal nach ca 20 sek (B) und einmal nach ca, 1,5 min (C)
> ausgeführt.

Für das, was du da machst, hätte man überhaupt keinen Compare Match 
benötigt :-) Das lässt sich ganz bequenm mit jeweils einem 
Software-Zähler, der von einem Startwert auf 0 runterzählt erledigen. 
Der Timer fungiert dann nur noch als 1-Sekunde Zeitsignal.
1
ISR( Overflow vom Timer 5 )    // so eingestellt, dass er jede Sekunde
2
                               // aufgerufen wird
3
{
4
  if( Display_Verzoegerung_Temp > 0 ) {
5
    Display_Verzoegerung_Temp--;
6
    if( Display_Verzoegerung_Temp == 0 )
7
      // Display wird abgedunkelt auf Schlafmodus (Pin PH3!)
8
      Displaybeleuchtung = 0;
9
  }
10
11
  if( Hauslicht_Zeit_Temp > 0 ) {
12
    Hauslicht_Zeit_Temp--;
13
    if( Hauslicht_Zeit_Temp == 0 )
14
      Hauslicht_Aktiv = 3;
15
  }
16
}

Zum einschalten des Displays, weist du der Variablen 
Display_Verzoegerung_Temp die Zeit in Sekunden zu, nach der es sich 
deaktivieren soll. Selbiges für Hauslicht_Zeit_Temp. Wird eine Taste 
gedrückt, wird einfach nur die jeweilige xxx_temp Variable wieder auf 
die verzögerungszeit gesetzt. Ist das Display noch aktiv, so beginnt die 
Zeit erneut zu laufen ... der Timer wird retriggert.

Und natürlich kann man noch 20 bis x weitere derartige Countdown-Timer 
nach demselben Muster machen.

Sind beide bereits abgeschaltet, wird zwar die ISR noch aufgerufen, aber 
das bischen Rechenzeit, das dabei verbraucht wird, fällt unter "ferner 
liefen".

von Christian B. (luckyfu)


Lesenswert?

das hätte dann aber einen nachteil:

wenn ich z.b. einen wert nach 4 sek haben will und das nach deiner 
methode mache dann habe ich bei 4 durchläufen das ergebnis in einem 
bereich von 4-5 oder 3-4 sekunden, je nach zählweise. in dem einen 
extremfall kann es ja sein, daß der zähler eben gerade aus der isr kommt 
wenn das neue signal getriggert wird. dann läuft er einmal komplett 
durch bis zum ersten hochzählen. der andere extremfall ist, daß die 
aktion getriggert wird und quasi mit dem folgenden takt die isr 
ausgelöst wird, somit würde ich eine sekunde unterschied haben.
gut, man kann sich nun drüber streiten ob 1 sek bei max 4 min laufzeit 
etwas ausmacht, das würde ich eher als "rauschen" abtun und währe mir 
egal. wenn ich aber etwas nur 2 sekunden lang z.b. machen möchte dann 
macht es imho schon einen unterschied, ob das einmal 2 und einmal 3 
sekunden lang dauert. alternativ könnte man eine 16 bit zählvariable 
verwenden und die zeitbasis auf 100ms stellen. ein "rauschen" von 100ms 
würde bei 1 sek nicht weiter stören, auch nicht bei 1 sek 
abarbeitungszeiten.

aber das führt jetzt weg vom toppic und hilft dem threadersteller 
überhaupt nicht weiter. er wollte ja etwas mit beiden comparewerten 
machen, daher lag für mich der verdacht nahe, daß es sich um die selbe 
zeitbasis handelt, sonst macht es ja wenig bis keinen sinn.

man darf ja auch nicht vergessen, daß ich bei meinem controller 6 timer 
zur verfügung habe, ich kann also etwas verschwenderisch damit umgehen 
wenn ich will :)

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.