PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [ERLEDIGT] 1 Interrupt pro Sekunde



Icon2k
03.09.2011, 14:53
Hy,
ich brauche einen Interrupt pro Sekunde. Mein uC läuft mit 1MHz und soll später mit 16MHz takten. Nach meinem Brechnungen müsste ich bei 1MHz (16MHz) Takt, einen Prescaler von 64 (1024) benutzen und, 1 000 000 / 64 = 15 625 (16 000 000 / 1024 = 15625), einen 16 Bit Timer von 49911 (65536 - 15625) zählen lassen um einen Interrupt pro Sekunde zu bekommen.

Zum Testen lasse ich in diesem Interrupt eine LED Blinken, die gefühlt aber nur alle 2-3 Sekunden wechselt.

Hab ich einen Fehler in meiner Rechnung, oder muss ich den Fehler woanders suchen?
MfG Icon

radbruch
03.09.2011, 15:03
49911 /15625 ergibt zufällig gefühlte 2-3 Sekunden. Läuft dein Timer vielleicht im CTC-Modus mit OCRx=49911? Dein Rechenweg scheint mir richtig.

Kampi
03.09.2011, 16:09
Aus folgendem Grund:
Timer startet und nach 1 Sekunde kommt ein Interrupt der die LED anschaltet. Danach startet der Timer neu und nach nochmal 1 Sekunde geht die LED wieder aus. Meinst du das vielleicht?
Weil die Rechnung ist richtig. Wenn es nicht es nicht geht liegt es an deiner Timerkonfiguration.

radbruch
03.09.2011, 17:09
Selbstverständlich mußt du in der Überlauf-ISR das Zählregister des Timers wieder mit dem errechneten Startwert laden. Aber das weißt du ja sicher alles. Vielleicht solltest du uns noch dein Testprogramm zeigen.

Icon2k
03.09.2011, 19:23
So hier ein Auschnitt aus meinen Code, ich hab eine LED Matrix:


#if F_CPU == 1000000
# define PRESCALER (1 << CS10) | (1 << CS11)
#elif F_CPU == 16000000
# define PRESCALER (1 << CS12) | (1 << CS10)
#endif

/*
...
*/

ISR(TIMER0_OVF_vect)
{
ml_plex();
px_send();
}

ISR(TIMER1_OVF_vect)
{
TCNT1 = 49911; // 65536 - 15625

ml_write [0] = ~(ml_write[0]);
ml_write [1] = ~(ml_write[1]);
ml_write [2] = ~(ml_write[2]);
ml_swap = 1;
}

void init(void)
{
TCCR0 |= (1 << CS01);
TCCR1B |= PRESCALER;
TCNT1 = 49911; // 65536 - 15625
TIMSK |= (1 << TOIE1) | (1 << TOIE0);
sei( );
}



Ich benutze den 8Bit Timer um die LED Matrix zu multiplexen und den 16Bit Timer um das Bild umzuschalten. Ich denke das wird der wichtige Teil sein. Davor wird nur der ml_write buffer befüllt, so dass alle LEDs brennen und es wird eine Endlosschleife durchlaufen.

Wenn die Rechnung stimmt muss der Fehler ja irgendwo im Programm liegen oder?

EDIT1: Es sind eher gefühlte 2 Sekunden wie 3, aber definitiv länger als 1 Sekunde

Icon2k
03.09.2011, 20:26
Hier noch ein kleines Video vom Testaufbau. Dann könnt ihr euch von der Zeit selbst ein Bild machen.

PS: Sry für die Quali, die Kamera vom Galaxy S schafft wohl die kurze Entfernung nicht.


http://www.youtube.com/watch?v=zISQ2m_ZueQ

Kampi
03.09.2011, 20:37
In deiner Timer 1 ISR lädst du 49911 in das Timer Register. Ich weiß jetzt nicht welchen Controller du hast aber ich meine bei allen AVRs wären die Timerregister nur 8bit breit. D.h. du musst die 49911 in High und Low aufteilen und in das High bzw. Low Register schreiben. Du kannst keine 49911 in ein 8bit Register quetschen.
Ich denke das sollte das Problem lösen.

Icon2k
03.09.2011, 20:48
Das wusste ich nicht. Ok, wie teile ich die Zahl denn Richtig auf? Einfach abschneiden?

49911 ==> 1100 0010 1111 0111
Also ist:
High = 1100 0010 => 194
Low = 1111 0111 => 247

