Forum: Mikrocontroller und Digitale Elektronik PIC Interrupt - Gedankenfehler?


von Mario (Gast)


Lesenswert?

Hallo,

ich möchte gerade ein UP von einem Interrupt aus starten.
Irgendwo klemmt es da.

Also das UP funktioniert wenn ich es normal per CALL aufrufe.
Der Interrupt funktioniet auch, wenn ich dort NUR einen PIN vom PIC ein 
und ausschalte. Im Interrupt werden auch die Register gesichert.

Gebe ich nun in der Interrupt-Routine CALL UP ein, dann geht nichts 
mehr.
Das UP liegt am Ende vom Programm aber innerhalb von dem selben BLOCK.

Irgend etwas habe ich da nicht beachtet.

von Dieter W. (dds5)


Lesenswert?

Kommt drauf an was für ein PIC es ist.
Die ganz kleinen haben nur Stack für 2 UP bzw. INT Ebenen, vielleicht 
liegt es daran.

von Jens Plappert (Gast)


Lesenswert?

Ich denke mal du meinst mit UP=Unterprogramm?

Das geht beim PIC nicht, hat auch nichts mit der Interrupttiefe (das 
wären z.b. 1 beim 16F, 2 beim 18F) oder der Stacktiefe (8 beim 16F, 18F 
grad ned im Kopp) zu tun. Geht einfach Prinzipbedingt nicht aus dem 
Interrupt heraus calls auszuführen. Hat mich auch schon oft gestört, is 
aber leider so.

von Gerhard. (Gast)


Lesenswert?

Bei den PIC18 kann ein HIGH PRIORITY interrupt eine interrupt routine 
unterbrechen. Im CCS Compiler Handbuch steht:

"The keywords HIGH and FAST may be used with the PCH compiler to mark an 
interrupt as high priority. A high-priority interrupt can interrupt 
another interrupt handler. An interrupt marked FAST is performed without 
saving or restoring any registers. You should do as little as possible 
and save any registers that need to be saved on your own. Interrupts 
marked HIGH can be used normally. See #DEVICE for information on 
building with high-priority interrupts.
A summary of the different kinds of PIC18 interrupts:
#INT_xxxx
Normal (low priority) interrupt. Compiler saves/restores key registers.
This interrupt will not interrupt any interrupt in progress.
#INT_xxxx FAST
High priority interrupt. Compiler DOES NOT save/restore key registers.
This interrupt will interrupt any normal interrupt in progress.
Only one is allowed in a program.
#INT_xxxx HIGH
High priority interrupt. Compiler saves/restores key registers.
This interrupt will interrupt any normal interrupt in progress.
#INT_GLOBAL
Compiler generates no interrupt code. User function is located
at address 8 for user interrupt handling."

Ich selber habe das noch nicht ausprobiert. Den Ausfuehrungen nach 
muesste es funktionieren solange die SFRs richtig konfiguriert sind.

Hoffe dass ein bischen hilft,
Gerhard

von Carsten (Gast)


Lesenswert?

@ Jens Plappert,
das musst du mir genauer erklären.
Also ich programmiere seit Jahren meine Interrupts als Unterprogramm das 
ich mit einem CALL an der Interrupt-Vectoradresse aufrufe (ggf. noch 
Kontextsavingmaßnahmen davor). Hatte nie Probleme damit.
Ich denke da eher an ein Stack-Problem.

von tastendrücker (Gast)


Lesenswert?

Jens Plappert wrote:
>Das geht beim PIC nicht, hat auch nichts mit der Interrupttiefe (das
>wären z.b. 1 beim 16F, 2 beim 18F) oder der Stacktiefe (8 beim 16F, 18F
>grad ned im Kopp) zu tun. Geht einfach Prinzipbedingt nicht aus dem
>Interrupt heraus calls auszuführen. Hat mich auch schon oft gestört, is
>aber leider so.

Warum sollte das nicht gehen? Das ist gängige Praxis bei meinen 
Programmen.


@Mario:
Vieleicht solltest Du mal ein bisschen Code posten!

von urgs (Gast)


Lesenswert?

Hallo Mario,

