Forum: Mikrocontroller und Digitale Elektronik Interrupts stören Timer


von Aike T. (biertrinker)


Angehängte Dateien:

Lesenswert?

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

von Timmo H. (masterfx)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

Also float in ner ISR ist schon etwas heftig.
1
zuenden=390-(3.9*helligkeit);
Der AVR hat keine FPU.


Peter

von Timmo H. (masterfx)


Lesenswert?

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.

von tastendrücker (Gast)


Lesenswert?

> 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.

von Timmo H. (masterfx)


Lesenswert?

>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.

von tastendrücker (Gast)


Lesenswert?

>> 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.

von Timmo H. (masterfx)


Lesenswert?

>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.

von tastendrücker (Gast)


Lesenswert?

>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.

von Timmo H. (masterfx)


Lesenswert?

Dann setzt man eben einfach ein Flag in der ISR.

von Michael G. (glunzl)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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.

von Aike T. (biertrinker)


Lesenswert?

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

von Matthias Kölling (Gast)


Lesenswert?

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

von Aike T. (biertrinker)


Lesenswert?

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

von Hannes L. (hannes)


Lesenswert?

> 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.

...

von Aike T. (biertrinker)


Lesenswert?

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?

von Hannes L. (hannes)


Lesenswert?

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.

...

von Aike T. (biertrinker)


Lesenswert?

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

von Hannes L. (hannes)


Lesenswert?

> Super, alles Korrekt so?

Ich sehe da keine Fehler...

...

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
Noch kein Account? Hier anmelden.