Hallo zusammen,
ich habe folgendes Problem. Ich möchte über einen PCF8574 Tasten
einlesen. Damit dies interruptgesteuert ist, habe ich den Portexpander
an INT0 angeschlossen. Jetzt habe ich aber das Problem, das der externe
Interrupt 0 Mal ausgelöst wird.
Hier die Konfiguration der Interrupts
EIMSK|=1<<INT1|1<<INT0;
EICRA|=1<<ISC11|1<<ISC01;
sei();
sowie die zunächst ganz banale ISR
ISR(INT0_vect){
_delay_ms(500);
i++;
_delay_ms(500);
}
Ziel war es den Interrupt auf fallende Flanke zu konfigurieren, sodass
Jede zustandsänderung a, PCF8574 genau einen Interrupt auslöst
Übersehe ich dabei irgendetwas?
Liebe Grüße und schönen Tag
M²H
M²H schrieb:> Hier die Konfiguration der Interrupts>> EIMSK|=1<<INT1|1<<INT0;> EICRA|=1<<ISC11|1<<ISC01;> sei();>> sowie die zunächst ganz banale ISR>> ISR(INT0_vect){> _delay_ms(500);> i++;> _delay_ms(500);> }> Übersehe ich dabei irgendetwas?
Ja. Wenn du zwei Interrupts erlaubst, brauchst du auch Handler für zwei
Interrupts...
Mit dem Codesschnipsel kann man nix anfangen.
1 Sekunde delay in einer ISR macht man nicht. Der µC macht in der Zeit
überhaupt nix mehr. ISR sollten immer kurz und schnell abgearbeitet
werden.
M²H schrieb:> Übersehe ich dabei irgendetwas?
Wo hängt der INT1 Pin? Wenn er ein "floating input" ist und du keinen
Handler für INT1 hast, kann nur schief gehen. Schalte INT1 ab, am INT0
mit Oszilloskop messen ob Signal vorhanden.
Erstmal danke für alle Antworten :)
@ Chris
Mir ist klar das dieses delay sehr lang ist ich hab das "Entprellung"
verwendet, mir ist durchaus klar, dass das in der Länge eigentlich nicht
notwendig wäre. Es stört aber meines WIssens an der Stelle aber auch
erstmal nicht.
@Ozvald
Der INT1 ist ebenfalls über einen 10K Widerstand an 5V und wird über
einen Taster nach Masse gezogen. Der Pin ist also nicht floating und ich
bekomme für diesen auch nur Interrupts wenn ich den Taster drücke.
allerdings mit dem selben Effekt, dass ich die ISR für INT1 zweimal
ausführe. Ich hab den Effekt auf INT0 auch wenn ich den INT1 in EIMSK
nicht freigebe.
M²H schrieb:> Es stört aber meines WIssens an der Stelle aber auch> erstmal nicht.
Problem: Der Delay verhindert nicht, dass ein zweiter Interrupt
"getriggert" wird.
d.H. wenn während des _delay_ms in der ISR eine neue Flanke kommt, dann
wird das Interrupt-Request-Bit (in EIFR) auch wieder neu gesetzt. Nach
dem Return aus dem Interrupt steht also sofort der nächste an => CPU
führt einen "normalen" Befehl aus, und springt sofort wieder in die ISR.
> Interrupt 0 Mal ausgelöst> selben Effekt, dass ich die ISR für INT1 zweimal
Also für mich passen die beiden Aussagen nicht zusammen, was übersehe
ich?
Und Letztere klingt nach Tasterprellen.
M²H schrieb:> @ Chris> Mir ist klar das dieses delay sehr lang ist ich hab das "Entprellung"> verwendet, mir ist durchaus klar, dass das in der Länge eigentlich nicht> notwendig wäre. Es stört aber meines WIssens an der Stelle aber auch> erstmal nicht.
An diesem Ort kann das nicht funktionieren!
Εrnst B. schrieb:> Problem: Der Delay verhindert nicht, dass ein zweiter Interrupt> "getriggert" wird.> d.H. wenn während des _delay_ms in der ISR eine neue Flanke kommt, dann> wird das Interrupt-Request-Bit (in EIFR) auch wieder neu gesetzt
Du brauchst einen Timer, der in der ISR gestartet wird. Der ISR BLEIBT
gesperrt! Außerhalb der ISR, wird durch den Timer dieser erst wieder
freigegeben, nachdem das zugehörige Flag gelöscht wurde.
Εrnst B. schrieb:> M²H schrieb:>> Es stört aber meines WIssens an der Stelle aber auch>> erstmal nicht.>> Problem: Der Delay verhindert nicht, dass ein zweiter Interrupt> "getriggert" wird.> d.H. wenn während des _delay_ms in der ISR eine neue Flanke kommt, dann> wird das Interrupt-Request-Bit (in EIFR) auch wieder neu gesetzt. Nach> dem Return aus dem Interrupt steht also sofort der nächste an => CPU> führt einen "normalen" Befehl aus, und springt sofort wieder in die ISR.
Danke Ernst für deine Antwort.
Das klingt schlüssig. Frage wie verhindere ich das Triggern des zweiten
Interrupts softwaremäßig?
wäre folgendes in der ISR denkbar?
ISR(INT1_vect){
EIMSK&=~(1<<INT1);// Sperren des Interrupts
_delay_ms(20);
EIFR|=1<<INTF1;// löschen des zugehörigen Flags(laut Datenblatt durch
setzen) nach delay falls prellen vorhanden
EIMSK|=1<<INT1;//Wiederfreigabe des Interrupt
...
...
// gewünschter Code für die ISR
...
...
}
M²H schrieb:> allerdings mit dem selben Effekt, dass ich die ISR für INT1 zweimal> ausführe.
Klar, der Taster prellt. Die erste negative Flanke löst den Interrupt
aus. Während der µC den Interrupt abarbeitet (delay), setzen weitere
negative Flanken das Interruptflag wieder. Taster abfragen macht man
besser mit Polling oder du musst mit Hardware den Taster entprellen
lassen.
M²H schrieb:> Das klingt schlüssig. Frage wie verhindere ich das Triggern des zweiten> Interrupts softwaremäßig?
in dem man das ganze so anlegt, dass das Problem garnicht erst
auftaucht.
es gibt kaum Anwendungen, wo du einen Tastendruck auf die millisekunde
genau brauchst. Deshalb fragt man Tasten normalerweise auch nicht mit
einem Interrupt-Pin ab. Besser: Timer/Task in der main-loop, das ganze
in die PeDa-Entprellung füttern.
Interrupt für Tasten hab ich normalerweise nur zum Wake-Up aus dem
sleep. Dann ist die ISR leer (oder schaltet sich direkt selber ab)
1
EMPTY_INTERRUPT(INT0_vect);
2
// Oder
3
ISR(INT0_vect){
4
GIMSK&=~_BV(INT0);
5
}
und der trigger steht auf LEVEL.
Abfrage in der Timer-ISR ist bei Tasten über I2C-Portexpander natürlich
etwas blöde, je nachdem was sonst noch am I²C hängt. Da würde ich das
auslesen/entprellen in die main-loop packen, und mit dem Timer nur eine
Zeitbasis dafür bereitstellen.
M²H schrieb:> Das klingt schlüssig. Frage wie verhindere ich das Triggern des zweiten> Interrupts softwaremäßig?> wäre folgendes in der ISR denkbar?>> ISR(INT1_vect){> EIMSK&=~(1<<INT1);// Sperren des Interrupts> _delay_ms(20);> EIFR|=1<<INTF1;// löschen des zugehörigen Flags(laut Datenblatt durch> setzen) nach delay falls prellen vorhanden> EIMSK|=1<<INT1;//Wiederfreigabe des Interrupt> ...> ...> // gewünschter Code für die ISR> ...> ...> }
Nein, das hilft garnix.
Erstens: Das Löschen des Bits in der Interruptmaske verhindert NICHT,
dass das zugehörige Interuptflag gesetzt wird. Es kann nur verhindern,
dass der Interrupt auch tatsächlich ausgelöst wird. Das würde aber
frühestens nach Ende deiner ISR passieren. Jedenfalls so lange du nicht
anfängst, in der ISR auch noch mit sei() zu hantieren, wovon man dir bei
deinem Kenntnisstand nur sehr dringend abraten könnte...
Aber nicht einmal diesen Job kann die Maske erledigen, weil du ja noch
in der ISR das Bit wieder setzt. Sprich: de facto völlig sinnlose
Operationen, sowohl das Löschen als auch das Setzen. Bewirkt garnix.
Zweitens: Wenn während der sehr länglichen Laufzeit von "// gewünschter
Code für die ISR" der Interrupt erneut erfolgt (was genau dein Problem
ist), dann kehrt deine ISR zurück, es wird genau eine Instruktion aus
main() ausgeführt und dann bist du wieder in deiner ISR.
Vielleicht lernst du erstmal, wie diese Interrupts überhaupt
funktionieren, bevor du sie benutzt? Weil: es fällt dann viel leichter,
sinnvollen Code zu schreiben...