Student-Edition
Installation
/
Allgemeines
Ein
neues C18-Projekt mit MPLAB
Hello
World
Zuerst wird MPLAB installiert, und
anschließend
C18. Danach befindet sich C18 im MPLAB-Unterordner MCC18.
Folgende
Unterordner werden angelegt:
Header-Files
Allerdings muss auch der C-Compiler
wissen, was für ein Datentyp sich hinter diesen Bezeichnern
verbirgt.
Es ist schließlich ein Unterschied, ob es ein einzelnes Bit, ein
Byte oder ein 16-Bit-Wort ist. Deshalb ist der Datentyp für alle
diese
Bezeichner speziell für den C-Compiler in einem
C-Compiler-Header-File
noch einmal festgelegt. All diese Files (für jeden PIC-Typ
natürlich
ein eigenes File) liegen im Ordner ...\MCC18\h\ (z.B.
p18f8720.h).
Linker-Skripts
Linker-Skripts beschreiben den
Speicheraufbau
eines PICs. Sie werden vom Linker benötigt, um aus den
Object-Files
das HEX-File zu erstellen.
Bibliotheken
C18 bringt eine Reihe hilfreicher
Bibliotheken
mit. Die dienen dem einfacheren Umgang mit spezieller PIC-Hardware
(ADC,
PWM, I2C ...), mathematischen Berechnungen oder String-Manipulationen.
Eine Beschreibung aller Bibliotheksfunktionen findet sich im Dokument MPLAB-C18-Libraries_51297f.pdf
im Ordner ...\MCC18\doc\ .
Jedes C-Projekt hat ein
Quelltext-File
mit dem Namen main.c. Das sollte man nun mit MPLAB
erstellen
(File - New) und im Projektordner unter dem Namen main.c
speichern.
Nun kann man es als Source-File in das Projekt aufnehmen (rechter
Mausklick
auf Source Files). In dieses File schreibt man sein C-Programm.
Außerdem ist ein zum PIC
passendes
Linker Skript nötig (rechter Mausklick auf Linker Scripts). Im
Verzeichnis .../mcc18/lkr/
sucht man das richtige aus. |
Folgendes kleine Progrämmchen
lässt
eine LED am Pin RB0 blinken. Echter Code ist grün. Alle Kommentare
habe ich grau eingefärbt.
/** I N C L U D E S
**********************************************************/
#include <p18cxxx.h> #include "delays.h" // für die Warteschleife /** Configuration
********************************************************/ /** D E C L A R A T I O N S
**************************************************/ while(1) |
Nehmen wir das Programm einmal
auseinander.
Als erstes stehen dort zwei #include-Direktiven:
... /** I N C L U D E S **********************************************************/ #include <p18cxxx.h> #include "delays.h" // für die Warteschleife ... |
Die erste ist nötig, damit wir
die
aus dem Assembler üblichen Bezeichner (PORTA, TRISA...) verwenden
können. Eigentlich müsste ich dazu das zum jeweiligen
PIC-Typ
gehörende C-Header-File einbinden. Für den PIC18F8720
wäre
das z.B. p18f8720.h. Die Datei p18cxxx.h erledigt das automatisch. Sie
prüft, welcher Prozessortyp im Projekt ausgewählt wurde, und
bindet dann selbständig das dazu passende C-Header-File ein. Ich
hätte
aber auch schreiben können:
#include <p18f8720.h>
Ob man spitze Klammern oder
Anführungszeichen
benutzt ist hier übrigens egal. Spitze Klammer bezeichnen eine
Standard-Include-Datei (z.B. Bibliothek des Compilers). Sie wird in
allen Include-Verzeichnissen gesucht. Anführungszeichen dagegen
sind für Include-Dateien gedacht, die man selbst geschrieben hat.
Sie wird zunächst im aktuellen Verzeichnis des Projektes gesucht.
Erst wenn Sie dort nicht gefunden wird, sucht der Compiler in den
Include-Verzeichnissen weiter.
Mit der zweiten #include-Direktive
binde
ich
eine Bibliothek (delays.h) ein, aus der ich
später
eine Funktion verwenden möchte.
Es folgt die Einstellung der
Configuration
des PIC.
Die #pragma-Direktive sagt dem
Compiler, wohin im PIC das Folgende geschrieben werden soll. In diesem
Fall handelt es sich also um Configurations-Einstellungen. Microchip
hat
die Vorgehensweise beim Einfügen von Configurationsdaten in den
Quelltext
überarbeitet. Das alte
Verfahren sollte für die PIC18-Typen nicht mehr benutzt
werden.
Dafür gibt es jetzt für jeden PIC eine Reihe von
Configurationseinstellungen,
die im Dokument PIC18-Config-Settings-Addendum_51537d.pdf
aufgelistet
sind. Dieses Dokument wird bei der Installation von C18 in das doc-Unterverzeichnis
geschrieben.
Da mein PIC mit 20 MHz laufen soll,
stelle
ich den Oszillator auf HS.
Den Power-up-Timer schalte ich ein. Das
ist nicht gerade wichtig, schadet aber auch nicht.
Brown-out-Reset (Unterspannungs-Reset)
schalte ich ab. Ich brauche es nicht.
Nun kommt das Wichtigste: Der
Watchdog-Timer
muss abgeschaltet werden, sonst läuft das Programm nicht.
Zum Schluss schalte ich noch
low-voltage-programming
ab, um Probleme beim nächsten Brennen zu vermeiden.
All diese Einstellungen könnte man
auch später im Brennprogramm
P18 oder US-Burn
vornehmen,
so ist es aber bequemer.
... /** Configuration ********************************************************/ #pragma config OSC = HS //CPU=20 MHz #pragma config PWRT = ON #pragma config BOR = OFF #pragma config WDT = OFF //Watchdog Timer #pragma config LVP = OFF //Low Voltage ICSP ... |
Nun kommt das eigentliche Programm.
Da das in den Programmspeicher geschrieben
werden soll, muss zuerst die #pragma code - Direktive
stehen.
Wie in C üblich, heißt das
Hauptprogramm main und ist eine Funktion. An eine Funktion kann
man Parameter übergeben, und man kann ein Ergebnis zurück
bekommen.
Beides wird im Funktionskopf festgelegt
Ergebnis Funktionsname(Parameter)
Unsere Funktion main verzichtet auf
beides.
Deshalb schreibt man jeweils void (leer). Die eigentlichen
Befehle
der Funktion werden dann in geschweiften Klammern eingeschlossen. Den
Inhalt
der Funktion habe ich im nachfolgenden Code-Abschnitt der
Übersichtlichkeit
halber entfernt.
... /** D E C L A R A T I O N S **************************************************/ #pragma code void main(void) { . . . . }//end main |
Betrachten wir nun den Inhalt der Funktion main. Zuerst stehen dort zwei Befehle, die beim Start der Funktion (also beim Start des PIC) genau einmal durchlaufen werden. Sie dienen der Initialisierung des Ports B.
Mit dem Befehl LATB = 0x00
schreibt
man 0 in das PortB. (Man
hätte
genauso
gut PORTB = 0x00
schreiben
können.
)
Anschließend mache ich RB0 zu einem
output-Pin, indem ich das Bit 0 des Registers TRISB auf 0
setze.
... LATB = 0x00; TRISB = 0xFE; ... |
Nun kommt die Blink-Schleife (while),
die endlos
durchlaufen wird.
In der Schleife passiert immer wieder
folgendes:
Die Routine Delay10KTCYx() stammt
aus
der
delay-Library. Da wir am Programmanfang die Header-Datei dieser
Library eingebunden haben, kennt der Compiler diese Routine, und ihren
Übergabeparameter. Die delay-Library ist eine fertig compilierte
Lib-Datei
im lib-Unterordner.
Die Funktion Delay10KTCYx() ist
eine Warteschleife, ihre Laufzeit ist 10000 Zyklen x
übergebenem
Wert. Da wir 100 als Wert übergeben, ergibt sich eine Laufzeit
von 100x10000=1000000 Zyklen. 1 Million Zyklen sind 4 Millionen Takte.
Bei 20 MHz Takt ergibt sich 1/5 Sekunde Wartezeit.
... while(1) { LATB = 1; Delay10KTCYx(100); LATB = 0; Delay10KTCYx(100); }//end while ... |
Das war's auch schon. Die LED blinkt.