Forum: Mikrocontroller und Digitale Elektronik Timer1-Interrupt alle 20 ms


von Thomas H. (datatom)


Lesenswert?

Hallo zusammen,

ich möchte bei meinem ATmega2561
http://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2549-8-bit-AVR-Microcontroller-ATmega640-1280-1281-2560-2561_datasheet.pdf

 mit dem Timer1 alle 50ms bzw. 49,875ms einen Interrupt erzeugen. 
Innerhalb des Interrupts zähle ich jeweils bis 20 hoch und habe somit 
eine Sekunde erreicht. Bei erreichen von 180 Sekunden soll dann eine 
while-Schleife beendet werden. Das klappt soweit, nur, die 
while-Schleife wird ca. 16 Sekunden zu früh beendet.

Woran liegt das?

F_CPU hat den Wert: #define F_CPU 7372800UL

Mein Code:


void Timer1_Frequency(uint8_t freq)
{

  TCCR1B |=
  (1<<CS12) | (1<<CS10) |        // Timer Prescaler 1024
  (1<<WGM12);           // Timer-Mode CTC

  TIMSK1 |= (1<<OCIE1A);         // Interrupt: Output Compare

  OCR1A = (F_CPU/(freq*2*1024)-1);

}

ISR(TIMER1_COMPA_vect)        // Interrupt Output Compare
{
  Timer1_interrupt++;

  if (Timer1_interrupt == 20)
  {
    Timer1_interrupt = 0;
    Timer1_180 = Timer1_180 + 1;
  }
}

Timer1_Frequency wird aus einem anderen Include-File aufgerufen mit:
Timer1_Frequency(171);

Dann folgt sofort die while-Schleife:
while(USART0_i == 0 && Timer1_180 <= 180)
{
 continue;
}

von Andreas B. (bitverdreher)


Lesenswert?

> #define F_CPU 7372800UL

und die CPU läuft vermutlic mit 8Mhz

von Dr. Sommer (Gast)


Lesenswert?

Du setzt freq auf 171, erwartest also dass die Interrupts mit 171Hz 
kommen, d.h. alle 5,8ms, und gehst dann davon aus dass sie alle 50ms 
kommen? Hä?
Und was ist 7372800 überhaupt für ne komische Zahl...

von Paul B. (paul_baumann)


Lesenswert?

Dr. Sommer schrieb:
> Und was ist 7372800 überhaupt für ne komische Zahl...

Für einen Baudraten-Quarz ist das gar nicht so komisch...

MfG Paul

von Stefan E. (sternst)


Lesenswert?

Und die 2 in der OCR1A-Formel ist auch falsch.
Ja, im Datenblatt steht die Formel so, aber die ist gedacht für 
"Frequenz des Ausgangssignals bei Toggeln des Pins". Für "Frequenz im 
Sinne von Interrupthäufigkeit" muss die 2 weg.

von Datatom (Gast)


Lesenswert?

Andreas B. schrieb:
> #define F_CPU 7372800UL
>
> und die CPU läuft vermutlic mit 8Mhz

73272800 ist der Quarz.

Wieso läuft die CPU mit 8 mhz, ich dachte die CPU läuft mit dem Quarz- 
Wert?

von Dr. Sommer (Gast)


Lesenswert?

Datatom schrieb:
> Wieso läuft die CPU mit 8 mhz, ich dachte die CPU läuft mit dem Quarz-
> Wert?

Hast du die Fuses denn auch so eingestellt dass der Quarz benutzt wird, 
und nicht der interne RC-Oszillator? Die F_CPU Angabe teilt dem Code die 
Frequenz mit, aber stellt die Frequenz/Taktquelle nicht aktiv mit...

von Rene K. (xdraconix)


Lesenswert?

Datatom schrieb:
> 73272800 ist der Quarz.
>
> Wieso läuft die CPU mit 8 mhz, ich dachte die CPU läuft mit dem Quarz-
> Wert?

Natürlich gibt es 7.3728MHz Quarze. Jedoch eher untypisch und für den TO 
eher ungewöhnlich bei seinem Wissensstand.

