Hallo, ich bastel ja gerade an einem Lichtwecker rum. Die Hardware habe ich inzwischen komplett, die Software macht mir jetzt etwas probleme. Das angehängte Programm enthält teile für den noch nicht fertigen DCF77 Empfang, ist eigentlich bisher nur die Interrupt-Routine dafür. Dann gibt es noch einen Timer, der für die Uhr zuständig ist, so wie einen zweiten Timer, der für den Phasenanschnittsdimmer gedacht ist. Wenn allerdings der Interrupt für die Nulldurchgangserkennung aktiviert wird, dann funktioniert nix mehr richtig, auf dem Display gibt es nurnoch hier und da mal ein Zeichen, keine Uhrzeit mehr. Sehe ich das richtig, das ich dem mega8 da etwas viel zumute im Moment, oder habe ich noch irgendwo nen Fehler drin? viele Grüße Biertrinker
Sehe ich das richtig, dass du die Uhr auf dem LCD die ganze Zeit in der while-Schleife aktualisierst? Das ist natürlich ein wenig heftig. Besser wäre das jede Sekunde bzw. jede halbe Sekunde zu machen.
Also float in ner ISR ist schon etwas heftig.
1 | zuenden=390-(3.9*helligkeit); |
Der AVR hat keine FPU. Peter
Jo float ist auch etwas krass. Die Float libs fressen auch mal eben ~2 KB. Immer schön mit Integer arbeiten. Einfach den ganzen Kram mal 10 oder so, dass du ohne Float auskommst. Lieber nen größeren Datentyp (long, long long) als float. Und wie gesagt, das Display nicht so oft aktualisieren, das kann nur schief gehen und ist zudem auch völlig unnütz.
> Besser wäre das jede Sekunde bzw. jede halbe Sekunde zu machen.
Am Besten wäre es, das Display nur zu aktualisieren, wenn es etwas neues
anzuzeigen gibt. Ein LCD ist nicht beliebig schnell und braucht einige
Zeit, um den Text anzuzeigen.
>Am Besten wäre es, das Display nur zu aktualisieren, wenn es etwas neues >anzuzeigen gibt. Eben und ist das nicht die Uhrzeit? Also Sekunde oder 1/2 s wenn man z.B. den berüchtigten Doppelpunkt blinken lassen will.
>> Am Besten wäre es, das Display nur zu aktualisieren, wenn es etwas neues >> anzuzeigen gibt. > Eben und ist das nicht die Uhrzeit? Also Sekunde oder 1/2 s wenn man > z.B. den berüchtigten Doppelpunkt blinken lassen will. Schon, aber wenn starr jede volle/halbe Sekunde aktialisiert wird, läuft die Aktualisierung nicht synchron mit der Zeit. Also: Ohne DP geblinke: Bei Sekundenwechsel(!) Zeit ausgeben Mit DP geblinke: Bei Sekundenwechsel(!) Zeit ausgeben mit DP an und nach 500ms Zeit ausgeben mit DP aus So meinte ich das.
>Bei Sekundenwechsel(!) Zeit ausgeben
Ja so meinte ich das auch. Ich habe meine Echtzeituhr komplett in der
ISR drin (AN von Atmel). Timer über Uhrenquarz getaktet, Teiler=128 =>
sekundengenauer Interrupt:
1 | typedef struct{ |
2 | unsigned char second; |
3 | unsigned char minute; |
4 | unsigned char hour; |
5 | unsigned char date; |
6 | unsigned char month; |
7 | unsigned int year; |
8 | }time; |
9 | |
10 | time t; |
11 | |
12 | ISR(TIMER2_OVF_vect){ |
13 | |
14 | if (++t.second==60){ |
15 | t.second=0; |
16 | if (++t.minute==60){ |
17 | t.minute=0; |
18 | if (++t.hour==24){ |
19 | t.hour=0; |
20 | if (++t.date==32){ |
21 | t.month++; |
22 | t.date=1; |
23 | }
|
24 | else if (t.date==31){ |
25 | if ((t.month==4) || (t.month==6) || (t.month==9) || (t.month==11)){ |
26 | t.month++; |
27 | t.date=1; |
28 | }
|
29 | }
|
30 | else if (t.date==30){ |
31 | if(t.month==2){ |
32 | t.month++; |
33 | t.date=1; |
34 | }
|
35 | }
|
36 | else if (t.date==29){ |
37 | if((t.month==2) && (not_leap())){ //Schaltjahr? |
38 | t.month++; |
39 | t.date=1; |
40 | }
|
41 | }
|
42 | if (t.month==13){ |
43 | t.month=1; |
44 | t.year++; |
45 | }
|
46 | }
|
47 | }
|
48 | }
|
49 | |
50 | }
|
Ist imho ideal, die Ausgabe aufm Display könnte man da einfach mit reinschreiben.
>Ist imho ideal, die Ausgabe aufm Display könnte man da einfach mit >reinschreiben. Autsch! (Meine Meinung). Ein 'call' in der ISR verbietet sich für mich. Hier schlägt mein eiserner Grundsatz zu: Mach möglichst viel mit Interrupts, aber möglichst wenig in Interrupts.
Hallo! In der ISR würde ich nur ein Flag zur Displayausgabe setzten und dann in der main zyklisch abfragen und Ausgeben. Die Displayausgabe kann durch "nichtvorteilhaftes" programmieren auch mal länger, als die ISR dauern. (Bei 1 Sek. muß man sich aber schon anstrengen :-) ) Gruß Michael
Ich halte die Laufzeit meiner Mainloop immer weit unter 1s, damit auf Tastendrücke sofort reagiert wird (<300ms) und ich mich nicht dem schmählichen Verdacht aussetze, es würde Windows in dem Gerät laufen. Das ist auch weiter kein Problem, da ich keine CPU-Zeit mit langen Wartezeiten (>1ms) vergeude, sondern dann die Rechenzeit sofort zur Mainloop zurückgebe. Ein Timerflag sagt dann der Task, sie kann weitermachen. Daher ist auch die Uhrentask in der Mainloop und der Timerinterupt setzt nur ein 1s-Flag, um diese auszuführen. Peter P.S.: Es war für mich schon eine große Überraschung, als ich mit MC-Programmierung angefangen habe, daß man Programme auch so schreiben kann, daß sie sofort auf Usereingaben reagieren. Leider scheinen das Programmierer kommerzielle Geräte (Videorekorder usw.) nicht zu wissen und lassen den User sehr lange warten bzw. ignorieren Eingaben völlig.
Hallo, erstmal viele Dank an alle, die geantwortet haben! Ich habe jetzt mal alle Tips beherzigt, das bessert auch das verhalten meines Programms etwas. Den eigentlichen Denkfehler hab ich aber selber gefunden. Ich hatte mit TCCR0 = (1<<CS00); den Vorteiler des Timer0 abgeschaltet, dann löst der ja bei jedem 255. CPU Takt aus, das kann ja kaum gut gehen. Allerdings grübel ich gerade daran, wie ich das anders Lösen könnte. Ich versuche gerade folgenden weg, der aber noch nicht ganz passt: Nulldurchgangsinterrupt setzt TCNT0 auf einen wert, der dafür sorgt, das der Timer0 nach der gewünschten Zeit zwischen 0-10 ms auslöst und den Dimmer-Triac zündet. Allerdings rätsel ich da gerade etwas dran rum, wie ich das anstellen soll? Ich brauche ja für den Dimmer schon mindestens 100 schritte, also darf der Timerinterrupt mindestens nach 25 ms auslösen und maximal nach 10 ms. Im moment habe ich einen 10 Mhz Quarz drin, da hätte ich noch 12 und 16mhz mit denen ich arbeiten könnte. Wenn ich jetzt einen Vorteiler von 256 nehme, dann habe ich ja knapp 152,59 überläufe pro Sekunde. Damit ist der ja ein bischen zu schnell für mein Vorhaben. Also knapp 6,5 ms bis zum Überlauf, wenn man bei 0 beginnt. Nehme ich einen vorteiler von 1024, dann habe ich noch knapp 38 Überläufe pro Sekunde, also knapp 26,3 ms, das könnte man doch schon akzeptieren, oder? Oder denke ich da total falsch und sehe die Lösung einfach nicht? viele Grüße Biertrinker
Versuchs doch mal mit Output Compare. Damit bekommst du Timings mit der Auflösung Deines Timer-Taktes. Wenn Dir das zu kompliziert ist, könntest Du das Timerregister in der Init und in der Interrupt-Routine auch voreinstellen. Es ist mir so wie so ein Rätsel, warum so viel mit Overflows rumgetan wird. Gruß Matthias
Hallo, das voreinstellen mache ich ja bereits. Funktioniert auch schon ganz brauchbar, allerdings muss ich sagen, das die 80 Schritte, die ich jetzt mit dem 10 Mhz Quarz in den 10ms unter bekomme doch etwas wenig sind. Man kann doch deutlich sehen, wie die Helligkeit springt. Werde jetzt mal versuchen das ganze mit nem 16 Mhz Quarz zu machen, dann habe ich ein paar mehr Stufen beim Dimmen. Leider brauche ich den 16Bit Timer schon für die Uhr, oder hat jemand ne idee, wie ich die Uhr mit nem 8 Bit Timer realisieren könnte? viele Grüße Biertrinker
> Leider brauche ich den 16Bit Timer schon für die Uhr,
Der 16-Bit-Timer kann mehrere Dinge gleichzeitig erledigen.
Nimm einen Compare für die Uhr, aber ohne CTC. In der ISR (z.B. alle
100ms) liest Du einfach den Compare-Termin des aktuellen Interrupts aus,
addierst das Intervall drauf und schreibst es ins OCR1x-Register zurück.
Dann einen Zeitvorteiler laufen lassen und bei Unterlauf (jede Sekunde)
den Merker für die Mainloop setzen.
In der Nulldurchgangs-ISR (am besten mittels ICP-Interrupt) nimmst Du
den momentanen Zeitstempel des Timers, addierst Deine Verzögerung bis
zum geplanten Zündtermin auf und schreibst es in das OCR-Register der
zweiten Comare-Einheit. In deren ISR zündest Du dann Deinen Triac.
Wichtig: Der Timer läuft frei durch, keine ISR hat das Recht, den
Timerstand zu manipulieren. Als Dank dafür kannst Du beide Compares und
ICP quasi parallel benutzen. Sie stören sich nichtmal gegenseitig, da
sie nicht allzu zeitkritisch sind, denn die OCR1x-Werte sind fest
(laufen nicht weg wie der Timerstand) und ICP scannt den Wert schon beim
Pegelwechsel am ICP-Pin.
Mit Code kann ich Dir nicht dienen, ich werkele in ASM.
...
Hallo Hannes, das klingt ja mal sehr interessant das ganz. An sowas in der Richtung hatte ich sogar schonmal selber gedacht, habe das aber wieder verworfen, weil es mir zu kompliziert vorkam ;-) Sehe ich das richtig, das bei dieser variannte der Überlauf komplett irgnoriert wird, also ich meine nicht den Interrupt, sonder den umstand, das der Timer überläuft. Dadurch, das ich auch bei den Compare-Werten einfach immer blind Addiere laufen die genau so über und ich komme wieder unten raus, richtig?
Aike Terjung wrote: > Hallo Hannes, > > das klingt ja mal sehr interessant das ganz. An sowas in der Richtung > hatte ich sogar schonmal selber gedacht, habe das aber wieder verworfen, > weil es mir zu kompliziert vorkam ;-) Ist es eigentlich nicht... > Sehe ich das richtig, das bei > dieser variannte der Überlauf komplett irgnoriert wird, also ich meine > nicht den Interrupt, sonder den umstand, das der Timer überläuft. Der Timer selbst wirft seinen Übertrag beim Überlauf doch auch weg. > Dadurch, das ich auch bei den Compare-Werten einfach immer blind Addiere > laufen die genau so über und ich komme wieder unten raus, richtig? Beides, Timer und Berechnung, ist eine 16-Bit Zahl (für mich in ASM zwei Register, für Dich vermutlich ein uint16), wenn sie überläuft, beginnt sie von vorn. Das System korregiert sich also selbst. Sieh den Wertevorrat einfach als Ring. Da Du Zündwinkel und Zeitzählung unter einen Hut bringen willst und dabei vermutlich eine hohe Auflösung für den Zündwinkel anstrebst, bietet sich ein Zeitmess-Intervall von 20 ms an (20000 bei 1 MHz Controllertakt und Timer-Vorteiler 1 zu 1). Mit einem Teiler durch 50 erhältst Du dann den Sekundentakt (per Merker der Mainloop melden). Mit einem weiteren Merker kannst Du der Mainloop bei Teilerstand 25 mitteilen, dass sie den Doppelpunkt der Uhr löschen sollte. Das Intervall von 20 ms ist auch hervorragend zum Entprellen von Tastern geeignet, die Du vermutlich zum Parametrieren (Stellen der Weckzeiten) brauchst. Da die Entprellung nur wenige Takte braucht (11 Takte in der Minimalversion, also ohne Tasten-Repeat, Variablen in Registern, ASM), kann sie in der ISR mit erledigt werden. Man kann sie aber auch als Job der Mainloop über Merker aufrufen, um die ISRs möglichst kurz zu halten. ...
Hallo Hannes, ich glaube ich habe es jetzt kapiert, erstaunlich simpel ;-) Ich vollziehe das gerade nochmal für meinen 10 Mhz Quarz nach: Also, ich habe 10.000.000 Takte pro sekunde. Da ja der Comparewert noch in 16 Bit passen muss, sollte der also kleiner als 65.536 sein. Nehme ich also einen Vorteiler von 8, dann wird mein Timer 1.250.000 mal pro Sekunde Inkrementiert. Damit kann ich schonmal den Dimmer mit 12500 Stufen Dimmen - Wow, das ist viel im vergleich zu 80, die vorher gingen ;-) Dafür muss ich ja nur den 2. Comparewert auf den aktuellen stand beim Nulldurchgangs-ISR + irgendwas zwischen 0 und 12500 setzen. Für die Uhrzeit erhöhre ich dann den 1. Comparewert immer um 25.000 und habe damit 20ms Auflösung, nach 50 aufrufen ist dann eine Sekunde vorbei, genau wie bei deinem Beispiel. Super, alles Korrekt so? vielen Dank Biertrinker
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.