Elektronik-Projekt: Elektronische Sanduhr


zurück zu Elektronik, Homepage

Dieses Projekt wurde in der Elektronik-Zeitschrift Elektor ( http://www.elektor.de) in der Ausgabe Februar 2003 publiziert.

Sanduhr im Betrieb

4. Softwarebeschreibung

4.1. Allgemeines zur Software

Die Aufgaben des Controllers (IC1, PIC16F84) lassen sich grob in folgende Teilaufgaben zerlegen:

Das Besondere bei diesem Projekt ist, dass die Adressen der 32 LEDs, welche gerade leuchten, in den RAM-Speicherregistern, mit den selbst gewählten Namen POS1 bis POS32 stehen. Im Ausgangszustand (die unteren 32 LEDs leuchten) beinhalten die RAM-Register POS1 bis POS32 die Leuchtdioden mit den Adressen der LEDs D33 bis D64. In POS1 steht also der Wert b10100011 (=Adresse der LED D33) und in POS32 der Wert b10000000 (=Adresse der LED D64). vgl Unterprogramm ZUSTAND0. Dass immer die Leuchtdioden leuchten welche in den Registern POS1 bis POS32 leuchten, dafür ist die Interrupt Service Routine (kurz ISR) zuständig. (Siehe Abschnitt 4.3. ISR).
Nach dem Betätigen der Starttaste soll nun die Sanduhr "umgedreht" werden, was bedeutet, dass nun die oberen 32 Leuchtdioden leuchten. In den RAM-Registern POS1 bis POS32 stehen nun die LED-Adressen der oberen 32 Leuchtdioden. In POS1 demnach b10111111 (=Adresse von LED D1) und in POS32 der Wert b10010100 (=Adresse von LED D32). Die ISR bekommt jetzt eine zusätzlich Aufgabe, sie muss jetzt bei jedem Aufruf das Zählregister AKTZEITx um 1 vermindern. Bei diesem Register handelt es sich um ein 16-Bit-Zählregister. Dieses muss daher auf zwei 8-Bit Register mit den Namen AKTZEITL und AKTZEITH aufgeteilt werden. Ist dieses 16-Bit-Register Null, so wird ein Bit (NaechstZust-Flag) gesetzt und das Hauptprogramm weis nun, dass ein Zustandswechsel der Leuchtdioden erfolgen muss. Für den Zustand, wo die oberen 32 Leuchtdioden leuchten und dem Ausgangszustand (die unteren 32 Leuchtdioden leuchten) stehen die zwei Unterprogramme ZUSTAND1 und ZUSTAND0 zur Verfügung. Für alle Zustände dazwischen ebenfalls ein eigenes Unterprogramm zu schreiben, wäre zwar eine Möglichkeit, aber mit einem PIC16F84 wahrscheinlich nicht zu realisieren, da dies zu viel Programmspeicher benötigen würde. Es geht auch anders, doch dazu mehr im Abschnitt Kapitel 4.4.5. NAECHSTZUST.

Etwas genauer soll hier noch ein Register namens SUSTATUS erklärt werden. Dieses Register ist sozusagen das Sanduhr-Statusregister, wobei nur die drei niederwertigsten Bits benötigt werden. Im Ausgangszustand besitzen alle Bits den Wert 0.

nach oben

4.2. Hauptprogramm

Die Aufgaben des Hauptprogramms lassen sich gut mit einem Flussdiagramm beschreiben

Flussdiagramm Hauptprogramm

Zuerst wird der Prozessor initialisiert, und der Ausgangszustand (die unteren LEDs leuchten) hergestellt. Diese beiden Tätigkeiten werden von je einem Unterprogramm ausgeführt (INIT bzw. ZUSTAND0). Erst jetzt werden der Timer0-Interrupt und der globale Interrupt freigegeben. Dafür ist das Register INTCON zuständig. Je nach benötigten Interrupts werden die entsprechenden Freigabebits (im Englischen: Enable) gesetzt. Wird ein Interrupt verwendet so muss zusätzlich zum verwendeten Interrupt auch die globale Interruptfreigabe GIE (General Interrupt Enable) gesetzt werden. Er ist sozusagen der Hauptschalter, der Interrupts ermöglicht. Das INTCON-Register kann einfach mit den Befehlen movlw b10100000 und movwf INTCON beschrieben werden. Der Timer0-Interrupt ist jetzt eingeschaltet. Er sorgt hier bei der Sanduhr unter anderem dafür, dass der Benützer den Eindruck hat, dass die 32 leuchtenden LEDs gleichzeitig leuchten. In Wirklichkeit leuchtet immer nur eine LED für einen Bruchteil einer Sekunde. Diese Verfahren wird Multiplex genannt.

Nun wartet das Hauptprogramm solange bis die Starttaste (S3 am Port RB7) gedrückt wird. Dieser Pin also LOW wird. Eine einfache Schleife wird dazu benötigt:

START           btfsc       PORTB,STARTTASTE
                goto        START

Wurde die Starttaste gedrückt, so wird der Port RB7 low und die oben genannte Schleife wird verlassen. Das Hauptprogramm geht in eine neue Schleife, in die HAUPTSCHLEIFE über. Hier erfolgt als erste Tätigkeit das setzen von SUSTATUS.0 (vgl Abschnitt Allgemeines zur Software)
Das Bit SUSTATUS.2 ("Fertigbits") könnte gesetzt sein, und muss daher wieder gelöscht werden. Warum kann das "Fertigbit" gesetzt sein? Das Flussdiagramm zeigt es deutlich. Wird zum Beispiel die Sanduhr mehrmals hintereinander benützt (ohne das sie dazwischen ausgeschaltet wird), beginnt das Hauptprogramm, nachdem die Starttaste wieder gedrückt wurde wieder in der Hauptschleife, und hier ist ja das "Fertigbit" noch gesetzt.

Es leuchten noch die unteren Leuchtdioden. Eine herkömmliche Sanduhr würde man jetzt auf den Kopf stellen. Hier, bei der elektronischen Version, werden einfach die unteren LEDs "ausgeschaltet" und die oberen LEDs "eingeschaltet". D. h. die Adressen der oberen 32 LEDs werden in die RAM-Register POS1 bis POS32 geladen. Diese Aufgabe übernimmt das Unterprogramm ZUSTAND1.

Damit das Programm weis in welchem Zustand es sich befindet (welche LEDs gerade leuchten), dient ein Zählregister namens AKTZUSTAND. Dieses Register muss ebenfalls mit dem entsprechenden Wert geladen werden. Dieser Wert steht in der Konstante ZUSTANZ. Mit den folgenden Befehlen wird der Inhalt der Konstante (ZUSTANZ) in das Register AKTZUSTAND kopiert:

                movlw       ZUSTANZ
                movwf       AKTZUSTAND

Die Konstante ZUSTANZ besitzt übrigens den Wert 112. Es gibt demnach 112 Schritte zwischen dem Zustand wo die oberen 32 Leuchtdioden leuchten und dem Zustand wo die unteren 32 leuchten.

Als nächstes erfolgt die Abfrage der eingestellten Zeit und das Laden der entsprechenden Register. Diese Aufgabe übernimmt wieder ein eigenes Unterprogramm (ZEITAUSWAHL), und soll daher an dieser Stelle nicht weiter beschrieben werden.

Es wurden nun alle Parameter vom Hauptprogramm erfasst und den entsprechenden Registern zugewiesen. Jetzt muss "nur" mehr das "Sanduhr-Status"-Register ständig geprüft und die entsprechenden Aktionen ausgelöst werden: Ist das Fertigflag gesetzt muss der Lautsprecher solange summen, bis die Starttaste erneut gedrückt wird. Andernfalls wir das "Zustandswechselflag" (SUSTATUS.1) geprüft. Ist dieses gesetzt, so erfolgt mit Hilfe des Unterprogramms NAECHSTZUST ein Zustandswechsel. Andernfalls wird das "Sanduhr-Status"-Register erneut geprüft. Dieses Prüfen geschieht solange, bis das "Fertig-Flag" gesetzt ist.

nach oben

4.3. ISR (Interrupt Service Routine)

Eine ISR (Interrupt Service Routine) ist im Prinzip ein Unterprogramm, welches aber im Gegensatz zu normalen Unterprogrammen, "unvorhergesehen" aufgerufen wird. Hier, beim Timer 0-Interrupt jedes Mal, wenn der Timer 0 überläuft, also von 255 auf 0 wechselt. Würde zum Beispiel ein RB-Interrupt verwendet werden, so würde bei jeder Pegeländerung von RB4 bis RB7 ein Interrupt auftreten und die entsprechende ISR wird ausgeführt. Eine ISR sollte daher so kurz wie möglich sein.

Ein weiterer wichtiger Punkt bei einer ISR ist, dass das w-Register (Working- oder Arbeitsregister) und das STATUS-Register in andere Register zwischengespeichert werden müssen, falls diese in der ISR ihren Registerinhalt verändern. Der Grund dafür ist, dass eine ISR eben unvorhergesehen aufgerufen wird, und die angesprochenen Register unter Umständen zu diesen Zeitpunkten gerade benötigte Werte enthalten. Nach Ausführung der ISR springt diese zwar wieder genau an die Stelle zurück, wo sie war, bevor der Interrupt auftauchte, aber mit einem möglicherweise falschen Wert im w-Register (bzw. STATUS-Register). Das Zwischenspeichern des w-Register bzw. des STATUS-Registers wird häufig auch als PUSH bezeichnet. Das Widerherstellen von w-Register und STATUS-Register nennt man POP.

Woher weis das Programm, dass ein Interrupt aufgerufen werden muss? Dazu gibt es für jede Interruptquelle ein Kontroll-Flag. Dies wird vom Controller gesetzt wenn dieser Interrupt auftritt. (Vorausgesetzt, dass diese Interruptquelle freigegeben ist). Damit aber die ISR nicht ständig aufgerufen wird, muss dieses Bit in der ISR wieder gelöscht werden.

Nun aber zur sanduhrspezifischen Timer 0-ISR. Auch hier soll wieder ein Flussdiagramm den Ablauf etwas anschaulicher darstellen. Die ISR wird übrigens alle 512 µs aufgerufen. (Dieser Wert ergibt sich folgendermassen: TMR0 wird mit dem Wert 0 geladen. ES dauert also 256µs bis das Register wieder den Wert 0 besitzt. Der Vorteiler besitzt den Wert 2. Diese beiden Werte multipliziert ergeben den Wert 256µs bei einer Taktrate von 1µs. Die Taktrate von 1µs ergibt sich bei der Verwendung eines 4-MHz-Quarz.)

Flussdiagramm ISR

Der erste Schritt ist die schon erwähnte PUSH-Befehlsfolge, also das Zwischenspeichern vom Arbeitsregister (w-Register) und dem STATUS-Register. Hierfür wurden eigens zwei Register mit den Namen w_TEMP und STAT_TEMP definiert.

PUSH            movwf       w_TEMP
                swapf       STAT,w
                movwf       STAT_TEMP

Wie schon mehrmals erwähnt werden die Leuchtdioden im Multiplex-Verfahren angesteuert. D.h. es werden nicht alle Leuchtdioden gleichzeitig betrieben sondern immer nur eine für einen Bruchteil einer Sekunde. Die Vorteile dieses Verfahrens sind: Einsparung an Leitungen und Portpins. Würde für die 64 LEDs je ein Portpin verwendet werden, würde man 64 Portpins benötigen. Mit einer 8x8-Matrix und zusätzlich noch zwei 8-aus-3-Dekoder reichen nur sechs Leitungen aus um alle 64 Leuchtdioden unabhängig voneinander anzusteuern. Ein weiterer Vorteil einer Multiplexansteuerung ist der geringere Stromverbrauch, da immer nur eine Leuchtdiode leuchtet und daher Strom benötigt.
Welche der 32 Leuchtdioden gerade leuchten soll, also am Port B ausgegeben wird, bestimmt das Zählregister ZAEHLLED. Dieses Register wird bei jedem Aufruf der ISR um eins vermindert. Hat es den Wert 0, so wird es wieder mit der Konstanten LEDANZAKTIV (=32) geladen. Im Register ZAEHLLED steht also eine Zahl zwischen 1 und 32. Diese Zahl gibt an welches POSx-Register am Port B ausgegeben werden soll. In den Registern POS1 bis POS32 stehen die Adressen der 32 aktuell leuchtenden LEDs. Um das entsprechende POSx-Register am Port B ausgeben zu können hilft uns die indirekte Adressierung. Bei dieser Adressierung wird nicht der Registername (z.B. POS1, SUSTATUS, PORTA usw.) sondern deren Adresse in das FSR-Register geladen. Im IND-Register steht dann der Registerinhalt. Beispiel: es soll mittels indirekter Adressierung auf das POS1-Register zugegriffen werden. Das POS1-Register hat die Adresse 11h. Das FSR-Register wird also mit dem Wert 11h geladen, und im IND-Register steht nun der Inhalt von POS1. Wozu das ganze, es geht ja einfacher mit dem Befehl movf POS1,w um auf das Register POS1 zuzugreifen? Um die Adresse der gerade leuchtenden LED zu erhalten muss nur das Zählregister ZAEHLLED mit 10h addiert werden und in das FSR-Register geschrieben werden. Im IND-Register steht nun die Adresse der aktuellen LED. Diese muss jetzt nur mehr über das w-Register in das PORTB-Register kopiert werden. Dies ist sicherlich die einfachste Möglichkeit das entsprechende POSx-Register in das PORTB-Register zu kopieren. Diese Methode benötigt nur 5 Befehle:

                movlw       10
                addwf       ZAEHLLED,w
                movwf       FSR
                movf        IND,w
                movwf       PORTB

Ist Bit 0 von SUSTATUS gesetzt. (Die Sanduhr ist also in jenem Zustand, wo die LEDs nach unten rieseln), wird bei jedem Aufruf der ISR das 16-Bit-Zählregister AKTZEIT um eins vermindert. Ist dieses Register 0 so wird das NaechstZustand-Flag gesetzt, und dem Hauptprogramm somit mitgeteilt dass ein Zustandswechsel zu erfolgen hat. Weiters muss das AKTZEIT-Register neu geladen werden. Dieses Register gibt also die Anzahl der ISR-Aufruf bis zum nächsten Zustandswechsel an. Da es sich hierbei um ein 16-Bit-Register handelt muss es auf zwei 8-Bit-Register (AKTZEITH bzw. AKTZEITL) aufgeteilt werden.

Die sanduhrspezifischen Funktionen sind nun erledigt. Was jetzt noch fehlt, ist das Interrupt-Anforderungsbit, hier für den Timer 0-Interrupt, wieder zu löschen (T0IF im Register INTCON),

                bcf         INTCON,T0IF

und den Zustand vom Arbeits- und Statusregister wiederherstellen.

POP             swapf       STAT_TEMP,w
                movwf       STAT
                swapf       w_TEMP,f
                swapf       w_TEMP,w

Jede ISR muss mit dem Befehl retfie beendet werden. (Beim Aufruf der ISR wird automatisch das Bit GIE gelöscht, damit während der Ausführung der ISR kein weiterer Interrupt ausgelöst werden kann, was bei mehreren freigegebenen Interruptquellen durchaus möglich sein kann. Mit dem Befehl retfie wird zunächst an die Stelle im Programm zurückgesprungen, wo sich die Programmabarbeitung befand bevor die ISR aufgerufen wurde, und die verwendeten Interrupts werden wieder freigegeben. Hier bei der Sanduhr nur der Timer0-Interrupt.)

nach oben

4.4. Unterprogramme

4.4.1. Unterprogramm INIT

Dieses Unterprogramm initialisiert den Prozessor, in dem folgende Parameter eingestellte werden:

               clrf         TMR0
               bsf          STAT,RP0
               movlw        b00000000
               movwf        OPTREG
               movlw        b10000000
               movwf        TRISB
               movlw        b11111
               movwf        TRISA
               bcf          STAT,RP0
               movlw        b00000000
               movwf        SUSTATUS
               movlw        LEDANZAKTIV
               movwf        ZAEHLLED
               return
nach oben

4.4.2. Unterprogramm ZUSTAND0

Diese Unterprogramm lädt die Adressen der unteren 32 Leuchtdioden (LED33 bis LED64) in die Register POS1 bis POS32. Die Adressen der Insgesamt 64 Leuchtdioden stehen als Konstanten (LED1 bis LED64) zur Verfügung

nach oben

4.4.3. Unterprogramm ZUSTAND1

Wie bei ZUSTAND0, es werden jedoch die oberen 32 Leuchtdioden (LED1 bis LED32) in die Register POS1 bis POS32 geladen.

nach oben

4.4.4. Unterprogramm ZEITAUSWAHL

Das Unterprogramm für die Zeiteinstellung sieht auf dem ersten Blick etwas komplex und verwirrend aus. Man soll sich aber von der Länge nicht täuschen lassen. Zunächst muss man wissen, dass die Schalterstellung "0" des HEX-Codierschalters (S1) am Port A einen Zustand von "x1111" ergibt. Die einzelnen Bits also invertiert sind. Die Schalterstellung "F" ergibt demnach den Zustand "x0000".

Das Unterprogramm fragt zuerst das höherwertige Bit des Codierschalters ab (PORTA.3). Ist dieses gesetzt, so bedeutet dies, dass sich die Schalterstellung zwischen 0 und 7 befindet. Als nächstes wird der Portpin PORTA.2 geprüft. Ist PORTA.3 LOW, so springt es zum Label Z8bis15. Das bedeutet, dass die eingestellte Zeit zwischen Schalterposition 8 und 15 steht. Dies wird für jeden der vier Portpins durchgeführt. Ist der Codierschalter zum Beispiel in der Position 5 (dies entspricht einer eingestellten Zeit von 3 Minuten) so werden vom Programm folgenden Schritte ausgeführt. Am Port A steht folgender Zustand: x1010. Zuerst wird das höchstwertige Bit (des Codierschalters) geprüft: btfss PORTA.3. Dieser ist "1", es wird also der Befehl goto Z8bis15 übersprungen. Als nächstes erfolgt der Befehl btfss PORTA.2. Dieser Pin ist in unserem Beispiel "0". Es wird daher der Befehl goto Z4bis7 ausgeführt. Wir stehen im Programm daher auf dem Label Z4bis7. Nun wird mit dem Befehl btfss PORTA.1 der Portpin mit der Wertigkeit 1 geprüft. In unserem Beispiel ist dieser wieder "1", es wird also der Befehl goto Z6u7 übersprungen. Als letzter Pin ist jetzt noch der niederwertigste zu testen.: btfss PORTA,0 dieser ist "1", folglich wird der Befehl goto ZEIT5 ausgeführt. Nun werden mit den folgenden Befehlen die Zeitregister mit ihren Werten (Konstanten) geladen.

ZEIT5          movlw        ZEIT5L
               movwf        ZEITL
               movwf        AKTZEITL
               movlw        ZEIT5H
               movwf        ZEITH
               movwf        AKTZEITH
               return

Die Register ZEITL und AKTZEITL werden in diesem Beispiel also mit der Konstante ZEIT5L (=67d) und die Register ZEITH und AKTZEITH mit der Konstante ZEIT5H (=13d) geladen. Dies entspricht einer Zeit von etwa 1,6 Sekunden zwischen zwei Zuständen. Bei 112 Zuständen ergibt dies eine Gesamtzeit von 180 Sekunden also 3 Minuten. Mehr dazu im Abschnitt 5.1. Zeitintervalle ändern.

nach oben

4.4.5. Unterprogramm NAECHSTZUST

Wie funktioniert nun ein Zustandswechsel? In den Registern POS1 bis POS32 stehen die Adressen der 32 Leuchtdioden, welche gerade leuchten. Es sind immer nur 32 Leuchtdioden aktiv. Bei einem Zustandswechsel muss also eine Leuchtdiode dunkel werden, und dafür eine andere leuchten. Die Adresse, jener Leuchtdiode welche erlischt, wird in den Registern POS1 bis POS32 gesucht, und durch die Adresse der neuen Leuchtdiode ersetzt. Woher weis das Programm welche Leuchtdiode erlöschen soll, und welche anstelle dieser leuchten soll? Dazu gibt es zwei Tabellen. In der Tabelle TABWIRDERSETZT stehen die Adressen der Leuchtdioden, die bei jedem Zustandswechsel erlöschen und die Tabelle TABNEUELED beinhaltet die Adressen der Leuchtdioden die bei jedem Zustandswechsel neu hinzukommen. Ein Beispiel macht diesen Zusammenhang etwas deutlicher. Hier der Übergang von Zustand 109 auf 110:

Zustandswechsel

Der genaue Ablauf wird wieder mit einem Flussdiagramm genauer beschrieben:

Flussdiagramm NAECHSTZUST

Das Register SUCHZUST wird zunächst mit POS1 geladen, im Register AKTZUSTAND steht die aktuelle Zustandsnummer von 1 bis 112. Mit Hilfe des AKTZUSTAND-Register wird nun aus der Tabelle TABWIRDERSETZT die Adresse der zu ersetzenden Leuchtdiode geholt und im Register ZWISCH zwischengespeichert. Nun beginnt der Suchvorgang. Auch hier ist uns die indirekte Adressierung behilflich. Der Registerinhalt von SUCHZUST wird nun in das FSR-Register geladen. Im IND-Register steht nun die Adresse der entsprechenden (gerade aktiven) LED, diese wird mit der Adresse, welche im Register ZWISCH steht verglichen, indem diese beide voneinander subtrahiert werden. Ist das Ergebnis der Subtraktion Null, so sind die Registerinhalte dieser beiden Register gleich. Ob das Ergebnis Null ist erkannt man daran, dass das Zero-Bit (Z) gesetzt ist. Mit Hilfe des Registers AKTZUSTAND wird nun aus der Tabelle TABNEUELED die Adresse der neuen LED geladen und in das IND-Register geladen. Im entsprechenden POSx-Register steht nun die Adresse der neuen aktiven LED. Ist das Ergebnis der Subtraktion jedoch nicht Null, so wird das Z(ero)-Bit vom Controller gelöscht. Das SUCHZUST-Register und das SUCHZAEHLER-Register werden um eins vermindert und ein erneuter Vergleichsvorgang beginnt.

Kann, aus welchem Grund auch immer, die zu suchenden LED-Adresse nicht gefunden werden, so wird mit dem SUCHZAEHLER-Register verhindert, dass irgendein anderes, zufällig passendes Register in ein POSx-Register geladen wird. Konnte die zu suchende LED-Adresse nach 32 Durchgängen nicht gefunden werden, so beginnt der gesamte Suchvorgang von vorne. Im Normalfall soll dies nicht passieren, im Entwicklungs- bzw. im Modifikations-stadium ist dies hilfreich.

Wurde also die zu suchende LED-Adresse gefunden und durch die Neue ersetzt, wird als nächstes das AKTZUSTAND-Zählregister um eins vermindert, damit beim nächsten Aufruf von NAECHSTZUST der nächste Zustand hergestellt werden kann. Wird dieses Register Null, ist die eingestellte Zeit abgelaufen. Das Fertigflag (SUSTATUS.2) wird gesetzt, und das LED-rieseln-Flag (SUSTATUS.0) wird gelöscht.

Der Zustandswechsel ist jetzt abgeschlossen (egal welchen Wert das AKTZUSTAND-Zählregister hat), das NächstZust-Flag wird daher wieder gelöscht. Optional kann bei jedem Zustandswechsel ein Beepton erfolgen. Dazu muss der Jumper JP1 gesteckt sein. Softwaremäßig wird dies mit dem Befehl btfsc PORTA,SUMMERSELEKT abgefragt. Ist der Jumper gesteckt, so ist der Portpin RA4 low und die folgenden Befehle werden ausgeführt:

               bsf          PORTB,SUMMER
               call         WARTESCHLEIFE
               bcf          PORTB,SUMMER

Es wird also der Lautsprecher für eine Zeit von 500µs (siehe Abschnitt 4.4.7. WARTESCHLEIFE) eingeschaltet und danach wieder ausgeschaltet. Dies ergibt dann einen kurzen Beep-Ton.

Abgeschlossen wird das Unterprogramm (wie üblich und notwendig) mit dem return-Befehl.

nach oben

4.4.6. Unterprogramm SUMMERAKTIV

Dieses Unterprogramm erzeugt ein Rechtecksignal mit einer Frequenz von etwa 1 kHz. Dazu wird der Lautsprecher 500µs lang eingeschaltet und 500µs lang ausgeschaltet.

               bsf          PORTB,SUMMER
               call         WARTESCHLEIFE
               bcf          PORTB,SUMMER
               call         WARTESCHLEIFE
               return

Diese Unterprogramm wird so oft aufgerufen bis die Starttaste erneut gedrückt, oder die Stopptaste gedrückt wird

nach oben

4.4.7. Unterprogramm WARTESCHLEIFE

Dieses Unterprogramm erzeugt eine Zeitverzögerung von exakt 500 µs bei einem 4-MHz-Quarz.

Wie erzeugt man genau 500 µs? Dazu sehen wir uns zunächst den Code genauer an:

               movlw        .164            (1)
               movwf        DELAY           (1)
VERZOEGERUNG   decfsz       DELAY,f         (1 bzw. 2)
               goto         VERZOEGERUNG    (2)
               nop                          (1)
               return                       (2)

In den Klammer befinden sich die Anzahl der Zyklen die diese Befehle benötigen. Eine Besonderheit gibt es beim Befehl decfsz DELAY,f. Solange dieser Befehl nur die angegebene Variable (hier DELAY) dekrementiert benötigt er nur einen Zyklus. Ist die Variable DELAY 0, überspringt sie den folgenden Befehl. In diesem Fall benötigt der Befehl 2 Zyklen.
Die Schleife VERZOEGERUNG benötigt 3 Zyklen (decfsz DELAY,f und goto VERZOEGERUNG) und wird 164-mal durchgeführt. 3x164=492. 1 Zyklus kommt für den Sprung bei DELAY = 0 hinzu, ebenso die Zyklen für die restliche Befehle. Nun sind wir bei 498 Zyklen. Zählt man noch 2 Zyklen für den Aufruf (CALL WARTESCHLEIFE) dazu, so ergibt dies die gewünschten 500 Zyklen, und bei einem Takt von 1µs (bei einem 4 MHz-Quarz) ergibt dies genau 500 µs.

nach oben


zurück zu Elektronik, Homepage

Autor: Buchgeher Stefan
Erstellt: 30. September 2003
Letzte Änderung: 10. Oktober 2004