Interrupt-Programmierung


;************************ Hauptprogramm************
main
     movf   porta,0    ;lade w mit porta
     movwf  portb      ;kopiere w nach portb
     goto main

Im obigen Beispielprogramm ist der Prozessor im wesentlichen damit beschäftigt, porta abzufragen und nach portb zu kopieren. Dieses Verfahren wird als polling bezeichnet. Hat der Prozessor noch andere Aufgaben, so kann es passieren, dass er längere Zeit porta nicht abfragt, obwohl dort ev. Bereits ein Ereignis eingetreten ist. Andererseits kann es aber auch sein, dass er porta viel zu häufig abfragt und Prozessorzeit verschwendet wird.

Eine Alternative zum polling bildet die sog. Interrupt-Technik, bei der eine unabhängige Quelle eine Unterbrechung des Hauptprogramms auslöst und in eine sog. Interrupt-Routine springt, die dann die eigentliche Ereignis-Behandlungsroutine (interrupt handler) darstellt. So könnte z.B. eine Änderung eines Schalters einen sog. Hardware-Interrupt auslösen. Eine weiter Möglichkeit besteht darin, durch einen Timer-Interrupt porta regelmäßig (aber unabhängig vom Hauptprogramm) abfragen zu lassen.

Der 16C84 besitzt Möglichkeiten, einen Hauptprozess durch sogenannte Interrupts unterbrechen zu lassen. Es gibt verschiedene Interruptquellen, eine davon ist der sogenannte Timerüberlauf. Der Timer ( rtcc = equ 2 : rttc : Real Time Clock/Counter) ist ein 8 Bit breites Register, daß im Hintergrund zählt. Läuft der Timer beim Hochzählen über (0FF 00 Timer overflow), so kann er einen Interrupt auslösen, falls das Intcon-Register ( intcon equ 0B )entsprechend initialisiert war. Der Wert des Programmzählers wird gerettet, so dass der unterbrochene Prozess nach Abarbeiten der Interruptroutine fortgesetzt werden kann.

Allerdings muss dem Prozessor mitgeteilt werden, an welche Stelle des Programms er verzweigen soll, falls ein Interrupt erfolgt. Komplexere Prozessoren haben ganze Tabelle von Interruptvektoren (Sprungadressen), die die Zieladressen solcher Sprünge enthalten. Beim 16C84 besteht diese Tabelle nur aus einer Adresse (004): Hier muss ein Sprungbefehl zur Interrupt-Routine stehen, z.B. goto interrupt.

Normalerweise kümmert man sich beim Brennen eines Programms ins Programm-EEPROM nicht darum, wo der Programmcode hingeschrieben wird. Bei der Interrupt-Programmierung muß man dafür sorgen, dass der Sprungbefehl tatsächlich bei Adresse 004 steht. Da macht man am besten mit der Assembleranweisung: org 004 unmittelbar vor dem Sprungebefehl.

; *********übliche Variablen-Deklaration
    goto  init         ; Startadresse des Hauptprogramms
    org   4            ; Erzwingt den nächsten Befehl in Adresse 04
    goto  interrupt    ; adr 004 : Sprung zurInterrupt-Routine

;************* übliche Initialisierungen
    clrf intcon        ; Interrupt-Register initialisieren
    bsf intcon,7       ; Interrupts zulassen
    bsf intcon,5       ; TimerInterrupt zulassen


;************************ Hauptprogramm************

main
    goto main


;****** Interruptroutine: wird bei Timerüberlauf angesprungen
interrupt
    movf   porta,1
    movwf  portb
    bcf intcon,2     ; timer neu auf 0ff setzen
    retfie           ; Return mit Interrupt enable

Das intcon-Register (0b) ermöglicht das Konfigurieren verschiedener Interruptquellen. Die Registertabelle zeigt, welcheBits für welche Funktionen zuständig sind. Ein weiteres wichtiges Register in diesem Zusammenhang ist das option-Register. Der Häufigkeit des Timerinterrupt kann mit den ersten drei Bits beeinflusst werden. Je nach Wert, den man dem Optionregister in diesen drei Bits gibt ( n = 0 .. 7) beträgt die Zugriffhäufigkeit 2n+1. Den Vorteiler 1 erhält man, wenn man das Watchdog-Timer-Bit setzt und 0 in die untersten drei Bits des intcon-Registers schreibt. Wählt man den Vorteiler für den Timerinterrupt zu klein, so kann es passieren, dass im obigen Beispiel während einer Tasterbetätigung der Schalter mehrere Male abgefragt wird. Umgekehrt kann man die Häufigkeit der Ausführung der Interruptroutine noch erniedrigen, indem man einen Zähler in dieser Prozedur herunter zählt und nur dann die eigentliche Interruptbehandlung ausführt, wenn der Zähler den Wert 0 erreicht hat. Vor Verlassen der Interruptroutine wird das Timerflag, das den Überlauf angezeigt hat wieder gelöscht: bcf intcon,2. Während einer Interruptroutine kann kein weiterer Interrupt ausgelöst werden, sie wird mit retfie (interrupt enable) verlassen.  

 

Timer in Echtzeit   

Es gibt verschiedene Möglichkeiten, die Häufigkeit des Aufrufs einer Interruptbehandlung zu beeinflussen:

a) Den sog. Vorteiler stellt man, indem man die Werte n = 0,..,7 in die untersten drei Bits des option-Registers schreibt. Der Timer wird dann um den Faktor 2n+1 heruntergetaktet. Setzt man zusätzlich auch noch das WDT-Bit, so kann man mit n = 0 im Vorteiler erreichen, daß der Timer nicht heruntergetaktet wird, sondern mit der Frequenz 1MHz (bei einem Quarz mit 4 Mhz Takt) zählt. Man kann also die Dauer für einen Zählerschritt von 11µs bis 2561µs variieren, d.h. die Zeit von einem Timerüberlauf zum nächsten beträgt 256 µs bis 65535µs.

b) Eine weitere Möglichkeit, die Häufigkeit der Interruptbehandlung zu beeinflussen, ist ein softwaremäßiger Vorteiler. Zu Beginn der Interruptroutine wird eine Zähler heruntergezählt und die eigentliche Interruptbehandlung nur dann vorgenommen, wenn dieser Zähler auf 0 steht.

c) Man kann nach jedem Interruptaufruf den Startwert des Timers auf einen definierten Wert setzen, indem man eine bestimmte Zahl ins RTCC schreibt.

Zusammenfassend wird die Zeit zwischen zwei aufeinanderfolgenden Ausführungen der Interruptbehandlung wie folgt berechnet:

T = Software-Vorteiler Hardwarevorteiler RTCC-Startwert.

Beispiel: Eine Zähler soll im Zehntel-Sekunden-Takt zählen:

                                T = 100.000 µs = 32 *3125 µs = 25* 24+1 125 µs.


Man würde also dem Software-Vorteiler den Wert 25 = 19h geben, den Wert 4 ins option-Register schreiben uns beim Verlassen der Interruptroutine den Timer mit dem Wert 256-125 = 131 = 83h starten lassen, so dass er nach 125 Schritten überläuft und einen Interrupt auslöst.

Zurück