Go Down

Topic: attach/detachInterrupt Problem (Read 4986 times) previous topic - next topic

Schwarzfuss

Es hat leider nicht hingehauen, der Interrupt wird noch immer willkürlich gesetzt.
Vielleicht sollte ich noch erwähnen, das der Atmega8 hier mit internem Takt läuft (1MHz).
Werde mich am Wochenende noch mal intensiver damit auseinandersetzen,
bis hierhin zunächst mal vielen Dank an alle Beteiligten.

udoklein

Hmm, vieleicht ist es dann gar nicht der Interrupt. Als erstes muß auf jeden Fall geklärt werden wo das Problem herkommt. Ich tippe zwar immer noch auf den Interrupt, aber so langsam wird die Ferndiagnose schwierig.

Gruß, Udo
Check out my experiments http://blog.blinkenlight.net

Schwarzfuss

Zum Testen hatte ich den delay zum Warten auf den Interrupt
auch mal durch eine Endlosschleife mit while ersetzt, ohne delay.
In der Interruptroutine wird dann ausschl. eine Variable geändert,
die die Endlosschleife beenden kann.
Aber auch so ist dasselbe Phänomen zu beobachten.

udoklein

Das verstehe ich jetzt nicht ganz. Willst Du sagen, daß Du glaubst der IRQ ist schuld oder nimmst Du an etwas anderes ist schuld?

Kannst Du vieleicht mal die Codestelle ins Forum stellen?
Check out my experiments http://blog.blinkenlight.net

Schwarzfuss

Ich klammere mich an jeden Strohhalm ;)

Der letzte Versuch heut morgen sah so aus,
andere stehen noch drin, sind aber als Text markiert.
Code: [Select]
//cbi(TIMSK, TOIE0);
attachInterrupt(0, Messung, FALLING);
//sbi(TIMSK, TOIE0);
//delayMicroseconds(5000); // 5 mS verzögern

while (DEBUG2 == 0) {
 // auf Interrupt warten
}

detachInterrupt(0); // Interrupt abschalten
digitalWrite(DEBUG, HIGH); // DEBUG für Oszi
Wert_IN_tmp = analogRead(Pin_Akku_IN); // Spannung des Akkus messen
digitalWrite(DEBUG, LOW); // DEBUG für Oszi
DEBUG2 = 0;

void Messung() {
 //detachInterrupt(0); // Interrupt abschalten
 //digitalWrite(DEBUG, HIGH); // DEBUG für Oszi
 //Wert_IN_tmp = analogRead(Pin_Akku_IN); // Spannung des Akkus messen
 //digitalWrite(DEBUG, LOW); // DEBUG für Oszi
 DEBUG2 = 1;
}

udoklein

Hmm, so war das nicht ganz gemeint. Die Interrupts für die Zeit des attachInterrupt zu sperren bringt nichts.  Du musst sperren bis der andere Interrupt dran war, also so:

Code: [Select]


cbi(TIMSK, TOIE0);
attachInterrupt(0, Messung, FALLING);

delayMicroseconds(5000); // 5 mS verzögern

while (DEBUG2 == 0) {
 // auf Interrupt warten
}

detachInterrupt(0); // Interrupt abschalten
sbi(TIMSK, TOIE0);
Check out my experiments http://blog.blinkenlight.net

udoklein

Nachtrag: delayMicroseconds sperrt während es läuft alle Interrupts. D.h. für die 5ms kann kein Interrupt auftreten.  Da Du aber asynchron auf der Flanke triggerst wird jeder Interrupt der während der 5ms aufläuft direkt hinterher abgearbeitet. So wie das also jetzt kodiert ist hättest Du die Interruptbearbeitung auch gleich weglassen können.

Der Code in wiring.c ist

Code: [Select]

void delayMicroseconds(unsigned int us)
{
     uint8_t oldSREG;

     // calling avrlib's delay_us() function with low values (e.g. 1 or
     // 2 microseconds) gives delays longer than desired.
     //delay_us(us);

#if F_CPU >= 16000000L
     // for the 16 MHz clock on most Arduino boards

     // for a one-microsecond delay, simply return.  the overhead
     // of the function call yields a delay of approximately 1 1/8 us.
     if (--us == 0)
           return;

     // the following loop takes a quarter of a microsecond (4 cycles)
     // per iteration, so execute it four times for each microsecond of
     // delay requested.
     us <<= 2;

     // account for the time taken in the preceeding commands.
     us -= 2;
#else
     // for the 8 MHz internal clock on the ATmega168

     // for a one- or two-microsecond delay, simply return.  the overhead of
     // the function calls takes more than two microseconds.  can't just
     // subtract two, since us is unsigned; we'd overflow.
     if (--us == 0)
           return;
     if (--us == 0)
           return;

     // the following loop takes half of a microsecond (4 cycles)
     // per iteration, so execute it twice for each microsecond of
     // delay requested.
     us <<= 1;
   
     // partially compensate for the time taken by the preceeding commands.
     // we can't subtract any more than this or we'd overflow w/ small delays.
     us--;
#endif

     // disable interrupts, otherwise the timer 0 overflow interrupt that
     // tracks milliseconds will make us delay longer than we want.
     oldSREG = SREG;
     cli();

     // busy wait
     __asm__ __volatile__ (
           "1: sbiw %0,1" "\n\t" // 2 cycles
           "brne 1b" : "=w" (us) : "0" (us) // 2 cycles
     );

     // reenable interrupts.
     SREG = oldSREG;
}


