Hallo zusammen,
ich habe eine Funktion (myZeit()) geschrieben, die in der Timer ISR jede
Millisekunde aufgerufen wird.
Die Funktion soll mir Rückschlüsse über die Laufzeit des uCs geben, also
wie lang ist er schon eingeschaltet.
Die ISR an sich wird alle 500 us aufgerufen.
varZeit ist ein struct:
1
typedefstruct
2
{
3
int16_tMillisekunde;
4
byteSekunde;
5
byteMinute;
6
byteStunde;
7
INT16_tTag;
8
byteJahr;
9
}strZeit;
10
11
12
voidmyZeit()
13
{
14
if(++varZeit.Millisekunde==1000)
15
{
16
varZeit.Millisekunde=0;
17
if(++varZeit.Sekunde==60)
18
{
19
varZeit.Sekunde=0;
20
if(++varZeit.Minute==60)
21
{
22
varZeit.Minute=0;
23
if(++varZeit.Stunde==24)
24
{
25
varZeit.Stunde=0;
26
if(++varZeit.Tag==365)
27
{
28
varZeit.Tag=0;
29
varZeit.Jahr++;
30
}
31
}
32
}
33
}
34
}
35
}
Nun meine Frage(n):
1. Ist diese Funktion so umsetzbar oder habe ich einen Gedankenfehler?
2.Wären diese Abfragen zuviel für die ISR bzw. kann es zu Problemen
führen, wenn weitere ISRs laufen?
3. Gibt es eine elegantere Möglichkeit oder habt ihr eine Idee, wie man
es eleganter lösen könnte?
Ich hoffe, ich habe nichts vergessen und die Fragen sind verständlich.
Vielen Dank für eure Hilfe.
Viele Grüße
HB.
Hans Bode schrieb:> int16_t
Hast/brauchst du auch negative Tage?
> byte Jahr;
Und nur 255 Jahre?
> 1. Ist diese Funktion so umsetzbar
Ja.
> oder habe ich einen Gedankenfehler?
Ja, du vergisst die Schaltjahre...
BTW: Monate und Jahre müssen nicht unbedingt im Interrupt erledigt
werden. Da macht es nichts aus, wenn die mal 10ms später aktualisiert
werden...
> 3. Gibt es eine elegantere Möglichkeit oder habt ihr eine Idee, wie man> es eleganter lösen könnte?
Ich würde das ausserhalb der ISR in der Hauptschleife machen:
In der ISR wird nur bis 1000 gezählt, und dann ein Sekundenflag gesetzt.
Das wird dann in der Hauptschleife entsprechend deiner Vorlage
ausgewertet.
Ja. Ja. So etwa. Nicht alle jahre haben 365 Tage. Ich wuerd das Jahr
weglassen, und mit Longint Tag arbeiten. Das Teil wird in einer Pyramide
eingemauert ?
Und die muss alle 500us aufgeufen werden weil die Information so
wahnsinig wichtig ist ? Einmal die Sekunde waere besser, oder das
maximale timer interval.
Hans Bode schrieb:> 2.Wären diese Abfragen zuviel für die ISR
Die paar Abfragen und Erhöhungen wirst du nicht wirklich merken.
Das sieht nur im C Code nach viel aus. Wenn man sich aber den Assembler
Code dazu ansieht merkt man ... das sind eine Handvoll Befehle .... wenn
überhaupt.
Das einzige:
Zieh den Code selber in die ISR rein.
Funktionsaufrufe aus einer ISR heraus sind generell teuer.
Lothar Miller schrieb:> Ich würde das ausserhalb der ISR in der Hauptschleife machen:> In der ISR wird nur bis 1000 gezählt, und dann ein Sekundenflag gesetzt.> Das wird dann in der Hauptschleife entsprechend deiner Vorlage> ausgewertet.
Vielen Dank für die Antwort.
Schaltjahre sind jetzt erstmal nicht so wichtig ;)
Aber ich werde die Funktion umschreiben, so dass nur noch ein
Sekundenflag gesetzt wird, daran habe ich gar nicht gedacht und schon
zig-Mal in anderen Versionen gemacht. Danke :)
A...aha Soooo. schrieb:> Und die muss alle 500us aufgeufen werden weil die Information so> wahnsinig wichtig ist ? Einmal die Sekunde waere besser, oder das> maximale timer interval.
Ich brauch die 500 us Takt, da ich noch Messungen vornehmen möchte und
dazu setze ich alle 500 us ein Flag mit dem ich dann weiterarbeiten kann
:)
Lothar Miller schrieb:>> int16_t> Hast/brauchst du auch negative Tage?
Ich glaube, ich stelle die Datentypen auf unsigned um :) Danke für den
Hinweis.
Jetzt noch eine Frage:
Ich habe das struct als volatile definiert und habe gelesen, dass man
die volatile Variable zuvor in eine Temp-Variable (nicht-volatile)
sichern sollte und am Ende wieder zurückschreiben - ist das hier
notwendig/sinnvoll?
Danke für die schnellen Antworten.
Karl heinz Buchegger schrieb:> Das einzige:> Zieh den Code selber in die ISR rein.> Funktionsaufrufe aus einer ISR heraus sind generell teuer.
Wäre eine Lösung, die Funktion als "inline" zu definieren?
Hans Bode schrieb:> Ich habe das struct als volatile definiert und habe gelesen, dass man> die volatile Variable zuvor in eine Temp-Variable (nicht-volatile)> sichern sollte und am Ende wieder zurückschreiben - ist das hier> notwendig/sinnvoll?
Es wird nichts oder nicht viel bringen.
Auch das Umkopieren kostet Taktzyklen und du greifst auf jede Variable
mehr oder weniger maximal 3 mal zu. Das wird nicht wirklich lohnen. Da
kostet dir das Hin und Her Kopieren mehr Takte als du einsparen kannst.
Zieh lieber den Funktionsinhalt in die ISR hinein. Das bringt dir viel
mehr.
(Ich würde hier ausnahmsweise auch kein Flag für die Hauptschleife
machen. Auf die Art kann die Hauptschleife machen was sie will und wenn
sie 10 Sekunden wartet, ist das auch kein Problem - die Uhr läuft
deswegen unbeirrt weiter solange nur die Interrupts aktiviert sind)
Hans Bode schrieb:> Karl heinz Buchegger schrieb:>> Das einzige:>> Zieh den Code selber in die ISR rein.>> Funktionsaufrufe aus einer ISR heraus sind generell teuer.>> Wäre eine Lösung, die Funktion als "inline" zu definieren?
Kann man so nicht sagen.
inline ist ein Hinweis für den Compiler. Er kann aber er muss nicht
inlinen.
Generell würde ich eh "nur" die Sekunden oder sogar Minuten in einem
passend großem int (oder mehreren ablegen) und wenn man dan wirklich an
details interessiert ist dann erst die Stunden..Tage...Wochen...Jahre...
daraus berechnen.
Ich wuerde nur die Sekunden in einer ulong hochzaehlen.
Bei der Abfrage kann man diese Sekunden dann in YYYY.MM.DD HH.MM.SS
umrechnen. So wie es auch im PC gemacht wird. Da werden auch nur die
Sekunden vom 1.1.1980 oder 1.1.1970 gezaehlt und bei der Abfrage
umgerechnet.
Karl heinz Buchegger schrieb:> (Ich würde hier ausnahmsweise auch kein Flag für die Hauptschleife> machen. Auf die Art kann die Hauptschleife machen was sie will und wenn> sie 10 Sekunden wartet, ist das auch kein Problem - die Uhr läuft> deswegen unbeirrt weiter solange nur die Interrupts aktiviert sind)
Ich könnte ja nur die Stunden, Tage, Jahre (und evtl. Minuten) in die
Hauptschleife packen, da es da nicht genau auf die Sekunde ankommt (bei
Minuten bin ich mir nicht so sicher).
Und den Rest der Funktion kopiere ich in die ISR.
Läubi .. schrieb:> Generell würde ich eh "nur" die Sekunden oder sogar Minuten in einem> passend großem int (oder mehreren ablegen)
Hat das denn Vorteile oder einfach nur um, ich sage mal, Variablen zu
"sparen"?
Vielen Dank für eure Antworten. Das hilft mir sehr.
Hans Bode schrieb:> Hat das denn Vorteile oder einfach nur um, ich sage mal, Variablen zu> "sparen"?
Deine ganze Routine dazu besteht nur aus ein
Seconds++;
Und das wars.
Helmut Lenzen schrieb:> Ich wuerde nur die Sekunden in einer ulong hochzaehlen.
Wäre das denn schneller? Ich meine long ist ja einiges größer als byte?
Ich vermute mal, dass es schneller ist, weil man nur noch einen Befehl
benötigt, der die Variable (und nicht 8 Variablen) inkrementiert?
Hans Bode schrieb:> Helmut Lenzen schrieb:>> Ich wuerde nur die Sekunden in einer ulong hochzaehlen.>> Wäre das denn schneller? Ich meine long ist ja einiges größer als byte?> Ich vermute mal, dass es schneller ist, weil man nur noch einen Befehl> benötigt, der die Variable (und nicht 8 Variablen) inkrementiert?
In so einem Fall gibt es nur eines.
Schalt das Listfile ein und sieh im Assembler Code nach.
Alles andere ist stochern im Nebel.
Hans Bode schrieb:> Ich könnte ja nur die Stunden, Tage, Jahre (und evtl. Minuten) in die> Hauptschleife packen, da es da nicht genau auf die Sekunde ankommt (bei> Minuten bin ich mir nicht so sicher).>> Und den Rest der Funktion kopiere ich in die ISR.
Aber aufpassen: du wirst bei so einer Aufteilung dann das Problem
bekommen, dass die Sekunden (und evtl. Minuten) sofort hochgezählt
werden, die restliche Zeit aber später...
Das kann dann z.B. bei einer Wecker-Funktion (Zeitvergleich) zu
seltsamen Effekten führen, weil die Sekunden im Interrupt schon von 59
auf 0 übergelaufen sind, aber die Minuten noch nicht aktualisisert
wurden.
An so einem Fehler kannst du dich dämlich suchen... ;-)
Lothar Miller schrieb:> An so einem Fehler kannst du dich dämlich suchen... ;-)
Genau.
Bei solchen Sachen sollte man 'die Zeit', egal wie sie dargestellt wird,
als integrale Einheit betrachten und immer alles durchziehen.
Wir reden doch hier nicht von einem zeitaufwändigem Datenbank update.
Wenn du Sekunden, Minuten, Tage etc getrennt haben willst, dann sind das
6 Bytes die im Fall des Falles erhöht werden. Das sind Peanuts im
Vergleich zum Rest. Nichts worüber es sich lohnt grossartig
philosophisch nachzudenken.
Die paar Takte die dafür draufgehen lohnen das Risiko nicht, dass man
sich da Race Conditions einhandelt.
Also, wenn ich die Zeit direkt aufteilen möchte, packe ich die ganzen
Abfragen von der Funktion einfach in die ISR. Die paar Bytes machen
keinen Unterschied. Ok :)
Die Version mit den Seconds:
Ich habe mal im Listfile nachgeschaut (ist das erste Mal, dass ich mir
das anschaue - ich hoffe, ich interpretiere es richtig):
Pro Abfrage sind es in etwa 13 Assembler-Befehle. Damit würde die
Version, die ich geschrieben habe einiges mehr an Befehlen abarbeiten
müssen und somit länger dauern.
Hier mal ein Ausschnitt für die Version mit Seconds(hier: varZeitInt32)
als einzige Variable (und Milliseconds (varMS))
1
113if(++varMS==1000)
2
\??TIMER_A0_ISR_1:
3
\000000D0....LDIR30,LOW(varMS)
4
\000000D2....LDIR31,(varMS)>>8
5
\000000D48180LDR24,Z
6
\000000D68191LDDR25,Z+1
7
\000000D89601ADIWR25:R24,1
8
\000000DA....LDIR30,LOW(varMS)
9
\000000DC....LDIR31,(varMS)>>8
10
\000000DE8380STZ,R24
11
\000000E08391STDZ+1,R25
12
\000000E23E88CPIR24,232
13
\000000E4E003LDIR16,3
14
\000000E60790CPCR25,R16
15
\000000E8F471BRNE??TIMER_A0_ISR_2
16
114varZeitInt32++;
17
\000000EA....LDIR30,LOW(varZeitInt32)
18
\000000EC....LDIR31,(varZeitInt32)>>8
19
\000000EE8100LDR16,Z
20
\000000F08111LDDR17,Z+1
21
\000000F28122LDDR18,Z+2
22
\000000F48133LDDR19,Z+3
23
\000000F65F0FSUBIR16,255
24
\000000F84F1FSBCIR17,255
25
\000000FA4F2FSBCIR18,255
26
\000000FC4F3FSBCIR19,255
27
\000000FE8300STZ,R16
28
\000001008311STDZ+1,R17
29
\000001028322STDZ+2,R18
30
\000001048333STDZ+3,R19
Edit:
Ich muss mich korrigieren:
Die Version mit einer Variablen entsprechen 33 Befehlen.
Meine alte Version: Im Minimum: 24 Befehle - Maximum: 64 Befehle(wenn
alle If-Anweisungen durchlaufen werden)
Genau das was Läubi vor mir gesagt hat ist mir sofort durch den Kopf
gegangen. Warum berechnest Du 2000 mal pro Sekunde Sekunden Minuten
Stunden Tage Monate und Jahre. Wann und wieoft brauchst Du diese Zeit?
Incrementiere mit einem long oder 64-Bit long Wert und mach die
Zeiterfassung/Auswertung so wie es C, Java und andere Sprachen machen.
Die teuere Auswertung in das Kalenderformat erst dann wenn man es
braucht.
Das ist dann zwar programmtechnisch aufwendiger aber du kannst alle
Spezialitäten (Monatslänge, Schaltjahre) problemlos erschlagen und hast
einen einfachen kleinen und wenig Speicher benötigenden Zeitwert, den
man auch problemlos irgendwo speichern oder übertragen kann.
Nachtrag:
Ich stelle mal eine kleine Rechnung an:
Angenommen, der uC läuft jeden 1000. Tick alle Befehle der If-Struktur
durch, dann braucht der bei jedem 1000. Tick: 64 CPU-Zyklen, um die
Struktur abzuarbeiten.
Die 999 zuvor hat er mit je 24 CPU-Zyklen abgearbeitet.
In Summe: 24040 CPU-Zyklen für 1000 Aufrufe.
Bei der Methode mit einer Variablen wäre das dann theoretisch:
33*1000 = 33000 CPU-Zyklen.
Wäre die Rechnung so korrekt?
Ich habe jetzt angenommen, dass für jeden Assembler-Befehl genau ein
CPU-Takt benötigt wird.
Vielen Dank für eure Hilfe.
(Das ist jetzt nur rein interessehalber)
Udo Schmitt schrieb:> Das ist dann zwar programmtechnisch aufwendiger aber du kannst alle> Spezialitäten (Monatslänge, Schaltjahre) problemlos erschlagen und hast> einen einfachen kleinen und wenig Speicher benötigenden Zeitwert, den> man auch problemlos irgendwo speichern oder übertragen kann.
Ich werde es vermutlich umschreiben, auf die genannte Methode. Scheint
ja einige Vorteile mit sich zu bringen.
Danke euch allen.
Noch was, wenn Du zeiten nur in einem long oder 64bit Long speicherst,
kannst Du so ein Datum/Zeitwert auch einfach vergleichen. Denk darüber
nach wie Du 2 Werte speichern müsstest (structs) und wie aufwendig für
dein Format ein Vergleich auf größer kleiner gleich ist!
Hans Bode schrieb:> Bei der Methode mit einer Variablen wäre das dann theoretisch:> 33*1000 = 33000 CPU-Zyklen.
Warum? Du sollst diese Variable jede Sekunde hochzaehlen nicht im ms
Schritten.
Helmut Lenzen schrieb:> Warum? Du sollst diese Variable jede Sekunde hochzaehlen nicht im ms> Schritten.
Ich wollte aber gern die Millisekunden für andere Zwecke mitloggen, da
kann ich die doch einfach mit verwenden ;)
Udo Schmitt schrieb:> Noch was, wenn Du zeiten nur in einem long oder 64bit Long speicherst,> kannst Du so ein Datum/Zeitwert auch einfach vergleichen. Denk darüber> nach wie Du 2 Werte speichern müsstest (structs) und wie aufwendig für> dein Format ein Vergleich auf größer kleiner gleich ist!
Da ist was dran. Wurde ja auch schon erwähnt.
Ich habe es jetzt umgeschrieben und verwende nur noch Millisekunden und
Sekunden; den Rest berechne ich dann in der main durch entsprechende
Funktionen.
Vielen Dank für eure große Hilfe.
Hans Bode schrieb:> Die 999 zuvor hat er mit je 24 CPU-Zyklen abgearbeitet.> In Summe: 24040 CPU-Zyklen für 1000 Aufrufe.
Das ist aber eine Milchmädchenrechnung, die so nicht gilt.
Natürlich werden in den allermeisten Fällen nur ganz wenige Takte
anfallen (BTW: Deine Zahlen kommen mir hoch vor, hast du die Optimierung
aktiviert?), entscheidend ist aber nicht der Durchschnitt sondern der
schlimmste Fall, das Maximum.
Wenn du Badewart bist und alle Minute zur Ablenkung immer nur 3 Sekunden
nicht aufs Becken siehst, du aber einmal am Tag 30 Minuten aufs Klo
gehst und in diesen 30 Minuten ertrinkt ein Kind, kannst du dich auch
nicht darauf berufen, dass du im Durchschnitt das Becken nur 3.1
Sekunden pro Minute aus den Augen gelassen hast.
Karl heinz Buchegger schrieb:> Wenn du Badewart bist und alle Minute zur Ablenkung immer nur 3 Sekunden> nicht aufs Becken siehst, du aber einmal am Tag 30 Minuten aufs Klo> gehst und in diesen 30 Minuten ertrinkt ein Kind, kannst du dich auch> nicht darauf berufen, dass du im Durchschnitt das Becken nur 3.1> Sekunden aus den Augen gelassen hast.
Ja, du hast recht. Meine Rechnung ist eine Milchmädchenrechnung - sollte
auch nur zum groben Vergleich (gaanz grob) dienen.
Karl heinz Buchegger schrieb:> (BTW: Deine Zahlen kommen mir hoch vor, hast du die Optimierung> aktiviert?
Ich habe einfach nur die Zeilen zusammengezählt aus dem lst-file. Und da
kam ich auf 24 bzw. 33.
Ich habe den Compiler einfach kompilieren lassen. Ich habe keine
Optimierung eingestellt, aber an den Einstellungen habe ich sowieso
nichts vorgenommen - habe den Compiler voreingestellt bekommen.