Interrupt Service Routine: Verzögerungen und Entprellung?

Hallo,

ich habe gerade gestern mein erstes Arduinoboard (Mega1280) erhalten und bin nun am rumspielen. Zum testen will ich erstmal eine nicht näher bestimmte Blinkfolge durch einen Pinchange Interrupt unterbrechen und eine zweite Blinkfolge darstellen. Den Code habe ich unten gepostet. Achtung, ich habe dort Pseudocode untergemixt, zur Kürzung.
Als erstes eine Frage zur Verzögerung innerhalb der ISR. Der Referenz zufolge funktioniert weder delay() noch delayMicroseconds(); innerhalb der ISR. Daher habe ich mir eine dreckige kleine Warteschleife gebaut.
Jetzt ist das natürlich nicht besonders elegant. Das muss doch bestimmt viel einfacher möglich sein, als ich das da unten reingebaut habe, oder?
Nun stört mich noch, dass meine ISR immer (!) zweimal hintereinander durchlaufen wird. Das sollte doch nicht sein, da ich die Interrupts während der ISR sperre. Muss ich das dort anders machen, oder wo liegt der Fehler?

Mag sein, dass das alles viel einfacher ist, aber ich bin noch nicht so richtig mit den Arduino-Eigenheiten warm geworden.
Daher schonmal danke für die Hilfe im Voraus.

Gruß

#include <avr/interrupt.h>
//LEDS
#define RED 2
#define GREEN 3
#define BLUE0 4
#define BLUE1 5
//PCINT0
#define pinPut 53

void setup() {                
     pinMode(RED, OUTPUT);
     pinMode(GREEN, OUTPUT);
     pinMode(BLUE0, OUTPUT);
     pinMode(BLUE1, OUTPUT);

//initialisieren
  digitalWrite(RED, LOW);
  digitalWrite(GREEN, LOW);
  digitalWrite(BLUE0, LOW);  
  digitalWrite(BLUE1, LOW);  

  pinMode(pinPut, INPUT);

 //INterrupts 0:7 aktivieren
 PCICR = 0x01;
 //interrupt für pin aktivieren
 PCMSK0 = 0x01;
 }

void loop() {

some_blink_stuff_here;

 }

ISR(PCINT0_vect)
{
cli();
UDR0 = '*'; //ISR Beginn ausgeben
for(x)
{
LED_XYZ_AN;
delayAsm(anyint);
LED_XYZ_AUS;
delayAsm(anyint);    
}
UDR0 = 'x'; //ISR zuende ausgeben
sei();
}

void delayAsm(int count)
{
 for(int i = 0; i<count; ++i)
{
 asm("NOP");
}
}

Innerhalb einer ISR sollte immer so wenig Code wie möglich ausgeführt werden. Da Du ja innerhalb der ISR Interrupts verhinderst, werden auch alle anderen Intteruptgetriebenen Dinge (z.B. Serielle Kommunikation, Timer etc.) auch nicht ausgeführt. Es reicht doch, innerhalb der ISR ein Flag zu setzen, das dann in der “normalen” Loop ausgewertet wird.
Auf was reagiert denn Deine ISR? genereller Flankenwechsel, oder wechsel von HIGH auf LOW, bzw. umgekehrt? Das würde sicher das doppelte Ausführen erklären.
Mario.

Den Interrupteingang mußt Du hartwaremäßig entprellen.
Mögliche Lösungen sind RC Glied, RC Glied mit invertierenden oder nicht invertierenden Schnitttrigger, ein retriggerbares Monoflop mit 2mS Verzögerung, der Entprellbaustein MC14490 …
Grüße Uwe

Das cli() is voellig unnoetig, weil das automatisch der fall ist.

http://www.nongnu.org/avr-libc/user-manual/group__avr__interrupts.html

Die 'queue' fuer interrupts in den AVR chips ist nur 1-bit lang (interrupt flag im jeweiligen register). Wenn also ein ueberzaehliges ausfuehren unmittelbar nach verlassen des ISR verhindert werden soll (warum auch immer es eintreten mag), dann muss am ende des interrupts das jeweilige flag geloescht werden. In der regel geschieht das durch das schreiben einer "1" auf das jeweilige bit (siehe datenblatt). Ordentliches debouncing erspart einem das aber nicht.

Und das debugging eines interrupts erledigt man besser mittels einer LED und eines kleinen billigen logik analysators (siehe z.b. watterott) oder osci.