Forum: Mikrocontroller und Digitale Elektronik Berechnung CTC Timer


von MCAnfänger (Gast)


Lesenswert?

Hallo,

ich möchte mit dem Timer arbeiten un dazu hab ich mir im Internet 
einiges durchgelesen. Zuerst möchte ich erfahren ob ich die Berechnung 
anhand eines Beispiels verstanden habe. Beispielsweise verwende ich 
einen MC mit einer Takfrequenz von 8 MHz (8-Bit Timer) und einem 
Prescaler mit dem Wert von 64. Der Interrup soll dabei alle 2 ms=500 Hz 
erfolgen.

Der Maximalwert :

Cmax=(f-cpu/Prescaler)*t=(8MHz/64)*2ms= 250

OCRx=Cmax-1=249

Alle zwei ms soll ein Interrupt ausgelöst werden, heißt es, dass die 
Interruptroutine alle 500 mal (500 Takte/sekunde) aufgerufen wird? Ich 
weiss nicht genau wie ich diese in der ISR umsetzen soll.

von H.Joachim S. (crazyhorse)


Lesenswert?

MCAnfänger schrieb:
> Hallo,
>
> ich möchte mit dem Timer arbeiten un dazu hab ich mir im Internet
> einiges durchgelesen. Zuerst möchte ich erfahren ob ich die Berechnung
> anhand eines Beispiels verstanden habe. Beispielsweise verwende ich
> einen MC mit einer Takfrequenz von 8 MHz (8-Bit Timer) und einem
> Prescaler mit dem Wert von 64. Der Interrup soll dabei alle 2 ms=500 Hz
> erfolgen.
>
> Der Maximalwert :
>
> Cmax=(f-cpu/Prescaler)*t=(8MHz/64)*2ms= 250
>
> OCRx=Cmax-1=249
>
Bis hierher ist doch alles richtig

> Alle zwei ms soll ein Interrupt ausgelöst werden, heißt es, dass die
> Interruptroutine alle 500 mal (500 Takte/sekunde) aufgerufen wird? Ich
> weiss nicht genau wie ich diese in der ISR umsetzen soll.

Das verstehe ich überhaupt nicht. Alle 2ms wird der OCR-Interrupt 
ausgelöst, das wolltest du ja. Also 500 mal /s.  Zwischen 2 Aufrufen 
liegen aber 16.000 Prozessortakte.
In der ISR musst du erstmal gar nichts umsetzen, das passiert einfach. 
Aber irgendwas wolltest du ja alle 2ms tun, also tu das dort.

von MCAnfänger (Gast)


Lesenswert?

z.b. alle 2 ms soll eine Led blinken. Ich dachte ich muss den Aufrauf 
(500mal/s) selbst programmieren gemäß des Teilcodes aus dem 
AVR-GCC-Tutorial, wobei ich bis jetzt nicht ganz verstehe was in der 
Routine passiert und wozu die If-Abfragen dienen.

ISR (TIMER0_COMPA_vect)
{
  millisekunden++;
  if(millisekunden == 1000)
  {
    sekunde++;
    millisekunden = 0;
    if(sekunde == 60)
    {
      minute++;
      sekunde = 0;
    }
    if(minute == 60)
    {
      stunde++;
      minute = 0;
    }
    if(stunde == 24)
    {
      stunde = 0;
    }
  }
}

von Carl D. (jcw2)


Lesenswert?

Na zuerst läuft diese ISR 1000 mal per Sekunde.
Dann zählt sie bis 1000 und ist bei einer Sekunden angekommen.
60s   -> 1min
60min -> 1h
24h   -> 1day
Schwierig ist anders.

von H.Joachim S. (crazyhorse)


Lesenswert?

MCAnfänger schrieb:
> z.b. alle 2 ms soll eine Led blinken.
Das kannst du wahrscheinlich nicht sehen :-)
Ich dachte ich muss den Aufrauf
> (500mal/s) selbst programmieren gemäß des Teilcodes aus dem
> AVR-GCC-Tutorial, wobei ich bis jetzt nicht ganz verstehe was in der
> Routine passiert und wozu die If-Abfragen dienen.
>
> ISR (TIMER0_COMPA_vect)
> {
>   millisekunden++;
>   if(millisekunden == 1000)
if(minute == 60)
>     {
>       stunde++;
>       minute = 0;
>     }
>     if(stunde == 24)
>     {
>       stunde = 0;
>     }
>   }
> }

Das ist ne Uhr für 1ms-Interrupt.

von Carl D. (jcw2)


Lesenswert?

BTW, 2ms Blinker sind ohne Oszi/LA schlecht zu erkennen. Ich benutze 
immer eine 10€-LA aus China + sigrok/pulseview um das Int-Timing zu 
beobachten.