wenn Du mit call die Inteterrupt-Service-Routine (ISR) aufrufst, springt 
er am Ende nach dem return *1) an die Adresse (+ 1 Adresse) zurück, von 
der sie gestartet wurde: also in Deinem Fall wieder direkt hinter den 
Interrupt-Vektor. Die Rücksprungadresse wird auf den Stack gelegt.

Gebe statt call den Befehl goto, bzw. bra zum Aufruf der ISR ein. Am 
Ende der ISR steht dann retfie. Damit landest Du dann beim Rücksprung 
(mit retfie) mitten im Programm eine Adresse hinter der Adresse, an der 
der Interrupt ausgelöst wurde. Nach dieser Aktion ist die Welt also 
wieder in Ordnung.

*1) Der Unterprogrammaufruf mittels call wird grundsätzlich mit return 
beendet, da call die Rücksprungadresse auf den Stack legt und er wieder 
mittels return bereinigt werden muß. Beim Interrupt-Aufruf wird bereits 
die Rücksprungadresse auf den Stack gelegt. Retfie korrigiert ihn 
wieder, ein zusätzliches call würde nur eine weitere Adresse auf den 
Stack legen, ohne daß er jemals wieder korrigiert werden würde.

Viel Spaß dabei,
urgs

von Mario (Gast)


Lesenswert?

Hallo.

Vielen Dank für die vielen Antworten.
Jetzt komme ich leider nicht mehr mit.

Leider fehlt mein 2. Posting von gestern. Hatte leider auch 
Internetprobleme.

Es geht um den PIC 16F876A
Die Anzahl der UP-Aufrufe (TIEFE) im Hauptprogramm liegt bei CALL bei 2 
oder 3.
Ich bin jetzt nicht sicher, aber 7 sind wohl noch möglich.
Mal angenommen es sind 3, dann würde doch ein weiterer Wert 
(Rücksprungandresse) im Stack gespeichert werden, wenn der Interrupt 
ausgeführt wird.
Dann folgt im Interrupt mein CALL. Macht 5
Das UP selber ruft mehrmalig ein UP per Call auf, die aber alle mit 
Return wieder in das UP zurückspringen. Macht eigentlich 6 Adressen im 
Stack. Oder nicht?

Call muss aber grundsätzlich funktionieren aus der Interruptroutine, 
denn da habe ich schon einige Demoprogramme wo es geht.

Hier mal der Code von der Interruptroutine, der Rest stark gekürzt:

 org   4
intvec
  movwf  w_copy    ; w retten
  swapf  STATUS, w   ; STATUS retten
  clrf  STATUS
  movwf  s_copy    ;
  movf  PCLATH, W
  movwf  p_copy
  clrf  PCLATH    ; Bank 0

; Intrupt servic routine
    BSF PORTC,5       ;per Oszi ist der Impuls sichtbar!
    nop
    BCF PORTC,5

  call  Tempmessen
  call  TempAusgabe  ; anzeigen am LCD


Int_end
  movf  p_copy, W
  movwf  PCLATH
  swapf  s_copy, w  ; STATUS zurück
  movwf  STATUS
  swapf  w_copy, f  ; w zurück mit flags
  swapf  w_copy, w

  bcf  INTCON, T0IF  ; Interupt-Flag löschen
  retfie


Tempmessen x
           Call A        ;Rücksprung per Return
           Call B        ;Rücksprung per Return
           Return


OHHHHHHHH
Jetzt wo ich das so sehe, das kann nicht gehen.
Das UP wird auch im Hauptprogramm benutzt.
Da stimmen ja keine Variablen mehr.
Und da wollte ich es mir leichter machen.
Also muss ich in diesem UP den Interrupt abschalten, sonnst kann das ja 
nicht gehen.

Sorry für meine Blödheit ;o)

von tastendrücker (Gast)


Lesenswert?

@urgs

>*1) Der Unterprogrammaufruf mittels call wird grundsätzlich mit return
>beendet, da call die Rücksprungadresse auf den Stack legt und er wieder
>mittels return bereinigt werden muß. Beim Interrupt-Aufruf wird bereits
>die Rücksprungadresse auf den Stack gelegt. Retfie korrigiert ihn
>wieder, ein zusätzliches call würde nur eine weitere Adresse auf den
>Stack legen, ohne daß er jemals wieder korrigiert werden würde.

