PI 16F84 Doku - Alexander

Transcrição

PI 16F84 Doku - Alexander
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
Berufsakademie Stuttgart / Außenstelle Horb
Studienbereich Technik
Studiengang Informationstechnik
Kurs IT2006, 3.Semester
Vorlesung: Rechnertechnik
Dozent: Stefan Lehmann
Student: Alexander Carls
Matrikelnummer: 166270
Aufgabe: Programmierung eines Simulationsprogramms für den µC PIC 16F84
Datum: 25.11.2007
Seite 1
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
Inhaltsverzeichnis
1. Motivation
1.1 Danksagung
1.2 Vorwort
1.3 Aufgabenstellung
1.4 Programmiersprache und Stil
3-4
3
3
3
4
2. Grundlagen
2.1 Simulator
2.1.1 Was ist ein Simulator?
2.1.2 Vor- und Nachteile einer Simulation
2.2 Mikrocontroller
2.2.1 Harvard-Architektur
2.2.2 Der Aufbau
2.2.3 Die Befehlsverarbeitung
5-7
5
5
5
6-7
6
6
7
3. Vertiefung
3.1 Programmzähler
3.2 Interrupts
3.3 Timer
3.4 WatchDog-Timer
3.5 Ein- und Ausgabe
3.6 Takt
3.7 Speicher
3.7.1 Programmspeicher
3.7.2 Datenspeicher
3.7.3 EEPROM
3.7.4 Stack
8-10
8
8
8
9
9
9
10
10
10
10
10
4. Programmumsetzung
4.1 Prinzipieller Ablauf
4.2 Befehle
4.3 Interrupts
4.4 Timer
4.5 WatchDog-Timer
4.6 Ein- und Ausgabe
4.7 Takt
4.7.1 Quarzakt
4.7.2 WDT-Takt
4.7.2 Externer Takt
4.8 Speicher (SFR, GPR, Stack)
4.8.1 Stack
4.8.2 SFR & GPR
4.9 Dateien einlesen / öffnen
11-26
11
12-18
18
19
20
21
22
22
22
22
23-24
23
23-24
25-26
5. Die Programmoberfläche
5.1 Die Menüleiste
5.2 Programmstart und Reset
5.3 Infobox
5.4 Programmspeicher
5.5 Stack
5.6 Special Function Register
5.7 General Purpose RAM
5.8 Aktive Anzeigeelemente
5.9 Interrupts und Takt über Checkboxen
5.10 Typischer Anwendungsfall
27-33
27-28
29
30
31
31
32
32
32-33
33
33
6. Fazit
34
34
34
34
6.1 Schwierigkeiten und Verbesserungsvorschläge
6.2 Programmdefizite
6.3 Erfahrungen
7. Glossar
8. Quellenverzeichnis
35
35
Seite 2
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
A. Programmlisting
1. Motivation
1.1 Danksagung
An dieser Stelle wollen wir uns vor allem bei Herrn Lehmann für die Unterstützung und die ständige
E-Mailbereitschaft während des Projekts bedanken. Außerdem danken wir dem IT Kurs 2005, so wie dem Rest
unseres eigenen Kurses für die Hilfsbereitschaft bei Problemen, die während des Projekts auftraten.
1.2 Vorwort
Diese Dokumentation ist begleitend zu einem Projekt in der Vorlesung Rechnertechnik an der Berufsakademie
Horb entstanden. Die Dokumentation soll helfen die Funktionen des µC PIC16F84 der Firma Microchip zu
verstehen und darüber hinaus die Programmoberfläche des Simulators erklären. Zur Erreichung dieses Ziels
werden unter Anderem einzelne Methoden aus dem Programmcode des Simulators anhand von Beispielen
erklärt. Da diese Dokumentation auch zeigen soll, dass das Funktionsprinzip des Mikrocontrollers verstanden
wurde, sollten diejenigen die lediglich eine reine Erklärung zur Programmoberfläche suchen, gleich zu Kapitel 5
springen
1.3 Aufgabenstellung
Ziel dieses Projekts war es eine Software zur Simulation eines µCs zu programmieren und dabei eine möglichst
detailgetreue Nachbildung der Funktionen und Features des µCs PIC 16F84 zu schaffen. Besonderes
Augenmerk sollte hierbei auf folgende Funktionen gelegt werden:
- Register
- Interrupt
- WDT
- Timer
- Vorteiler
- Latchfunktion
- Stack
Ebenfalls war es ein Ziel eine benutzerfreundliche Programmoberfläche zu schaffen, um dem Benutzer einen
schnellen Einstieg in den Simulator zu gewähren.
Zusätzlich konnte eine Kommunikation über die serielle Schnittstelle implementiert werden, um eine
Ergebnisausgabe auf einer Testplatine zu ermöglichen.
Zur Umsetzung dieser Vorgabe sollte eine C-Basierte Sprache zu verwendet werden (z.B. C#, C++).
Seite 3
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
1.4 Programmiersprache und Stil
Da das Programm Visual Studio von Microsoft bereits die Option beinhaltet eine grafische Oberfläche per Drag &
Drop zu generieren und darin schnell und einfach Einstellungen vorzunehmen, haben wir uns für die
Programmiersprache C# mit .NET Framework 2.0 entschieden. Diese auf C basierende Sprache ermöglicht es
die Prinzipien der Objektorientierung zu nutzen, aber auch über leichte Umwege im „klassischen Stil“ zu
programmieren. Wir haben uns vor Beginn der Programmierung dafür entschieden alle Klasse, Methoden und
Attribute als public und statisch zu kennzeichnen. Das heißt sie können von jeder beliebigen Programmstelle
ohne „setter“ und „getter“-Methoden und ohne Erzeugung eines Objekts verwendet oder verändert werden. Dies
hatte mehrere Gründe:
Zum einen wurde während den vorangegangenen Vorlesungen meistens auf eine saubere objektorientierte
Programmierung hingearbeitet, dass in der Praxis aber häufig Programme, vor allem auch auf Mikrocontrollern
zum Einsatz kommen, die nicht nach den Prinzipien der Objektorientierung programmiert sind, wurde kaum
beachtet. Darum hielten wir es für sinnvoll uns zur Übung während dieses Projekt mit der „harten
Programmierung“ auseinander zu setzen. Zum anderen bietet die Objektorientierung in punkto Wartbarkeit zwar
sehr viele Vorteile gegenüber der prozeduralen Programmierung, bedeutet dadurch aber in der
Entwicklungsphase einen deutlich höheren Aufwand. Da beim PIC-Simulator davon ausgegangen werden kann,
dass er in Zukunft nicht mehr oft gewartet werden wird, würde die objektorientierte Programmierung einen
unnötigen Mehraufwand bedeuten.
Um den Programmcode trotzdem Übersichtlich zu halten, wurde versucht gängige Programmierkonventionen
einzuhalten. Zusätzlich sind zum besseren Verständnis Beispiele, Klassendiagramm und Flussdiagramme in
dieser Dokumentation enthalten.
Seite 4
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
2. Grundlagen
2.1 Simulator
2.1.1 Was ist ein Simulator?
Eine Simulation ist eine versuchte Abbildung eines realen Objektes. Bei einer Simulation werden Experimente mit
festgelegten Parametern an einem Modell durchgeführt, um Erkenntnisse über das reale System zu gewinnen.
Ein Simulator spart Zeit, Ressourcen und Arbeitsmittel und somit Kosten. Außerdem bietet ein Simulator die
Möglichkeit selbst komplexeste Zusammenhänge in annehmbarer Zeit mit nahezu beliebig genauem
Annäherungsgrad nach zu bilden (z.B. Klimasimulationen). Weiterführend sollte es dabei Ziel eines Simulators
sein, selbst einem Laien die Ergebnisse einer Simulation verständlich darzustellen, um somit auch als
Anschauungsmaterial z.B. bei der Gewinnung von Aufträgen dienen zu können.
Simulationen kommen meist dann zum Einsatz, wenn die Beschreibung oder Nachbildung eines Systems zu
komplex für Formeln und andere systematische Vorgehensweisen ist oder Ergebnisse selbst von ungeschultem
Personal erzielt werden sollen (z.B. Simulation des Verkehrsaufkommens bei Ampelschaltungen). Sie können
aber auch dazu dienen Systeme zu modellieren, die noch nicht existieren und bei denen es sich lohnt den Nutzen
vorher zu ermitteln (z.B. Brückenbau). Eine Simulation muss nicht zwingend auf der Grundlage eines Computers
basieren, so sind Crashtests in gewisser Weiße auch eine Form der Simulation, denn hier werden
Ausgabeparameter (Grad der Zerstörung) auf Basis von konkreten Eingabeparametern (Autos, Dummies, etc.)
erzeugt.
2.1.1 Vor- und Nachteile einer Simulation
Vorteile:
•
•
•
•
•
•
•
•
Eine Simulation liefert Ergebnisse ohne bei falschen Eingabeparametern eine eventuelle Gefährdung für
seine Umwelt darzustellen (z.B. Raketensimulation)
In der Regel beliebig oft wiederholbar
Schnelle Fehlererkennung möglich. Zusätzliche Messungen sind zumeist nicht nötig
Häufig günstiger als ein gescheitertes „reales“ Projekt
Ergebnisse können auch für Laien verständlich dargestellt werden
Erfordern nicht immer tief greifende Fachkenntnisse
Simulationen sind häufig schneller entwickelt, als das dazugehörige reale System
Zeit, Kosten und Ressourcenersparnis
Nachteile:
•
•
•
•
•
•
Simulationen sind nur so genau wie ihre Nachbildung, je genauer die Simulation umso höher der
Aufwand
Fehler in der Simulation können später verheerende Auswirkungen auf das reale System haben
Zusätzlicher Kosten- und Zeitfaktor
Wenn Simulation an der falschen Stelle zum Einsatz kommt, können sie Projekte bremsen statt fördern
Eine Simulation ist immer begrenzt durch Zeit und Ressourcen (Endlichkeit des Speichers und der
Rechenkapazität)
Hoher Aufwand an Datenbeschaffung um Simulation detailgetreu nachzubilden und Fehler zu vermeiden
Seite 5
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
2.2 Mikrocontroller
2.2.1 Harvard-Architektur
Die so genannte „Harvard-Architektur“ stellt ein
Grundprinzip zum Entwurf von Mikrocontroller dar.
Anders als bei der „von Neumann-Architektur“ ist der
Datenspeicher bei der Harvard-Architektur vom
Programmspeicher getrennt. Dies ermöglicht das
gleichzeitige Ausführen und Einlesen eines Befehls,
bzw. seines Nachfolgerbefehls. Dies hat eine schnellere
Befehlsverarbeitung, aber gleichzeitig einen höheren
Schaltungsaufwand zur Folge, denn statt einem
einzigen Bus wird, für Datenspeicher und
Programmspeicher jeweils ein eigener Bus benötigt.
Dafür ist es möglich die Datenbreite des
Grafik 1: Schematische Darstellung der Harvard-Architektur
Programmspeichers wesentlich höher als die des
Datenspeichers zu machen. Man erreicht damit die
Möglichkeit einen größeren Befehlsvorrat zur Verfügung zu stellen (RISC / CISC). Wie auch in der von NeumannArchitektur üblich, werden in der Harvard-Architektur die Befehle von einem Rechenwerk verarbeitet und mittels
eines Ein / Ausgabegeräts die Interaktion mit dem Benutzer ermöglicht.
2.2.2 Der Aufbau und die Befehlsverarbeitung des PIC 16F84
Grafik 2: Schematische Architekturdarstellung des PIC 16F84
Seite 6
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
Der PIC 16F84 folgt den Regeln der Harvard-Architektur. Er verfügt über einen 1K großen Programmspeicher
(ROM), so wie einen 68 Byte großen Datenspeicher (RAM). Als Ein- / Ausgabewerk dienen 13 Pins, bezeichnet
als Port A (5 Pins) und Port B (7 Pins), sowie 2 Pins um die Taktgeschwindigkeit des Mikrocontrollers
vorzugeben. Der Speicherbereich kann über ein zusätzliches EEPROM um 64 Byte erweitert werden. Als Feature
verfügt der PIC16F84 über eine Interruptfunktion, einen Timer und einen WatchDog-Timer. Trotz seiner relative
großen Befehlsbreite von 14 Bit, verfügt der PIC16F84 über einen so genannten RISC-Befehlssatz. D.h. er
verfügt über wenige, dafür sehr mächtige Befehle, mit denen es in der richtige Kombination möglich ist, nahezu
jeden vorstellbaren Befehl nachzubilden. Dadurch kann es aber vorkommen, dass komplexere Befehle mehrere
Zyklen in Anspruch nehmen, was bei einem CISC-Befehlssatz nicht der Fall gewesen wäre.
Der Stack des PIC dient dazu Programmspeicheradressen zwischen zu speichern. Zur Analyse und Modifikation
von Befehlen bietet das Status-Register so genannte Flags, die auftretende Ereignisse dokumentieren (z.B.
„Ergebnis einer Rechnung ist 0“).
2.2.3 Befehlsverarbeitung
Bei jedem Taktsignal das der PIC empfängt wird der Befehl der sich an der Programmspeicheradresse des
Programmzählers befindet geladen. Dazu wird er in das so genannte Instruction-Register geschrieben. Von dort
aus, wird der Befehl decodiert und dann verarbeitet. So kann z.B. der WDT zurückgesetzt werden oder eine
Addition durchgeführt werden. Bei den mathematischen und logischen Operationen wird in aller Regel das so
genannte W-Register mit einbezogen. Dieses dient zum einen als Zwischenspeicher für Rechenergebnisse, zum
anderen als Basis für Rückgabewerte z.B. bei einem Unterprogrammende. Denn beim Aufruf eines
Unterprogramms, wird die derzeitige Programmspeicheradresse auf den Stack gelegt. Bei Beendigung des
Unterprogramms wird zurück an die Programmspeicheradresse gesprungen, die ganz oben auf dem Stack liegt.
Bei dieser Operation kann z.B. eine Konstante in das W-Register geschrieben werden.
Während ein Befehl z.B. mit ALU und W-Register verarbeitet wird, wird dank der Harvard-Architektur bereits der
nächste Befehl eingelesen. So dass sich die Prozedur mit dem nächsten Takt wiederholt, bis das Programmende
erreicht ist.
Seite 7
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
3. Vertiefung
3.1 Programmzähler
13
Beim PIC 16F84 ist der Programmzähler 13 Bit breit. Das bedeutet, dass eine aus 2 Stellen des
Programmspeichers mit nur einem Zyklus adressiert werden kann (bei Sprüngen in 2 Zyklen). Konkret sind dies
die Programmspeicherstellen 0000h – 03FFh. Für gewöhnlich wird der Programmzähler am Ende jeden Zykluses
um 1 erhöht. Eine Ausnahme bildet hier z.B. der GOTO Befehl, der es ermöglicht an eine beliebige
Programmspeicheradresse zu springen. Der Programmzähler wird durch 2 Register im SFR-Register realisiert.
Dabei stellt das Register PCL die unteren 8 Bit des Programmzählers und das Register PCLATH die oberen 5 Bit
des Programmzählers bereit. Letzteres kann nicht direkt durch Programmcode verändert werden.
3.2 Interrupts
Interrupts stellen eine Ausnahme in der regulären Befehlsverarbeitung dar. Tritt ein spezielles, vorher definiertes,
Ereignis auf, so wird sofort darauf an die Programmspeicherstelle 4h gesprungen und die alte
Programmspeicheradresse auf dem Stack abgelegt. Ein Interrupt ist also im weitesten Sinne ein in der Regel
nicht durch das Programm vorgegebener Unterprogrammaufruf. Ereignisse die ein Interrupt auslösen können
sind unter anderem der Überlauf des Timers, das Verändern des Pins 4-7 am Port B, das Auftreten einer
steigenden bzw. fallenden Flanke am Pin 0 des Port B und die Beendigung eines Schreibvorgangs in den
EEPROM-Speicher. Welche Interrupts genau zugelassen werden ist abhängig von den Einstellungen im INTCON
Register (Datenspeicheradresse: 11h). Dort kann mit dem GIE-Bit eine allgemeine Freigabe für Interrupts erteilt
werden. Mit den Bits EEIE, T0IE, RBIE und INTE wird die Freigabe für die unterschiedlichen Interrupttypen erteilt.
Unabhängig davon, ob ein spezieller Interrupt zugelassen ist, wird immer ein spezielles Bit zur Anzeige des
Eintretens eines Interrupts gesetzt. So wird z.B. bei einem Timerüberlauf immer das T0IF Bit im INTCON Register
gesetzt.
3.3 Timer
Der Timer ist ein 8 Bit breites Register zum Zählen von Takten bzw. Taktflanken. Sollte der Timer über seine
Speichergrenze von 255d hinauslaufen, so beginnt er wieder von vorne zu zählen und setzt zeitgleich das T0IF
Bit im INTCON-Register. Dadurch wird angedeutet, dass der Timer einen „Überlauf“ hatte und falls eine Freigabe
für den Timer0-Interrupt erteilt worden sein sollte, wird durch diesen „Überlauf“ ein Interrupt ausgelöst. Wann der
Timer um eins erhöht wird, wird durch das Option-Register bestimmt. Dort kann die Taktquelle gewählt werden.
Zur Auswahl stehen der interne Oszillatortakt der vom Außen anliegenden Quarz vorgegeben wird und ein Takt
der an PIN 4 des Ports A angelegt werden kann. Sollte letzterer gewählt werden, kann durch das T0SE-Bit
bestimmt werden, ob bei einer steigenden oder einer fallenden Flanke gezählt werden soll. Ob das TMR0Register jedoch direkt erhöht wird, oder erst einige Takte später, ist abhängig vom Vorteiler. Dieser gibt vor, wie
viele Takte eingehen müssen, bis der Timer einmal inkrementiert wird. Da der PIC16F84 nur über einen Vorteiler
verfügt, muss sich der Anwender entscheiden, ob dieser für den WatchDog-Timer oder für den Timer0 gültig sein
soll. Dies kann mit Hilfe des PSA-Bits im Option Register realisiert werden. Die Bits PS0-PS2 entscheiden dann
darüber wie hoch der Vorteiler ist. Sollte der Vorteiler für den WDT eingesetzt werden, so muss trotzdem beachtet
werden, dass der Timerß maximal bei jedem 4. Takt (1 mal pro Zyklus) inkrementiert werden kann.
Seite 8
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
3.4 WatchDog-Timer
Der WatchDog-Timer ist ein vom externen Takt unabhängiger Oszillator der alle 18 ms dafür sorgt, dass der µC in
seinen Anfangszustand zurückgesetzt wird. Dies kann z.B. durch den Befehl CLRWDT verhindert werden, denn
dadurch wird der WatchDog-Timer neu gestartet. Der WDT hat die Aufgabe einen Fehler (z.B. Dead Lock) des
Systems zu erkennen, um Unfälle zu vermeiden und einen geregelten Ablauf des Programms zu sichern.
Dahinter steckt folgender Gedanke: Dient ein µC z.B. zur Steuerung der Motoren einer Presse, so muss alle 18
ms von der Presse eine Rückmeldung an den µC gehen um zu bestätigen, dass diese noch Ordnungsgemäß
funktioniert. Ist dies nicht der Fall wird die Presse automatisch in ihren Ausgangszustand gefahren um z.B. das
Einklemmen einer Hand zu verhindern Funktioniert der µC oder eine angeschlossene Peripherie nicht
ordnungsgemäß, so sorgt der WDT für einen Reset.
Standardmäßig passiert das nach 18 ms, durch den Vorteiler kann die Zeit bis zu einem WDT-Reset auf maximal
2,3 Sekunden erhöht werden. Geht man davon aus, dass ein Befehl 1 µs für seinen Abarbeitung benötigt, so
würden ohne Vorteiler 18.000.000 Befehle abgearbeitet bis der WDT einen Reset durchführt.
3.5 Ein- / Ausgabe
Die Ein- bzw. Ausgabe erfolgt beim PIC16F84 über 13 Pins, die als Ein- oder Ausgang definiert werden können
und in der Lage sind digitale Signale zu verarbeiten (PICs der Reihe 18xxx können auch analoge Signale
verarbeiten). Die Pins sind auf zwei Ports (PortA / PortB) aufgeteilt. Ob ein Pin ein Eingang oder ein Ausgang ist,
wird im TRISx Register festgelegt. Dabei hat jeder Pin sein eigens Bit. Beispielsweise repräsentiert Bit 0 im TRIS
A-Register den Pin 0 an Port A. Ist dieses Bit eine logische 1, so ist dieser Pin als Eingang definiert. Der
eigentliche Zustand des Pins wird im Register PortX (Datenspeicherstelle: 5H und 6H) gespeichert. Das
Schreiben auf einen Pin erfolgt nach dem „Read-Modify-Write“ Verfahren. Das bedeutet, dass selbst wenn nur ein
Bit an einem Port verändern wird (z.B. mit dem BSF Befehl), zuerst der gesamte Port (nicht das dazugehörige
Register sondern der Port) in die CPU eingelesen wird, dort verarbeitet wird und anschließend in das
korrespondierende PortX-Register geschrieben wird. Dies hat zur Folge, dass Abhängig davon, ob die Pins eines
Ports als Ein- oder Ausgang definiert sind und welche Pegel von außen am Pin liegen, eventuell eine andere
Berechnung durchgeführt wird, als man als Programmierer eigentlich erwartet.
Beispiel: Wird vor einer Berechnung, ein zuvor als Ausgang definierter Pin zu einem Eingang gemacht und dann
von Außen ein Pegel angelegt, der Gegenteilig zum im Port-X Register enthaltenen Wert ist, so wird z.B. eine 1
statt einer 0 addiert.
Ein so genannter Latch sorgt dafür, dass sich der Wert an den Pins auch ohne MOV, BSF, o.ä. Befehle dem in
Port-X Register enthaltenen Werten anpasst.
Beispiel: Liegt in Bit 1 des Port A eine 1, ist aber Pin 1 des Port A als Eingang definiert, sieht der Pin nach Außen
hin aus, als wenn in Port A – Bit 1 eine 0 stehen würde, was aber nicht der Fall ist. Wechselt nun irgendwann im
Laufe des Programms das Bit 1 im TRIS A Register den Zustand, so wird Pin 1 an Port A ein Ausgang und die 1
im Port A Register wird sofort nach Außen hin sichtbar, ohne erneut einen Befehl zu benötigen.
3.6 Takt
Der Takt ist ein Rechtecksignal, dass durch einen Quarz am OSC Pin des µC angelegt wird. Der Takt gibt die
Geschwindigkeit der CPU vor. Dabei benötigt jeder Befehl mindestens eines Zyklus, d.h. mindestens 4 Takte um
abgearbeitet zu werden. Einige Befehle wie GOTO benötigen 2 Zyklen zur Abarbeitung. Der Außen anliegende
Takt darf jedoch nicht mit dem Takt des WDT verwechselt werden. Dieser Takt wird durch einen internen
Oszillator generiert und selbst wenn der äußere Quarz nicht mehr funktionieren sollte, kann der WDT einen Reset
auslösen.
Seite 9
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
3.7 Speicher
Grundlegenden unterscheidet der PIC 16F84, vier voneinander getrennte Speicher. Den Programmspeicher, den
Datenspeicher, den Zusatzdatenspeicher EEPROM und den Stack. Die Kommunikation mit den Speichern
geschieht über Busse und Steuerleitungen die signalisieren, ob auf den Speicher geschrieben oder gelesen
werden soll. Wie bereits erwähnt ist der PIC architekturbedingt in der Lage gleichzeitig einen Befehl einzulesen
und einen weiteren Befehl zu verarbeiten. Dies wird durch die Trennung der unterschiedlichen Speicher
ermöglicht.
3.7.1 Der Programmspeicher
Dieser 1KByte große Speicher enthält alle Befehle in der Reihenfolge in der sie abgearbeitet werden sollen.
Dabei sollte sich an Stelle 4 H immer die Interruptroutine befinden, da bei einem Interrupt immer diese Adresse
angesprungen wird. Mit Sprungbefehlen ist es möglich die natürliche Befehlsreihenfolge zu manipulieren. Der
Programmspeicher kann nicht über Befehle verändert werden, da er ein Flash / ROM Speicher ist. Das heißt er
ist nur beim Laden des Programms über eine externe Schnittstelle beschreibbar. Früher mussten
Programmspeicher zuvor z.B. durch UV-Licht gelöscht werden, bevor sie beschrieben werden konnten.
3.7.2 Der Datenspeicher
Beim PIC 16F84 kann ein Programmierer 68 KByte Daten im Datenspeicher ablegen. Dabei ist der
Datenspeicher in zwei Bänke unterteilt. Der Unterschied zwischen den Bänken ist aber sehr gering (z.B. OptionRegister auf Bank 1). Da die Bänke in den Speicherstellen in denen sie sich nicht unterscheiden gespiegelt sind,
macht es keinen Unterschied ob man an die Adresse 0C oder 8C schreibt, da der übermittelte Wert in beiden
Speicherstellen wieder zu finden ist. Die ersten 12 Byte der beiden Bänke werden als so genanntes Special
Function Register bezeichnet. Dort hat jedes Byte bis hinunter zum einzelnen Bit eine eigene Aufgabe. Eines der
wichtigsten Bytes im SFR ist das Status-Register. Es enthält Flags die z.B. als Indikator für das Ergebnis einer
Rechenoperation dienen. So wird z.B. bei einem Übertrag ins 9. Bit bei einer Addition zweier 8 Bit Zahlen, das so
genannte Carry-Flag gesetzt. Ist das Ergebnis einer Rechenoperation 0 wird das Zero-Flag gesetzt. Usw.
Ab dem 13. Byte steht der Speicher dem Programmierer für Zwischenergebnisse einer Rechnung o.ä. zur freien
Verfügung. Adressiert wird der Datenspeicher entweder direkt über eine im Befehl enthaltene Adresse oder
indirekt über das IND-Register (0H). Wird versucht von dieser Speicherstelle zu lesen bzw. zu schreiben wird
automatisch an die Adresse gesprungen die im SFR-Register steht.
Beispiel. Steht im SFR-Register 15 H und nun wird mittels eines MOV Befehls in das IND-Register geschrieben,
so wird stattdessen in die Speicherstelle 15 H geschrieben.
3.7.3 EEPROM
Da beim Ausschalten des PIC alle Daten im Datenspeicher verloren gehen, ist es für manche Benutzer sinnvoll
die zuvor erhaltenen Daten bis zum nächsten Start des PIC zu speichern. Dazu dient der 64 Byte große
EEPROM-Speicher. Dieser kann über indirekte Adressierung beschrieben werden und löst nach Beendigung des
Schreibvorgangs einen Interrupt aus (Falls Interrupt erlaubt). Auch wenn die Spannunsgversorgung vom PIC
getrennt wird, wird dieser Speicher nicht gelöscht und ist daher z.B. für Voreinstellungen bei einem Neustart
nützlich. Nicht geeignet ist der EEPROM für zeitkritische Anwendung, da das Beschreiben relativ langsam ist (ca.
4 ms).
3.7.4 Stack
Dieser Speicher ist 8 x 13 Bit groß und dient dazu bei einem Interrupt oder einem Unterprorgammaufruf den
derzeitigen Wert des Programmzählers zu speichern, um es dem Mikrocontroller zu ermöglichen am Ende eines
Unterprogramms wieder an die alte Programmstelle zurück zu springen. Der Stack funktioniert nach dem LIFOPrinzip (Last in First Out). Das heißt der letzte Wert der auf den Stack gelegt wurde, wird als erster Wert wieder
heruntergenommen. Sollten sich bereits 8 Werte auf dem Stack befinden und es wird versucht einen weiteren
Wert auf den Stack zu legen, so werden alle bis dahin im Stack befindlichen Werte um eine Stelle nach unten
geschoben. Der unterste Wert wird aus dem Stack geschoben und geht verloren, während oben eine Stelle für
den neuen Wert frei wird.
Seite 10
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
4. Programmumsetzung
4.1 Prinzipieller Ablauf
Jeder Programmablauf verläuft im normalen Ablaufmodus nach dem selben Prinzip. Eine Ausnahme bilden die
Interrupts und interaktiven Schaltflächen der GUI, diese können den normalen Ablauf verändern oder
beeinflussen. Folgende Flussdiagramme sollen den normalen Ablauf erläutern:
Grafik 3: Flussdiagramm - Befehlsverarbeitung
Grafik 4: Flussdiagramm – Befehl einlesen
Seite 11
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
4.2 Befehle
Die in der Klasse „Befehle“ enthaltene Methode „befehlsauswahl()“ führt, auf Grundlage des aktuellen Befehls in
der Variable „aktueller Befehl“ der Klasse FileRegister, eine Aufschlüsselung der Befehle auf den richtigen
Befehlscode durch. Das heißt jedem Befehl ist ein bestimmter Zahlenrbereich zugeordnet. Wo dieser Bereich
beginnt, bzw. endet ist von der Semantik des jeweiligen Befehls abhängig.
Beispiel: Ein Befehl mit der Semantik: 00 0111 dfff ffff hat 8 Frei wählbare Bits (rot markiert). Das bedeutet der
Befehl kann sich in einem Zahlenraum von 00 0111 0000 0000 und 00 0111 1111 1111 aufhalten. Genau diese
Zahlenräume werden mittels einzelner if-Abfragen in dieser Methode überprüft und danach der dazugehörige
Befehlsalgorithmus ausgeführt.
4.2.1 BTFSx
if (aktuellerBefehl >= 7168 && aktuellerBefehl <= 8191)
{
if (((FileRegister.registerInhaltLesen(aktuellerBefehl & 127) >>
((aktuellerBefehl >> 7) & 7)) & 1) == 1)
{
FileRegister.programmzaehler++;
// Überspringen
FileRegister.zyklenbreite = 2;
}
else
{
FileRegister.zyklenbreite = 1;
}
}
Der BTFSx Befehl frägt ab, ob ein bestimmtes Bit in einem bestimmten Register gesetzt bzw. nicht gesetzt ist.
Sollte dies der Fall sein so wird der nächste Befehl übersprungen und stattdessen keine Operation ausgeführt.
Dadurch dauert der Befehl einen Zyklus länger als üblich (sonst 1 Zyklus). Ist die Bedingung nicht erfüllt, wird
ganz normal der nächste Befehl ausgeführt. Diese Funktion eignet sich z.B. zur Abfrage eines Signalswechsels
an einem Eingangspin.
Um vom Programm als BTFSS Befehl erkannt zu werden muss der aktuelle Befehlscode zwischen 7168 und
8191 liegen.
Der Befehl folgt in diesem Falle folgender Bitaufschlüsselung: 01 11bb bfff ffff
Die rot markierten Bits geben dabei an in welchem Register sich das abzuprüfende Bit befindet. Damit das
Programm weiß welche Stelle im SFR-Array gemeint ist, wird die Funktion „registerInhaltLesen“ in der Klasse
FileRegister aufgerufen. Dabei werden die rot markierten Bits durch eine UND Verknüpfung übertragen um die
restlichen Bits abzuschneiden. Wurde der Inhalt des Registers ausgelesen, wird dieser um soviele Stellen wie die
grün markierten Bits es vorgeben verschoben. Dazu wird zuerst der Befehl um 7 Bits nach links verschoben und
anschließend das geladene Register um die angegebene Stellenzahl verschoben. Danach wird das verschobene
Register mit 1 verUNDet und dann abgeprüft, ob das Ergebnis 1 ist. Ist dies der Fall, so ist das abgefragte Bit
gesetzt und der nächste Befehl wird übersprungen, indem der Programmzähler zusätzlich zur normalen
Erhöhung um 1 erhöht wird. Anschließend wird die Zyklenbreite auf 2 gestellt, da der Zyklus in diesem Falle zwei
Zyklen breit ist.
Ausführungsbeispiel:
Befehl = 01 1100 1000 0011
Status-Register = 0000 1000
Abgefragt wird Bit 3 im Status-Register, welches im Beispiel gesetzt ist
Daraus folgt, dass der nächste Befehl übersprungen wird und Befehl 2 Zyklen zur Abarbeitung benötigt
Seite 12
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
4.2.2 CALL
if (aktuellerBefehl >= 8192 && aktuellerBefehl <= 10239)
{
FileRegister.push(FileRegister.programmzaehler + 1);
FileRegister.programmzaehler = ((aktuellerBefehl & 2047)-1);
FileRegister.zyklenbreite = 2;
}
Der CALL-Befehl führt den Aufruf eines Unterprogramms durch. Dazu wird in die im Befehl enthaltene
Programmstelle gesprungen und der dort befindliche Programmcode ausgeführt. Nach Beendigung des
Unterprogramms wird zurück an die Programmstelle, an der der Unterprogrammaufruf stattgefunden hat,
gesprungen. Dazu legt der CALL-Befehl den derzeitigen Programmzählerstand auf dem Stack ab.
Um vom Programm als CALL-Befehl erkannt zu werden muss der aktuelle Befehl zwischen 8192 und 10239
liegen. Ist dies der Fall, so wird zuerst mit der push()-Methode der Klasse „FileRegister“ der aktuelle Wert des
Programmzählers + 1 auf den Stack gelegt. Zum Programmzähler muss eins dazu addiert werden, weil sonst
nach Beendigung des Unterprogramms wieder an die Stelle des CALL-Befehls gesprungen würde und es so zu
einer Endlosschleife kommen würde.
Anschließend wird an die im Befehl befindliche Stelle im Programmspeicher gesprungen. Die Sprungstelle
befindet sich im Befehl in den rot markierten Bits (10 0kkk kkkk kkkk). Um diese aus dem Befehl auszusondern,
muss eine UND Verknüpfung mit 2047 (111 1111 1111 Binär) vorgenommen werden. Dadurch werden die
vorderen 3 Bits abgeschnitten. Da der Programmzähler am Ende des Befehls um eins erhöht wird, muss von der
Sprungstelle 1 abgezogen werden. Der CALL Befehl benötigt 2 Zyklen. Dies ist der Grund dafür, dass am Ende
die Zyklenbreite auf 2 gestellt wird
Ausführungsbeispiel:
Befehl = 10 0000 1000 0000
Programmzähler = 20 Dez
Stack = (0,0,0,0,0,0,0,0)
Der aktuelle Programmzählerstand wird auf den Stack gelegt. Stack ist also (0,0,0,0,0,0,0,20)
Programmzähler wird mit 128 geladen
Programm wird an Programmstelle 128 weitergeführt
Seite 13
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
4.2.3 MOVF
if (aktuellerBefehl >= 2048 && aktuellerBefehl <= 2303)
{
if ((aktuellerBefehl & 128) == 128)
{
FileRegister.registerInhaltSchreiben(aktuellerBefehl & 127,
FileRegister.registerInhaltLesen(aktuellerBefehl & 127));
zeroFlagInSFRSetzen();
}
else
{
FileRegister.wRegister = FileRegister.registerInhaltLesen
(aktuellerBefehl & 127);
zeroFlagSetzen();
}
FileRegister.zyklenbreite = 1;
}
Der MOVF Befehl schreibt den Wert eines Registers im Datenspeicher entweder in sich selbst oder ins
WRegister. Wohin geschrieben wird, wird durch ein Bit im Befehl bestimmt. Der Sinn davon den Wert eines
Registers in sich selbst zu schreiben liegt darin, zu testen ob ein Register z.B. den Wert 0 enthält. Wäre dies der
Fall, so würde der Befehl das Setzen des ZeroFlags im Status-Register bewirken.
Um vom Programm als MOVF-Befehl erkannt zu werden muss der aktuelle Befehl zwischen 2048 und 2303
liegen. Gesetz diesem Fall wird zu allererst überprüft wohin das Ergebnis geschrieben werden soll. Der Befehl ist
folgendermaßen codiert: 00 0100 dfff ffff. Das grün markierte Bit dient zur Angabe des Ziels. Ist dieses Bit gesetzt
so wird der Wert zurück in das Herkunftsregister geschrieben. Um zu überprüfen ob das grün markierte Bit
gesetzt ist, wird im Programmcode der aktuelle Befehl mit 128 verUNDet, wenn danach das Ergebnis 128 ist, so
weiß man, dass das Bit gesetzt ist. In diesem Falle wird mittels der Methoden registerInhaltSchreiben() und
registerInhaltLesen() der Wert im Register, dass durch die rot markierten Bits codiert wird, in sich selbst
geschrieben. Sollte das Register den Wert 0 enthalten, so erkennt dies die Methode zeroFlagInSFRSetzen() und
setzt dann das ZeroFlag im Status Register. Ist das grün markierte Bit nicht gesetzt, ist die Zieladresse statt der
Herkunftsadresse das WRegister. Ebenfalls wird danach geprüft, ob das WRegister den Wert 0 enthält und wenn
ja wird das Status-Register gesetzt. Zum Abschluss wird die Anzahl der benötigten Zyklen in die Variable
„Zyklenbreite“ geschrieben.
Ausführungsbeispiel:
Befehl: 00 0100 0000 1111
Inhalt Speicherstelle 15Dez = 0
Inhalt von Speicherstelle 15 wird ins WRegister geschrieben
Zero-Flag wird gesetzt, weil Inhalt von Speicherstelle 15 = 0
WRegister = 10
Seite 14
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
4.2.4 RRF
if (aktuellerBefehl >= 3072 && aktuellerBefehl <= 3327)
{
// Zwischenspeiche für "altes" Carry
int carry = FileRegister.SFR[3] & 1;
FileRegister.SFR[3] = (FileRegister.SFR[3]&254) |
(FileRegister.registerInhaltLesen(aktuellerBefehl & 127) & 1);
if ((aktuellerBefehl & 128) == 128)
{
FileRegister.registerInhaltSchreiben(aktuellerBefehl & 127,
(FileRegister.registerInhaltLesen(aktuellerBefehl & 127) >> 1)
| (carry << 7));
}
else
{
FileRegister.wRegister =
(FileRegister.registerInhaltLesen(aktuellerBefehl & 127) >> 1) | (carry << 7);
}
FileRegister.zyklenbreite = 1;
}
Der Befehl RRF verschiebt den Inhalt eines im Befehl codiert enthaltenen Registers
um eine Position nach rechts. Dabei wird das LSB ins Carry geschoben, während
das alte Carry an die Stelle des MSB des Registers geschoben wird. Durch ein
zusätzliches, im Befehl enthaltenes Bit, bestimmt der Befehl ob das verschobene
Register in sich selbst geschrieben werden soll oder ins WRegister geschrieben
werden. Sollte nach der Rechtsverschiebung eine 1 im Carry stehen, so wird das
Carry-Bit im Status Register gesetzt.
Grafik 5: Schematische
Darstellung des RRF Befehls
Der RRF Befehl wird vom Prorgamm als solcher erkanntm wenn sich der Befehl im Bereich zwischen 3072 und
3327 befindet. Sollte dies der Fall sein, so wird zu allererst das aktuelle Carry, dass sich auf dem ersten Bit des
Statusregisters befindet zwischengespeichert. Um das Carry-Bit aus dem Status-Register auszusieben, wird das
Status-Register (SFR[3]) mit 1 verUNDet. Anschließend wird das derzeitige Carry im Status-Register auf 0
gesetzt, dazu wird das Status-Register mit 254 verUNDet. Darauf folgend wird das LSB des Registers, dass nach
rechts verschoben werden soll, in das Status-Register geschrieben, in dem es mit dem Status-Register per
ODER verknüpft wird.
Welches Register verschoben werden soll, ist im Befehl der auf folgende Weise codiert ist, enthalten:
00 1100 dfff ffff.
Die rot markierten Bits codieren das zu verschiebende Register, während das grün markierte Bit angibt, wohin
das Ergebnis geschrieben werden soll. Wenn die Abfrage des grün markierten Bits im Programmcode eine 1
ergibt, so wird das verschobene Register zurück in sich selbst geschrieben, andernfalls wird es ins WRegister
geschrieben. Dazu wird das Register mittels „>>“ Operator um 1 nach links geschoben und danach mit dem um 7
Positionen nach links verschobenen Carry-Zwischenspeicher per ODER verknüpft.
Ausführungsbeispiel:
Befehl: 00 1100 1001 0000
Inhalt von Register an Stelle 16 = 11001010
Carry = 1
Register 16 wird um eins nach rechts verschoben und Carry vorne rein geschoben = 11100101
Carry = 0
Ergebnis wird zurück ins Register 16 geschrieben
Seite 15
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
4.2.5 SUBWF
if (aktuellerBefehl >= 512 && aktuellerBefehl <= 767)
{
// DC: Wird gesetzt wenn unteres Halbbyte Register f minus unteres Halbbyte WRegister
größer oder gleich 0 ist
if (((FileRegister.registerInhaltLesen(aktuellerBefehl & 127) & 15) (FileRegister.wRegister & 15)) >= 0)
{
FileRegister.SFR[3] = FileRegister.SFR[3] | 2;
}
else
{
FileRegister.SFR[3] = FileRegister.SFR[3] & 253;
}
// C: Wird gesetzt Register f minus WRegister größer oder gleich 0 ergibt
if (((FileRegister.registerInhaltLesen(aktuellerBefehl & 127) & 255) –
(FileRegister.wRegister & 255)) >= 0)
{
FileRegister.SFR[3] = FileRegister.SFR[3] | 1;
}
else
{
FileRegister.SFR[3] = FileRegister.SFR[3] & 254;
}
if ((aktuellerBefehl & 128) == 128)
{
FileRegister.registerInhaltSchreiben(aktuellerBefehl & 127,
(FileRegister.registerInhaltLesen(aktuellerBefehl & 127) –
FileRegister.wRegister) & 255);
zeroFlagInSFRSetzen();
}
else
{
FileRegister.wRegister =
(FileRegister.registerInhaltLesen(aktuellerBefehl & 127) –
FileRegister.wRegister) & 255;
zeroFlagSetzen();
}
FileRegister.zyklenbreite = 1;
}
Der Befehl SUBWF subtrahiert das WRegister von einem im Befehl angegeben Register. Durch ein zusätzliches
Bit im Register wird entschieden ob das Ergebnis zurück in das Herkunftsregister geschrieben wird oder ins
WRegister geschrieben wird. Zusätzlich wird bei diesem Befehl sowohl das Carry-Flag als auch das Digit-Carry
Flag und das Zero-Flag gesetzt. Das Zero-Flag wird gesetzt, wenn das Ergebnis der Subtraktion 0 ist. Während
das Carry Flag gesetzt wird, wenn das WRegister kleiner ist, als das Register von dem es abgezogen wird. Das
Digit Carry Flag wird gesetzt, wenn das untere Halbbyte des WRegisters kleiner ist, als das untere Halbbyte der
Zahl von der das WRegister abgezogen wird. Also z.B. 15-14 = 1. DC = 1.
Befehlsverschlüsselung: 00 0010 dfff ffff
Liegt der aktuelle Befehl zwischen 512 und 767, so wird der Befehl als SUBWF Befehl erkannt. In diesem Falle
wird zuerst das C und das DC Flag nach den oben genannten Kriterien ermittelt. Anschließend wird mit dem grün
markierten Bit entschieden ob ins WRegister oder ins das Herkunftsregister geschrieben werden soll. Dann wird
die Subtraktion ausgeführt und anschließend entschieden, ob das Zero-Flag gesetzt werden soll oder nicht.
Ausführungsbeispiel
Befehl: 00 0010 0010 0000
WRegister = 14 Carry = 0
Speicher 32 = 15
WRegister wird von der Speicherstelle 32 abgezogen
Komplementaddition ergibt, dass das Ergebnis = 1 und kein Übertrag im Halfcarry stattgefunden hat
Weil kein Übertrag stattgefunden hat, ist Carry gesetzt (Fehler im Mikrocontroller)
Seite 16
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
Ergebnis wird ins WRegister geschrieben
4.2.6 DECFSZ
if (aktuellerBefehl >= 2816 && aktuellerBefehl <= 3071)
{
if ((aktuellerBefehl & 128) == 128)
{
FileRegister.registerInhaltSchreiben(aktuellerBefehl & 127,
(FileRegister.registerInhaltLesen(aktuellerBefehl & 127) - 1) & 255);
FileRegister.zyklenbreite = 1;
if (FileRegister.registerInhaltLesen(aktuellerBefehl & 127) == 0)
{
FileRegister.programmzaehler++;
// Überspringen
FileRegister.zyklenbreite = 2;
}
}
else
{
FileRegister.wRegister =
(FileRegister.registerInhaltLesen(aktuellerBefehl & 127)- 1) & 255;
FileRegister.zyklenbreite = 1;
if (FileRegister.wRegister == 0)
{
FileRegister.programmzaehler++;
FileRegister.zyklenbreite = 2;
}
// Überspringen
}
}
Der Befehl DECFSZ vermindert den Wert eines im Befehl angegeben Registers um 1. Ist dieses Register danach
0, dann wird der nächste Befehl übersprungen. Sollte dies nicht der Fall sein, so wird ganz normal mit dem
nächsten Befehl weitergemacht. Abhängig von einem weiteren Bit im Befehl wird das Ergebnis der Verminderung
um 1 entweder ins WRegister oder ins Herkunftsregister geschrieben.
Befehlscode: 00 1011 dfff ffff
Um als DECFSZ Befehl vom Programm erkannt zu werden muss der aktuelle Befehl zwischen 2816 und 3071
liegen. In diesem Falle wird zuerst das grün markierte Bit abgefragt. Sollte dies der Fall sein, wird das Ergebnis in
das Herkunftsregister geschrieben. Andernfalls ins WReister. Danach wird das Register, dass durch die rot
markierten Bits aufgeschlüsselt werden kann um 1 vermindert. Solltet die darauf folgende Abfrage ergeben, dass
das Ergebnis der Subtraktion 0 ist, dann wird der nächste Befehl übersprungen. Dies wird realisiert in dem der
Befehlszähler zusätzlich um 1 erhöht wird. Letztendlich wird der Befehl dadurch zu einem 2 Zyklen langen Befehl.
Ausführungsbeispiel:
Befehl: 00 1011 0000 0100
FSR-Register = 1
Programmzähler = 10
FSR-Register wird um eins vermindert
Weil Ergebnis der Subtraktion = 0 wird der nächste Befehl übersprungen. Programmzähler = 12
Ergebnis der Subtraktion wird ins WRegister geschrieben weil 8 Bit des Befehls = 0
Seite 17
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
4.2.7 XORLW
if (aktuellerBefehl >= 14840 && aktuellerBefehl <= 15103)
{
FileRegister.wRegister = ((aktuellerBefehl & 255) ^ FileRegister.wRegister) & 255;
FileRegister.zyklenbreite = 1;
zeroFlagSetzen();
}
Mit dem Befehl XORLW kann eine Konstante die im Befehl übergeben wird mit dem WRegister per XOR
verknüpft werden. Das daraus resultierende Ergebnis wird zurück ins WRegister geschrieben.
Befehlscode: 11 1010 kkkk kkkk
Liegt der aktuelle Befehl zwischen den Werten 14840 und 15103 so wird der XORLW-Befehl als solcher erkannt.
Die Konstante die im Befehl übergeben wird (rot markiert im Befehlscode) kann maximal den Wert 255 haben.
Darum wird der aktuelle Befehl mit 255 verUNDet und danach per XOR mit dem WRegister verknüpft. Sollte das
Ergebnis dieser Verknüpfung 0 sein, dann wird dies durch die Methode zeroFlagSetzen() erkannt und das ZeroFlag wird gesetzt.
Ausführungsbeispiel:
Befehl: 11 1010 1010 1010
WRegister = 0101 0101
WRegister wird per XOR mit 1010 1010 verknüpft
Ergebnis der Verknüpfung: 1111 1111
Ergebnis wird ins WRegister geschrieben
Zero-Flag wird nicht gesetzt weil das Ergebnis nicht 0 ist
Zero-Flag = 0
4.3 Interrupts
Das Erkennen von Interrupts geschieht auf zwei Arten. Entweder so wie
bei den Interrupts von RB0 oder RB4-RB7 über den
Checkbox_Geklickt() Eventhandler, der eine Abfrage dafür bereit hält
ob die angeklickte Checkbox eine der oben genannten Checkboxen ist
oder über die Methode Timer0() die einen Überlauf des Timers erkennt
und daraufhin das T0IF Flag setzt.
Ist das GIE Flag und das Enable-Flag für die jeweiligen Interrupts
gesetzt und ein Interrupt wurde erkannt, so wird die Methode Interrupt()
in der Klasse FileRegister aufgerufen. Diese arbeitet die in Grafik 6 als
Flussdiagramm dargestellte Routine ab:
Grafik 6: Flussdiagramm
Interruptabarbeitung
Seite 18
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
4.4 Timer
Die Methode Timer0() stellt die Abfragen zur Erkennung eines Taktes zur Erhöhung des Timers bereit. Die
Methode unterscheidet zwischen Takten mit Vorteiler und ohne Vorteiler, so wie zwischen internen Takten und
externen Takten. Folgendes Flussdiagramm soll die Funktion der Methode verdeutlichen:
Grafik 7: Flussdiagramm – Timer0
Seite 19
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
4.5 Watchdog-Timer
Das Herunterzählen des Watchdog-Timers wird durch einen zweiten Timer, der unabhängig vom Timer der den
Befehlstakt vorgibt ist, bestimmt. Folgendes Struktogramm, erläutert schematisch den Ablauf beim Auslösen
eines WDTTimer_Tick Events
Grafik 8: Struktogramm – WatchDog-Timer
Seite 20
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
4.6 Ein- und Ausgabe
Bei der Ein- und Ausgabe der Werte an PortX muss zwischen zwei Prinzipien unterschieden werden. Beim ersten
wird das Ergebnis auf der grafischen Oberfläche ausgegeben, während bei der zweiten Lösung das Ergebnis an
eine Testplatine über die RS232 Schnittstelle gesendet wird.
Beide Ausgaben funktionieren nach dem selben Prinzip. Wird auf den PortX geschrieben so wird der Wert aus
der Latch-Variablen, die bei jedem Schreibbefehl auf PortX durch das zugehörige Register aktualisiert wird, Bit für
Bit mit dem dazugehörigen TrisX Register verglichen. Sollte das Tris-Register einen Pin als Eingang definieren,
so wird keine Veränderung an den Checkboxen vorgenommen. Wird ein Pin als Ausgang deklariert so wird der
Wert so wie er im Latch-Verzeichnis steht (0 für Low, 1 für High) ausgegeben.
Bei der GUI wird die Anzeige durch Checkboxen repräsentiert. Diese werden zyklisch durch eine for-schleife, die
das zuvor generierte Array von Checkboxen durchläuft, aktualisiert. D.h. dass dadurch die Latch-Funktion des
µC’s realisiert wird, denn nach jedem Zyklus wird automatisch eine Änderung am Tris-Register erkannt und die
Veränderung führt dann zu einer Veränderung der Ausgabe,
Bei der RS232 Schnittstelle ist eine Bit für Bit Überprüfung der TrisX und des LatchX nicht nötig. Stattdessen
werden beide Werte direkt über die serielle Schnittstelle übertragen. Mit Hilfe des Schreibebefehls
RS232Port.Write wird zuerst das obere Halbbyte des TRIS A Registers übersendet, gefolgt vom unteren
Halbbyte des TRIS A Registers. Danach oberes Halbbyte Latch A und anschließend unteres Halbbyte Latch A.
Das ganze wird für Port B nocheinmal wiederholt und mit einem so genannten Carriage Return beendet. Dieses
Symbol aus der ASCII-Tabelle symbolisiert der Platine das Ende des Schreibbefehls. Darauffolgend kann der
Wert von der Platine mit der RS232Port.ReadTo() Methode eingelesen werden. Diese liest alle ankommenden
Bits bis zum ersten Carriage Return. Dabei sendet die Platine die Daten in folgendem Schema:
Oberes Halbbyte Port A |
Unteres Halbbyte Port A | Oberes Halbbyte Port B | Unteres Halbbyte Port B
Zur Kommunikation mit der seriellen Schnittstelle ist es wichtig die Kommunikationsparameter anzugeben. Diese
beinhalten vor allem, die Adresse der Schnittstelle (COM 1), die Übertragungsgeschwindigkeit (4800 Bit/s), so wie
die Anzahl der Datenbits (9) und das so genannte Stopbit, dass angibt wann eine Übertragung beendet ist.
Seite 21
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
4.7 Takt
Beim Takt muss zwischen drei Arten unterschieden werden. Zum einen der Takt der in einer reellen
Hardwareumgebung mit einem Quarz erzeugt würde, und die Befehlsabarbeitungsgeschwindigkeit des
Mikrocontrollers vorgibt, zum anderen der Takt der Hardwaremäßig durch einen internen Oszillator generiert wird
und dazu dient den WDT selbst bei einem Ausfall der CPU-Funktionen noch am Leben zu erhalten und zu guter
Letzt den externen Takt der am Pin 4 des Ports A von „Hand“ generiert werden kann. um eine Taktquelle für den
Timer darzustellen.
4.7.1 Der Quarztakt
Der Quarztakt wird durch ein Objekt der Klasse Timer, die bereits standardmäßig in der C#-Bibliothek enthalten
ist, realisiert. Dieser stößt je nach Einstellung der Geschwindigkeit, alle 200-600 ms ein Event an. Dieses wird von
einem Eventhandler „aufgefangen“ und dann weiterverarbeitet. Weiterverarbeitet heißt, dass das Lesen des
nächsten Befehls beginnt (siehe prinzipieller Ablauf).
4.7.2 WatchDog-Timer Takt
Der Takt für den Watchdog-Timer wird auf gleiche Weise wie der Quarztakt generiert. Jedoch wird hierzu ein
zweites Objekt der Klasse Timer verwendet um die Quarze unabhängig voneinander zu halten, so wie es auch
der Realität entspricht. Ebenfalls anders bei diesem Timer, ist die nicht Variable Überlaufszeit. Diese beträgt 1000
ms und verhält sich wie 1µs bei einem Takt von 10 Mhz.
4.7.3 Externer Takt
Der externe Takt wird durch einen anderen Eventhandlerm als bei den vorherigen Takten realisiert. Dieser
Eventhandler wartet darauf, dass eine der im Programm durch ein Array von Checkboxen automatisch
generierten Checkboxen, geklickt wird. Kommt es hierzu, so wird der Checkbox_Geklickt() Eventhandler
angestoßen. Ein Teil dieses Eventhandlers frägt ab, ob das T0CS Bit gesetzt ist. Dies würde bedeuten, dass der
Timer auf den externen Takt reagiert. Dann ermittelt die Methode, ob es sich beim Klicken der Checkbox um eine
negative oder einen positive Taktflanke gehandelt hat und entscheidet demzufolge mit dem T0SE Bit, ob der
Timer0 erhöht werden soll oder nicht, indem er gegebenenfalls die timer0() Methode der Klasse FileRegister
aufruft
// externer Takt für TMR0
if ((sender == checkboxArray[8]) && (((FileRegister.SFR[80] & 32) == 32)))
{
// Negative Flanke vom Takt
if (((FileRegister.SFR[80] & 16) == 16) && (checkboxArray[8].Checked == false))
{
if ((FileRegister.SFR[80] & 8) == 8) // Wenn kein Vorteiler
{
FileRegister.SFR[1]++;
}
else
{
FileRegister.timer0Zyklen++;
FileRegister.timer0();
}
}
// Positive Flanke des Takts
if (((FileRegister.SFR[80] & 16) != 16) && (checkboxArray[8].Checked == true))
{
if ((FileRegister.SFR[80] & 8) == 8) // Wenn kein Vorteiler
{
FileRegister.SFR[1]++;
}
else
{
FileRegister.timer0Zyklen++;
FileRegister.timer0();
}
}
Seite 22
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
4.8 Speicher
Alle im Programm angelegten Speicher sind entweder reine
Integer-Zahlen oder Arrays von Integern. In diesem Punkt
sollen 2 Speicher und ihre Funktionen zur Bearbeitung ihrer
Inhalte, exemplarisch erklärt werden
4.8.1 Stack
Der Stack verfügt über die altbekannten Methoden push() und
pop(). Diese beiden Methoden kommen zum Beispiel bei den
Befehlen CALL und RETURN zum Einsatz. Push() legt einen
Wert auf untersten noch freien Platz auf dem Stack ab,
während Pop() den obersten Wert vom Stack herunternimmt.
Mit Hilfe der Grafik 9 soll die Push() Funktion erklärt werden.
4.8.2 SFR & GPR
Obwohl das SFR und das GPR auf zwei Bänken im Speicher
physikalisch abgelegt sind, werden sie im Programm nur durch
ein gemeinsames Array dargestellt. Dabei entsprechen die
ersten 12 Adressen der Bank 0, den ersten 12 Adressen im
Array mit dem Namen „SFR“. Die Adressen 13-79 sind für das
GPR reserviert. Die Arraystellen 80-84 dienen dazu die von
der Bank 0 verschiedenen Register (z.B. Tris und
OptionRegister) auf Bank 1 darzustellen. Diese Aufteilung ist
nur möglich, da im Mikrocontroller die Register auf den beiden
Bänken zum größten Teil gespiegelt werden. Das heißt es
macht keinen Unterschied ob in die Adresse 8CH oder 0CH
Grafik 9: Flussdiagramm – Wert auf Stack ablegen
geschrieben wird. Zur Ermittlung in welche Speicheradresse
geschrieben werden muss bzw. von welcher Speicheradresse
gelesen werden muss, dienen die Methoden RegisterInhaltSchreiben() und RegisterInhaltLesen(). Der Methode
RegisterInhaltSchreiben() muss dabei die Zieladresse, die im Befehl enthalten ist übergeben werden, so wie der
Wert der in das Register geschrieben werden soll. Danach führt diese Methode eine Fallunterscheidung durch:
Unterschieden werden dabei die Register auf Bank 0 und 1, so ist z.B. Register 5 auf Bank 0 nicht gleich Register
Bank 1 (nämlich PortA auf Bank0 und TrisA auf Bank 1). Außerdem wird zwischen direkter und indirekter
Adressierung unterschieden, sollte also das Register 0 (IND) bei einem Schreibbefehl angesprochen werden, so
wird statt in das IND-Register in das Register geschrieben deren Adresse im FSR-Register hinterlegt ist. Zu guter
Letzt ist diese Methode noch in der Lage zu erkennen, wenn auf PortX geschrieben wird und schreibt dann den
zu schreibenden Wert automatisch noch einmal in die Zwischenspeichervariablen LatchX.
Codeausschnitt:
// Indirekte Adressierung
if (f == 0)
{
return SFR[SFR[4]];
}
Einige Arraystellen kamen bei der Programmierung besonders häufig zum Einsatz. Beim Nachvollziehen des
Programmcodes ist es also sinnvoll diese zu kennen:
SFR[3] = Status Register
SFR[11] = INTCON Register
SFR[80] = Option Register
Seite 23
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
Folgendes Klassendiagramm veranschaulicht noch einmal alle angelegten Variablen und Methoden zur
Verwaltung des Speichers (rot markiert):
Grafik 10: Klassendiagram – Gesamtes Projekt
Seite 24
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
4.9 Dateien lesen / öffnen
Das Öffnen der PDF-Doku geschieht in folgenden Schritten:
1. Öffnen eines Acrobat Reader Prozesses
2. Suchen des Pfades zur Dokumentation
3. Übergabe des Pfades als Parameter für den Acrobat Reader
4. Starten des Acrobat Readers
System.Diagnostics.Process AcrobatReader = new System.Diagnostics.Process();
// Adobe Prozess starten
AcrobatReader.StartInfo.FileName =
System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Application.ExecutablePath),"Doku.pdf")
// Pfad zur Dokumentation suchen
AcrobatReader.StartInfo.WorkingDirectory = @"C:\";
AcrobatReader.Start();
// Pfad öffnen
Ein wenig anders hingegen funktioniert das Einlesen einer LST Datei in den Programmspeicher. Wählt der
Benutzer aus der Menüleiste das Item „Laden“, so wird mittels einer aus der C#-Bibliothek übergebenen Klasse,
ein Lade-Fenster geöffnet. Diesem Ladefenster kann ein Name, so wie eine Einschränkung auf bestimmte
Dateitypen folgendermaßen übergeben werden:
DateiLadenFenster.Title = "Öffnen einer LST-Datei";
DateiLadenFenster.Filter = "LST-Dateien (*.LST)|*.lst";
Hat der Benutzer eine Datei ausgewählt und mit „Öffnen“ bestätigt beginnt der eigentliche Einlesevorgang. Dieser
läuft nach dem in Grafik 11 aufgezeigten Schema ab:
Seite 25
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
Grafik 11: Flussdiagramm – Einlesen einer Datei in den Programmspeicher
Seite 26
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
5. Die Programmoberfläche
5.1 Die Menüleiste:
Die Menüleiste enthält grundlegende Funktionen wie das Laden einer Datei oder die Bestimmung der Taktrate.
•
Mit dem Menüpunkt Datei Laden kann eine LST Datei geöffnet werden. Diese muss den
Programmcode in Hexadezimaler Darstellung, Speicheradresse, Sprungmarken und AssemblerCode
enthalten um richtig interpretiert zu werden.
•
Mit dem Menüpunkt Datei Beenden kann die Applikation beendet werden
Grafik 12: Menüleiste - Datei
•
Wird der Menüpunkt: Hilfe PDF-Doku angewählt so versucht die Applikation den Acrobat Reader mit
der Datei Doku.PDF zu öffnen. Falls dies nicht möglich ist, z.B. wenn kein Acrobat Reader installiert ist,
wird eine Fehlermeldung ausgegeben.
Grafik 13: Menüleiste - Hilfe
Seite 27
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
Im Menü Einstellungen können folgende Einstellungen getätigt werden:
•
Zahlendarstellung Diese Menüpunkte verändern die Darstellung der Zahlen im SFR-Register, dem
GPR-Register, dem Stack sowie die der Vorteileranzeige, der Programmcounteranzeige, etc.
(Infoboxen).
•
Taktrate Dieser Menüpunkt verändert die Geschwindigkeit mit der die Simulation durchgeführt wird.
Dabei ist langsam eine Geschwindigkeit bei der, der Überblick über die Veränderungen von Befehl zu
Befehl behalten wird, während schnell sich eher dazu eignet das Programm so schnell wie möglich
durchlaufen zu lassen um das Endergebnis zu erhalten.
•
WatchdogAn Ist dieser Menüpunkt mit einem Haken gekennzeichnet, dann läuft im Automatischen
Ablaufmodus der WatchDog-Timer je nach Vorteilereinstellung eher schnell / bzw. langsam nach unten.
•
PlatineAn Ist dieser Menüpunkt mit einem Haken gekennzeichnet, versucht der Simulator die Pegel
an Port A und Port B über eine RS232 Schnittstelle an eine Platine zu übersenden. Vorsicht! Wird dieser
Menüpunkt gewählt, obwohl keine Platine angeschlossen ist, kann eine Fehlerfreie Ausführung des
Programms nicht garantiert werden. Durch die zeitlich begrenzte Übertragungsgeschwindigkeit der
RS232 Schnittstelle kann es insbesondere bei der Taktrate „schnell“ zu Verzögerungen im
Programmablauf kommen. Außerdem ist zu beachten, dass die Platinen gegenüber den Checkboxen
auf der Programmoberfläche Vorrang hat. D.h. liegt z.B. an der Platine an PORT A Pin 1 eine logische 1,
auf den Checkboxen wurde dieser vom Benutzer als 0 definiert, so gewinnt in diesem Falle die Platine
und der Pin wird als logisch 1 ausgewertet.
Grafik 14: Menüleiste - Einstellungen
Seite 28
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
5.2 Programmstart und Reset
Grafik 15 Button-Leiste
Die Buttons in dieser Leiste ermöglichen es das Programm je nach Wunsch automatisch oder per Einzelschritt zu
durchlaufen und stellen verschiedene Reset-Methoden zur Verfügung, um bei einem Fehler im Programm noch
einmal von vorne beginnen zu können.
•
Nächster Schritt: Wird dieser Button betätigt, wird der nächste anstehende Befehl ausgeführt
•
Automatischer Ablauf: Dies ist ein Toggle-Button. Wird er einmal betätigt beginnt das Programm mit der,
im Menü Einstellungen, eingestellten Taktrate automatisch abzulaufen. Wird der Button erneut gedrückt
stoppt der automatische Ablauf an der derzeitigen Position.
•
Prg.-ROM Reset: Da bei keinem Reset der Programmspeicher mitgelöscht wird, kann mit diesem Button
der Programmspeicher gelöscht werden
•
!MCLR: Durch das Betätigen dieses Buttons wird ein MCL-Reset durchgeführt durch den unter Anderem
der Programmzähler zurückgesetzt wird und die Register mit bestimmten Werten vorbelegt werden
•
Power-On-Reset: Durch das Betätigen dieses Buttons wird ein Power-On-Reset durchgeführt durch den
unter Anderem der Programmzähler zurückgesetzt wird und die Register mit bestimmten Werten
vorbelegt werden (unterscheidet sich in manchen Werten vom !MCLR)
Seite 29
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
5.3 Info-Box
Die Info-Box enthält grundlegende Informationen für den Benutzer, die ihm bei der Orientierung zum derzeitigen
Befehl helfen.
•
Instruction-Reg.: Hier steht welcher Befehl sich derzeit im Instruction-Register befindet, also beim
nächsten Takt ausgeführt wird.
•
W-Register: Hier steht welcher Wert sich derzeit im W-Register befindet.
•
Prg.-Counter: Der darin enthaltene Wert gibt an, auf welche Programmstelle der Programmzähler derzeit
verweist.
•
Port A Extern: Hier steht welcher Wert von Port A eingelesen würde, wenn der nächste Befehl ein Leseoder Schreibebefehl auf Port A wäre. (z.B. BSF Port A, MOV Port A, etc.) (Vorsicht: Platine hat Vorrang)
•
Port B Extern: Hier steht welcher Wert von Port B eingelesen würde, wenn der nächste Befehl ein Leseoder Schreibebefehl auf Port B wäre. (z.B. BSF Port A, MOV Port A, etc.) (Vorsicht: Platine hat Vorrang)
•
Watchdog: Dieser Wert zählt bei einem eingehenden Taktsignal des WDT-Oszillators nach unten. Ist
dieser Wert = 0 wird ein Reset durchgeführt. Der Watchdog-Timer läuft nur im automatischen Modus ab,
um den Benutzer bei einer Einzelschrittanalyse nicht in Hektik zu versetzen.
•
Vorteiler: Anzahl der Takte für den WDT oder Timer0 die kommen müssen, bis einmal der WDT oder
TMR0 verändert wird.
•
Zyklen: Zeigt an wie viele Zyklen das Programm schon läuft.
Grafik 15: InfoBox
Seite 30
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
5.4 Programmspeicher
In der Programmspeicher-Liste stehen die von der LST Datei eingelesen Werte. In der Spalte OP-Code steht
welcher Befehl, an der in der ersten Spalte angegeben Adresse, im Programmspeicher abgelegt wurde. Je
nachdem welchen Wert der Programmzähler momentan enthält, wird die derzeitig aktive Programmzeile blau
markiert und automatisch „angescrollt“. Programmveränderungen sind in dieser Anzeige nicht möglich, diese
müssen direkt in der LST Datei vorgenommen werden.
Grafik 16: Programmspeicher
5.5 Stack
Die Stackbox enthält die momentan auf dem Stack abgelegten Werte. Dabei ist der
unterste Wert, der Werte der durch pop-Funktionen als letzter vom Stack genommen
würde. Eine Veränderung der Werte im Stack über die Tabelle ist nicht möglich. Im
Beispielbild ist eine hexadezimale Zahlendarstellung zu sehen, bei der Wahl einer
anderen Darstellung würde sich die Darstellung des Stacks mit verändern.
Grafik 17: Stack
Seite 31
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
5.6 Special Function Register
In dieser Box sind die momentan in den
Registern des Special Funktion Registers
enthaltenen Werte aufgezeigt. Auch hier
würde eine Veränderung der
Zahlendarstellung, Auswirkungen auf die
Darstellung haben. Eine Veränderung der
Werte in den Registern über die Textboxen
ist nicht möglich. Dies muss durch das
Programm geschehen.
5.7 General Purpose Register
Da das General Purpose Register über mehr
Speicherstellen als das SFR-Register
verfügt, wurde hier zur Darstellung eine
tabellarische Anzeigeform gewählt. Eine
Veränderung der darin enthaltenen Werte #
ist auch hier nicht über die GUI möglich.
Grafik 18: General Purpose RAM
Box
Grafik 19: Special Funktion
Register Box
5.8 Aktive Anzeigeelemente
Grafik 20: Checkboxen als aktive Anzeige-Elemente
Die auf der Oberfläche enthaltenen Checkboxen stellen aktive Anzeigeelemente dar. Das heißt, sie spiegeln zum
einen eine Aufschlüsselung von konkreten Registern in einzelne Bits dar und ermöglichen es zum Anderen dem
Benutzer den in den Registern enthaltenen Wert zu beeinflussen.
•
Status-Reg., INTCON und Option-Reg.: Wird eine der Checkboxen in diesen Anzeigeboxen verändert
wird sofort eine Veränderung im korrespondieren Register durchgeführt. Beispiel.: Bei dem in der Grafik
angezeigten Wert des Status-Registers würde 00011001 als binärer Wert im Status-Register an Stelle
3H im Datenspeicher stehen. Würde man nun die Checkbox mit der Bezeichnung „C“ für Carry
anklicken, würde anschließend der binäre Wert 00011000 im Status Register stehen.
Seite 32
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
•
TRIS A / Port A und TRIS B / Port B: Das Anklicken einer dieser Checkboxen hat nicht zwangsläufig
eine Veränderung im dazugehörigen Register zur Folge. So wäre es zum Beispiel nicht möglich, in der
oben gezeigten Einstellung, die Checkboxen RA0-RA3 anzuklicken, weil diese als Ausgang definiert
sind. Da als Ausgang definierte Checkboxen durch den am Latch anliegenden Wert bestimmt werden
und nicht von einer von außen anliegenden Spannung, ist ein Anklicken in diesem Falle nicht möglich.
Anders ist dies bei Checkboxen die durch das TRIS-Register als Eingang definiert sind. Diese sind nach
belieben anklickbar, ziehen aber nicht automatisch eine Veränderung im dazugehörigen Register nach
sich. Lediglich die Werte in den Textboxen „Extern Port A“ und „Extern Port B“ würden sich verändern.
Eine wirkliche Übernahme in das Register würde aufgrund des „Read-Modify-Write“-Verfahrens z.B. erst
mit einem MOV Befehl auf Port A stattfinden. Aufgrund der Latchfunktion des PIC 16F84, besteht auch
die Möglichkeit das z.B. in Register Port A Pin RA4 als High Pegel definiert ist. Wenn dieser Pin aber wie
im obigen Bild als Eingang definiert ist, wird der High Pegel nach außen hin nicht sichtbar. Erst wenn die
Checkbox B4 von TRIS A, betätigt würde, würde Pin RA4 als High Pegel angezeigt werden.
5.9 Interrupts und Takt über Checkboxen
•
RB0 Interrupt: Ein Interrupt über die RB0 Checkbox ist sowohl dann möglich wenn RB0 als Eingang
definiert ist, als auch wenn er als Ausgang fungiert. Wenn RB0 als Ausgang definiert ist, wird zwar beim
Anklicken ein Interrupt ausgeführt, der Wert der durch das Anklicken an RB0 anliegen sollte, bleibt aber
nicht bestehen sondern wird durch den am Latch anliegenden Wert bestimmt. Ein Interrupt über die
Platine ist nicht möglich. Dieser muss weiterhin über die Checkboxen vorgenommen werden.
•
RB4- RB7 Interrupt: Der RB4-RB7 Interrupt kann über die Checkboxen nur dann ausgelöst werden,
wenn sie durch das TRIS-Register als Eingang definiert sind, da eine Änderung bei einem als Ausgang
definierten Pin keine Auswirkung auf den Pegel hätte.
•
Takt über Checkboxen: Über die Checkbox RA4 ist es durch Anklicken der Checkbox möglich einen
Takt für den Timer 0 zu generieren. Dies ist jedoch nur möglich wenn RA4 durch das TRIS-Register als
Eingang gekennzeichnet wurde. Dabei wird zwischen steigender und fallender Flanke unterschieden.
5.10 Typischer Anwendungsfall
Im Folgenden soll die Vorgehensweise bei einer typischen Anwendung exemplarisch vorgestellt werden. Hierbei
wird davon ausgegangen, dass der Benutzer ein Programm laden möchte und dieses schnell durchlaufen lassen
will um lediglich das Endergebnis abzulesen.
Schritt 1: Öffnen der Datei MikroprozessorSimulator.EXE (wichtig .NET Framework 2.0 benötigt)
Schritt 2: Menü Datei Laden anwählen
Schritt 3: Die gewünschte LST Datei in der Ordnerstruktur anwählen und mit „Öffnen“ bestätigen
Schritt 4: Über das Menü Einstellungen Taktrate „schnell“ die Simulationsgeschwindigkeit erhöhen
Schritt 5: Den Button „Automatischer Ablauf“ einmal anklicken
Schritt 6: Falls ein Interrupt o.ä. im Prorgamm auftreten sollte, diesen über die Checkboxen ausführen
Schritt 7: Warten bis Programm durchgelaufen und dann erneut den Button „Automatischer Ablauf“
Betätigen
Seite 33
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
6. Fazit
6.1 Schwierigkeiten und Verbesserungsvorschläge
Eine der größten Schwierigkeiten für das Projekt stellte das häufig sehr unpräzise, stellenweise sogar fehlerhafte
Datenblatt des PIC 16F84 dar. So sind z.B. die Beispiele im Befehlsverzeichnis häufig von Fehlern behaftet
(siehe z.B. IORLW). Oft werden durch die Beschreibungen nicht alle Fragen abgedeckt, die sich während der
Programmierung stellen können. So war z.B. das Latch an den I/O Pins lange Zeit unverständlich. Dort half nur
das Nachfragen bei Herrn Lehmann oder das Nachlesen in einschlägiger Literatur bzw. dem Internet
(www.sprut.de war hierbei sehr hilfreich).
Eine weitere Schwierigkeit stellte die Entwicklungsumgebung Visual Studio mit der Programmiersprache C# dar.
Dort wäre eine Einführung, z.B. in partielle Klassen (einmalig in C#) für das Verständnis sinnvoll gewesen.
Alternativ wäre es von Nutzen gewesen, nicht erst zu Beginn des 3. Semesters, sondern schon gegen Ende des
2. Semesters von diesem Projekt zu erfahren um die Möglichkeit zu haben sich längerfristig in die Materie
einzuarbeiten und eventuelle Programmierdefizite zeitnah ausgleichen zu können.
6.2 Programmdefizite
Aufgrund von Zeitmangel konnten folgende Funktionen nicht implementiert werden:
- Schreiben und Lesen auf EEPROM
- Hochwertige Grafiken für die Oberfläche
Außerdem, kommt es beim Schreiben auf die Platine häufig zu Fehlern, während die Kommunikation über ein 0Modem Kabel mit dem Platinen-Simulator PIC_VIEW keine Probleme bereitet.
6.3 Erfahrungen
Müsste das Projekt ein weiteres Mal programmiert werden, so würden wir beim nächsten Mal den Speicher von
vorne herein so implementieren, wie er jetzt implementiert ist. Zu Beginn, war dieser aufgeteilt in zwei, 2Dimensionale Arrays. Ein Array für das SFR-Register und eines für das GPR-Register. Die zweite Dimension
diente dazu jedes einzelne Bit adressieren zu können. Die einzelne Bitaufschlüsselung stellte sich bald als äußert
unpraktisch dar, da für eine Ausgabe des kompletten Registers immer erst jedes Bit addiert werden musste.
Nachdem einige Befehle implementiert waren, stellte sich auch bald heraus, dass eine Unterteilung des SFR und
GPR Registers in 2 Arrays nicht hilfreich ist, da jedes Mal beim Eintragen eines Ergebnisses in ein Register die
beiden Fälle unterschieden werden musste (doppelter Aufwand bei der Programmierung der Befehle).
Ansonsten verlief das Projekt fast reibungslos und die vorhanden Kenntnisse, in Digitaltechnik, Programmieren,
Software-Engineering, Informatik, etc. konnten vertieft werden, so wie ein grundlegender Wissensstand im
Umgang mit Visual Studio und C# erarbeitet werden.
Die Entscheidung bei der Programmierung nicht auf die Prinzipien der Objektorientierung zu achten, sollte sich
während des Projektes nicht als falsch herausstellen. Der Programmieraufwand wurde wie erwartet gesenkt oder
zumindest nicht gesteigert und Erfahrungen in der prozeduralen Programmierung konnten gewonnen werden.
Seite 34
Gruppe: Alexander Carls, Johannes Dietze
Vorlesung: Rechnertechnik
Datum:25.11.2007
„PIC 16F84 Simulationsprogramm“
7. Glossar
SFR
GPR
RAM
MSB
LSB
ROM
CPU
LIFO
µC
Special Function Register – Ein Speicherbereich im RAM des PIC
General Purpose RAM – Einer Speicherbereich im RAM des PIC
Random Access Memory – Beliebig beschreib und lesbarer Speicher
Most Significant Bit – Höchstwertiges Bit in einer Bitfolge
Least Significant Bit – Niederwertigstes Bit in einer Bitfolge
Read Only Memory – Speicher auf den nur lesend zugegriffen wird
Central Processing Unit – Hauptrechenwerk
Last in First Out – Methode zum Ablegen von Daten
Abkürzung für Mikrocontroller
8. Quellenverzeichnis
•
Datenblatt zum PIC 16F84
http://www.ortodoxism.ro/datasheets/microchip/30430c.pdf
•
Erklärungen auf
www.sprut.de
•
Taschenbuch der Elektrotechnik und Elektronik
Von Helmut Lindner
Erschienen im Fachbuchverlag Leipzig
•
Einführung in Visual C#
http://www.galileocomputing.de/openbook/visual_csharp/
Seite 35

Documentos relacionados