Forum: Mikrocontroller und Digitale Elektronik diverse Interruptquellen beim PIC


von Thomas M. (tm112)


Lesenswert?

Hallo,

Falls mehrere Interruptquellen beim PIC verwendet werden, sieht das 
Vorgehen wie folgt aus:

An Programmadresse 4 (Int-Vektor) steht folgendes:

    movwf   w_temp
    swapf   STATUS,w
    bcf     STATUS, RP0       ; status_temp in Bank 0
    movwf   status_temp

Hier wird der Inhalt Statusregisters und des Arbeitsregisters aus dem 
"Hauptprogramm" gesichert.
Anschließend werden die verschiedenen Interruptquellen geprüft (-> wo 
kam der Interrupt her), indem die Flags der zugelassenen 
Interruptregister geprüft werden.

Am Ende werden die Inhalte der Register wieder zurück gesichert.

    swapf   status_temp,w
    movwf   STATUS
    swapf   w_temp,f
    swapf   w_temp,w
    retfie

Soweit, so gut ...

Offenbar hab' ich aktuell ein Problem wenn innerhalb der Int-Routine die 
Inhalte des Status-Registers verändert werden (Z-Flag, etc.). Solange 
nur eine Interruptquelle existiert ist es noch kein Problem, wohl aber 
bei zwei oder mehreren. Gibt es dafür eine clevere und einfach Abhilfe 
oder bleibt tatsächlich nur über, für jede Interruptquelle eine ähnliche 
Sicherung und Rücksicherung anzufertigen, wie für das "Hauptprogramm"?

Thomas

von Dieter W. (dds5)


Lesenswert?

Hallo Thomas

Ich verstehe nicht was Du genau fragen willst.
Jeder Interrupt sollte doch in sich abgeschlossen sein und nicht den 
(unveränderten) Inhalt des Status-Reg beim nächsten Aufruf wieder 
benötigen.
Oder setzt Du während ein Int bearbeitet wird wieder das GIE Flag und 
ein zweiter Int kann dazwischenhauen? Dann ist natürlich wieder die 
Sicherung von allen benutzten Registern in einem anderen 
Zwischenspeicher erforderlich (müsste man wohl mit einem Zählregister 
feststellen in welcher Interruptebene man gerade steckt).

Dieter

von Thomas M. (tm112)


Lesenswert?

Hallo Dieter,

Also versteh' ich's richtig, dass durch Aufrufen der INT-Routine sowieso 
alle weiteren INTs abgeschaltet sind? Das würde das GAnze wesentlich 
vereinfachen und das Problem liegt wo anders.

Kurze Erklärung:
Falls TMR0 überläuft, erzeugt er ca. alle 0,1 s einen Interrupt und 
arbeitet eine relativ lange Routine ab.

Wenn TMR1 überläuft, soll er lediglich alle 0,52s einen Interrupt 
erzeugen. Die aufgerufene Routine soll dann diese 0,52s "vervielfachen" 
und eine LED zum Blinken, bzw. ein Relais zum anziehen bewegen.

Beides voneinander unabhängig funktioniert einwandfrei.
Wird jedoch das Ereignis aus TMR0 aufgerufen, während TMR1 überläuft, 
gibt's Probleme, die LED geht nicht aus, bzw. das Relais fällt nicht ab.

Vielleicht ist auch die "Vervielfachung" der 0,52s nicht clever genug 
gelöst. Mir kommt es vor, als bekäme der PIC nicht mit, wenn einer der 
Zähler auf 0 geht und zählt dann noch mal von 255 runter. Von der 
gemessenen Zeit her sieht es ganz danach aus.
Siehe Anhang. Gestartet wird die Routine mit

 movlf Z1,24
 movlf Z2,24
 movlf Z3,24
 movlf Z4,24
 movlf Z5,24
 movlf T1CON,00110001b ; Timer1 einschalten / starten

Ziel der Übung ist dann folgender Rhythmus an PORTB,7:
12s an, 12 aus, 12s an, 12s an, aus

Eine reine Zählschleife übrigens mit DECFSZ funktioniert übrigens 
deutlcih zu wacklig, dafür springt der PIC viel zu oft über TMR0 in 
seine lange INT-Routine.

Bin für jeden Tipp dankbar,
Thomas

von Chief Brady (Gast)


Lesenswert?

Hallo Thomas,

1. Tritt ein Interrupt auf, sind bis zum 'retfi' alle weiteren 
Interrupts gesperrt. Tritt ein weitere Interrupt auf, wird nach 'retfi' 
sofort wieder in die ISR gesprungen.

2. Du kannst während eines Interrupts weitere zulassen (GIE-Flag). Davon 
ist allerdings abzuraten.

3. Bedenke immer: Vieles mit, aber nur Weniges in Interrupts machen. 
Es scheint mir, deine ISR's sind zu lang (zeitmäßig gesehen).

