Interrupt in ISR freigeben

Gast #2237655
Lesenswert?

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
Gast #2237671
Lesenswert?

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.
Gast #2237677
Lesenswert?

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
Gast #2237688
Lesenswert?

>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.
Gast #2237699
Lesenswert?

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
Gast #2237705
Lesenswert?

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.
Gast #2237711
Lesenswert?

>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.
#2237712
Lesenswert?

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.
Gast #2237720
Lesenswert?

>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.
#2237724
Lesenswert?

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.
#2237726
Lesenswert?

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.
#2237729
Lesenswert?

"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.
Gast #2237730
Lesenswert?

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:
1
ultoa(totalvolt,Text2,10);
2

3
if(totalvolt<100000)
4
{   Text2[7]=0;
5
  Text2[6]=0;
6
        Text2[5]=0;
7
  Text2[4]=Text2[1];
8
  Text2[3]='.';
9
  Text2[2]=Text2[0];
10
  Text2[1]=' ';
11
  Text2[0]=' ';
12
}
13
else if(totalvolt<1000000)
14
{   Text2[7]=0;
15
  Text2[6]=0;
16
  Text2[5]=0;
17
  Text2[4]=Text2[2];
18
  Text2[3]='.';
19
  Text2[2]=Text2[1];
20
  Text2[1]=Text2[0];
21
  Text2[0]=' ';
22
}
23
else if(totalvolt<10000000)
24
{   Text2[7]=0;
25
  Text2[6]=0;
26
  Text2[5]=0;
27
  Text2[4]=Text2[2];
28
  Text2[3]='.';
29
}
30
else
31
{   for(char i=0;i<10;i++)
32
    Text2[i]=0;
33
}
34
WriteText(Text2,179,10);
Gast #2237735
Lesenswert?

>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.
#2237797
Lesenswert?

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.
#2237808
Lesenswert?

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.
#2237816
Lesenswert?

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.
Gast #2238045
Lesenswert?

Hallo!

Ich habe gerade, wie empfohlen, meine eigene "itoa" geschrieben.
1
void itoa2(unsigned long n, char s[], char vor, char nach, char div)
2
{   char i=0;
3
    
4
    for(char x=0;x<div;x++)
5
    n /= 10;
6
    
7
    for(i;i<vor+nach+1;i++)
8
    {  if(i==nach)
9
       s[i] = '.';
10
       
11
       else
12
       {  if(n > 0)
13
           s[i] = n % 10 + '0';
14
   
15
           else
16
           s[i] = ' ';
17
          
18
          n /= 10;
19
       }        
20
    }
21
    s[i] = '\0';    
22
    reverse(s);
23
}

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
Gast #2238264
Lesenswert?

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
#2238466
Lesenswert?

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
2
void itoa2(uint16_t n, char* s, uint8_t dot)
3
{   
4
    char* str = s;
5
    for(uint8_t i = 0; i < 4; i++, str++)
6
    {
7
        if(dot-- == 0)
8
            *str++ = '.';
9
        *str = '0';
10
        while(n >= _10[i])
11
        {
12
            n -= 10[i];
13
            (*str)++;
14
        }
15
    }
16
    *str = '0' + n;
17
     str = s;
18
     while(*str == '0')
19
        *str++ = ' ';
20
     if(*str == '.')
21
        *(str-1) = '0';
22
}
#2238472
Lesenswert?

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.
Gast #2239033
Lesenswert?

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
#2239097
Lesenswert?

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
void itoa2(uint16_t n, char* s, uint8_t dot, uint8_t len)
3
{   
4
    char* str = s;           //Pointer kopieren
5
    for(uint8_t i = 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.
Gast #2239300
Lesenswert?

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_t i = 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
Gast #2239303
Lesenswert?

Folgendes wäre mir für eine Integer to Ascii Funktion eingefallen ( also 
signed )
1
    *str++ = ' ';
2
    
3
    if(n < 0)
4
    {   n = -n;
5
        *(str-1) = '-';
6
    }

Das  hätte ich am Anfang dazugeschrieben.
Was sagst du dazu?

Danke!

Grüße

Hubert
#2239319
Lesenswert?

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.
#2239440
Lesenswert?

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
void itoa2(uint16_t n, char* s, uint8_t dot, uint8_t len)
3
{   
4
    char* str = s;           //Pointer kopieren
5
    for(uint8_t i = 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.
Gast #2239454
Lesenswert?

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
#2239477
Lesenswert?

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;
#2239508
Lesenswert?

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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren