Was genau meinst du mit "Preloader neu gesetzt"? Wenn du den Timer "von Hand" vorlädst, hast du eh nur durch Glück einen genauen Zeitabstand zwischen den Interrupts. Benutze den CTC-Mode.
Hallo Experten
ich habe da mal eine Prinzipfrage. Ich habe zwei Timers am Laufen (T1 und T2). T1 erzeugt mir einen IRQ alle Sekunde, der T2 läuft auf einer kleineren Frequenz.
Im Timer1 ISR wird nur ein Port getoggled und der Preloader neu gesetzt. Im T2 wird nur ein Flag-Bit gesetzt und auch der Preloader neu gesetzt.
Die Auswertung des Flags erfolgt im Hauptprogramm. Ist das Bit gesetzt, werden Zeichen an's LCD gesendet (lcd_putc()) (P. Fleury) und ein bisschen mit strcpy und sizeof gearbeitet.
Mir ist aufgefallen, dass wenn ich das Setzen des Busy-Flags im T2-ISR rauslasse, mein Timer1 wirklich präzise meinen Port toggled (nachgemessen). Wird das Flag dagegen gesetzt, dann hängt mein T1 immer wieder ein bisschen hinterher <-> ungenau. Folglich liegt die ungenauigkeit von T1 nicht am T2-Überlauf, aber an irgendetwas anderem.
Habt ihr eine Idee, ob sizeof, strcpy oder die LCD-Routinen irgendwie die Interrupts sperren/blockieren könnten?
Besten Dank,
Simon
Was genau meinst du mit "Preloader neu gesetzt"? Wenn du den Timer "von Hand" vorlädst, hast du eh nur durch Glück einen genauen Zeitabstand zwischen den Interrupts. Benutze den CTC-Mode.
MfG
Stefan
Wie schon gesagt, besser ist der CTC Modus. Mit Preload in der ISR kriegt man nur gleichmäßige Zeiten, wenn man einen hohen Prescaler hat. Mit 2 ISRs muss der Prescaler für eine ISR und den Anfang der anderen reichen. Da wird dann auch ein Prescaler von 64 schon knapp, bei 256 als Prescaler sollte es aber reichen.
Hi,
okay, also ich habs jetzt geschafft den T1 im CTC modus alle Sekunde in den Compare Match Vector springen zu lassen.
Den anderen Timer (T0) betreibe ich bisher noch "klassisch" über den Überlauf. Der T0 funktioniert aber nur dann, wenn ich den T1 unkonfiguriert lasse.
Gibt's dafür irgendeinen Grund wieso T1 im CTC Modus die anderen Timer affektiert?
Merci,
Simon
Hab den "Fehler" gefunden. Offensichtlich muss man den Timer, der im CTC Modus laufen soll in der Reihenfolge vor jenen Konfigurieren, die "normal" laufen. Verstehe ich zwar nicht aber das hat das Problem irgendwie gelöst...
Ciao,
Simon
Ne, muss man nicht.Offensichtlich muss man den Timer, der im CTC Modus laufen soll in der Reihenfolge vor jenen Konfigurieren, die "normal" laufen.
Du hast einen Fehler in deinem Code. Da du den ja aber nicht zeigst, kann ich dir auch nicht sagen welchen genau. Aber wenn ich raten soll, würde ich sagen, dass sich die beiden Timer ein Interrupt-Enable-Register teilen, und du bei der Konfiguration des einen Timers dieses mit einem "=" beschreibst, und beim anderen mit einem "|=".
MfG
Stefan
Hi,
alles klar.
Stimmt, Code sollte ich mal posten. Werde ich heute Abend nachholen, sorry.
Besten Dank soweit,
Simon
Hi,
etwas verspätet, aber trotzdem. Ihr hattet Recht, es lag am Code und einer fehlenden Veroderung im TIMSK-Register. Jetzt geht's:
Ich habe eine andere Frage, und zwar kann ich den Timer2 ja ebenfalls im CTC Modus laufen lassen. Jetzt ist es so, dass ich Frequenzen im Bereich von 60Hz bis 10kHz rausgeben möchte. Der AVR ist mit einem 16MHz Quarz getaktet. Ich dachte Anfangs, ich könnte das einfach so realisieren:Code://Timer0 so Langsam wie möglich TCCR0 |= (1<<CS02) | (1<<CS00); //Prescaler=1024 TCNT0 = 0; TIMSK |= (1<<TOIE0); //timer 1 auf sekundentakt TCCR1B = ( (1 << CS12) | (1 << CS10)| (1 << WGM12) ); // Prescaler auf 1024 und CTC mode akivieren OCR1A = 15625; // wert für 1s vorladen TIMSK |= (1 << OCIE1A) ; // Output Compare Interrupt aktiveren
Im ISR Vector hatte ich dann vor, den Port immer wenn eine gewisse Periode erreicht wurde zu togglen:Code://setze Timer2 in den CTC Modus und lasse jede µs ISR auslösen. TCCR2 |= (1<<CS20) | (1<<WGM21); // Prescaler von 1 | CTC-Modus OCR2 = 8; // Vergleichswert für 1µs Periode TIMSK |= (1<<OCIE2); // Interrupts aktivieren und damit Timer starten
Nachdem ich das ausprobierte ging das zwar, aber mir fiel auf, dass egal was ich für einen Wert in OCR2 schreibe, ich keinen Einfluss habe. Nach Überlegungen bin ich drauf gekommen, dass es sein kann, dass alles quasi zu schnell läuft, sprich viele ISRs verschluckt werden, da die Ausführung und Einsprung in die ISR ja wieder Zyklen kostet.Code:ISR(TIMER2_COMP_vect) { T2count++; if (T2count == T2period) { MOT_A_PORT ^= (1<<MOT_A_C_PIN); MOT_B_PORT ^= (1<<MOT_B_C_PIN); T2count = 0; } }
Deswegen wollte ich mir diese Form der Realisierung abschminken. Hättet ihr einen klugen Lösungsansatz, mit dem ich quasi ausgehend von der gewünschten Frequenz den passenden Prescaler und Vorladewert quasi "autodetecten" lassen könnte? (Die Formeln zur Berechnung aus dem Datenblatt kenne ich, aber das Ganze kam mir zu unelegant vor...)
Besten Dank,
Simon
Hallo nochmal,
also falls es jemanden interessiert, ich habe das ganze für Meine Zwecke (Ausgabefrequenz habe ich mal auf zwischen 35 bis 2100 Hz festgelegt) folgendermaßen realisiert:
Bin für Kritik natürlich offen.Code://toggled zwei Ausgänge ISR(TIMER2_COMP_vect) { MOT_A_PORT ^= (1<<MOT_A_C_PIN); MOT_B_PORT ^= (1<<MOT_B_C_PIN); } ... uint8_t autodetect(uint16_t *ps, uint8_t *ocr, uint16_t freq) { uint32_t i, ocra; uint32_t nenner; uint32_t prescale[] = {1,8,32,64,128,256,1024}; //mögliche Values für Timer2 lt Datenblatt... for (i = 0; i < 7; i++) { nenner = 2*prescale[i]*freq; ocra = round((16000000/nenner)); //wenn Wert gefunden wurde, der in OCRA passt, schreiben und Funktion beenden //nach Tests war immer der erste ermittelte Wert der genaueste für die Ausgabefrequenz. Alle folgenden waren ungenauer... if (ocra < 255) { *ps = prescale[i]; *ocr = ocra; return 0; } } return 1; } ... // irgendwo im Programm uint8_t ocr_value; uint16_t prescaler; if (autodetect(&prescaler, &ocr_value,isr_frequency) == 1) return 1; else { if (prescaler == 1) TCCR2 |= (1<<CS20); else if (prescaler == 8) TCCR2 |= (1<<CS21); else if (prescaler == 32) TCCR2 |= (1<<CS21) | (1<<CS20); else if (prescaler == 64) TCCR2 |= (1<<CS22); else if (prescaler == 128) TCCR2 |= (1<<CS22) | (1<<CS20); else if (prescaler == 256) TCCR2 |= (1<<CS22) | (1<<CS21); else if (prescaler == 1024) TCCR2 |= (1<<CS22) | (1<<CS21) | (1<<CS20); } TCCR2 |= (1<<WGM21); //setze CTC Modus OCR2 = ocr_value; TIMSK |= (1<<OCIE2); //starte Timer
Viele Grüße,
Simon
Ok, dann lege ich mal los.Bin für Kritik natürlich offen.
* Warum ist i ein uint32_t? uint8_t würde reichen.
* Warum das uint32_t bei prescale[]? uint16_t würde reichen.
(wenn du das änderst, muss aber auch die Zeile der nenner-Berechnung geändert werden)
* Warum ist prescale[] eine automatic- und keine static-Variable?
* Wozu soll die Funktion round() da gut sein? "16000000/nenner" ist eine Integer-Division und hat immer ein ganzzahliges Ergebnis. Da gibt es für round() rein gar nichts zu runden.
All diese Punkte produzieren keinen Fehler, drücken aber auf die Performance. Was allerdings ein Fehler ist, ist dass du bei der OCR-Berechnung für den CTC-Modus ein "-1" vergisst.
Noch was: Du scheinst 0 für "Erfolg" und 1 für "Fehler" zu verwenden. Das finde ich etwas ungeschickt, weil es genau entgegen der boolschen Logik ist (0 = False, 1 = True). Ich würde auch nicht den Prescaler-Wert speichern, sondern den Index, denn den kann man im restlichen Code dann direkt verwenden.
So in etwa würde die Funktion bei mir aussehen:
Und im restlichen Code dann:Code:uint8_t autodetect (uint8_t *ocr, uint8_t *pre, uint16_t freq) { static uint16_t prescale[] = {1,8,32,64,128,256,1024}; //mögliche Values für Timer2 lt Datenblatt... for (uint8_t i = 0; i < 7; i++) { uint32_t nenner = 2UL * prescale[i] * freq; if (nenner >= (F_CPU/256)) { *ocr = ((F_CPU+(nenner/2)) / nenner) - 1; *pre = i + 1; return 1; } } return 0; }
Code:uint8_t prescaler_index; uint8_t ocr_value; if (!autodetect(&ocr_value,&prescaler_index,isr_frequency)) return 0; TCCR2 |= (prescaler_index<<CS20); OCR2 = ocr_value;
Geändert von sternst (08.04.2011 um 02:04 Uhr)
MfG
Stefan
Lesezeichen