Aus RN-Wissen.de
Wechseln zu: Navigation, Suche
Rasenmaehroboter Test

Einführung

Bit, Byte, Nibble, Bin und Hex

Ein Mikrocontroller (kurz: µC) kann eigentlich nur durch ein Portpin eine Spannung einlesen bzw. ausgeben. Er kann aber nur erkennen, ob eine Spannung vorhanden ist oder nicht. Wenn fast keine Spannung vorhanden ist erkennt er das als 0 und wenn eine Spannung fast so gross, wie seine Versorgungsspannung anliegt, als 1.

Genauso bei der Ausgabe, wenn er 0 ausgibt ist auf dem Portpin fast keine Spannung, wenn 1, eine Spannung fast gleich gross seiner Versorgungsspannung. Und das ist ein Bit, die kleinste Menge einer Information. Das Bit ist binär, weil er nur zwei unterschiedliche Werte 0 bzw. 1 haben kann.

Wenn wir gleichzeitig (paralell) 8 Bits haben, dann ist es ein Byte, der mehrere Bitkombinationen von 00000000b bis 11111111b enhält, weil ein Bit auf jeder Stelle 0 bzw. 1 seien kann. Das "b" bedeutet, das es sich um binäre (kurz: bin) Darstellung (auch Zahl genannt) handelt. Binäre Zahlen sind aber lang, weil jeder Bit eine Stelle benötigt.

Um die Schreibweise zu verkürzen, wurden hexadezimale (kurz: hex) Zahlen eingeführt. Zuerst wurde ein Byte auf zwei 4-Bit Nibbles verteilt und danach ein Nibble als Ziffer genommen. Weil 4 Bit mehr als 10 Kombinationen ergeben, haben die Ziffer 0 bis 9 aus dem Dezimalsystem nicht ausgereicht und wurden um Buchstaben A bis F erweitert. Die hex Zahlen haben "h" Zeichen.

Die Umwandlung zwischen bin und hex Zahlen für ein Nibble zeigt folgende Tabelle:

            0b = 0h           100b = 4h          1000b = 8h          1100b = Ch
            1b = 1h           101b = 5h          1001b = 9h          1101b = Dh
           10b = 2h           110b = 6h          1010b = Ah          1110b = Eh
           11b = 3h           111b = 7h          1011b = Bh          1111b = Fh

Damit kann ein Byte mit zwei hex Ziffern definiert werden z.B. 1100 0011b = C3h. Für zwei Bytes braucht man 4 hex Ziffern z.B.

101 0111 1010 1001b = 57A9h, u.s.w. So wie im Dezimalsystem werden führende Nullen nicht geschrieben, aber in einem µC Register existieren immer 8 Bits also auch führende Nullen. Zum Beispiel die hex Zahl 3h sieht im Register so aus: 00000011b. Bei einer Wandlung bin->hex fängt man immer von der rechten Seite der bin Zahl an, da die Anzahl führenden Nullen unbekannt ist.

Speicher und Register

Als Speicher bezeichnet man ein Teil der Hardware, in die eine Information eingeschrieben, in der gespeichert und aus der wieder ausgelesen werden kann.

Es gibt eigentlich nur zwei Arten von elektronischen Speicher: flüchtige und nichtflüchtige. Die Information die sich im flüchtigen Speicher befindet, geht verloren, wenn die Versorgungsspannung des Speichers unterbrochen oder abgeschaltet wird. Bei PICs ist es Dataspeicher (RAM).

Wenn die Versorgungsspannung vom nichtflüchtigen Speicher abgeschaltet wird, ist die gespeicherte Information zwar momentan nicht lesbar, bleibt aber erhalten und sobald der Speicher wieder mit Spannung versorgt wird, kann sie ausgelesen werden. Ein PIC hat zwei solche Speicher: Programmspeicher (Flash) und EEPROM.

Der wichtigste Unterschied zwischen den Speicherarten ist, das die flüchtigen direkt (sehr schnell) beschreibbar sind und das Beschreiben den nichtflüchtigen benötigt spezielle Algorithmen, die leider im Vergleich zu direkten Zugriffen langsamer sind.

Ein Speicher besitzt bestimmte Menge von s.g. Speicherstellen. Jede Speicherstelle hat seine individuelle Adresse und kann eine binäre Information mit bestimmter Anzahl von Bits abspeichern.

Bei PIC haben die drei Arten von Speicher, wegen verschiedener Anwendung, auch unterschiedliche Struktur. Die beiden Speicher für Daten (RAM und EEPROM) haben jeweils 8-bitigen und Programmspeicher (Flasch) bei Mid-Range hat 14-bitigen Speicherstellen. Die Anzahl den Speicherstellen im bestimmten Speicher ist vom PIC-Typ abhängig.

Eine 8-bitige Speicherstelle im RAM wird bei PICs Register genannt und kann so skiziert werden:

X X X X X X X X
MSB LSB
bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
High Nibble Low Nibble
Byte


Der bit 7 wird als hochwertigste (MSB = Most Significant Bit) und bit0 als niederwertigste (LSB = Least Significant Bit) bezeichnet. Jeder Bit im Register (X) kann gleich 0 bzw. 1 sein.

Um ein Databyte in ein Register schreiben oder aus einem Register lesen, muss zuerst das Register durch seine Adresse gewählt werden. Dafür gibt es beim PIC folgende Möglichkeiten:

Direkte Adressierung per absolute Adresse: movwf 0x20

Direkte Adressierung per vorher definierten Namen des Registers (z.B. Temp equ 0x20): movwf Temp