In der Regel sind alle Timingaufgaben mit nur einem Timer zu lösen, der 
z. B. alle 100ms einen Interrupt auslöst und eine Variable "hochzählt" 
(mehr sollte in der Timer-ISR nicht passieren!). Hieraus können alle 
möglichen Zeiten generiert und Ereignisse (LED an/aus) ausgelöst werden.

CB

von Thomas M. (tm112)


Lesenswert?

Hallo CB,

Danke für die Tipps. Also wäre Dein Ansatz das Timing Intervall des T1 
kürzer zu setzen, als das kürzeste Intervall des T0 (T0 hat keine feste 
Zeitkonstante) und daraus die 0,5 Sekunden abzuleiten?

Als zweiter Ansatz könnte dann das Auseinanderpflücken der TMR0-Routine 
kommen. Wird aber richtig fies - ist eine übernommene Routine.

Eignet sich Deiner Meinung nach die TMR1-Routine, oder gibt's auch da 
noch Optimierungsmöglichkeiten? Kurz noch: Warum werden P1 ... P5 nicht 
nochmal in Schleifen gepackt - die Zeiten können variieren (sind nicht 
immer 12s) und zum Testen wollte ich es etwas übersichtlicher halten.

Thomas

von Aleksandar (Gast)


Lesenswert?

Hi !

Schau Dir erstmal das an:

http://www.fernando-heitor.de/index.php?option=com_jd-wiki&Itemid=85&id=mikrocontroller:interrupts

Bei den 18Fxxx PICs haben die Interruptquellen eine Prioritaet. Damit 
ist es moeglich, dass die Interruptquellen mit hoeerer Prioritaet einen 
Interrupt mit einer niedrigeren Prioritaet unterbrechen. Allerdings 
sollte man das benutzen nur, wenn es wirklich unbedingt sein muss unda 
man weiss genau, was man tut. Das heisst alle 
Interruptbehandlugsroutinen, die gleichzeitig auftreten koennen, muessen 
reentrant sein.

In der 16F Familie gibt es diese Probleme nicht, da werden die 
Interrupts, auch wenn sie gleichzeitih auftreten sequentiell bearbeitet. 
In einem Interrupt-Aufruf koennen zwar gleichzeitig zwei Interrupt-Flags 
gleihzeitig gesetzt werden, aber eine Unterbrechung der 
Interruptbehandlungsroutine finden nicht statt.

Man kann es durch Einschalten des GIE Flags "ausprovozieren" aber das 
mach wenig Sinn, wie andere schon erklaert haben.


Gruss,
Aleksandar

von Peter D. (peda)


Lesenswert?

Aleksandar wrote:
> Das heisst alle
> Interruptbehandlugsroutinen, die gleichzeitig auftreten koennen, muessen
> reentrant sein.

Nein, das heißt es nicht.

Jeder Interrupt hat entweder die hohe oder niedrige Priorität und kann 
sich daher nicht selbst unterbrechen.

Er muß also nur die verwendeten Register sichern, wie es auch ohne 
Prioritäten  der Fall ist.


Die 8051-er haben sogar bis zu 4 Prioritäten und das funktioniert völlig 
problemlos und störungsfrei. Allerdings haben sie auch getrennte 
Interruptvektoren.


Peter

von Aleksandar (Gast)


Lesenswert?

Hi Peter!

>> Das heisst alle
>> Interruptbehandlugsroutinen, die gleichzeitig auftreten koennen, muessen
>> reentrant sein.

>Nein, das heißt es nicht.

Du hast Recht, ich habe mich nicht ganz genau ausgedruckt - die muessen 
sich nur bei der gegenseitigen Unterbrechung nicht beeinflussen.
"gegenseitig reentrant" waere vl. genauer ;)



>Er muß also nur die verwendeten Register sichern, wie es auch ohne
>Prioritäten  der Fall ist.

Genau, aber wenn zB zwei Interrupts gleichzeitig auftreten koennen, dann 
muessen zwei Sets der Variablen fuer "Ablage" definiert und benutzt 
werden - konkret bei PIC waeren das zwei mal mindestens W und STATUS.

Ein Nachteil bei PIC 16 ist, dass es keine PUSH/POP Befehle gibt, man 
muss immer selbst Variablen zum Ablagern definieren.

Gruss,
Aleks

von Peter D. (peda)


Lesenswert?

Aleksandar wrote:

> Ein Nachteil bei PIC 16 ist, dass es keine PUSH/POP Befehle gibt, man
> muss immer selbst Variablen zum Ablagern definieren.

Das stimmt, daran habe ich nicht gedacht.

Bei Architekturen mit PUSH/POP hat es der Assemblerprogrammierer 
wesentlich einfacher.



Peter

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.