www.mikrocontroller.net

Forum: Compiler & IDEs Sekundentakt mit Timer erzeugen


Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo!


Ich hab gerade Probleme mit meinem Sekundentakt. Ich hab eine 
Interruptroutine, die eigentlich mit genau 1Hz aufgerufen werden sollte. 
Zum Vergleich, hab ich mittels der _delay_ms Funktion ein Blinken einer 
anderen LED in die Hauptschleife geschrieben.


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

#define F_CPU 1000000UL
#include <util/delay.h>

volatile int second = 0;

void init_timers()
{
  //Timer 1 auf Sekunden-Takt einstellen
        // 1Mhz/(64*15625) = 1 Hz 
  OCR1A= 15625;
  TIMSK |= (1<<OCIE1A);
  TCCR1A = 0x00;
  TCCR1B |= (1<<WGM12)|(1<<CS11)|(1<<CS10); // CTC mode; 64 prescale
}

ISR(TIMER1_COMPA_vect)
{  
  //toggle led
  if( PORTB & (1<<LED_SEC) )
  {
    LED_SEC_ON;
  }
  else
  {
    LED_SEC_OFF;
  }

  second++;
}


int main()
{
  init_timers();

  sei();

  while (1)
  {
    LED_MESSURE_ON
    _delay_ms(1000);
    LED_MESSURE_OFF
    _delay_ms(1000);
  }
}


Theoretisch sollten die LEDs doch fast gleichschnell blinken. (In der 
Hauptschleife ganz minimal langsamer).

Aber leider blinkt die timergesteuerte LED in einer Minute ca. 29 mal. 
Die LED in der Hauptschleife blinkt (ausreichend genau) 30 mal.

Könnte mit jemand ein Fehler im Code zeigen, oder bestätigen, dass der 
so richtig ist? Der ORC1A Wert is doch richtig berechnet, oder? Danke 
schonmal!

P.S.: Der Einfluss der Ungenauigkeit des internen RC-Oszillators sollte 
sich ja auch beide LEDs gleich auswirken und somit keinen Einfluss auf 
die unterschiedliche Blinkgeschwindigkeit haben.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Könnte mit jemand ein Fehler im Code zeigen,

Wenn das der ganze Code wäre, vielleicht. Bei deinem gezeigten Code 
dürfte gar nichts blinken, da kein Pin von PORTB jemals auf Ausgang 
geschaltet wird.

>Der ORC1A Wert is doch richtig berechnet, oder?

1Mhz/(64*15625) = 1 Hz ist richtig.

>Aber leider blinkt die timergesteuerte LED in einer Minute ca. 29 mal.
>Die LED in der Hauptschleife blinkt (ausreichend genau) 30 mal.

Es wäre interessant zu wissen, ob die Timer-LED tatsächlich langsamer 
blinkt, oder ob (z.B. zu Beginn) einmal Blinken fehlt.

Oliver

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>   // 1Mhz/(64*15625) = 1 Hz
>   OCR1A= 15625;

Das müsste IMHO 15624 sein.
Der Timer soll 15625 unterschiedliche Zustaände einnehmen. Auch 0 ist 
ein Zustand. D.h. wenn der Timer bis 15624 gezählt hat, hat er mit dem 
nächsten Tick bereits bereits 15625 Timerticks abgezählt.