von Klaus (Gast)


Lesenswert?

MCAnfänger schrieb:
> z.b. alle 2 ms soll eine Led blinken.

Probiers mal damit:

While(1)
{


    // Die Bedingung wird nur einmal durchlaufen. Also jede Sekunde 
einmal.
    if (  Alte_Sek != Sekunde)
    {
       // Alle 5 Sekunden Port PB3 Toggeln
       if ( ( Sekunde % 5 ) == 0 ))
       {
            // An Port PB3 Soll alle 5 Sekunden eine LED Toggeln
            PORTB ^=  ( 1 << PB3 );
      Alte_Sek = Sekunde;

       }

    }

}

von MCAnfänger (Gast)


Lesenswert?

Carl D. schrieb:
> Na zuerst läuft diese ISR 1000 mal per Sekunde.

sorry für diese Frage, aber wird die ISR ( der Inhalt der Routine) 1000 
mal aufgerufen und abgearbeitet. Wenn der Timer alle 2 ms= 500 1/s den 
Interrupt aufruft, bedeutet dass die LED 500 mal blinkt?

von Axel S. (a-za-z0-9)


Lesenswert?

MCAnfänger schrieb:
> z.b. alle 2 ms soll eine Led blinken. Ich dachte ich muss den Aufrauf
> (500mal/s) selbst programmieren

Der Aufruf der ISR 500-mal pro Sekunde wird doch von der Hardware 
erledigt. Dazu mußt du überhaupt nichts programmieren. OK, du mußt den 
Timer einmalig entsprechend deinen Berechnungen intitialisieren und 
die Interrupts einschalten. Der Aufruf der ISR passiert dann ganz 
automatisch. Genau deswegen verwendet man ja Timer und Interrupts.

