www.mikrocontroller.net

Forum: Compiler & IDEs Timer: 1s - verschiedene Wege


Autor: funker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi,

ich möchte mich etwas besser mit den Timerfunktionen vertraut machen
und bin am überlegen welche Vorgehensweise die geringsten Fehler
verursachen.
Ganz schön kompliziert das Thema.

Ich habe einen Takt von 3,6864 MHz und möchte mir eine Sekundenfunktion
erzeugen und diese dann auf Minuten und Stunden umrechnen.

1.delay() braucht ja bei 16bit 4 Zyklen um auf 1µs zu kommen.
4Zyklen * (1/3,6864MHz) =~ 1,085... µs
ich müsste dann noch einen Faktor von ~0,9216 reinmultiplizieren...

delay(50000) würde mir ja theoretisch schon mal 0,05s erzeugen
wenn ich delay(50000) dann in eine schleife packe und diese dann 20mal
durchlaufen lasse ergibt sich ja rechnerisch 1sec.

Frage1: Kann man die "Durchlaufzeit" der Schleife nicht ermitteln?
Ähnlich den 4Takten für delay() und mit einem Faktor weiterarbeiten.
Soll d(50000) soll ja nicht genau sein, warum verstehe ich noch nicht
ganz, vielleicht kann mir jemand Licht ins dunkle bringen.

2.(mein eigentliches Anliegen)
Ich benutze jetzt einen 16bit Timer und mit 3,6864 MHz liefert mir
AVRcalc auch verschiedene Möglichkeiten, je nachdem welchen prescale
faktor ich wähle:

bei 5ms z.B.:

kein faktor: TCNT1L=0x00; TCNT1H=0xb8;
1/1024: TCNT1L=0xee; TCNT1H=0xff;

2.Frage: Ist meine Annahme richtig, dass ich ohne einen prescale faktor
ein genaueres Ergenis bekomme oder habe ich da falsch gedacht?

Ich will 10sec:
also nehme ich 1/1024 und erhalte die TCNT1H/L Werte. Jetzt könnte ich
doch die 10sec in eine Schleife packen die 6mal z.B. durchläuft.
Dann könnte ich doch den Fehler hochrechnen und einen Korrekturfaktor
für einen Durchgang in einer Schleife berechnen. Beziehungsweise öfter
die Schleife durchlaufen lassen 1Tag z.B. um genauere Werte zu
erhalten.

3.Frage: Macht man das so, wenn man eine Stopp-Uhr möglichst genau
"machen" will? Oder ist der Fehler nicht wahrnehmbar bei 1-5Minuten.
Vielleicht hat das ja schon mal jemand gemessen.

4.Frage: Gibt es noch andere Ansätze wie man ein möglichst genaues
Zeitintervall bestimmen will - ausser die Signale der Atomuhr zu
empfangen.

Autor: Joerg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Zu 1.) Ich mach mir nicht erst die Mühe das nachzurechnen.  Eine delay
loop für 1 Sekunde ist mittelprächtiger Unfug.  Nimm einen Timer,
dafür sind die Dinger ja da.  Wenn Du dann nix weiter zu tun hast,
kannst Du immer noch den Prozessor schlafen legen und Strom sparen.

Zu 2.)

> 2.Frage: Ist meine Annahme richtig, dass ich ohne einen prescale
> faktor ein genaueres Ergenis bekomme oder habe ich da falsch
> gedacht?

Wie Du zu dieser Annahme kommst, ist mir rätselhaft.

Solange Dein prescaler einen ,,runden'' Wert als Ergebnis liefert
(so
daß Du danach keine Rundungsdifferenzen hast), kannst Du das allemal
machen.

Die Faktorenzerlegung für 3.6864E6 ergibt 2^14 * 3^2 + 5^2.  Du kannst
Dir also einen Prescaler von maximal 2^14 (16384) leisten.  Da der
maximale Prescaler 2^10 ist, bleibt danach als Frequenz 2^4 + 3^2 *
5^2 übrig (3600 Hz).  Wenn Du jetzt noch den Timer im CTC-Modus
betreibst, brauchst Du also nur noch 3600 ins OCR1A oder OCR1B zu
schreiben, dann hast Du einen 1 s Interrupt, der so genau ist, wie
Deine Oszillatorfrequenz auch tatsächlich 3,6864 MHz ist.

Overflow interrupt würde ich nicht benutzen, wenn Du denn sowieso
einen Timer hast, der CTC kann (clear timer on compare match).

> Oder ist der Fehler nicht wahrnehmbar bei 1-5Minuten.

Der Fehler ist so genau, wie Dein Oszillator.  Für 1 - 5 Minuten
dürfte er selbst bei einem schlechten Oszillator (Fehler 1E-4)
vernachlässigbar sein (30 ms pro 5 Minuten).

Für längere Zeiten sollte man wenigstens einen Trimmer am Quarz haben,
um ihn auf genaue Frequenz ziehen zu können.

Autor: funker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Infos

Autor: funker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
int main(void)
{

    DDRD = 0xF0;                        /*PIND5 - LED*/
    TCCR1A = _BV(COM1A0) | _BV(COM1B0); /*compare1 mode set*/
    TCCR1B = _BV(CS10) | _BV(CS12) | _BV(CTC1); /*CTC mit 1/1024*/

    OCR1AH = 0x0e;
    OCR1AL = 0x10;

    for(;;){}
}