Das ist so nicht richtig. Selbstverständlich kann in einer ISR ein CALL 
auftauchen, ohne das da irgendwetwas "durcheinander kommt". Selbst in 
den Unterroutine können weitere CALLs vorhanden sein. Es muss nur wie 
immer beachtet werden: Jedes CALL muss mit einem RETURN enden. Der 
Unterschied zu RETFIE ist, das bei RETFIE der GIE wieder enabled wird.

Dieses ist z. B. gar kein Problem:

ISR:
  ...
  ...
  CALL abc
  ...
  ...
  RETFIE


abc:
  ...
  ...
  ...
  RETURN


Wie gesagt: CALL->RETURN. Und die Stacktiefe muss beachtet werden, wobei 
besonders zu beachten ist, das beim Auftreffen eines Interrupts schon 
Adressen auf dem Stack liegen könnten.

von Jens P. (Gast)


Lesenswert?

Leute ihr habt mich da aber n bissl falsch verstanden. Dass man die ISR 
per Call aufrufen kann ist ja klar. Ich wollte nur darauf hinweisen dass 
man aus der ISR heraus keine Calls ausführen  sollte...

von Sven S. (stepp64) Benutzerseite


Lesenswert?

Genau das haben doch nun aber mehrere Leute hier gesagt ist eben gerade 
kein Problem. Warum solltest du in einer ISR kein CALL ausführen dürfen? 
Ist doch alles kein Problem solange du beachtest das der Stack nicht 
überläuft und du den CALL mit RETURN beendest. Der Stack hat Platz für 8 
Aufrufe. Also

ISR
  CALL
    CALL
      CALL
        CALL
          CALL
            CALL
            :
            RETURN
          RETURN
        RETURN
      RETURN
    RETURN
  RETURN
RETFIE

sollte kein Problem sein (vorausgesetzt vor dem Aufruf der ISR wurde 
nicht schon ein CALL ausgeführt ** ups ** )

Sven

von Thomas B. (dertom83)


Lesenswert?

ich schlage mich auch gerade mit den interrupts rum und lese mich gerade 
durch die Interrupt postings bezüglich PIC...

es müsste doch auch so gehen:


Call
|  ISR
|  |  Call
|  |  |  Call
|  |  |  |
|  |  |  |
|  |  |  return
|  |  return
|  retfie
return

Um nicht noch ein Posting dazu zu öffnen,
welche Register muss ich retten, zurücksetzen, oder umschreiben, wenn 
ich mich gerade in der ISR befinde?

Muss man den GIE zurücksetzen, oder macht der PIC das automatisch nach 
dem retfie?

ich habe im moment den fall, dass ich mit einem Signal mit pull down 
(100k evtl zu viel?)am RA2 eine Intrrupt starten möchte und so lange in 
der Routien bleiben möchte, biss das signal wieder auf 0 ist.
Hier mein Code:

org 0x0
goto  Start

org 0x04
timer_0
bsf  PORTB,7
BTFSC  PORTA,2
GOTO  timer_0
bcf  OPTION_REG,6
bcf  INTCON,INTF
bcf     PORTB,7
retfie

von Meister E. (edson)


Lesenswert?

@dertom83