Autor: Oliver (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>In CTC mode the counter is cleared to zero when
>the counter value (TCNT1) matches either the OCR1A (WGM13:0 = 4) or the ICR1 
>(WGM13:0 = 12).

IMHO sind 15625 und 0 ein und derselbe Zustand - daher passt 15625.

Oliver

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oliver schrieb:

> IMHO sind 15625 und 0 ein und derselbe Zustand - daher passt 15625.

Nein, sind es nicht. Das Datenblatt ist hier ein wenig missverständlich. 
Das Rücksetzen auf Null erfolgt erst im nächsten Takt, nicht 
gleichzeitig mit dem Compare-Match.

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Da kannst einfach den Compiler (bzw. Präprozessor) die richtigen Werte 
ausrechnen und auch warnen lassen, falls der Timer ungenau arbeiten 
sollte:
#include <avr/io.h>

// 1 MHz
#define F_CPU 1000000

// IRQs pro Sekunde
#define IRQS 1

#define PRESCALE 64

#if F_CPU / PRESCALE / IRQS -1 < 1
# warning Timer1 arbeitet falsch
#endif

#if F_CPU / PRESCALE / IRQS -1 > 0xffff
# warning Timer1 arbeitet falsch
#endif

#if (F_CPU % (PRESCALE * IRQS)) != 0
#   warning Timer1 arbeitet ungenau
#endif

void timer1_init (void)
{
    // CTC-Mode und PRESCALE=64
    TCCR1A = 0;
    TCCR1B = (1 << WGM12) | (1 << CS11) | (1 << CS10);

    // OutputCompare für gewünschte Timer-Frequenz
    OCR1A = (unsigned short) ((unsigned long) F_CPU / PRESCALE / IRQS -1);

    // OutputCompare-Interrupt A für Timer 1
    TIMSK |= (1 << OCIE1A);
}

Autor: Andreas Vogt (tico)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:
>>   // 1Mhz/(64*15625) = 1 Hz
>>   OCR1A= 15625;
>
> Das müsste IMHO 15624 sein.
> Der Timer soll 15625 unterschiedliche Zustaände einnehmen. Auch 0 ist
> ein Zustand. D.h. wenn der Timer bis 15624 gezählt hat, hat er mit dem
> nächsten Tick bereits bereits 15625 Timerticks abgezählt.

Das ist korrekt.

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
so, ich habs nun nochmal nachgemessen, indem ich im Interrupt bis 15 
Minuten gezählt hab, und mit ner Stopuhr verglichen hab. Der Takt stimmt 
tatsächlich.

Die Variante mit _delay_ms(1000) is diejenige, die falsch läuft. Das es 
nicht so genau ist, ist ja klar, aber eine so große Abweichung hätte ich 
nicht erwartet. Möglicherweise die verringerte Auflösung aufgrund der 
großen Zeit? Obwohl in der Doku eigentlich 10 ms Auflösung steht. Und 
die zusätzlichen Befehle zwischen den Delays sollten ja auch keinen so 
großen Einfluss haben. Naja, aber das is jetzt ja auch gar nicht mehr 
wichtig :)


Und danke für den Hinweis, dass ich den Wert eins zu hoch gewählt hatte 
:)

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Nenenene, Moment mal!

Richtig ist natürlich 15625-1=15624!

Mega16 Datenblatt (10/06) Seite 77:

Das gilt zwar für den OC-Output. Aber da die Frequenz durch zwei 
Timerüberläufe und einem Toggle gemacht wird gilt:

macht bei 15625: 0,99993600409573787277614232689108 Hz.
=> Richtig ist also 15624 als OCR Wert.

PS: Nur um das noch mal zu bestätigen. ;)

Ich würde in dem Falle die Makros von Georg bevorzugen. Mache ich in 
meinen Programmen eigentlich auch so. Da braucht man dann nicht den 
Taschenrechner vorkramen.

Autor: icke (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Klaus schrieb:
>Obwohl in der Doku eigentlich 10 ms Auflösung steht.

10 * 60 = 600

logisch oder

Autor: Klaus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Klaus schrieb:
>>Obwohl in der Doku eigentlich 10 ms Auflösung steht.
>
> 10 * 60 = 600
>
> logisch oder

Bitte, was?^^ Logisch ja, aber auf was für eine Frage war das jetzt die 
Antwort? ;)

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
icke schrieb:
> Klaus schrieb:
>>Obwohl in der Doku eigentlich 10 ms Auflösung steht.
>
> 10 * 60 = 600
>
> logisch oder

Schon.
Aber eine Sekunde besteht aus 100 Stück 10ms Abschnitten.
Also nicht 10*60 sondern 10*100 = 1000

Daher ist
  _delay_ms( 1000 )
schon korrekt.

Die Abweichung wird wahrscheinlich 2 Ursachen haben.
* _delay_ms wird auch nicht auf die µs genau die Zeit einhalten
* du machst ja neben dem delay auch noch sonst was drumherum.

Ich denke mal, ersteres wird den größten Einfluss haben. _delay_ms wird 
nicht jeden Taktzyklus zählen, wenn es die Warteschelifen aufsetzt.

Autor: VonNixNeAhnung (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wurde noch nicht erwähnt, vielleicht könnte das hier 
Beitrag "Die genaue Sekunde / RTC" interessant sein.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.