Ich habe mit einem AVR128DB eine Software interrupt programmiert, aber
irgendetwas stimmt da nicht. Wenn ich den Interrupt Typ auf LEVEL setze,
dann wird der Interrupt zweimal ausgelöst wenn ich den entsprechenden
Pin lösche. Wenn ich den Typ auf FALLING setze dann macht es was ich
will, d.h. der interrupt wird nur einmal ausgelöst. In der ISR habe ich
dazu mal einen Counter gebaut den ich jedesmal incrementiere. Im ersten
fall steht nach einmal Auslösen immer ein Wert +2 und im anderen Fall
ein Wert +1 wie ich das eigentlich erwartet hätte. Habe ich da etwas
übersehen?
Initialisierung
1
sbi VPORTE_OUT, 0 ; Set Level = High
2
sbi VPORTE_DIR, 0 ; Set Output to protect interrupt
Mal so ins Blaue spekuliert: Bis aus einer Pegeländerung am Pin ein
Interrupt wird, sollte es 2 bis mehrere Taktzyklen dauern. Ob die
Änderung von draußen kommt oder über das Output-Register erzeugt wird,
sollte egal sein. Allerdings könnte es von SBI bis zur Pegeländerung
auch nochmal einen extra Takt dauern. SBI braucht wohl 2 Zyklen.
Es wäre also denkbar, dass der LEVEL-Interrupt auf dem Umweg über den
externen Pin noch ansteht wenn das INTFLAG gelöscht wird. Daraufhin
gibt's den zweiten Interrupt, aber jetzt ist der Level inzwischen wie
gewünscht inaktiv, also gibt's keinen dritten.
Wenn du den sbi VPORTE_OUT vor den lds zl verschiebst, bliebe genug
Zeit.
Normalerweise wird das Interrupt-Flag gelöscht, wenn der
Interrupt-Handler aufgerufen wird. Da Du aber den Level-Interrupt
gewählt hast, wird das Interrupt-Flag sofort wieder gesetzt, noch bevor
Du das Pin zurück setzen kannst. Deswegen wird der Interrupt sofort nach
Verlassen der ISR ein zweites Mal aufgerufen. Um das zu verhindern musst
Du in der ISR das Flag manuell löschen.
Bauform B. schrieb:> Wenn du den sbi VPORTE_OUT vor den lds zl verschiebst, bliebe genug> Zeit.
Spricht was dagegen, das gleich ganz am Anfang der ISR zu tun?
Mario M. schrieb:> Um das zu verhindern musst Du in der ISR das Flag manuell löschen.
Das hat er getan.
Wie Bauform B. vermutet: die 'sbi' in der ISR dürfen nicht direkt
aufeinander folgen. ('sbi' benötigt zwar nur einen Takt, das spielt hier
ja aber keine Rolle)
Rolf M. schrieb:> Mario M. schrieb:>>> Um das zu verhindern musst Du in der ISR das Flag manuell löschen.>> Das hat er getan.
Jetzt wo Du's schreibst sehe ich es auch. 😁
an Mario M.:
Nun, dann sei noch ergänzt:
> Normalerweise wird das Interrupt-Flag gelöscht, wenn der> Interrupt-Handler aufgerufen wird.
Das gilt nicht für die AVR-Dx.
Bauform B. hat den entscheidenden Tip gegeben. SBI braucht zwar beim
AVR-Dx nur einen Zyklus aber der Pin ändert sich wohl einen Zyklus
später. Ein einziges NOP zwischen den SBI reicht übrigens aus und das
Problem ist behoben. Ich habe jetzt natürlich kein NOP im aktuellen Code
(das war nur zum Verifizieren der Vermutung), sondern habe den ersten
SBI wie vorgeschlagen nach vorne genommen. Jetzt funktioniert es auch
mit dem LEVEL interrupt wie gewünscht, vielen Dank. Werde den SBI wohl
noch weiter vorziehen, denn im finalen Code werde ich keinen intfcount
mehr hoch zählen müssen.
Peter S. schrieb:> aber der Pin ändert sich wohl einen Zyklus> später.
Nein, der Pin andert seinen Status sofort. Was (mindestens) einen Takt
hinterher hinkt, ist das entsprechende PINx-Registerbit. Das ist
allerdings nicht erst bei den AVRxDyz so, sondern war schon immer so.
Deswegen taucht diese Problematik sogar explizit im DB praktisch jedes
AVR8 auf...