Indirekte Adressierung durch FSR Register, in den die absolute Adresse des Registers Temp eingeschrieben wird und der Wert aus dem Temp sich im INDF Register befindet. Wie vorher wurde Temp equ 0x20 definiert und weiter:

      movlw   Temp      ;in W-Register wird die absolute Adresse des Registers Temp geladen
      movwf   FSR       ;diese Adresse wird in das FSR Register kopiert
      movf    INDF,0    ;der Wert aus dem indirekt adressierten Register Temp wird in das
                         W-Register geladen.

Prozessor

Der Prozessor (kurz: CPU) von Mid-Range PICs gehört zu den RISC Prozessoren und hat nur 35 Befehle zum Erlernen, was seine Programmierung deutlich vereinfacht. Jeder Befehl benötigt im Programmspeicher nur eine Speicherstelle und im Quellcode nur eine Zeile.

Der CPU besitzt keine eigene Intelligenz, liest nur ein Befehl aus dem Programmspeicher und führt ihn aus. Das geschieht in vier Perioden des Oszillators. Deswegen die Taktfrequenz des CPUs entspricht durch 4 geteilter Frequenz des Oszillators.

                CPU Vorgang           Richtung   Speicher
                -----------------------------------------   -
                1.Befehl lesen        <-------   Flasch      |
                2.Daten lesen         <-------   RAM         | 1 Prozessortakt =
                3.Daten verarbeiten                          | 4 Oszillatortakten
                4.Daten schreiben     ------->   RAM         |  
                                                            -

Nur o.g. CPU Vorgänge sind direkt möglich. Es können deswegen keine Befehle aus dem RAM oder EEPROM ausgeführt werden.

Das Lesen/Schreiben aus/in den EEPROM Speicher ist mit Hilfe speziellen Register und Unterprogrammen bei allen Mid-Range PICs möglich. Der Lese und Schreibzugriff auf den Programmspeicher ist aber nur bei wenigen PIC-Typen (z.B. PIC16F87X) möglich.

Assembler

Die Maschinensprache, auch Assembler oder kurz ASM genannt, ist eine Sprache die nur bestimmter CPU versteht. Für einen Menschen ist sie unverständlich, da sie nur aus hex Zahlen besteht.

Um sich die Sprache verständlicher zu machen wurden den hex Zahlen s.g. Mnemonics aus Buchstaben zugewiesen. Jeder Befehl für einen CPU hat somit ein "Namen", der aus englischer Sprache stammt. Siehe: Kurzübersicht Assembler Befehle

Obwohl sie 200 bis 1000 mal schneller als die meisten Hochsprachen ist, wird sie wegen dem grossen Aufwand bei Erstellung umfangreichen Programmen, selten benutzt. Man findet sie aber oft in fast allen Hochsprachen, in eigebundenen Funktionen, überall dort wo die Hochsprachen zu langsam sind oder nötigen Aufgaben (z.B. Maus in Q-Basic) nicht unterstützen.

ASM eignet sich aber sehr gut für kleine Anwendungen (meistens Steuerungen) mit µC, weil nur bei dieser Programmiersprache ein direkter Zusammenhang zwischen einem bit im Programm und einer Spannung am I/O Pin besteht.

Dank der integrierten oder an Portpins angeschlosenen Hardware und dem entsprechenden Program kann ein µC umfangreiche Aufgaben realisieren, die fast unbegrenzt und schwer vorstellbar sind.

Die Aufgabe eines ASM-Programmierers ist, ein Programm zu schreiben, das das Assemblerprogramm (z.B. MPASM) fehlerfrei in die Machinensprache "übersetzt" und der bestimmte CPU "versteht". Sie endet eigentlich erst dann, wenn das geschriebene Programm so wie geplannt funktioniert.

Beispiel für ein PAD

Weil ASM Programme nicht besonders durchschaubar sind, wurde als Hilfsmittel ein Programmablaufdiagramm (kurz: PAD) erfunden. Beim Programmerstellung fängt man damit an ein PAD zu erstellen, das die wichtigsten Programmschritte enthält.

Weiter werden alle Befehle nach dem PAD mit einem üblichen Texteditor in eine Textdatei mit Erweiterung .asm (Quellcode) geschrieben, durch ein Assemblerprogramm (für PICs: MPASM oder GPASM) von dem für Menschen noch verständlichen Code in die Maschinensprache "übersetzt" und als Texdatei mit Erweiterung .hex gespeichert. Diese Datei wird danach in den Programmspeicher des µC übertragen ("gebrannt").

Das Assemblerprogramm MPASM kann kostenlos von der Homepage des Herstellers von PICs [1] runtergeladen werden. Es muss zuerst vom Downloads die "MPLAB IDE v7.50 Full Zipped Installation" runtergeladen und erst danach können gewählte Programme (z.B. nur MPASM) intalliert werden. Für MPASM benutzer werden auch folgende .pdf Dateien empfohlen:

MPASM/MPLINK User's Guide (2628 KB) [Benutzerhandbuch]

MPASM™/MPLINK™ PICmicro® Quick Chart (81 KB) [Kurzübersicht]

Nach dem Eischalten der Betriebsspannung des µC, fängt der CPU an, sich im Programmspeicher befindliches Programm mit dem Befehl, der an der Adresse 0 steht, auszuführen.

Aber wann das Programm endet? Natürlich wenn die Versorgungsspannung abgeschaltet wird. Nein! Das ist die einfachste Lösung um ein laufendes Programm auf zufälliger Stelle zu unterbrechen, aber keine um ihn auf einer definierten Stelle zu beenden.

Wenn an den µC angeschlossene externe Hardware (z.B. Grafikdisplay), eine bestimmte Befehlsfolge vor dem Abschalten benötigt oder wichtige Daten (in EEPROM oder Flash) abgespeichert werden sollen, darf die Spannung erst dann abgeschaltet werden, wenn der CPU eine Meldung ausgibt, dass er sich schon auf der "STOP" Stelle des Programms befinet. Es muss auch definiert werden (z.B. durch eine Tastenkombination), wann der CPU zum letzten Fragment des Programms vor dem "STOP" gehen soll.

Grundbeschaltung

Der Prozessor von einem PIC kann sofort nach dem Eischalten der Versorgungsspannung arbeiten. Allerdings nur, wenn er den Takt, in dem er die Befehle ausführen soll, vorgegeben hat. Manche PICs besitzen einen internen Oszillator, (z.B. PIC12F629, PIC16F630, PIC16F628, u.s.w.). Bei diesen reicht es bereits Spannung anzulegen und sie laufen bereits. Die meisten haben ihn aber nicht (z.B. PIC16F84, PIC16F870, u.s.w.) und brauchen fürs Funktionieren zusätzliche Bauteile (Resistor + Kondensator, Quarz + 2 Kondensatoren oder Keramik-Resonator + 2 Kondensatoren, bzw. Quarzoszillator) die an Pins OSC1/OSC2 angeschlossen werden um notwendigen Prozessortakt zu erzeugen. Durch das Konfiguration-Word muss noch angegeben werden, welcher Oszillator verwendet wird.

Bei externen Oszillatoren bleibt der Pin OSC2 nicht angeschlossen und kann als I/O benutzt werden. Falls ein interner Oszillator benutzt wird, können beide OSC Pins als I/O dienen.

Damit ein Programm zuverlässig ausgeführt werden kann, muss die Versorgungspannung störungsfrei sein. Dafür wird ein Keramik-Vielschicht-Kondensator 100 nF möglichts am kürzesten direkt zwischen VDD und VSS Pins geschaltet.

Folgende Skizzen zeigen die Grundbeschaltung eines PICs:

Entstörkondensator beim PIC
Quarz
externer Quarzoszillator
externer RC-Oszillator

Wahl des PICs

Es gibt PIC µC die im Typenbezeichnung den Buchstaben "C" oder "F" haben.

Die älteren mit "C" haben EPROM Programmspeicher und die gibt es in zwei Versionen: ohne und mit Fenster (aus Quarz-Glass) fürs Löschen des EPROMs mit UV Strahlung. Bei denen ohne Fenster kann der Programmspeicher nur einmal beschrieben und nicht mehr gelöscht werden.

Die neuen mit "F" besitzen einen Flash-Programmspeicher, der bis zu 100 000 mal mit angelegter Spannung gelöscht und danach neu beschrieben werden kann.

Für die Wahl eines PICs für bestimmte Anwendung wichtig sind:

- Max. Taktfrequenz des Prozessors.

- Grösse des Datenspeichers (für Variablen).

- Grösse des Programmspeichers (für Programm).

- Integrierte Hardware (Komparatoren, A/D Wandler, Timer, USART, I²C, SPI, PWM, u.s.w.).

- Freie I/O Pins für externe Hardware (Display, Tasten, u.s.w.).

- Vorhandene Betriebspannung (Netzteil, Akku, Batterie).

In der Praxis wird meistens für die Programmerstellung ein grösserer PIC genommen (wenn möglich pinkompatibler z.B. PIC16F628 für PIC16F84 oder PIC16F630 für PIC12F629) und erst nach der Optimierung des lauffägiges Programms, der tatsächlich nötiger, da seine Parameter am Anfang nur geschätzt werden können. Wenn man viel Programme für verschiedene PICs entwickelt, optimal wäre der grösste PIC16F877 mit 20 MHz max. Taktfrequenz.

Diese Lösung hat auch den Vorteil, dass während der Programmerstellung kurze Hilfsprogramme (z.B. PIC Trainer) in den Programmspeicher kopiert und benutzt werden können, da sie sowohl ein bischen Programmspeicher und RAM als auch 2 freie I/O Pins fürs PIC Miniterminal brauchen.

Programm

Allgemeines

Jedes Program kann man auf klenere Fragmente unterteilen, die auf bestimmter Weise miteinander verknüpft sind und gemeinsam die Aufgabe des Programms erfüllen. Das wichtigste Teil eines Programms ist s.g. Hautprogram (kurz:HP), das eine führende Rolle spielt. Dem HP sind fast alle andere Programmteile untergeordnet (weiter als Unterprogramm (kurz:UP) genannt) und werden nach Bedarf von ihm aufgerufen um eine bestimmte Aufgabe zu erledigen.

Die Struktur eines Programs ist aber komplizierter, da ein UP kann auch ein oder mehrere UPs nacheinander aufrufen. Zuerst werden die UP1s erstellt, die ganz einfache Sachen erledigen. Danach kommt das nächste Ebene mit UP2s die schon mehr komplizierten Aufgaben durch ein Aufruf der UP1s erledigen können, u.s.w. Bei Mid-Range PICs (12FXXX und 16FXXX) können maximal bis zu 8 Ebenen benutzt werden.

Hauptprogramm - Unterprogramm

Jedes UP kann jederzeit aufgerufen werden, je nach dem was gerade eledigt werden muss. Weil das nicht egal ist, welches UP augerufen wird, da jedes nur eine bestimmte Funktion im Programm hat, muss der Programmierer dafür sorgen, dass alles richtig nach Programablaufdiagramm, und nicht chaotisch, abläuft.

Die Programmierung in ASM ist änlich wie bei Hochsprachen, wenn man sich Bibliotheken mit Prozessorspezifischen UPs erstellt. Um ein lauffähiges Programm zu erstellen, braucht man nur benötigte UPs ins Program kopieren und ein geignetes HP, das sie aufruft, schreiben.