Du verschweigst wie so viele, welchen PIC du einsetzt. :(
(man beachte den zweiten Post)

Was man schon mal aus dem Stand sagen kann:

>ich schlage mich auch gerade mit den interrupts rum und lese mich gerade
>durch die Interrupt postings bezüglich PIC...

Am besten lies auch gleich noch das Datenblatt des PIC, den du 
verwendest. Was du dir aus den Posts zusammengereimt hast, ist so noch 
nicht praktikabel.

>welche Register muss ich retten, zurücksetzen, oder umschreiben, wenn
>ich mich gerade in der ISR befinde?

Genau genommen gar keine, als sinnvoll haben sich aber das 
zwischenspeichern des Arbeitsregisters w, des Statusregisters und je 
nach PIC des BSR oder PCLATH erwiesen. Das hängt davon ab, ob diese 
Register in der ISR manipuliert werden oder nicht.

>Muss man den GIE zurücksetzen, oder macht der PIC das automatisch nach
>dem retfie?

Probiers aus, das kannst du sogar ohne PIC in MPSIM testen.

>ich habe im moment den fall, dass ich mit einem Signal mit pull down
>(100k evtl zu viel?)am RA2 eine Intrrupt starten möchte und so lange in
>der Routien bleiben möchte, biss das signal wieder auf 0 ist.

Nochmal, schau ins Datenblatt. Was soll denn heissen: am RA2 ein 
Interrupt auslösen???

So wie ich deinen code (dem übrigens das Hauptprogramm fehlt) 
interpretiere, fragst du PORTA, 2 in der Hauptschleife ab und setzt dann 
per Hand das INTF. Das macht keinen Sinn, es sei dann, du wolltest so 
das Verhalten von GIE überwachen. Dann hättest du aber nicht danach 
gefragt, sondern wüsstest es jetzt...

Was dein code macht, kann man problemlos ohne Interrupt lösen.

Gruss,
Edson

von Thomas B. (dertom83)


Lesenswert?

okok, ich habe ein 16F690
habe mir das datenblatt schon mehrfach durchgelesen, da steht aber nicht 
genau drin was ich zu tun habe...
Wenn ich da lesen:

"When an Interrupt is serviced:
The GIE is cleard
the returnadress is pushed onto the stack
The PC is loaded with 0004"

Sagt mir das zwar was passiert, wenn ich den Interrupt auslöse, aber 
nicht was ich während der ISR machen muss um diesen evtl erneut auslösen 
zu können.

Klar, hier steht noch, dass ich mit INTE diesen Interrupt ab- und 
einschalten kann. Und dass INTF gesetzt wird wenn ich diesen so 
genannten "RA2/INT Interrupt" auslöse und von hand zurückgesetzt werden 
muss.

in der ISR muss also nur stehen?:

      org 0x04
ISR   BSF  INTF
      BSF  LED
      BCF  LED
      RETFIE


Mir ist klar, dass ich das ganze auch ohne intererupt lösen kann... hab 
ich schon.... aber ich denke, so ist es am einfachsten in die Interrupts 
einzusteigen.

Gruß
Thomas

von Meister E. (edson)


Lesenswert?

>habe mir das datenblatt schon mehrfach durchgelesen, da steht aber nicht
>genau drin was ich zu tun habe...

Prinzipiell steht im Datenblatt, was und wie der PIC tut. Um das alles 
zu verstehen, brauchts am Anfang Geduld und Spucke.

>in der ISR muss also nur stehen?:

Du schreibst selber,

>dass INTF gesetzt wird

also warum setzt du dieses Bit in der ISR nochmal? Das anschliessende 
toggeln von LED bringt dir ausser einem kurzen Impuls mit der Dauer 
eines Maschinenzyklus nichts ein.

>Mir ist klar, dass ich das ganze auch ohne intererupt lösen kann... hab
>ich schon....

Versteh mich jetzt nicht falsch, aber das würde ich gerne sehen. Aus 
deinen bisherigen Codefragmenten geht nicht gerade hervor, das du es 
kannst.

>aber ich denke, so ist es am einfachsten in die Interrupts
>einzusteigen.

Kaum Gedacht, schon falsch gemacht ;)

Aber im Ernst: Ein Interrupt oder das Setzten des zugehörigen 
Interrupt-Flags, das den Sprung in die ISR bewirkt, werden durch ein 
bestimmtes (und zu bestimmendes) Ereignis ausgelöst. In deinem Fall eine 
Flanke am Eingang RA2. Das Ereignis führt zum Sprung in die ISR, wenn 
das zugehörige Enable-Bit gesetzt ist. GIE ist das globale Enable-Bit 
für alle Interrupts des PIC. Es muss gesetzt sein, wenn Interrupts 
ausgelöst werden sollen. In der ISR brauchst du dich nicht darum zu 
kümmern, wohl aber um das Interrupt-Flag des eingetretenen Ereignisses.

Dein Code aus dem vorigen Post beinhaltet demnach einen Kardinalfehler, 
den du jetzt auch erkennen solltest.

Alles klar jetzt?

Gruss,
Edson

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.