Somal er bei der Formel (F_CPU/(freq*2*1024)-1) eigentlich schon selbst 
hätte drauf kommen können das da was nicht passt. Ich rechne mal: 
(F_CPU/(50*1024)-1)

von Kirsch (Gast)


Lesenswert?

Für einen 16Bit Timer:

Eingangstakt: 7372800Hz
Modus: CTC
Prescaler: 1024
OCRxA: 359

Dann bekommst du einen Compare-Match oder Overvlow alle 50ms

Die Formel lautet:
f_CM = f_IO / ( Prescaler • ( OCRxA + 1 ) )

von Dr. Sommer (Gast)


Lesenswert?

Warum eigentlich nicht den Prescaler auf 1024 setzen, OCR1A auf 64799, 
und dann in der ISR von 0-19 zählen? Das ergibt dann exakt
1
7372800 / 1024 / 64800 / 20 = 1/180 Hz
bei minimaler Prozessor-Last (1 Interrupt alle 9sec).

von c-hater (Gast)


Lesenswert?

Thomas H. schrieb:

>  mit dem Timer1 alle 50ms bzw. 49,875ms einen Interrupt erzeugen.

Ja was ist denn nun tatsächlich das Ziel? 50 oder 49,875? Ich gehe mal 
von 50 aus.

> #define F_CPU 7372800UL

Das geht vollkommen problemlos. Denn:

1/50ms = 20 Hz

->

   7372800 = 5^2 * 3^2 * 2^15
-  20           = 5^1          * 2^2
                     ----------------------
                      5^1 * 3^2 * 2^13

-> ganzzahlig teilbar. Damit steht schonmal fest, das die 20Hz exakt
    erreichbar sind.

Es geht also nun nur noch darum, wie man den Teilerfaktor auf die 
Timerbaugruppen verteilt. Der Prescaler kann bis zu 2^10 wegteilen, 
also:

  5^1 * 3^2 * 2^13
-                   2^10
  ----------------------
  5^1 * 3^2 * 2^3  = 360

Sprich: das OCR-Register ist mit 360 - 1 = 359 zu laden und alles ist 
schick.

Tja: zumindest wenn die Kiste tatsächlich mit 7,3728Mhz läuft. Allein 
davon, dass du das als #define hinschschreibst, tut sie das aber noch 
lange nicht...

Übrigens: wenn das Ziel eigentlich garnicht die 20Hz, sondern 1Hz sind, 
geht das auch, wie man leicht erkennen kann:

 360 * 20 = 7200

-> Passt noch locker in das 16Bit-Zählregister.

Sprich: Wenn das eigentliche Ziel 1Hz ist, wird der Interrupt erstens 
viel zu häufig aufgerufen und ist zweitens viel zu kompliziert. Rüstet 
man ihn auf das Nötige ab, bleibt bloß noch das Setzen des Flags übrig. 
Das aber gibt es schon in Hardware, also wird die ISR am Ende des Tages 
vollkommen überflüssig...

von Thomas H. (datatom)


Angehängte Dateien:

Lesenswert?

Dr. Sommer schrieb:
> Datatom schrieb:
>> Wieso läuft die CPU mit 8 mhz, ich dachte die CPU läuft mit dem Quarz-
>> Wert?
>
> Hast du die Fuses denn auch so eingestellt dass der Quarz benutzt wird,
> und nicht der interne RC-Oszillator? Die F_CPU Angabe teilt dem Code die
> Frequenz mit, aber stellt die Frequenz/Taktquelle nicht aktiv mit...


Hier mein Quarz 7372800 Mhz:
https://www.alvidi.de/shop/product_info.php?language=en&info=p17_Quarz-16-MHz.html

Also die Fuses habe ich folgenermaße eingestellt:
s.Bild