Was Du brauchst wäre aber wohl eher

Code: [Select]

void delayMicroseconds(unsigned int us)
{
//      uint8_t oldSREG;

     // calling avrlib's delay_us() function with low values (e.g. 1 or
     // 2 microseconds) gives delays longer than desired.
     //delay_us(us);

#if F_CPU >= 16000000L
     // for the 16 MHz clock on most Arduino boards

     // for a one-microsecond delay, simply return.  the overhead
     // of the function call yields a delay of approximately 1 1/8 us.
     if (--us == 0)
           return;

     // the following loop takes a quarter of a microsecond (4 cycles)
     // per iteration, so execute it four times for each microsecond of
     // delay requested.
     us <<= 2;

     // account for the time taken in the preceeding commands.
     us -= 2;
#else
     // for the 8 MHz internal clock on the ATmega168

     // for a one- or two-microsecond delay, simply return.  the overhead of
     // the function calls takes more than two microseconds.  can't just
     // subtract two, since us is unsigned; we'd overflow.
     if (--us == 0)
           return;
     if (--us == 0)
           return;

     // the following loop takes half of a microsecond (4 cycles)
     // per iteration, so execute it twice for each microsecond of
     // delay requested.
     us <<= 1;
   
     // partially compensate for the time taken by the preceeding commands.
     // we can't subtract any more than this or we'd overflow w/ small delays.
     us--;
#endif

     // disable interrupts, otherwise the timer 0 overflow interrupt that
     // tracks milliseconds will make us delay longer than we want.
//      oldSREG = SREG;
//      cli();

     // busy wait
     __asm__ __volatile__ (
           "1: sbiw %0,1" "\n\t" // 2 cycles
           "brne 1b" : "=w" (us) : "0" (us) // 2 cycles
     );

     // reenable interrupts.
//      SREG = oldSREG;
}


Also zusätzlich dazu was ich vorher schon gesagt habe.

Gruß, Udo
Check out my experiments http://blog.blinkenlight.net

Schwarzfuss

Ich gebe auf, das wird mir doch alles zu kompliziert.
>>So wie das also jetzt kodiert ist hättest Du die Interruptbearbeitung auch gleich weglassen können.

Genauso werd ich das wohl auch machen:

wenn der PWM-Pin schon mit Digital-Pin2 (Interrupt0) verbunden ist,
könnte der ja als Eingang definiert werden und in der Messroutine
wird per Endlosschleife gewartet bis er LOW-Pegel hat,
vorher vielleicht noch auf HIGH-Pegel warten, damit der Zeitpunkt der Messung immer gleich ist.

Das entspräche auch einem Prg.Code, bei dem ich noch durchblicke.
So ungefähr:
Code: [Select]
Messung(); // ohne Interrupt

void Messung() {
 while (digitalRead(2) == 0) {
   // auf HIGH-Pegel warten
 }
 while (digitalRead(2) == 1) {
   // auf LOW-Pegel warten
 }
 Wert_IN_tmp = analogRead(Pin_Akku_IN); // Spannung des Akkus messen
}

Udo, an dieser Stelle möchte ich mich mal bei dir für die großartige Hilfe,
Informationen und natürlich deine Geduld :) ganz herzlich bedanken.

udoklein

Einfach halten ist immer eine gute Idee. Du bekommst mit dem Ansatz etwas Jitter weil die digitalRead Kommandos nicht besonders schnell ausgeführt werden. Kann sein, daß das kein Problem ist, ansonsten kann man die Ports auch direkt manipulieren.

Aber erst mal schauen ob das nicht sowieso schon ausreicht.

Gruß, Udo
Check out my experiments http://blog.blinkenlight.net

Schwarzfuss

Der Jitter scheint keine große Rolle zu spielen,
habe mal 4 Akkus so geladen und es gab absolut keine Ausreisser bei den Messwerten!
Geladen mit ca. 1C und gemessen wird ca. alle 10 Sek.,
pro Akku also knapp über 400 Messwerte.
Im Gegensatz zur Interrupt-Steuerung, spricht dies eindeutig für sich!
Mit gnuplot grafisch dargestellt siehts so aus

Im linken Teil habe ich versucht den Prg.-Ablauf und die Messung durch Ladestromänderung aus dem Tritt zu bringen,
zum Glück nicht sehr erfolgreich.
Die Spannungsspitze rechts ist allerdings ungewöhnlich,
obwohl zum richtigen Zeitpunkt abgeschaltet wird,
könnte dies auf einen defekten Akku hinweisen.
Jedenfalls funktioniert es so und das Thema Interrupt ist für mich erstmal erledigt.

udoklein

Na dann weiter viel Spaß. Erkenntnis: Finger weg von Interrupts wenn man die nicht wirklich braucht :)

Zu der Spannungsspitze: zu welchem Zeitpunkt wurde abgeschaltet? Und ist das der einzige Akku der dieses Verhalten zeigt?
Check out my experiments http://blog.blinkenlight.net

Schwarzfuss

>>Finger weg von Interrupts wenn man die nicht wirklich braucht
Diesen Rat werd ich auch beherzigen, obwohl...
NEIN!!! Thema Interrupt ist erstmal erledigt! :D

Und ja, es ist momentan der einzige Akku der dieses Verhalten zeigt.
In der Grafik ist zu sehen, das er bei ca. 410 abschaltet:
410*10=4100 Sek = 68 Min
Das passt schon.

Go Up