Ein ASM Programm (Quellcode) muss in einer Texdatei .asm in der vom Assemblerprogramm erwarteter Form verfasst werden, um fehlerfreie Konvertierung in die Maschinensprache (Assemblierung) zu gewährleisten. Dieses Prozess verläuft in der Form eines Dialoges.

Der Programmierer schreibt und gibt es dem Assemblerprogram zum Übersetzen. Alles was das Programm nicht versteht oder nicht richtig ist, erscheint als Fehlermeldungen, die der Programmierer kennen muss um die Fehler korrigieren zu können. Eine .hex Datei wird erst erstellt, wenn das Assemblerprogramm keine Fehler mehr im Quellcode findet. Deswegen sehr wichtig ist, sich mit dem Assemblerprogramm vertaut zu machen, um die Dialogzeit zu minimieren.

Programmdurchlaufdiagramm

Der Programdurchlaufdiagram (kurz: PAD) ist eine vorläufige und laufend änderbare Stufe zwischen einer Idee und ihrer Verwirklichung. Er wird erst dann fertig, wenn nach ihm erstelltes ASM Program auf einem µC so wie gewünscht funktioniert. Jedes sein Symbol (ausser "Start/Stop") muss später als Befehlsreihenfolge für den bestimmten CPU in den Quellcode übertragen werden. Die Anschriften "Ein" und "Aus" gehören nicht zu Symbolen des PADs und wurden nur zur Erklärung benutzt.

Der PAD ist sehr eifach zu erstellen, weil dafür nur drei Symbole benötigt sind:

Symbole des PAD

Das "Start/Stopp" Symbol bedeutet, dass das gesamte Programm sich im stabilen Zustand befindet und nicht "läuft". Anstatt "Stopp" kann auch "Schlaf" (Sleep) agewendet werden, da das Programm in dem Fall auch nicht aktiv ist. Das "Tun" Symbol stellt meistens ein UP mit Reihenfolge von Befehlen dar. Das "Prüfen" bedeutet eine Prüfung bestimmter Bedingung und abhängig davon einen weiteren Lauf eines Programms, endweder in der "ja" (J) oder "nein" (N) Richtung.

Als allgemeinnutziges Standard für µCs kann man folgender PAD bezeichnen:

PAD                                _____
                                  /     \
        Spannung ein (Ein) ----->( Start )
                                  \_____/
                                     |                   -
                                     V                    |
                             .---------------.            |
                             |Initialisierung|            |
                             '---------------'            |
                                     |                    |
                          +--------->|                    |
                          |          V                    |
                          |  .---------------.            |
                          |  | Hauptprogramm |            |
                          |  '---------------'            |
                          |          |                    |
                          |          V                    |
                          |          |                     > Gesamtes Programm
                          |         / \                   | 
                          |       /Ende?\____             |
                          |       \     /J   |            |
                          |         \ /      |            |
                          |          |       |            |
                          |          V       |            |
                          |         N|       |            |
                          +----------+       |            |
                                             V            |
                                     .---------------.    |
                                     |    Beenden    |    |
                                     '---------------'    |
                                             |            |
                                             V           -
                                           _____
                                          /     \
        Spannung aus (Aus) <-------------( Stopp )
                                          \_____/

Das Hauptprogram wird in einer endlosen Schleife ausgeführt, die durch die Prüfung "Ende?" unterbrochen werden kann. In dem Fall wird vor dem Beenden des gesamten Programms noch ein UP "Beenden" ausgeführt, das z.B. Daten in EEPROM speichert.

Es ist nicht nötig immer die Symbole zu zeichnen, man kann sich sie vorstellen und nur den Text schreiben. Die Prüfungen werden mit "?" gekenzeichnet und die Zeichen "V", "<" und ">" zeigen die Richtung des weiteren Verlaufs. Dann sieht der PAD so aus:

PAD1                                Ein > Start
                                            V                 - 
                                     Initialisierung           |
                                   /------->V                  |
                                   |  Hauptprogramm             > Gesamtes Programm
                                   |        V                  | 
                                   |      Ende? J > Beenden    |
                                   |        N          V      -
                                   |        V        Stopp > Aus
                                   \--------/

In der Praxis werden aus Platzgründen meistens die vereinfachten PADs benutzt.

Der PAD1 kann aber für Hauptprogramme, die in beliebigem Moment unterbrochen werden dürfen, deutlich vereifacht werden, da die Prüfung "Ende?" ob das Hauptprogram beendet werden soll, und das UP "Beenden", entfallen.

Die meisten ASM Programme für µC sind deswegen nach solchem PAD erstelt:

PAD2                               Ein > Start
                                           V         -          
                                    Initialisierung   |
                                  /------->V          |
                                  |  Hauptprogramm     > Gesamtes Programm
                                  |        V          |
                                  \--------/         _|
                                       

Für Testprogramme wird meistens fogender PAD angewendet, weil es ziemlich einfach festzustellen ist (z.B. durch Stromverbrauchmessung des µCs), wann sich der CPU schon im Schlaf befindet. Erst dann, darf die Betriebspannung des µCs ausgeschaltet werden.

PAD3                               Ein > Start
                                           V         -
                                    Initialisierung   |
                                           V           > Gesamtes Programm
                                     Hauptprogramm    |
                                           V         -
                                        Schlaf > Aus

Und eine batteriebetriebene Uhr wird überwiegend so gestaltet:

PAD4                               Ein > Start
                                           V         -
                      Interrupt     Initialisierung   |
            Timer------------------------->V           > Gesamtes Programm
                                     Hauptprogramm    |
                                           V         -
                                        Schlaf

In dem Fall reicht es aus, wenn der CPU jede Minute vom Timer aufgeweckt wird, um die Zeit zu aktualisieren. Eine Uhr ist immer (ausser Batteriewechsel) ununterbrochen mit Spannung versorgt.

Für komplizierte Programme ist es unmöglich ein PAD zu erstellen, in dem jeder CPU Befehl sein eigenes Symbol hat. Man beschränkt sich nur auf alle Prüfungen, die über den Lauf des Programms entscheiden, und ganze UPs (z.B. "Initialisierung") nur als ein Symbol verwendet. Für jedes UP wird dann ein eigener PAD erstelt.

Das Erstellen von PAD bei ASM Programmen ist sehr wichtig und darf nicht unterschätzt werden. Je stärker ein Programmierer glaubt, dass er das ohne PAD schaft, um so mehr Zeit wird er danach bei Fehlersuche oder Änderungen im ASM Programm verlieren. Für einfache ASM Programme, die gut kommentiert sind, reicht es meistens aus, ein PAD nur "im Kopf" zu erstellen, aber ganz ohne PAD geht es sicher nicht.

Wenn ein ASM Programm nicht wie geplannt funktioniert, wird zuerst ein Fehler im PAD gesucht. Und erst wenn er i.O. ist, im als fehlerhaft festgestellten Codefragment.

Hauptprogramm

Wie sein Namen schon vermuten lässt, ist das Hauptprogram das wichtigste Teil des gesamten Programms. Meistens ist es auch das kleinste Teil, vor allem, wenn die UPs sehr komplex sind. Seine Aufgabe ist die benötigte UPs in bestimmter Reihenfolge nachainander aufzurufen, um die alle Funktionen des gesamten Programms zu realisieren.

Das HP ist meistens als endlose Schleife , wie im PAD2, aufgebaut. Weil die endlose Schleife sehr schnell läuft, werden die alle, die durch die UPS realisierte Aufgaben quasi gleichzeitig ausgeführt. Wenn es unerwünscht ist, müssen einige UPs als Verzögerungen realisiert werden.

Typischer PDA für ein HP sieht so aus:

                                                   /--->V
                                                   |   UP1
                                                   |    V
                                                   |   UP2
                                                   |    V
                                                   |   ...
                                                   |    V
                                                   |   UPn
                                                   |    V
                                                   \----/

In den Quellcode wird es so eigeschrieben:

                                         Haupt   call    UP1												
                                                 call    UP2
                                                 ...........
                                                 call    UPn
                                                 goto    Haupt

In der Praxis wird das HP schrittweise erstellt. Am Anfang wird sich nur ein UP im HP befinden und die folgenden kommen nach dessen Erstellung und Prüfen dazu, bis das HP fertig wird.

Unterprogramm

Unterprogramm wird durch übergeordnetes Programmteil (Aufrufer) aufgerufen und nach seinem Ausführen, wird zurück zum Aufrufer gesprungen. Der Rückkehr zum Aufrufer wird durch "return" Befehl, der sich am Ende jedes UPs befinden muss, erreicht. Und das ist der einzige Unterschied zwischen einem HP und einem UP.

Jedes UP hat folgender PDA:

                               vom Aufrufer ------->     V
                                                        Tun
                                                         V
                        zurück zum Aufrufer <-------   return 

Ein HP von einem Programm kann in anderem, mehr umfangreichem Program als UP benutzt werden, wenn der sich am Ende des HPs befindlicher Befehl "goto" durch "return" ersetzt wird. Ein Beispiel dazu:

            Haupt1  call    UP11                          Haupt1  call    UP11
                    call    UP21                                  call    UP21
                    ...........             ------->              ...........
                    call    UPn1                                  call    UPn1 
                    goto    Haupt1                                return 

Jetzt können wir im mehr komplexen HP (Haupt) das Haupt1 als Unterprogramm aufrufen:

                                  Haupt    call    UP1      
                                           call    Haupt1
                                           ...........
                                           call    UPn
                                           goto    Haupt

Jedes UP kann auch von einem anderen übergeordneten UP aufgerufen werden, wenn das was es realisiert, benötigt wird.

In der Praxis wird oft ein UP von mehreren anderen UPs benutzt. Zum Beispiel um LCD Display zu steuern, brauchen wir entweder ein Befehl (Cmd) oder ein Zeichen (Data) an Display zu schicken. In beiden Fällen wird ein Byte geschickt, einmal mit RS=0 (Befehl) und einmal mit RS=1 (Zeichen) laut folgendem PDA:

                                       "Cmd"   "Data" 
                                         V       V
                                       RS=0    RS=1
                                         V       V 
                                         \-->V<--/
                                             |
                                             V
                                  "Send" Byte schicken
                                             V
                                           return

Das wird z.B. in den Quellcode so eingeschrieben:

                                    Cmd     bcf     RS
                                            goto    Send
                                    Data    bsf     RS
                                    Send    ............
                                            return

Das UP "Send" ist den UPs "Cmd" und "Data" untergeordnet, da es von beiden benutzt wird, kann aber weder "Cmd" noch "Data" benutzen.

Initialisierung

Damit der PIC ein Programm asführen kann, muss er vollständig und richtig initialisiert werden. Deswegen als erstes UP, das vom HP aufgerufen wird , ist "Initialisierung" (kurz: Init)

Variablen

