Hallo!
ich habe ein Problem, undzwar habe ich einen Interrupt, der absoluten
Vorrang hat, das heißt es muss auch möglich sein, dass er einen anderen
Interrupt unterbricht.
Ist dies überhaupt möglich?
Ich verwende AVR-GCC und einen AtMega644
Danke euch.
Besten Gruß
Hubert
Nein. Bei den AVRs unterbricht ein Interrupt nicht die Ausführung einer
gerade abgearbeiteten Interrupt-Routine.
Allenfalls könnte man das emulieren, in dem man in einer ISR die
Interrupt-Flags prüft ob inzwischen ein anderer Interrupt aufgetreten
ist.
Natürlich ist die Reaktionszeit länger im Vergleich zum eingebauten
Interrupt-Mechanismus.
Danke Klaus!
Ich verwende das One-Wire Bussystem von Peter Dannegger.
Dabei wird bei einem Pin Change, ein Interrupt ausgeführt, und mit dem
Timerzustand verglichen, um zu wissen wie lange die Bitdauer war.
Problem ist nun, dass ich jede Sekunde mein Display mit neuen Werten
aktualisieren, das ganze in einem Interrupt. Dieser dauert etwas länger,
und wenn in dieser Zeit ein Pin Change Interrupt auftritt, kommt es
zeitweise vor, dass es Timing Probleme gibt, und das Byte nicht korrekt
erkannt wird.
Gruß
Hubert
>Problem ist nun, dass ich jede Sekunde mein Display mit neuen Werten>aktualisieren, das ganze in einem Interrupt.
Dann liegt hier der Ansatzpunkt für eine Lösung.
Ist das ein Interrupt dessen eigentlicher Zweck darin besteht die 1s
abzumessen? Nur das Du halt darin auch noch das Display aktualisierst?
Was dauert in dem Interrupt so lange?
Verkürze es. Falls möglich setze in diesem Interrupt nur ein Flag und
mache die Display-Aktualisierung aus einer Endlosen Schleife in main.
Hubert schrieb:> Problem ist nun, dass ich jede Sekunde mein Display mit neuen Werten> aktualisieren, das ganze in einem Interrupt.
Besser: in dem 1-Sekunden Interrupt nur ein Flag setzen und die
Displayaktualisierung dann ausserhalb der ISR machen.
Danke euch zwei!
Das hört sich tatsächlich intelligenter an.
Es geht darum, dass ich einen Coloumb Zähler auch habe, daher muss ich
auch alle 1 Sekunde den Strom messen, und zählen, um im endeffekt die
entnommene Ladung zu kennen.
Ich habe für das Display leider auch viele Berechnungen und if-Abfragen,
da ich Fonts verwende, da die Display eigenen zu klein sind für meinen
Verwendungszweck.
Ich denke ich würde es verkürzen können, wenn ich Float variablen
verwende, aber aus früheren Zeiten weiß ich, dass man dies tunlichst
vermeiden sollte, gerade bei µC weils viel Speicher und Rechenfähigkeit
bedarf.
Momentan "male" ich einfach das Komma hin.
das heißt ich habe eine Abfrage, bei allen 10er Schritten, um den
Kommapunkt jeweils richtig setzen zu können.
Das heißt ich nehme die Integer Zahl, wandle sie in einen String, und
danach setze ich den Punkt korrekt, daher muss ich je nach 10er Schritt
den Punkt unterschiedlich setzen.
bei 1000 zb. an der 3 Stelle.
bei 100 an der 2. Stelle.
Ich hoffe man kann verstehen wie ich das meine.
Grüße
Hubert
Aus einem anderen Grund war meine Aussage sowieso falsch. Enabled man in
der einen Interruptroutine die interrupts wieder so kann ein weiterer
Interrupt die laufende ISR unterbrechen. Sorry.
Ganz einfach ist das dennoch nicht und für einen Anfänger nicht zu
empfehlen. Die Methode mit dem Flag ist einfacher zu verstehen und zu
handhaben. Ausserdem ist nested interrupts in der Regel garnicht
erforderlich.
>Ich hoffe man kann verstehen wie ich das meine.
Das Problem ist, das ich nicht verstehe was Du damit sagen willst. Du
stellst ja keine Frage.
Interrupts so kurz und einfach wie irgend möglich. Das ist alles.
Hubert schrieb:> Momentan "male" ich einfach das Komma hin.>> das heißt ich habe eine Abfrage, bei allen 10er Schritten, um den> Kommapunkt jeweils richtig setzen zu können.>> Das heißt ich nehme die Integer Zahl, wandle sie in einen String, und> danach setze ich den Punkt korrekt, daher muss ich je nach 10er Schritt> den Punkt unterschiedlich setzen.>> bei 1000 zb. an der 3 Stelle.> bei 100 an der 2. Stelle.>> Ich hoffe man kann verstehen wie ich das meine.
Ist schon in Ordnung so. Aber bitte ausserhalb von Interrupt Routinen.
>Also besser viele Abfragen als eine Float variable zu verwenden?
Nein. Nur: Die ursprüngliche Frage ist beantwortet,
Falls Du ein noch ein anderes Problem hast mach besser einen neuen
Thread auf.
Aber kurz: Float ist immer Sch.... Ob das nun "viele Abfragen" notwendig
macht ist eine ganz andere Frage und hängt davon ab was Du tun willst
und wie Du es tun willst. Aus Deiner Beschreibung geht das so nicht
hervor. Intuitiv würde ich sagen, das Ausgaben von eigenen Fonts oder
Kommas auf dem Display nicht mehr Abfragen benötigen wenn sie im
Interrupt erledigt werden als wenn sie nicht im Interrupt erledigt
werden.
Ich habe so den Verdacht, dass Du den Font zur Laufzeit irgendwie neu
berechnest. Das ist natürlich wenn möglich zu vermeiden. Aber mach am
besten einen Thread für diese Problem auf.
Klaus schrieb:> Das Problem ist, das ich nicht verstehe was Du damit sagen willst. Du> stellst ja keine Frage.
Ich denke er hat ein paar if-Abfragen um herauszufinden, wo der
Dezimaltrenner zu setzen ist.
Hubert schrieb:> Also besser viele Abfragen als eine Float variable zu verwenden?
Wie sehen denn Deine Werte aus. Bei 16bit integer und zwei
Nachkommastellen reichen doch zwei if-Abfragen.
Hubert schrieb:> Also besser viele Abfragen als eine Float variable zu verwenden?
Ja, float sollte man auf dem Avr grundsätzlich vermeiden (kostet dich
übrigens ein paar kb Flash).
printf macht auch nichts anderes als bei Float einen Punkt reinzusetzen
- nur der Rechenaufwand ist deutlich höher als bei Festkomma.
Und Funktionen sollte man in der ISR in C erst recht nicht aufrufen, da
der Compiler da mal ein haufen register sichert, was pro register 2
Takte kostet.
"Ich hab einen Interupt, der höchste Priorität hat,...usw." ist
mißverständlich
m.E. richtiger formuliert:
"Ich möchte einem Interupt höchste Priorität geben, der im Moment
niedrigrangiger ist"
Bei den AVR's ( kann nur für atmega8 sprechen) ist die Priorität
entsprechend der Tabelle der Int-Vektoren zugeteilt, also int0 hat
höchste Priorität.
Int0 kann aber, wenn er frei ist, per software aufgerufen werden.
Man schreibt also in die Routine niedriger Priorität den Befehl, mit dem
das int0-flag gesetzt wird.
Int0 wird dadurch aufgerufen, als int0-Routine muss dann im Programm
eben die Routine des Ints niedriger Priorität stehen.
mit den retis und den flags wirds eventuell verzwickt, aber gehen tuts
wohl.
Hallo Klaus
Natürlich macht es in der Geschwindigkeit keinen Unterschied ob ich
diese Abfragen im Interrupt mache oder nicht, aber ich dachte, ich
stelle diese Frage gleich hier, um nicht für alles einen neuen Thread
aufmachen zu müssen.
Momentan sieht das ganze so aus:
>Natürlich macht es in der Geschwindigkeit keinen Unterschied
Das habe ich auch nicht geschrieben. Du hast von der Anzahl Abfragen
geschrieben und ich habe geantwortet, das die Anzahl nicht
unterschiedlich sein wird.
>aber ich dachte, ich stelle diese Frage gleich hier, um nicht für alles >einen
neuen Thread aufmachen zu müssen.
Warum? Was ist daran nachteilig einen neuen Thread aufzumachen. Aber da
das ein neues Thema ist, wird es hier als Vorteil empfunden, wenn dafür
ein neuer Thread aufgemacht wird.
> der Compiler da mal ein haufen register sichert, was pro register 2> Takte kostet.
korriegiere: Sind 4, da sie zurückgeschrieben werden müssen.
Schreib doch deine eigene itoa Funktion inkl. Kommasetzung.
Hubert schrieb:> Ist dies überhaupt möglich?
Wenn ein Interrupt ausgelöst wurde, werden alle Interrupts global
abgeschaltet. Alles, was du tun musst, ist am Anfang der ISR diese mit
sei() wieder einschalten. Anstatt daruf zu warten, daß sie mit reti von
selbst wieder eingeschaltet werden.
Kannst du auch vom Compiler frühest möglich einbauen lassen:
ISR(XXX_vect, ISR_NOBLOCK)
{
...
}
siehe auch AVR-Libc >> Library Reference >> <avr/interrupt.h>:Interrupts
>> Nested Interrupts
mfg.
Thomas Eckmann schrieb:> Hubert schrieb:>> Ist dies überhaupt möglich?>> Wenn ein Interrupt ausgelöst wurde, werden alle Interrupts global> abgeschaltet. Alles, was du tun musst, ist am Anfang der ISR diese mit> sei() wieder einschalten.
Im Prinzip geb ich dir recht. technisch wäre das eine Möglichkeit.
Aber in seinem Fall ist es die weitaus bessere Variante, seine ISR
Routine auszumisten. Er hat da viel zu viele Dinge drinnen, im
speziellen offenbar die Ausgabe auf ein GLCD, die da nicht
hineingehören.
Spielt man nach den Regeln, dann benötigt man keine derartigen dirty
Tricks, wie die Freigabe der Interrupts in einer ISR.
Karl Heinz Buchegger schrieb:> Spielt man nach den Regeln, dann benötigt man keine derartigen dirty> Tricks, wie die Freigabe der Interrupts in einer ISR.
Das ist sicherlich richtig. Aber die Frage war halt, ob es möglich ist.
Das heisst ja nicht, daß man das unbedingt machen muß. Wobei es bei
wirklich zeitkritischen Sachen, wie beispielsweise Soft-USB, durchaus
hilfreich sein kann.
mfg.
Karl Heinz Buchegger schrieb:> Er hat da viel zu viele Dinge drinnen, im> speziellen offenbar die Ausgabe auf ein GLCD, die da nicht> hineingehören.
Besonders lustig wirds, wenn das Main ebenfalls aufs GLCD schreibt und
die sich dann gegenseitig in die Quere kommen.
Das gibt dann nen hübschen Pixel-Salat.
Peter
Kann das vielleicht jemand von euch Profis überfliegen, und mir sagen,
ob das noch verbesserungswürdig ist.
Das ganze funktioniert so:
zb. 100000 soll zu 10.000 werden:
itoa2(100000,s,2,3,1);
Vorkomma = 2
Nachkomma = 3
Divisor = 1
Das heißt bei 10000 kommt " 1.000" heraus. So ist es gewunschen, es soll
praktisch das Komma immer fix sein, wobei auch die Länge es Strings
konstant sein muss, und alles davor mit ' ' aufgefüllt werden muss.
Danke schonmal
Grüße
Hubert
Das /10 finde ich nicht so schön, da Divisionen auf dem Avr nicht so
günstig sind. Wenn schon Divisionen im Programm sind, ist das aber egal.
Mit einem 10^x Array könnte man da noch ein bisschen rausholen.
Hallo!
naja ich brauche ja auch Zahlen wir 23442. Da sollte das 10^x Array doch
keinen Vorteil haben oder ?
Wie siehts mit der restlichen Programmierung aus?
Das einzige Problem was ich habe ist bei Zahlen die " .xx" ergeben,
dass ich hier eine Null vor dem Komma erhalte, ich denke das geht nur
mit paar Abfragen, was die Funktion wieder etwas länger und langsamer
machen dürfte oder ?
Lg
Hubert
Klaus schrieb:> Nein. Bei den AVRs unterbricht ein Interrupt nicht die Ausführung einer> gerade abgearbeiteten Interrupt-Routine.
Das geht schon,
1
ISR(INT0_vect, ISR_NAKED)
wenn man sich dann um das sichern der Register usw. selbst kümmert.
Aber wie schon erwähnt ist das nicht zu empfehlen, ausser es geht
wirklich nicht anders.
Hubert schrieb:> naja ich brauche ja auch Zahlen wir 23442. Da sollte das 10^x Array doch> keinen Vorteil haben oder ?
Ungetestet, so ähnlich mache ich es:
1
uint16_t_10[]={10000,1000,100,10};//Hier nimmst du deine 10ner Zahlen raus und teilst nicht mehr durch 10
Stefan P. schrieb:> Klaus schrieb:>> Nein. Bei den AVRs unterbricht ein Interrupt nicht die Ausführung einer>> gerade abgearbeiteten Interrupt-Routine.>> Das geht schon,> ISR(INT0_vect, ISR_NAKED)> wenn man sich dann um das sichern der Register usw. selbst kümmert.
Auf was willst du hinaus? Zwischen einer naked ISR und sich
unterbrechenden ISRs besteht überhaupt kein Zusammenhang.
Meine Funktion braucht laut avrstudio kanpp 400Cycles. Deine liegt bei
6000. Das liegt wahrscheinlich an den vielen Divisionen. Außerdem dreht
deine Funktion das Ergebnis um.
Hallo Samuel!
Wahnsinn dein Programm ist echt super.
Einzig eine Frage habe ich, undzwar, ich möchte einstellen können
wieviele Nachkommastellen ich habe. Und leider weiß ich nicht, wie ich
das integrieren kann. Genausowenig wie ich verstehe, warum es "i<4" ist.
So viel ich es auch versuche, ich kann es nicht 100%ig verstehen. Leider
hab ich mit diesen vielen Pointer Operationen nicht so viel Erfahrung.
Danke !!!
Grüße
Hubert
verstehen solltest du es schon. Ich kommentiere es mal ein bisschen.
Die < 4 steht für die 5 Stellen abzüglich der letzen, welche am Ende
einfach addiert wird.
Ein Pointer zeigt auf die eine Stelle eines char arrays. Inkreminiert
man ihn, zeigt er auf die nächste.
1
uint16_t_10[]={10000,1000,100,10};//Hier nimmst du deine 10ner Zahlen raus und teilst nicht mehr durch 10
2
voiditoa2(uint16_tn,char*s,uint8_tdot,uint8_tlen)
3
{
4
char*str=s;//Pointer kopieren
5
for(uint8_ti=0;i<4;i++,str++)
6
{
7
*str='0';//0 an aktuelle string Position setzen
8
while(n>=_10[i])
9
{
10
n-=_10[i];//sooft man 10^x subtrahieren kann
11
(*str)++;//inkreminiert man die vorige 0
12
}
13
if(--dot==0)//Punkt setzen wenn
14
*str++='.';//dot == 0 ist
15
}
16
*str++='0'+n;//letzte Ziffer ist das was übrig geblieben ist
17
*(s+len)=0;//Abschließende 0
18
str=s;//Startpos des strings
19
while(*str=='0')//Führende 0len löschen
20
*str++=' ';
21
if(*str=='.')//Vor komma eine null setzen (falls gelöscht)
22
*(str-1)='0';
23
}
Wenn du alle Stellen ohne Komma haben willst musst du die Funktion mit
len=5 und dot>=5 aufrufen. Mit Komma und alle Stellen mit len=6. Len
bestimmt im Endeffekt nur die Länge des Strings.
Hallo!
Danke das du es kommentiert hast, das hilft echt weiter! Und ich möchte
es ja auch lernen, so gut programmieren würd ich auch gern können.
Allerdings gibt es ein Problem.
Gebe ich als Zahl "12345" an, als dot "2" und als len "4" dann kommt
"1." heraus.
Ich hab mir die Funktion angeschaut, ändere ich die Funktion auf das:
1
for(uint8_ti=0;i<4;i++,str++)
2
{if(dot--==0)//Punkt setzen wenn
3
*str++='.';//dot == 0 ist
4
5
*str='0';//0 an aktuelle string Position setzen
6
while(n>=_10[i])
7
{
8
n-=_10[i];//sooft man 10^x subtrahieren kann
9
(*str)++;//inkreminiert man die vorige 0
10
}
11
}
Dann funktioniert alles.
Kannst du mir eventuell sagen, was --dot macht? Ich kenne ausschließlich
dot--
Eine weitere Frage wäre noch, und zwar
1
*str++='0'+n;
heißt dass, an die aktuelle Position wid '0' + n geschrieben, und erst
DANN wird der Pointer incrementiert? Wenn ja, wie weiß man sowas? Es
könnte doch genausogut vorher incrementiert werden und erst nachher
hineingeschrieben?
Grüße
Hubert
Hubert schrieb:> Ich verwende das One-Wire Bussystem von Peter Dannegger.> Dabei wird bei einem Pin Change, ein Interrupt ausgeführt, und mit dem> Timerzustand verglichen, um zu wissen wie lange die Bitdauer war.
Ist der Controller hierbei der 1-Wire Master oder der Slave?
Dann scheint mir die Methode, dafür einen Interrupt zu verwenden,
ziemlich anspruchsvoll. Denn dadurch landest du bei der beschriebenen
Forderung, auf diesen Interrupt extrem schnell reagieren zu können.
Was spricht dagegen, den 1-Wire Code wie sonst üblich im Hauptprogramm
abzuwickeln? Der sorgt dort zwar für abgeschaltete Interrupts im Bereich
von unter 20µs, um das Timing sicher einzuhalten, aber das wird nur zum
Problem, wenn noch mehr derart Zeitkritisches dabei ist.
Ebenso unverständlich bleibt mir, weshalb die sekündliche komplexe
Aktivität unbedingt im Interrupt ablaufen muss. Der übliche Ansatz ist
eine sekündliche Signalisierung im Interrupt mit der eigentlichen
Aktivität im diese Signalisierung abfragenden Hauptprogramm.
Hubert schrieb:> Gebe ich als Zahl "12345" an, als dot "2" und als len "4" dann kommt> "1." heraus.
Hab das nochmal schnell debuggt:
Kleiner aber feiner Fehler:
1
uint16_t_10[]={10000,1000,100,10};//Hier nimmst du deine 10ner Zahlen raus und teilst nicht mehr durch 10
2
voiditoa2(uint16_tn,char*s,uint8_tdot,uint8_tlen)
3
{
4
char*str=s;//Pointer kopieren
5
for(uint8_ti=0;i<4;i++,str++)
6
{
7
*str='0';//0 an aktuelle string Position setzen
8
while(n>=_10[i])
9
{
10
n-=_10[i];//sooft man 10^x subtrahieren kann
11
(*str)++;//inkreminiert man die vorige 0
12
}
13
if(--dot==0)//Punkt setzen wenn
14
*++str='.';//dot == 0 ist
15
}
16
*str++='0'+n;//letzte Ziffer ist das was übrig geblieben ist
17
*(s+len)=0;//Abschließende 0
18
str=s;//Startpos des strings
19
while(*str=='0')//Führende 0len löschen
20
*str++=' ';
21
if(*str=='.')//Vor komma eine null setzen (falls gelöscht)
22
*(str-1)='0';
23
}
*++str = '.';
Das ++ muss hier vor str, da für den Punkt erst zum nächsten Zeichen
gesprungen wird.
x++ gibt x zurück und erhöht x.
++x erhöht x und gibt dann x zurück.
Dadurch, dass dot nun unter While steht, kann der Punkt nur nach einer
Zahl gesetzt werden (dot=1 heißt nach der ersten Zahl folgt der Punkt,
dot=0 bedeutet kein Punkt). Nimm die Variante die dir besser gefällt.
Hallo Samuel!
Das heißt es ist beides gleich schnell und gut und macht keinen
Unterschied oder?
Empfiehlt es sich 2 Funktionen für long und integer zu schreiben, oder
sollte ich immer die long to Ascii Funktion verwenden?
Grüße
Hubert
Hubert schrieb:> Das heißt es ist beides gleich schnell und gut und macht keinen> Unterschied oder?
Einmal wird davor inkreminiert einmal danach. MAcht keinen zeitlichen
Unterschied.
> Empfiehlt es sich 2 Funktionen für long und integer zu schreiben, oder> sollte ich immer die long to Ascii Funktion verwenden?
Wenn du genügend Flash frei hast, ja, wenn nicht nimm nur Eine.
Evtl. sollte man bei einer Funktion, den Anfang des Strings zurückgeben,
damit man bei 123 nicht 5 Leerzeichen davorhat:
while(*str == '0') //Führende 0len löschen
str++;
if(*str == '.') //Vor komma eine null setzen (falls gelöscht)
*--str = '0';
return str;
Klaus schrieb:>>aber ich dachte, ich stelle diese Frage gleich hier, um nicht für alles >einen> neuen Thread aufmachen zu müssen.>> Warum? Was ist daran nachteilig einen neuen Thread aufzumachen. Aber da> das ein neues Thema ist, wird es hier als Vorteil empfunden, wenn dafür> ein neuer Thread aufgemacht wird.
Stimmt!
Wenn man hier auf das neue Thema antwortet, hilft das nur dem einen, da
niemand anderes es hier suchen und finden wird.
Daher werde ich hier auch nicht antworten.
Ein bischen mitdenken sollte man schon und nicht so egoistisch sein.
Peter