> gemäß des Teilcodes aus dem
> AVR-GCC-Tutorial, wobei ich bis jetzt nicht ganz verstehe was in der
> Routine passiert und wozu die If-Abfragen dienen.
>
> ISR (TIMER0_COMPA_vect)
> {
>   millisekunden++;
>   if(millisekunden == 1000)
>   {
...

In diesem Beispiel löst der Timer den Interrupt offensichtlich 1000-mal 
pro Sekunde aus. Entsprechend zählt die ISR die verflossenen Milli- 
sekunden hoch. Und basierend auf den verflossenen Millisekunden dann 
auch Sekunden, Minuten usw.

Wenn es dir nur darauf ankommt, eine LED an einem Portpin blinken zu 
lassen, dann mußt du einfach nur den Pegel des Portpins bei jeder 
Ausführung der ISR wechseln. Also von H zu L und von L zu H. Ob du das 
mit einer if() Bedingung machst oder dafür den XOR-Operator benutzt, ist 
ein Detail. Du könntest sogar eine Hilfsvariable nutzen, die du zwischen 
0 und 1 umschaltest und dann abhängig vom Wert der Variaben die LED ein- 
oder ausschaltest. Sei kreativ und probiere verschiedene Wege aus!

Eins noch: wenn die ISR alle 2ms ausgelöst wird und jedesmal den Zustand 
der LED von "ein" zu "aus" (und umgekehrt) wechselt, dann dauert ein 
kompletter Zyklus nicht 2ms, sondern 4ms. Die Blinkfrequenz ist also 
nicht 500Hz, sondern nur 250Hz.

von Carl D. (jcw2)


Lesenswert?

MCAnfänger schrieb:
> Carl D. schrieb:
>> Na zuerst läuft diese ISR 1000 mal per Sekunde.
>
> sorry für diese Frage, aber wird die ISR ( der Inhalt der Routine) 1000
> mal aufgerufen und abgearbeitet. Wenn der Timer alle 2 ms= 500 1/s den
> Interrupt aufruft, bedeutet dass die LED 500 mal blinkt?

Die 1000/s bezogen sich auf den angegebenen Code, denn die Frage 
lautete: "was macht dieser Code?"
Wenn man bis 1000 zählt und dann die Sekunde erhöht, dann sollte das 
jede Millisekunde passieren. Bei 2ms würde die Uhr deutlich nachgehen. 
Es sei denn, man zählt nur bis 500.

von Klaus (Gast)


Lesenswert?

MCAnfänger schrieb:
> Wenn der Timer alle 2 ms= 500 1/s den
> Interrupt aufruft, bedeutet dass die LED 500 mal blinkt?

Wenn du die LED in der Interupt Routine Toggelst. Ja!

Wirst aber kein Blinken feststellen, weil es so schnell von statten 
geht,
das du nur ein Dauerleuchten siehst.
Vllt. auch etwas flackern.

Frag lieber in der Main ab ob eine bestimmte Zeit Vergangen ist und
und Schalte deine LED ein oder aus.

Z.B.

if ( (millisekunden % 500) == 0 )

Wobei millisekundenvom TYP uint16_t sein muss damit auch auf 1000 
Gezählt werden kann.

von MCAnfänger (Gast)


Lesenswert?

Danke für die wunderbaren Antworten.
So eine letzte Frage, wozu brauchen wir eigentlich die Hilfsvariable in 
der ISR Routine?


ISR(...)
{
tue was
}

von Klaus (Gast)


Lesenswert?

wozu brauchen wir eigentlich die Hilfsvariable in
der ISR Routine?

Aus dem Tutorial hab ich folgrndes für dich.



Wird innerhalb einer ISR mehrfach auf eine mit volatile deklarierte 
Variable zugegriffen, wirkt sich dies ungünstig auf die 
Verarbeitungsgeschwindigkeit aus, da bei jedem Zugriff mit dem 
Speicherinhalt abgeglichen wird. Da bei AVR-Controllern innerhalb einer 
ISR keine Unterbrechungen zu erwarten sind, bietet es sich an, einen

Zwischenspeicher in Form einer lokalen Variable zu verwenden, deren 
Inhalt zu Beginn und am Ende mit dem der volatile Variable 
synchronisiert wird. Lokale Variable werden bei eingeschalteter 
Optimierung mit hoher Wahrscheinlichkeit in Prozessorregistern verwaltet 
und der Zugriff darauf ist daher nur mit wenigen internen Operationen 
verbunden. Die ISR aus dem vorherigen Beispiel lässt sich so optimieren:

//...
ISR(TIMER1_COMPA_vect)
{
   uint8_t tmp_kc;

   tmp_kc = gKeyCounter; // Uebernahme in lokale Arbeitsvariable

   if ( !(KEY_PIN & (1<<KEY_PINNO)) ) {
      if (tmp_kc < CNTREPEAT) {
         tmp_kc++;
      }
   }
   else {
      tmp_kc = 0;
   }

   gKeyCounter = tmp_kc; // Zurueckschreiben
}

von MCAnfänger (Gast)


Lesenswert?

MCAnfänger schrieb:
> Der Interrup soll dabei alle 2 ms=500 Hz
> erfolgen.

Heißt es, dass bei einer Wiederholung von 500 Takte ich eine 
Hilfsvariable mit 500 bzw. 0 initialisiere. Diese dann auf null/500 
dekrementiere/inkrementiere.

bsp.:
volatile unsigned int zähl;

ISR(...)
{
zähl++;

if(zähl>500)
{
....
}

von Klaus (Gast)


Lesenswert?

@MCAnfänger
Hab auch mal ne Frage.
Hast du überhaubt die Grundlagen in c erlernt bzw. bist absoluter 
Anfänger?

Wie wäre es, wenn du,  ein bischen von deinem bis jetzt selbst 
geschriebenen C-Code hier zu Posten würdest.

von Carl D. (jcw2)


Lesenswert?

Klaus schrieb:
> wozu brauchen wir eigentlich die Hilfsvariable in
> der ISR Routine?

weil in
1
volatile unsigned int zähl;
2
3
ISR(...)
4
{
5
zähl++;
6
7
if(zähl>500)
8
{
9
....
10
}
11
12
}
zähl immer wieder neu gelesen werden muß, wärend die lokale Kopie in 
einem CPU-Register zwischengelagert wird.
volatile sagt nämlich: diese Variable kann sich jederzeit ändern, was 
richtig ist aus Sicht der main-Loop, aber natürlich innerhalb der ISR 
nicht stimmt. Die ISR wird (falls nicht anders gewünscht) auf einem AVR 
nicht unterbrochen, es kann also auch keine überraschenden Änderungen 
geben. Und der Weg das dem Compiler mitzuteilen, ist eine lokale 
Variable zu verwenden.

Wenn man übrigens lernen will, was da genau abgeht, dann sollte man 
unbedingt das .lss File lesen lernen. Da kann man dann auch den 
Unterschied mit/ohne lokale Kopie anschauen.

Edit: ich sehe gerade (und lasse es zur Abschreckung drin):
keine nicht-ASCII-Zeichen in Variablennamen verwenden!

: Bearbeitet durch User
von MCAnfänger (Gast)


Lesenswert?

Klaus schrieb:
> @MCAnfänger
> Hab auch mal ne Frage.
> Hast du überhaubt die Grundlagen in c erlernt bzw. bist absoluter
> Anfänger?

Also in C habe ich selbst kleine Programme(Funktionen, Felder usw) 
geschrieben. Auf dem Themengebiet MC bin ich ein Neuling (MCAnfänger:)) 
und wie es üblicherweise ist, habe ich als erstes LEDs blinken lassen. 
Der Unterschied zwischen lokalen und globalen Varible sowie volatile 
Variable ist mit bekannt. Nur ich verstehe nicht warum wir in die ISR 
eine Variable einfügen, wenn wir die LED nur blinken haben wollen. Was 
passiert durch die zusätzliche Variable? Wie die If-Abfrage 
funktioniert, weiss ich auch. Vielleicht ist meine Frage überflüssig.

-----------------------------mit Variable------------------------

#include <avr/io.h>
#include <avr/interrupt.h>

int extraTime = 0;

.......


ISR(TIMER0_COMPA_vect)
{
    extraTime++;

    if(extraTime > 100)
    {
        extraTime = 0;
        PORTB ^= (1 << PORTB0);
    }
}
--------------------------ohne Variable----------------------------
#include <avr/io.h>
#include <avr/interrupt.h>

int extraTime = 0;

.......


ISR(TIMER0_COMPA_vect)
{
      PORTB ^= (1 << PORTB0);
}

von Carl D. (jcw2)


Lesenswert?

MCAnfänger schrieb:
> extraTime++;
>
>     if(extraTime > 100)
>     {
>         extraTime = 0;

was genau verstehst du an diesen 5 Zeilen Code nicht?

An der Programmiersprache kann das kaum liegen, denn außer "++"->"erhöhe 
um eins", steht da doch nur Klartext.

von MCAnfänger (Gast)


Lesenswert?

Carl D. schrieb:
> MCAnfänger schrieb:
>> extraTime++;
>>
>>     if(extraTime > 100)
>>     {
>>         extraTime = 0;
>
> was genau verstehst du an diesen 5 Zeilen Code nicht?
>
> An der Programmiersprache kann das kaum liegen, denn außer "++"->"erhöhe
> um eins", steht da doch nur Klartext.


//f_CPU:20 MHZ
//Prescaler: 1024
//OCR: 195
//toverflow= (1024/20Mhz)*196=10ms
//10 ms = 100 Mal/sekunde

ISR(TIMER0_COMPA_vect)
{
    extraTime++;           //--->extraTime=0 bis 100 ist dann 1 sec rum

    if(extraTime > 100)    //Wenn eine sec vorbei ist, wird die variable
    {
        extraTime = 0;     // auf Null zurück gesetzt
        PORTB ^= (1 << PORTB0);    //LED toggeln lassen
    }
}

Frage: Warum wird bis 100 gezählt (eine Sekunde vorbei ist) und dann 
erst LED "an" und "aus" geschaltet? Warum wird diese extra Zeit 
eingefügt?

von Klaus (Gast)


Lesenswert?

MCAnfänger schrieb:


> int extraTime = 0;
Also wenn ich mich nicht irre sollte volatile vor int extraTime = 0; 
stehen.



>
> ISR(TIMER0_COMPA_vect)
> {
>       PORTB ^= (1 << PORTB0);
> }

Der Unterschied zu deiner mit if abfrage ist,
dass die LED, vor sich hin flackert.
Also du wirst nichts vom Blinken sehen.

Und warum willst du Unbedingt Variabeln in der ISR haben
und auch noch eine Menge Code Ausführen lassen?

Du kannst auch folgendes in der ISR machen:


//Globale Variable
volatile uint16_t counter1 = 0;


ISR(TIMER0_COMPA_vect)
{
     if(counter1 > 0) counter1--;
}

Und in der main machst du folgendes:

main()
{
  while(1)
  {

      if (( counter1 )   == 0)
      {
         PORTB ^=  ( 1 << PB3 ); // LED Toggeln
         // Startwert der Angepasst werden kann.
         counter1 = 50;
      }

  }

}

Damit Hast du dann eine schönes Blinki Programm.


PS; Du scheinst auch Lesefaul zu sein.
So habe ich den Eindruck jedenfalls.
Sieh dir mal die obigen Posts näher an.

von MCAnfänger (Gast)


Lesenswert?

Klaus schrieb:
> PS; Du scheinst auch Lesefaul zu sein.
> So habe ich den Eindruck jedenfalls.
> Sieh dir mal die obigen Posts näher an.

Danke, werde ich machen.

von H.Joachim S. (crazyhorse)


Lesenswert?

MCAnfänger schrieb:
> if(extraTime > 100)

Wobei das genaugenommen falsch ist. Zumindest wenn man aus 10ms 1s 
machen möchte. Auch wenn es hier keine Rolle spielt, sollte man sich 
solche Schlampigkeiten gar nicht erst angewöhnen.

if(extraTime >= 100)
oder
if(extraTime > 99)

wären korrekt.

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.