Weil nach dem Einschalten der Spannung im RAM sich zufällige Werte befinden, wird meistens als erstes der benutzte Bereich des RAMs (z.B. 20h bis 7Fh) gelöscht. Es wird einfach und sparsam mit einer Schleife, die indirekte Adressierung verwendet, gemacht:

                                                  V
                            Adresse des ersten Registers in FSR laden (20h)
                            /-------------------->V
                            |Indirekt adressierter Register löschen (INDF)
                            |                     V
                            |              Adresse erhöhen
                            |                     V
                            |        Letzte Adresse + 1 (80h) J>Return
                            |                     N
                            |                     V
                            \---------------------/

Es wird wie folgt in Quellcode eingeschrieben:

                                            movlw   0x20
                                            movwf   FSR
                                   RAMClr   clrf    INDF
                                            incf    FSR,1
                                            btfss   FSR,7
                                            goto    RAMClr
                                            return

Danach können den nötigen Variablen die gewünschte Werte gegeben werden:

                                            movlw   0x3C
                                            movwf   LimH
                                            movlw   0x5A
                                            movwf   LimL
                                            u.s.w.

Somit sind die Variablen initialisiert.

Midrange

Kurzübersicht Assembler Befehle

ADDLW Add literal and W
ADDWF Add W and f
ANDLW AND literal with W
ANDWF AND W with f
BCF Bit Clear f
BSF Bit Set f
BTFSC Bit Test f, Skip if Clear
BTFSS Bit Test f, Skip if Set
CALL Call subroutine
CLRF Clear f
CLRW Clear W
CLRWDT Clear Watchdog Timer
COMF Complement f
DECF Decrement f
DECFSZ Decrement f, Skip if 0
GOTO Go to address or label
INCF Increment f
INCFSZ Increment f, Skip if 0
IORLW Inclusive OR literal with W
IORWF Inclusive OR W with f
MOVF Move f
MOVLW Move literal to W
MOVWF Move W to f
NOP No Operation
RETFIE Return from interrupt
RETLW Return with literal in W
RETURN Return from Subroutine
RLF Rotate Left f through Carry
RRF Rotate Right f through Carry
SLEEP Go into standby mode
SUBLW Subtract W from literal
SUBWF Subtract W from f
SWAPF Swap nibbles in f
XORLW Exclusive OR literal with W
XORWF Exclusive OR W with f

Kurzübersicht zum Ausdrucken

Ausführliche Beschreibung zu den Befehlen

Erklärungen zu den Verwendeten Platzhaltern:

  • k stellt einen fest definierten Wert da. z.B. 0x20, d'42' oder b'00101010'
  • W steht für das W-Register.
  • d steht für destination (Ziel). Im code wird d durch ein w bzw. 0 (der Wert wird in das W-Register gespeichert ) oder f bzw. 1 (der Wert wird in das davor definierte Register gespeichert)
  • b steht für Bitnummer im Register (eine Zahl zwischen 0 und 7)
  • R steht für ein Register
  • fett geschrieben Bedeutet, dass es ein Platzhalter ist und im Quellcode durch eine Registeradresse oder einen Wert ersetzt werden muss
  • Schreibmaschinenstil bedeutet, dass es so im Quellcode geschrieben werden kann.
ADDLW k Add W and literal - Addiere W und Zahl
Es wird die Rechenoperation [math]k+W[/math] ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register
ADDWF R,d Add W and f - Addiere W und f
Es wird die Rechenoperation [math]R+W[/math] ausgeführt und das Ergebniss entweder in das W-Register (d=W=0) oder in R gespeichert (d=F=1). Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register
ANDWF k AND literal with W
Es wird bitweise die logische Funktion [math]W\ and\ k[/math] ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl setzt das Z bit des STATUS-Register, falls W=k und das Ergebnis 0 ist.
Zur Verdeutlichung der Operation:
1100 1010 ---- and 1000
ANDWF R,d AND W with f - ???
Es wird bitweise die logische Funktion [math]W\ and\ R[/math] ausgeführt und das Ergebniss entweder in das W-Register (d=W=0) oder in R gespeichert (d=F=1). Vergleiche ANDWF
BCF R,b Bit Clear f - Bit b im R wird gelöscht
Mit dem Befehl BCF wird das Bit b im Register R gelöscht. Ein Beispiel:
MOVLW b'11111111'  ;es wird b'11111111' in das W-Register geschrieben BCF W,2  ;es wird bit 2 im W-Register gelöscht.  ;das Ergebnis ist: b'11111011'
</dd> BSF R,b Bit Set f - Bit b im R wird gesetzt
Mit dem Befehl BSF wird das Bit b im Register R gesetzt. Ein Beispiel:
CLRW  ;es wird b'00000000' in das W-Register geschrieben BSF W,2  ;es wird bit 2 im W-Register gesetzt.  ;das Ergebnis ist: b'00000100'
</dd> BTFSC R,b Bit Test f, Skip if Set - Wenn das Bit b im Register R 1 ist, überspringe den nächsten Befehl
Mit dem Befehl BTFSC kann eine Verzweigung im Programmablauf bewirkt werden. Wenn das Bit b im Register R 0 ist, wird der nächste Befehl übersprungen. Ein Beispiel:
MOVLW b'00000001'  ;es wird die Zahl 1 in das W-Register kopiert. BTFSC W,0  ;es wird bit 0 geprüft.  ;wenn es 0 ist, wird der nächste Befehl übersprungen GOTO IST_NULL  ;springt zur Marke "IST_NULL" <- in diesem Fall wird dieser Sprungbefehl ausgeführt. GOTO IST_EINS  ;springt zur Marke "IST_EINS"
</dd> BTFSS R,b Bit Test f, Skip if Set - Wenn das Bit b im Register R 1 ist, überspringe den nächsten Befehl
Mit dem Befehl BTFSS kann eine Verzweigung im Programmablauf bewirkt werden. Wenn das Bit b im Register R 1 ist, wird der nächste Befehl übersprungen. Ein Beispiel:
MOVLW b'00000001'  ;es wird die Zahl 1 in das W-Register kopiert. BTFSS W,0  ;es wird bit 0 geprüft.  ;wenn es 1 ist, wird der nächste Befehl übersprungen GOTO IST_NULL  ;springt zur Marke "IST_NULL" GOTO IST_EINS  ;springt zur Marke "IST_EINS" <- in diesem Fall wird dieser Sprungbefehl ausgeführt, da der darüber übersprungen wurde.
</dd>