Wobei mir die Start-up time leider nichts sagt:-(

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

Thomas H. schrieb:

> Also die Fuses habe ich folgenermaße eingestellt:
> s.Bild

Und auch so programmiert? Davon, dass man's einstellt, ist's 
normalerweise noch nicht programmiert. Dazu muß man üblicherweise noch 
irgendeinen Button anklicken, beschriftet mit "Program" o.s.ä....

> Wobei mir die Start-up time leider nichts sagt:-(

Die spielt für den Takt selber keine Rolle, die sorgt nur dafür, dass 
der Quarzoszillator genug Zeit zum Anlaufen bekommt, bevor der Takt 
tatsächlich benutzt wird. Und du hast die "konservativste" Einstellung 
gewählt. Also: die Konfiguration der Fuses an sich ist vollkommen 
korrekt.

von Thomas H. (datatom)


Lesenswert?

Ich habe jetzt einen 16000 Mhz Quarz eingebaut. So wie im 
ATMEL-Beispiel, deshalb.

Wenn ich jetzt den Wert für OCR1A mit folgender Formel berechne:

OCR1A = (F_CPU/(freq*2*256)-1);

und freq = 2 ist, dann sind die 180 Sekunden auch wirklich 180 Sekunden. 
Aber eigentlich müssten doch bei freq = 1 die 180 Sekunden exakt sein.

Oder stimmt die Formel nicht?

von Rene K. (xdraconix)


Lesenswert?

Thomas H. schrieb:
> OCR1A = (F_CPU/(freq*2*256)-1);

Falsch... Wenn dein Vorteilen 256 ist dann DARF dort nicht Frequenz*2 
stehen. Denn dies ist dafür da den Flankenwechsel am Pin auszufüllen - 
wie in deinem Beispiel. Richtig ist:

OCR1A = (F_CPU/(freq*256)-1);

von S. Landolt (Gast)


Lesenswert?

Vielleicht wäre es für den Moment zielführender, ganz ohne Formel die 
Sache naiv anzugehen: 16,000 MHz, daraus soll 1 Hz werden per Timer, 
folglich muss dieser durch 16000000 teilen; der Timer hat feste 
Vorteiler, versuchen wir es mit dem größten, nämlich 1024: 16000000/1024 
= 15625. Also folgt OCR1A = 15625-1 (er beginnt bei 0). Ziehen wir, 
warum auch immer, als Vorteiler 256 vor, so geht auch das: 16000000/256 
= 62500, OCR1A = 62500-1.
  Danach kann man sich selbst eine Formel daraus entwickeln.

von Thomas H. (datatom)


Lesenswert?

Rene K. schrieb:
> Thomas H. schrieb:
>> OCR1A = (F_CPU/(freq*2*256)-1);
>
> Falsch... Wenn dein Vorteilen 256 ist dann DARF dort nicht Frequenz*2
> stehen. Denn dies ist dafür da den Flankenwechsel am Pin auszufüllen -
> wie in deinem Beispiel. Richtig ist:
>
> OCR1A = (F_CPU/(freq*256)-1);

Wenn ich die von dir genannte Formel anwende, dann wird, bei freq = 1, 
der Interrupt 45 x in 180 Sekunden durchlaufen.

Bei einer Frequenz von 1, sollte der Interrupt doch 1 x pro Sekunde 
durchlaufen werden oder nicht?

von Stefan E. (sternst)


Lesenswert?

Thomas H. schrieb:
> Oder stimmt die Formel nicht?

Das habe ich dir weiter oben doch schon erklärt.
Das "Freq" in dieser Formel steht für was anderes, als du denkst.

von Stefan E. (sternst)


Lesenswert?

Thomas H. schrieb:
> Bei einer Frequenz von 1, sollte der Interrupt doch 1 x pro Sekunde
> durchlaufen werden oder nicht?

Eben nicht. Das freq in der Formel steht für die Frequenz eines 
Ausgangssignsls, wenn im Interrupt (oder direkt per Compare Match) ein 
Pin ge-toggle-t wird. Daher der Faktor 2.

Edit: Mit "Formel" meine ich die im Datenblatt.

: Bearbeitet durch User
von Ralph S. (jjflash)


Lesenswert?

... oder auch, wenn eine Frequenz manuell per Bitbanging in einer ISR 
erzeugt werden würde (was man nicht unbedingt machen muss):

ISR wird alle 1 mSek angesprungen und darin ein Pin getoggelt. Dann 
geschieht der Aufruf eben alle mSek und ein Puls- und eine Pausezeit 
sind dann jeweils 1 mSek lang, die Gesamtperiodendauer aber 2 mSek.

Analoges gilt für TAKTERZEUGUNG mittels Hardware.

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.