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