CALL Call Subroutine - Rufe Unterprogramm auf
Mit dem CALL Befehl wird ein Unterprogramm aufgerufen. Mit dem RETURN-Befehl wird das Unterprogramm beendet und man kehrt zum Befehl nach dem CALL-Befehl zurück. Das Unterprogramm wird so definiert, dass im Quellcode der Name des Unterprogramms nicht eingerückt steht. Ein Beispiel:
MOVLW d'13'  ;in das W-Register wird 13d geladen CALL Unterprogramm1  ;es wird das Unterprogramm "Unterprogramm1" aufgerufen MOVWF ergebnis  ;das W-Register wird in das Register "ergebnis" kopiert.  ;im Register "ergebnis" steht nun 23d Unterprogramm1  ;zählt 10 zum W-Register ADDLW d'10'  ;es wird 10d zum W-Register addiert RETURN  ;kehre zurück zum Aufrufer
</dd> CLRF R Clear f - Schreibe 0 in das Register R
Das Register R wird mit Nullen gefüllt (gelöscht).
CLRW Clear W - Schreibe 0 in W
Das W-Register (W) wird mit Nullen gefüllt (gelöscht).
CLRWDT Clear Watchdog Timer - Setzt den Watchdog-Timer zurück
Es wird der WDT (Watchdog-Timer) zurückgesetzt und der Zähler des WDT auf 0 gesetzt, zusätzlich werden die STATUS-bits TO und PD gesetzt.
COMF R,d Complement f - vertauscht 0 und 1 im Register R
Von der Binärzahl im Register R werden die 0 und 1 vertauscht. Das Ergebnis wird entweder in das W-Register (d=W=0) oder in R gespeichert (d=F=1). Ein kleines Beispiel: aus Ah (1010b) wird 5h (0101b).
DECF R,d Decrement f, Skip if 0 - Subtrahiert 1 vom Regiser f
Vom Wert des Registers R wird 1 subtrahiert und das Ergebnis entweder in das W-Register (d=W=0) oder in R gespeichert (d=F=1). Dieser Befehl beeinflusst das C-Flag im STATUS-Register nicht.
DECFSZ R,d Decrement f, Skip if 0 - Subtrahiert 1 vom Regiser f, überspringe wenn 0
Vom Wert des Registers R wird 1 subtrahiert und das Ergebnis entweder in das W-Register (d=W=0) oder in R gespeichert (d=F=1). Der Zusatz SZ steht für skip if zero, d.h. wenn das Ergebnis der Rechnung Null ist, wird der nächste Befehl übersprungen. Dieser Befehl wird für Schleifen mit bestimmter Anzahl der Durchläufe benutzt.
GOTO Go to address - Gehe zu Adresse/Sprungmarke
Nach dem GOTO Befehl wird das Programm ab der Adresse weiter ausgeführt, die nach dem GOTO-Befehl steht. Diese Adresse wird durch so genannte Sprungmarken definiert, welche, im Gegensatz zu den Befehlen nicht eingerückt im Quellcode stehen.
INCF R,d Increment f - Addiere 1 zum Register f
Zum Wert des Registers R wird 1 addiert und das Ergebniss entweder in das W-Register (d=W=0) oder in R gespeichert (d=F=1). Dieser Befehl beeinflusst das C-Flag im STATUS-Register nicht.
INCFSZ R,d Increment f, Skip if 0 - Addiere 1 zum Regiser f, überspringe wenn 0
Zum Wert des Registers R wird 1 addiert und das Ergebniss entweder in das W-Register (d=W=0) oder in R gespeichert (d=F=1). Der Zusatz SZ steht für skip if zero, d.h. wenn das Ergebnis der Rechnung Null ist, wird der nächste Befehl übersprungen. Dieser Befehl wird für Schleifen mit bestimmter Anzahl der Durchläufe benutzt.
IORLW k Inclusive OR literal with W - ???
Es wird bitweise die logische Funktion [math]W\ ior\ k[/math] ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl setzt das Z bit des STATUS-Register, falls W=k und das Ergebnis 0 ist.
Zur Verdeutlichung der Ooperation:
1100 1010 ---- ior 1110
IORWF R,d Inclusive OR W with f - ???
Es wird bitweise die logische Funktion [math]W\ ior\ R[/math] ausgeführt und das Ergebniss entweder in das W-Register (d=W=0) oder in R gespeichert (d=F=1). Vergleiche IORLW
MOVF R,d Move f - Bewege f
Das Register R wird in das W-Register (d=W=0) oder wieder in R kopiert (d=F=1). Letzteres mag sinnlos scheinen, ist aber nützlich, da durch den Befehl das Z-Bit im STATUS-Regsiter gesetzt wird, falls R Null ist.
MOVLW k Move literal to W - Bewege Zahl in W-Register
Der festgelegte Wert k wird in das W-Register kopiert.
MOVWF R Move W to f - Bewege W-Register in das Register F
Das W-Register wird in das Register R kopiert.
NOP No Operation - Kein Befehl zum Ausführen (warte)
Dieser Befehl macht nichts. Er verbraucht nur Zeit, welche sich einfach mit folgender Formel berechnen lässt. [math]t=\frac{4}{f}[/math],wobei [math]f[/math] für die Frequenz des Oszillators steht.
RETFIE Return from interrupt - Kehre zurück aus der Unterbrechung
Mit diesem Befehl wird die Interrupt Service Routine (ISR) beendet und das Programm wird an der Zeile weiter ausgeführt, vor der es durch den Interrupt angehalten wurde. Es werden auch alle Interrupts wieder erlaubt (das GIE bit wird gesetzt). Siehe hierzu auch Interrupt
RETLW k Return with literal in W - Kehre zurück mit Zahl k im W-Register
Wurde ein Programmteil mit dem Befehl CALL aufgerufen, dann springt man mit dem Befehl RETLW zurück in die nächste Zeile nach der Zeile aus der das CALL Befehl ausgeführt wurde. Der in k angegebene Wert wird dabei in das W-Register geschrieben. Dieser Befehl wird vor allem für s.g Sprungtabellen (eng: lookup tables) und Computed-Gotos verwendet.
RETURN Return from Subroutine - Kehre zurück zum Übergeordneten Programmteil
Wurde ein Programmteil mit dem Befehl CALL aufgerufen, dann springt man mit dem Befehl RETURN zurück zu der nächsten Zeile nach der Zeile aus der das CALL Befehl ausgeführt wurde.
RLF R,d Rotate Left f through Carry - Verschiebe das Register f mithilfe des Carry-bits nach links
Alle Bits im Register R werden um eine Position nach links verschoben. Dabei wird das Carry bit (STATUS,C) in das Bit 0 des Registers R geschoben. Bit 7 aus dem Register R wird in das Carry bit "geschoben". Das Ergebnis wird entweder in das W-Register (d=W=0) oder in R gespeichert (d=F=1).
Zur Verdeutlichung:
|C| |-Register R-| ;C steht für das Carry-bit, STATUS,C c 7 6 5 4 3 2 1 0 ;vor dem Verschieben 7 6 5 4 3 2 1 0 c ;nach dem Verschieben
RRF R,d Rotate Right f through Carry - Verschiebe das Register f mithilfe des Carry-bits nach rechts
Alle Bits im Register R werden um eine Position nach rechts verschoben. Dabei wird das Carry bit (STATUS,C) in das 7.Bit des Registers R geschoben. Bit 0 aus dem Register R wird in das Carry bit "geschoben". Das Ergebnis wird entweder in das W-Register (d=W=0) oder in R gespeichert (d=F=1).
Zur Verdeutlichung:
|C| |-Register R-| ;C steht für das Carry-bit, STATUS,C C 7 6 5 4 3 2 1 0 ;vor dem Verschieben 0 C 7 6 5 4 3 2 1 ;nach dem Verschieben
SLEEP Go into standby mode - Versetze den Mirokontroller in Bereitschaftsmodus
Der µC wird in den Sleep-Mode versetzt, in dem er weniger Strom verbraucht. Er kann durch einen Reset, einem Watchdog-Timer-Reset oder durch einen Interrupt wieder aufgeweckt werden.
SUBLW k Subtract W from literal - Ziehe W von Zahl ab
Es wird die Rechenoperation [math]k-W[/math] ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register
SUBWF R,d Subtract W from f - Ziehe W von f ab
Es wird die Rechenoperation [math]R-W[/math] ausgeführt und das Ergebniss entweder in das W-Register (d=W=0) oder in R gespeichert (d=F=1). Dieser Befehl beeinflusst das STATUS-Register. Siehe hierzu Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Register
Beispiel:
movlw d'20'  ;schreibe 20 in das W-Register movwf Register1  ;bewegt das W-Register in das Register1 movlw d'10'  ;schreibt 10 in das W-Register SUBWF Register1,F ;schreibt Register1(20)-W(10) in Register1
SWAPF R,d Swap nibbles in f - Vertausche die Halbbytes (Nibbles)
Es werden die höheren 4 bit (bit7-bit4) mit den niedrigeren 4 bit (bit3-bit0) eines Registers vertauscht und entweder in das W-Register (d=W=0) oder in R gespeichert (d=F=1).
Beispiel:
movlw b'00001111' ;schreibe b'00001111' in das W-Register movwf Register1  ;kopiert das W-Register in das Register1 SWAPF Register1,W ;vertauscht die ersten 4 bit mit den letzen  ;4 bit in Register 1 und schreibt es in das W-Register  ;im W-Register steht nun b'11110000'
XORLW k Exclusive OR literal with W
Es wird bitweise die logische Funktion [math]W\ xor\ k[/math] ausgeführt und das Ergebniss in das W-Register gespeichert. Dieser Befehl setzt das Z bit des STATUS-Registers, falls W=k und das Ergebnis 0 ist.
Zur Verdeutlichung der Operation:
1100 1010 ---- xor 0110
XORWF R,d Exclusive OR W with f - ???
Es wird bitweise die logische Funktion [math]W\ xor\ R[/math] ausgeführt und das Ergebniss entweder in das W-Register (d=W=0) oder in R gespeichert (d=F=1). Vergleiche XORLW

Überprüfung von Rechenergebnissen mit Hilfe des STATUS-Registers

Auswirkungen auf das STATUS-Register bei Subtraktionen
Ergebnis STATUS,C STATUS,Z
positiv 1 0
negativ 0 0
Null 1 1
Auswirkungen auf das STATUS-Register bei Addition
Ergebnis STATUS,C STATUS,Z
positiv 0 0
Überlauf 1 0
Null 1 1

LiFePO4 Speicher Test