Ist ja gar nicht so schwierig, die Timer zu benutzen.

Eine Frage habe ich aber noch:
Wie kann man es umgehen, dass man z.B. alle LEDs an PinD damit zum
blinken bringt? Oder einen anderen Port benutzt?
Ein Ansatz reicht mir schon

Autor: Florian Pfanner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich weiß jetzt nicht genau was du mit diesem Code machst, aber
normalerweise ist nur ein Pin vom Compare-Match betroffen. OCR1A heißst
der glaub ich. Wenn du einen anderen pin nehmen willst, so musst du
eine Interruptroutiene schreiben, welche dir dann den benötigten Pin
setzt/löscht.
Wenn du ein Code-Beispiel benötigst, so sag mir für welchen
Controller.

Gruß, Florian

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Für eine Uhrenanwendung ist es ja gar nicht nötig, 100% genau 1s zu
erzeugen, daß kann eh kein Mensch überprüfen.

Abgesehen davon wird Dein Quarz auch nie exakt auf 3,6864 MHz
schwingen, d.h. Du wirst immer einen Gangfehler bemerken, aber eben
erst, wenn die Uhr mehrere Tage läuft.

Zur Theorie, wie man eine genaue Uhr hinkriegt, habe ich hier was
geschrieben:

http://www.specs.de/users/danni/appl/soft/clock/timebase.htm

Ist zwar für den 8051, aber das Prinzip läßt sich leicht auf einen AVR
übertragen. Man muß bloß etwas Bruchrechnung können.


Peter

Autor: funker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das Programm lässt mir die LED blinken.

Du meinst mit overflow interrupt?
Mit den TCNT1H/L Werten?

Nach dem Motto:
TCCR1A = 0x00;       /*CTC und PWM aus*/
TCCR1B = (_BV(CS12) + _BV(CS10)) /*(1/1024)-faktor*/
TCNT1  = 0xf1f0     /* reset alle 1000ms bei 3,6864 clk*/

Ja, wäre schön wenn du mir source schnipsel für 4433,M8,8515 etc.
posten könntes. Freue mich über alles.


Problem meines Vorhabens kann ich selber noch nicht ganz abschätzen.
Ich versuche daher momentan eigentlich alle Funktionen zu beherrschen.

Konkretes Problem ist, dass ich für eine freiwillige Studienarbeit
"irgendetwas" entwerfen will. Ich möchte einfach mal eine Art
Demonstration mit AVR bauen. Poti an den ADC und Ausgabe über Uart und
Display in Volt, Sleepfunktion, Schalter... was man halt so reinbaun
kann. Praktisch das aufwendigste Poti, ausserdem denke ich, dass ich
nach dem Projekt auch mal was sinnvolles aufbauen kann.

Ich kann mit Taster, LEDs, Ports und Uart umgehen. Jetzt habe ich mir
die Timer vorgenommen und prinzipiell verstehe ich sie auch. Was würdet
ihr mir raten als nächstes zu machen.
Was ich derzeit mit UART versuche ist, dass ich über Tasten zähle wie
oft in einer Minute gedrückt wurde, mit entprellten Tastern und dann
die Werte über Uart ausgebe. Mit dem Timer wollte ich noch eine Art
Zeitanzeige realisieren.

Vielleicht schreibe ich es gleichmal in den Beitrag, bevor ich wieder
einen aufmachen muss.

Ich habe eine static int variable, die mir die Tastendrücke hochzählt
und eine UART-transmit-Funktion die vom typ unsigned char ist.
Brauche ich dafür itoa()?

unsigned char sign = 'k';
...
transmit(sign);

Ich möchte jetzt einfach mal "12345" vom Typ integer ans Terminal
senden. Habe gedacht, ich müsste nur transmit(unsigned int sign)
umändern, aber ich bekomme nur Smilies ans Terminal :-)

void transmit(unsigned char data){
  while(! (USR & (1<<UDRE)))
  ;
  UDR = data;
}

main(){...
transmit('\xxx')
...}

Autor: Eule (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
unsigned char sign[4] = "Test";
int i;
for(i=0, i<=4, i++){
transmit(sign[i]);
}

funktioniert auch, aber wie wandle ich integer in einen string?

Autor: Eule (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Muss wohl an der Eule liegen... hat sich (wieder mal) erledigt.

habe die ganze Zeit nach itos gesucht, habs ja oben schon geschrieben
itoa()

Eule

Autor: Joerg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>    OCR1AH = 0x0e;
>    OCR1AL = 0x10;

Du magst es umständlich, oder? ;-)

Warum schreibst Du nicht einfach

    OCR1A = 3600;

?

Autor: funker (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja, oft etwas umständlich. Habe nicht gewusst, dass dezimal auch möglich
ist, deshalb danke für den Hinweis.

Autor: Leonahrdt (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo !

Ich will mit dem 90S8535 einen Sekundentakt mit dem Timer1 (16Bit)
erzeugen. Hat da jemand ein Code Schnipsel???
Wäre dafür sehr dankbar.
Tagelang sitze ich davor uns probiere, aber es klappt nicht.

Autor: Joerg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Dann poste doch lieber mal, was Du schon versucht hast.

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.