Forum: Compiler & IDEs Unklarheit bei Berechnung von OCR1A / Timer1-Delay
Hallo,
ich stehe grade irgendwie auf dem Schlauch:
Ich habe einen atmega 168 mit 16Mhz externem Quarz. Ich möchte eine LED
im 1-Sek-Takt blinken lassen (minimal Beispiel, natürlich ist die
eigentliche Anwendung komplexer).
Dazu lasse ich den Timer1 (16bit) im CTC (clear timer on compare match)
laufen. Um den Wert, mit dem der Timer geladen werden muss, findet sich
im Datenblatt in Abschnitt "15.9.2 Clear Timer on Compare Match (CTC)
Mode" folgende Formel:
Dabei bedeutet: (bitte korrigieren wenn falsch)
* N: der Prescaler (bei mir: 1024)
* f_OCnA : die Frequenz bei der Interrupt ausgelöst wird (Ziel: 1x pro
Sec)
* f_clk I/O : die Taktgeschwindigkeit des uC
* OCR1nA: der Wert, der in den Timer geladen muss (das Datenblatt
spricht von "TOP" im Zusammenhang mit CTC-Mode).
Um OCR1nA zu bestimmen wird die Formel umgestellt:
Ich setzte meine Werte ein (N = 1024, f_clkI/O = 1,6x10^7)
und erhalte für 1+OCR1nA den Wert 7812,5.
Diesen packe ich in meinen Code (Auszug): 1 | //PORTC &= ~(1 << PINC0); // spikes vermeiden
| 2 | DDRC = (1 << PINC0);
| 3 | // timer configurieren
| 4 | TCCR1B = ( (1 << CS12) | (1 << WGM12) ); // Prescaler auf 1024 und CTC mode akivieren
| 5 | OCR1A = 7812; // wert vorladen
| 6 | TIMSK1 = (1 << OCIE1A) ; // Output Compare Interrupt aktiveren
| 7 | sei(); // interrupts aktivieren
|
In der ISR wird ein Pin getoggelt, an dem ne LED hängt.
Ich lade den Code in den uC und die LED blinkt munter, aber NICHT im
1-Sec-Takt, sondern schneller.
Ein erster Verdacht: Fuses nicht richtig gesetzt, Systakt wird durch 8
geteilt, und siehe da, wenn ich 8x7812 in OCR1A lade, blinkt die LED
schön 1 Sec an, 1 Sec aus, wie ich es haben will.
Jedoch sagt AVRDude zu den Fuses: 1 | avrdude: Device signature = 0x1e9406
| 2 | avrdude: safemode: lfuse reads as FF
| 3 | avrdude: safemode: hfuse reads as DF
| 4 | avrdude: safemode: efuse reads as 1
|
Der Online Calc ( http://www.engbedded.com/fusecalc/ ) sagt dazu, dass
Divide clock by 8 internally; [CKDIV8=0] aus ist. Ich interpretiere das
so, dass der Systakt also die vollen 16Mhz abbekommt, wundere mich daher
über das oben beschriebene Verhalten.
Kann mir jmd. sagen, an welcher Stelle der Fehler liegt?
Vielen Dank!
p.s. hach, schön ma wieder was in latex zu notieren 8)
Robert F. schrieb:
> Ein erster Verdacht: Fuses nicht richtig gesetzt, Systakt wird durch 8
> geteilt, und siehe da, wenn ich 8x7812 in OCR1A lade, blinkt die LED
> schön 1 Sec an, 1 Sec aus, wie ich es haben will.
Deine Berechnung sieht erst mal gut aus.
Du kannst noch einen Test machen
1 | #define F_CPU 16000000UL
| 2 |
| 3 | #include <avr/io.h>
| 4 | #include <utils/delay.h>
| 5 |
| 6 | // einstellen, wo die LED hängt
| 7 | #define LED_DDR DDRx
| 8 | #define LED_PORT PORTx
| 9 | #define LED_PIN PBx
| 10 |
| 11 | int main()
| 12 | {
| 13 | LED_DDR = ( 1 << LED_PIN );
| 14 |
| 15 | while( 1 ) {
| 16 | LED_PORT |= ( 1 << LED_PIN );
| 17 | _delay_ms( 1000 );
| 18 | LED_PORT &= ~( 1 << LED_PIN );
| 19 | _delay_ms( 1000 );
| 20 | }
| 21 | }
|
wenn damit die LED 1 Sekunde an/aus ist, dann ist es nicht die CKDIV8.
Ist die LED aber 8 Sekunden an/aus, dann ist es die CKDIV8
Robert F. schrieb:
> * f_OCnA : die Frequenz bei der Interrupt ausgelöst wird (Ziel: 1x pro
> Sec)
Nein. In der Formel ist das die Frequenz, die an einem Ausgang entsteht,
wenn man diesen bei jedem Interrupt toggelt.
Du willst aber eine Frequenz in der Bedeutung "Interrupthäufigkeit".
Lass einfach in der Formel den Faktor 2 raus.
dank euch 2 für die Antworten.
Hab den Code mal in den uC geladen um die Einstellung der Fuses gegen zu
checken.
Ergebnis:
mit den o.g. Fuses blinkt die LED 1x Sekunden (damit meine ich: 1 Sek
an, 1 Sec aus, eine Periode dauert also 2 sec)
mit aktivierten 8er Teiler ( -U lfuse:w:0x7f:m -U hfuse:w:0xdf:m )
erwartungsgemäß 8sec an, 8sec aus
-> an den Fuses liegt es schon mal nicht...
Wenn ich aus der Formel die 2 raus lasse, erhalte ich ja eine Zahl
DOPPELT so groß, ich habe aber beobachtet, das sie ACHTMAL so groß sein
muss, um das gewünschte Ergebnis (1sec an, 1 sec aus) zu erhalten...
Stehe also nach wie vor auf dem Schlauch :/
Robert F. schrieb:
> Ergebnis:
> mit den o.g. Fuses blinkt die LED 1x Sekunden (damit meine ich: 1 Sek
> an, 1 Sec aus, eine Periode dauert also 2 sec)
Dann würde ich mal vermuten, dass der Controller mit dem internen
8MHz-Oszillator läuft (habe jetzt nicht die Lust, deine Fuse-Angaben zu
checken).
Denn wenn du in obige Formel für f_OCnA eine 1 einsetzt, ergeben sich
auf jeden Fall 2 Interrupts pro Sekunde, also LED 0,5 Sek an und 0,5 Sek
aus = 1 Hz Ausgangsfrequenz.
Robert F. schrieb:
> -> an den Fuses liegt es schon mal nicht...
Gut. Schon mal was.
Dann sollte man als nächstes die Timer Einstellung prüfen :-)
Du setzt CS12 und sonst nichts.
Ich hab jetzt kein Datenblatt zum 168 zur Hand, aber beim Mega16 ist das
ein Vorteiler von 256 und nicht 1024.
Das macht einen Faktor von 4.
Dazu nehmen wir noch, dass du 2 ISR Aufrufe für einmal blinken brauchst,
und wir haben die 8 beisammen :-)
Stefan Ernst schrieb:
> Dann würde ich mal vermuten, dass der Controller mit dem internen
> 8MHz-Oszillator läuft (habe jetzt nicht die Lust, deine Fuse-Angaben zu
> checken).
Verlangt ja auch keiner von dir :)
Deine Vermutung kann aber nicht zutreffend sein, da angenommen der Takt
ist 8Mhz statt 16, müsste das Ergebnis ja halb so schnell sein. Wie im
1. Post beschrieben, blinkt die LED aber 8x so schnell.
Hardwaremäßig wurde durch ziehen des Quarzes (Steckbrett) verifiziert,
dass die Schaltung nicht mit dem Internen Quarz läuft.
Falls du doch mal eine Fuse chkn willst, bereite ich mal den (dir
bestimmt wohlbekannten) Link zum anklicken vor :)
http://www.engbedded.com/fusecalc/
Stefan Ernst schrieb:
> Denn wenn du in obige Formel für f_OCnA eine 1 einsetzt, ergeben sich
> auf jeden Fall 2 Interrupts pro Sekunde, also LED 0,5 Sek an und 0,5 Sek
> aus = 1 Hz Ausgangsfrequenz.
Ich HABE für f_OCna eine 1 eingesetzt, und erhalte dann wie oben
beschrieben:
Mit 1 | OCR1A = 7812; // wert vorladen
|
erhalte ich aber nicht das gewünschte Ergebnis (2Hz Ausgangsfrequenz -
1sec an, 1 sec aus) sondern das 8 fache...
Mir geht es grade nicht darum, ob es 1 oder 2 Hz Ausgangsfrequenz
ist/heißt, sondern warum die Berechnungen um den Faktor 8 daneben gehen
:(
Ich gebe mal das vollständige Programm an, ich denke es ist grade so
noch kurz genug um es direkt anzugeben: 1 | /*
| 2 | * Eine LED soll in einer definierten zeit blinken
| 3 | *
| 4 | *
| 5 | */
| 6 |
| 7 | #include <avr/io.h>
| 8 | #include <avr/interrupt.h>
| 9 | #include <stdint.h> // uint8_t
| 10 |
| 11 | static volatile uint8_t flag;
| 12 | void init(void)
| 13 | {
| 14 | // datenstrukuren init
| 15 | flag = 0;
| 16 | // ausgangsport setzen
| 17 | //PORTC &= ~(1 << PINC0); // spikes vermeiden
| 18 | DDRC = (1 << PINC0);
| 19 | // timer configurieren
| 20 | TCCR1B = ( (1 << CS12) | (1 << WGM12) ); // Prescaler auf 1024 und CTC mode akivieren
| 21 | OCR1A = 7812; // wert vorladen (eigentlich Interrupts abschalten (vgl 15.3, s116ff, inbesonsere )
| 22 | TIMSK1 = (1 << OCIE1A) ; // Output Compare Interrupt aktiveren
| 23 | sei(); // interrupts aktivieren
| 24 |
| 25 |
| 26 | }
| 27 |
| 28 | ISR(TIMER1_COMPA_vect){
| 29 | flag = !flag; // toggeln
| 30 | // ausgang setzen
| 31 | if (flag)
| 32 | {
| 33 | PORTC |= (1 << PINC0);
| 34 | }
| 35 | else
| 36 | {
| 37 | PORTC &= ~(1 << PINC0);
| 38 | }
| 39 | }
| 40 |
| 41 | void main(void)
| 42 | {
| 43 | init();
| 44 |
| 45 | for(;;) {}
| 46 | }
|
Karl heinz Buchegger schrieb:
> Robert F. schrieb:
>
>> -> an den Fuses liegt es schon mal nicht...
>
> Gut. Schon mal was.
>
> Dann sollte man als nächstes die Timer Einstellung prüfen :-)
>
> Du setzt CS12 und sonst nichts.
> Ich hab jetzt kein Datenblatt zum 168 zur Hand, aber beim Mega16 ist das
> ein Vorteiler von 256 und nicht 1024.
>
> Das macht einen Faktor von 4.
>
> Dazu nehmen wir noch, dass du 2 ISR Aufrufe für einmal blinken brauchst,
> und wir haben die 8 beisammen :-)
kopfaufdentischschlag
1024 ist CS12 und CS10, nicht nur CS12 argl
Es muss natürlich 1 | TCCR1B = ( (1 << CS12) | (1 << CS10)| (1 << WGM12) ); // Prescaler auf 1024 und CTC mode akivieren
|
heißen.
Danke für den entscheidenden Hinweis Karl Heinz!
Und wie Stefan richtig angemerkt hat, ist die Ausgangsfrequenz jetzt 1Hz
( 0,5sec an, 0,5sec aus).
Vielen Dank, die Unklarheit ist beseitigt!
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|