Forum: Mikrocontroller und Digitale Elektronik Software Interrupt


von Peter S. (cbscpe)


Lesenswert?

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
3
    sbi     VPORTE_INTFLAGS,0
4
;   ldi     temp, PORT_ISC_FALLING_gc   ; Falling Edge Interrupt
5
    ldi     temp, PORT_ISC_LEVEL_gc ; Level Sense Interrupt
6
    sts     PORTE_PIN0CTRL, temp    ; Set PF0

Auslösen
1
    cbi     VPORTE_OUT, 0

Interrupt routine
1
intf:
2
    push    r8
3
    in      r8, SREG
4
    push    zh
5
    push    zl
6
    lds     zl, intfcount
7
    inc     zl
8
    sts     intfcount, zl
9
    sbi     VPORTE_OUT, 0
10
    sbi     VPORTE_INTFLAGS, 0
11
    ...

von Bauform B. (bauformb)


Lesenswert?

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.

von Mario M. (thelonging)


Lesenswert?

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.

von Rolf M. (rmagnus)


Lesenswert?

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.

: Bearbeitet durch User
Beitrag #6783232 wurde vom Autor gelöscht.
von S. Landolt (Gast)


Lesenswert?

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)

von Mario M. (thelonging)


Lesenswert?

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. 😁

von S. Landolt (Gast)


Lesenswert?

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.

von Peter S. (cbscpe)


Lesenswert?

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.
1
        push    r8
2
        in      r8, SREG
3
        push    zh
4
        push    zl
5
        sbi     VPORTE_OUT, 0
6
        lds     zl, intfcount
7
        inc     zl
8
        sts     intfcount, zl
9
        sbi     VPORTE_INTFLAGS, 0
10
        ldi     zl, low(intf)
11
        ldi     zh, high(intf)
12
        rjmp    unblocki

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

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...

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.