Kampi
03.09.2011, 20:59
Jop genau.
Das mit der 16bit Zahl in einem Timerregister kann schon deswegen nicht funktionieren weil die AVRs alle 8bit Prozessoren sind. Und über einen 8bit Datenbus kannst du keinen 16bit Timerwert schicken :D
Deswegen musst du die Zahl aufteilen. Und das macht man so wie du gesagt hast.
Und ich glaube du musst das Timer High Register als erstes beschreiben und dann das Low Register.

Icon2k
03.09.2011, 21:02
Ok, vielen Dank euch, jetzt könnte eine Sekunde hinkommen!

PS: Ich würde das Thema jetzt als Gelöst Markieren, kann aber nicht herausfinden wie das geht .. können das nur Admins?

Kampi
03.09.2011, 21:10
Nein das kannst du auch machen. Musst du mal gucken indem du auf "Bearbeiten" klickst. Kann dir selber nicht genau sagen wie das geht.
Und die 1 Sekunde kannst du am besten mit einer Stoppuhr oder einer Digital Uhr mit Sekundenanzeige nachprüfen ;)

Icon2k
03.09.2011, 21:17
Ich will ne Uhr draus bauen, also lass ich die Sekunde jetzt erst mal ne Sekunde sein und mess dann Später die Verzögerung über nen ganzen Tag ;-) Ist, glaube ich, einfacher wie die Sekunde jetzt zu messen.

EDIT1: Man, bin ich blind: Thema als erledigt Markieren ist ganz einfach. Einfach oben auf "Themen-Optionen"->"Markiere Thema als erledigt"

Kampi
03.09.2011, 21:25
Für eine Uhr würde ich aber eher ein 32kHz Quarz nehmen. Lässt sich besser runterfallen als 1 bzw. 16 MHz ;)

Slein
03.09.2011, 22:02
1 Int pro Sekunde geht auch stressfreier:

// ****** timer int, send message to counter tiny
ISR(SIG_OUTPUT_COMPARE1A) {
// --- irgendwas machen
}

// ****** init 16bit timer
void init_timer(void) {
// --- set counter to 0
TCNT1 = 0;
// --- control registers
TCCR1A = 0;
TCCR1B = (1<<WGM12) | (1<<CS12); // CTC mode, prescaler 256 for 8 MHz
TIMSK = 0x10; // besser: TIMSK |= 0x10; da TIMSK hier auch für timer0+2 zuständig ist...
// --- set max for compare
OCR1A = 0x7a12; // 0x7a12 bei clk/256 @ 8,0 MHz ohne Rest :)
}
Hier wird der Timer1 (16Bit) eines ATMega32 im CTC Mode benutzt. Dabei läuft der Zähler von 0 bis Max (hier 0x7a12 bzw. 31.250) und wird dann da automatisch auf 0 gesetzt, wodurch man sich das counter neu setzen jedesmal in der ISR spart :). da du noch nen 8 bit timer benutzt kann es sonst zu überschneidungen kommen, was das neu setzen des Counters verzögern würde, die Sekunden wären also ab und zu ein paar Takte länger.

Für ne LED Matrix tät ich eher empfehlen in der refresh ISR die frames zu zählen und danach das Bild zu ändern, bzw. die Zeit zu zählen.
Noch schicker wirds wenn du 2 Framebuffer benutzt und dann immer im gerade nicht angezeigten rummalst, dann wirkt das Ganze noch stabiler :)
Einen externen Uhren IC könnte man dann noch per Knopfzelle puffern (z.B. DS1307 hat da direkt Pins für), die Synchronisation per GPS ist einfacher als man denkt (und für <€40,00 machbar!) und beides täte auch 1x pro Sekunde nen Puls liefern. Also am Besten gleich RXD/TXD, Int0 oder Int1 und SDA+SCL freilassen, spart später das umstricken...

Ich hoffmal das war jetzt nützlich und halbwegs verständlich :P

Icon2k
04.09.2011, 22:18
Ja ich hatte eigentlich nen 32k Quarz, aber ich hab meine Atmega32 über falsche Fusebits zerstört und hab jetzt nur noch nen Atmega8 und da ist es nicht so einfach nen 2ten Timer anzuschliessen.
Ich benutze jetzt den 16 Bit Timer, so wie im ersten Post beschrieben um die Zeit zu "zählen" und den 8 Bit Timer um die Matrix zu multiplexen. Mal schauen wie genau die Uhr dann wird und ob ich dann noch was ändern soll.

PS: Hab 2 Buffer, damit nichts flackert^^