PIC Codebeispiele
Diese Seite ist noch in Bearbeitung. Die Codebeispiele sind noch nicht vollständig bzw. korrekt. Ich gelobe jedoch Besserung bis zum 15.8.08 --- Naja aus zeitlichen Gründen hat es leider mit der weiteren Verbesserung doch nicht geklappt. Ich habe mir die anderen Artikel angesehen und muss sagen: "sind nen paar beeindruckende dabei." Ich wünsche Allen viel Erfolg bei dem Wettbewerb. mfg Thomas
von Thomas1123
Einleitung
Ich persönlich benutze den Hitech PIC-C Lite Compiler für meine Projekte.
Hierbei handelt es sich um die abgespeckte Version des Hitech PIC-C Compilers von der Firma HiTech Software.
Ich möchte hier eine Sammlung von Codebeispielen beginnen mit der es Einsteigern ermöglicht wird verschiedene interne und externe Pereferie zu initialiesieren und zu benutzen.
Da der PIC16F690 mein persönlicher Liebling ist möchte ich die hier gezeigten Beispiele anhand dieses PICs aufzeigen. Die nachfolgenden Beispiele sind nicht nur für den PIC16F690 vewendbar z. B. sind die Timer0-Module in vielen Pics identisch. Dies trifft auch auf die meisten anderen Module zu jedoch kann ein zusätzlicher Blick in das Datenblatt nicht schaden, da eventuell manche Register einen anderen Namen besitzen.
Selbstverständlich kann dieser Artikel noch mit Codebeispielen für cc5x und ASM erweitert werden, des Weiteren kann man auch Beispiele für andere PICs der 10er, 12er, und 16er Kategorie einfügen.
Pinbelegung
Der PIC16F690 verfügt über eine grosse Anzahl an interner Peripherie beispielsweise 10-Bit AD-Wandler, SPI/I2C Interface, mehrere Timer (2*8-Bit 1*16-Bit), EUSART (RS232 etc.) usw.
Der Nachfolger des PIC16F690 ist übrigens der PIC16F1829 mit identischer Pinbelegung, erweiterter Hardware und schnellerem internem Oszillator (bis 32 MHz).
In diesem Zusammenhang wäre noch zu erwähnen, dass Microchip auch pinkompatible Serien herausgebracht hat, die jeweils 8, 14 bzw. 20 Pins haben. Die jeweils 6 zusätzlichen Pins sind meist nur I/O-Pins oder Pins von zusätzlichen Funktionen, sodass man das Hardware-Design bei einer Platinenerweiterung nur geringfügig ändern muss.
Bei den älteren PICs sind dies z.B. PIC12F683 (8pin), PIC 16F684 (14pin) und PIC16F690 (20pin), bei den neueren Serien (C-optimiert) sind dies z.B. PIC12F1822 und PIC12F1840 (8pin), PIC16F1825 (14pin) und PIC16F1829 (20pin). Diese PICs haben allerdings nur Ports, die jeweils 6 Bit breit sind, für viele Anwendungen ist dies ausreichend.
Wer 8 Bit breite Ports braucht, z.B. weil hexadezimale Zahlen an Peripherie weitergegeben werden muss, der kann zu anderen PICs greifen. Auch heute noch gibt es den "Urvater" aller Flash-PICs, den PIC16F84(A), den sollte man aber nicht mehr verwenden, er ist langsam und teuer. Ein häufig genutzter pinkompatibler Typ ist der PIC16F88, aber auch der hat schon wieder modernere Nachfolger, die PIC16F1827 bzw. PIC16F1847, sie sind wesentlich schneller, haben erweiterte Hardware und sind für die Programmierung in C optimiert. Wer noch mehr Pins braucht, nimmt die 28- bzw. 40-pinnigen PIC16F886 bzw. PIC16F887 oder von der moderneren Serie die PIC16F1938 bzw. PIC16F1939. Viele dieser Typen gibt es auch in der sog. XLP-Ausführung mit minimiertem Stromverbrauch.
Codebeispiele
Wichtig: Wie bei den meisten µC sind die Ports der PICs beim Einschalten als hochohmige Eingänge geschaltet, zudem sind sie meist noch im analogen Modus. Es ist ein beliebter Anfängerfehler, dies zu übersehen. Man muss also häufig erst mal die Ports - dort wo nötig - in den digitalen Modus schalten und ggf. auch als Ausgänge. Hierfür werden - je nach Typ - die Register ANSEL, ANSELH, ANSELA, ANSELB etc, TRISIO, TRISA, TRISB etc. genutzt, es gibt auch Typen, die einen TRIS-Befehl hierfür benutzen. Gelegentlich muss auch noch der Comparator-Modus über das Register CMCON ausgeschaltet werden.
Auch wichtig: Die PIC-Serien 10, 12 und 16 haben einen begrenzten Adress-Bereich, der Adress-Bereich ist daher in Bänke aufgeteilt. Viele Register liegen nicht in Bank0, die jeweilige Bank muss daher vor dem Zugriff erst mal umgeschaltet werden (und danach evtl. wieder zurück). C-Compiler machen das automatisch, wer aber in Assembler programmiert, muss das alles "zu Fuss" machen.
Ein gute Einführung in die Probleme und Programmierung findet sich bei sprut
Timer0
Timer0 Hardware
Der Timer0 ist ein 8-Bit Timer/Zähler, welcher entweder mit dem internen Takt des PIC oder mit einer externen Taktquelle am Pin T0CKI (je nach Baureihe an RA2 oder RA4) Betrieben werden kann. Beim Betrieb mit einer externen Taktquelle kann man zusätzlich auswählen ob bei einer steigenden oder fallenden Flanke das Timerregister inkrementiert werden soll. Gleich welche Taktquelle gewählt wird, kann bei einem Überlauf des Timerregisters ein Interrupt ausgelöst werden. Hierfür ist es notwendig die Bits GIE und T0IE auf 1 zu setzen. Beide Bits befinden sich im INTCON-Register und sind im Datenblatt auf Seite 38 zu finden.
Nochwas: Timer0 kann mit einem asynchronen Prescaler betrieben werden. Das ist eine sehr wichtige Eigenschaft, denn der Prescaler ist SEHR schnell. Vor 15 Jahren lag seine maximale Zählfrequenz bei ca. 70 MHz (50 MHz garantiert) und bei neueren Typen mit kleineren Strukturen reicht sie mittlerweile bis ca. 150 MHz. Damit ist Timer0 für Frequenzmessungen gut geeignet. Es gibt bei Microchip eine Appnote, wo beschrieben ist, wie man trotz fehlender Auslesemöglichkeit des Prescalers trotzdem herausbekommt, welche Zahl in ihm steckt. Zusammen mit dem eigentlichen Timer gibt das direkte 16 Bit Zählerumfang und mit dem T0IF kommt man auf 17 Bit - ohne Interrupts zu gebrauchen.
Im nebenstehendem Diagramm ist der aufbau des Timer0 zusammen mit dem Watchdogtimer dargestellt.
Im Option Register (OPTION Datenblatt Seite 83) kann mit Bit 5 (T0CS) ausgewählt werden ob der interne Takt (0) oder ein externer Takt (1) verwendet wird. Bei einem externen Takt kann mit Bit 4 (T0SE) auswählen ob bei einer steigenden (0) oder bei einer fallenden Flanke (1) das Timer0-Register inkrenemtiert werden soll. Mit Bit 3 (PSA) wird der Prescaler entweder dem Timer (0) oder dem Watchdogtimer (1) zugewiesen. Der Prescaler selbst wird mit den Bits 0 bis 2 (PS0 PS1 PS2) des Option Registers eingestellt.
Eine Übersicht der Einstellmöglichkeiten ist auf Seite 83 im Datenblatt einsehbar.
Timer0 Codebeispiel mit Verwendung des Programmtaktes Fosc/4
Szenario:
Der PIC verwendet als Taktquelle einen 32,768 kHz Uhrenquarz.
An PORTB sind 8 LEDs mit Vorwiderständen gegen Masse verschaltet.
Jede Sekunde soll PORTB um den Wert 1 inkrementiert werden.
Zu diesem Zweck wird der Timer0 mit Prescaler verwendet.
Die Einstellungen des Prescaler errechenen sich wie folgt:
- [math]\displaystyle{ \frac{Fosc}{4*Prescaler*2^8}= t \, }[/math]
Fosc/4 weil der PIC für eine Anweisung 4 Takte benötigt.
Der Prescaler als zusätzlicher Teiler des Timertaktes.
2^8 (256) Schritte benötigt der Timer 0 zum Überlauf.
- [math]\displaystyle{ \frac{32768Hz}{4*2^8*32}= \frac {1}{Sek}\, }[/math]
Bei einem Prescaler von 1:32 lösen wir somit exakt jede Sekunde einen Interrupt aus.
//------------------------------------------------------------------------
#include "htc.h"
__CONFIG (0x30e0);
//------------------------------------------------------------------------
static void interrupt global_int(void)
{
GIE = 0; // Alle Interruptquellen ausschalten
T0IF = 0; // Timer0 Interruptflag Löschen
PORTB++; // PORTB inkrementieren
GIE = 1; // Interrupts wieder einschalten
}
//------------------------------------------------------------------------
void t0_ini ()
{
// Datenblatt Seite 83
OPTION = 0b11000100;
// 1------- (Betrifft nicht Timer0)
// -1------ (Betrifft nicht Timer0)
// --0----- Internen Takt für Timer0 verwenden
// ---0---- Bei externem Takt an steigender Flanke inkrementieren
// (in diesem Fall egal ob 1 oder 0 weil der interne Takt verwendet wird)
// ----0--- Prescaler für Timer0 verwenden
// -----100 Prescaler 1:32
T0IF = 0; // Interruptflag von Timer0 löschen
GIE = 1; // Alle nichtmaskierten Interrupts erlauben
T0IE = 1; // Timer0 Interrupt erlauben
}
//------------------------------------------------------------------------
void setup ()
{
ANSEL = 0b00000000; // Pins als digital I/O
ANSELH = 0b00000000; // Pins als digital I/O
PORTA = 0; // Latch von Port A,B und C löschen
PORTB = 0;
PORTC = 0;
TRISA = 0b00000000; // Port A,B und C als Ausgänge schalten
TRISB = 0b00000000;
TRISC = 0b00000000;
}
//========================================================================
void main ()
{
setup();
t0_ini();
while(1);
}
Timer0 Codebeispiel mit Verwendung eines externen Taktes
Der Code ist fast identisch, es wurden lediglich Änderungen im OPTION-Register vorgenommen und der Pin 17 (RA2) wurde als Eingang definiert. Weiters wurde der interne Oszillator mit 4MHz verwendet.
Wichtig: Der externe Takt kann nicht beliebig schnell sein. siehe Datenblatt Kapitel 5.1.5 Seite 82 und Kapitel 17 Seite 243.
Szenario
Der PIC verwendet als Taktquelle den internen 4MHz Oszillator.
An PORTB sind 8 LEDs mit Vorwiderständen gegen Masse verschaltet.
PORTB soll bei jedem Timer0-Überlauf um den Wert 1 inkrementiert werden.
Zu diesem Zweck wird der Timer0 mit Prescaler verwendet.
Die Einstellungen errechenen sich wie folgt:
- [math]\displaystyle{ \frac{Takt_{T0CKI}}{Prescaler*2^8}= t \, }[/math]
Gehen wir mal davon aus, dass der takt an T0CKI 100kHz beträgt und der Prescaler als 1:2 Vorteiler eingestellt ist. Somit läuft der Timer0 alle 512 Takte über.
- [math]\displaystyle{ \frac{100kHz}{2*2^8}= \frac{195,3125}{Sek} \, }[/math]
Bei diesen Einstellungen bekommen wir 195,3125 Timer0-Überläufe pro Sekunde was wiederum einem Zeitinterwall von 5,12ms entspricht.
//------------------------------------------------------------------------
#include "htc.h"
__CONFIG (0x30e4);
//------------------------------------------------------------------------
static void interrupt global_int(void)
{
GIE = 0; // Alle Interruptquellen ausschalten
T0IF = 0; // Timer0 Interruptflag Löschen
PORTB++; // PORTB inkrementieren
GIE = 1; // Interrupts wieder einschalten
}
//------------------------------------------------------------------------
void t0_ini ()
{
// Datenblatt Seite 83
OPTION = 0b11000100;
// 1------- (Betrifft nicht Timer0)
// -1------ (Betrifft nicht Timer0)
// --1----- Takt an T0CKI für Timer0 verwenden
// ---0---- Bei externem Takt an steigender Flanke inkrementieren
// ----0--- Prescaler für Timer0 verwenden
// -----000 Prescaler 1:2
T0IF = 0; // Interruptflag von Timer0 löschen
GIE = 1; // Alle nichtmaskierten Interrupts erlauben
T0IE = 1; // Timer0 Interrupt erlauben
}
//------------------------------------------------------------------------
void setup ()
{
ANSEL = 0b00000000; // Pins als digital I/O
ANSELH = 0b00000000; // Pins als digital I/O
PORTA = 0; // Latch von Port A,B und C löschen
PORTB = 0;
PORTC = 0;
TRISA = 0b00000100; // RA2 als Eingang setzen und restliche Pins als
// Ausgang setzen.
TRISB = 0b00000000;
TRISC = 0b00000000;
}
//========================================================================
void main ()
{
setup();
t0_ini();
while(1);
}
Links zu Application Notes und weiterführenden Informationen zum Timer0
- Microchip Timer Tutorial (Part 1) PDF
- Microchip Timer Tutorial (Part 2) PDF
- Microchip Timer0 Manual PDF
- AN592 Frequency Counter Using PIC16C5x
- AN590 A Clock Design Using the PIC16C54 for LED Displays and Switch Inputs
- Erklärung zum Timer0 auf der Homepage von Sprut
- Timer0 auf PIC-Projekte.de
Timer1
Timer1 Hardware
Der Timer1 ist ein 16-Bit Timer/Zähler. Für den Timer1 gibt es viele Verwendungsmöglichkeiten auf Grund seiner zahlreichen Einstellmöglichkeiten. Man kann Ihn als einfachen Timer verwenden, wofür er eigentlich schon fast zu schade ist, oder als raffinierten Zähler, welcher erst bei einem bestimmten Ereignis wie z. B. dem auslösen des Komparators oder eines bestimmten zustandes am TG1-Pin (RA4/Pin 3) zu zählen beginnt.
Die 16 Bit des Timers teilen sich auf zwei 8-Bit-Register auf, TMR1H (obere 8 Bit) und TMR1L (untere 8 Bit).
Der Timer1 wird zusätzlich noch vom CCP-Modul verwendet, insbesondere vom Capture- und vom Compare-Modul. Für das PWM-Modul wird der Timer2 als Zeitbasis verwendet.
Timer1 Codebeispiel mit Verwendung des Programmtaktes Fosc/4
Szenario
Der PIC verwendet als Taktquelle den internen Oszilator mit 8MHz.
An PORTB sind 8 LEDs mit Vorwiderständen gegen Masse verschaltet.
//------------------------------------------------------------------------
#include "htc.h"
__CONFIG (0x30e4);
//------------------------------------------------------------------------
static void interrupt global_int(void)
{
GIE = 0; // Alle Interruptquellen ausschalten
TMR1IF = 0; // Timer1 Interruptflag Löschen
PORTB++; // PORTB inkrementieren
GIE = 1; // Interrupts wieder einschalten
}
//------------------------------------------------------------------------
void t1_ini ()
{
TMR1IF = 0; // Interruptflag von Timer1 löschen+
// Datenblatt Seite 88
T1CON = 0b00110000;
// 0------- Timer1 Gate-invertier-Bit (in diesem Beispiel uninteressant)
// -0------ Timer1 Gate-enable-Bit (auf 0 gesetzt sodass der timer1
// mit dem TMR1ON-Bit direkt ein und ausgeschaltet werden kann
// --11---- Prescaler auf 1:8 einstellen
// ----0--- Low Power Oszillator aus
// -----0-- (in diesem Beispiel uninteressant weil Bit 1 == 0)
// ------0- interen Takt verwenden
// -------1 Timer1 einschalten
GIE = 1; // Alle nichtmaskierten Interrupts erlauben
PEIE = 1; // Pereferieinterrupt erlauben
// Die Bits GIE und PEIE befinden sich in INTCON Register
// und sind im Datenblatt auf Seite 38 erläutert.
TMR1IE = 1; // Timer1 Interrupt erlauben
// Das TMR1IE-Bit ist im ist im PIE1 Register
// und im Datenblatt auf Seite 39
}
//------------------------------------------------------------------------
void setup ()
{
OSCCON = 0b01110000; // 8 MHz Datenblatt Seite 48
ANSEL = 0b00000000; // Pins als digital I/O
ANSELH = 0b00000000; // Pins als digital I/O
PORTA = 0; // Latch von Port A,B und C löschen
PORTB = 0;
PORTC = 0;
TRISA = 0b00000000; // Port A,B und C als Ausgänge schalten
TRISB = 0b00000000;
TRISC = 0b00000000;
}
//========================================================================
void main ()
{
setup();
t1_ini();
while(1);
}
Links zu Application Notes und weiterführenden Informationen zum Timer1
- Microchip Timer1 Manual PDF
- AN580 Using Timer 1 in Asynchronous Clock Mode
- AN582 Low-Power Real Time Clock
- AN649 Yet Another Clock Featuring the PIC16C924
- Erklärung zum Timer1 auf der Homepage von Sprut
- Timer1 auf PIC-Projekte.de
Timer2
Timer2 Hardware
Der Timer2 ist ein 8-Bit Timer. Mit dem Timer2 lässt sich nahezu jede Zeitbasis einstellen da er über einen Prescaler von 1:1, 1:4, 1:16 und über einen Postscaler von 1:1 bis 1:16 verfügt. Des Weiteren verfügt der Timer2 über einen Comparator, welcher kontinuirlich den Wert des Timer2-Registers und des PR2-Registers vergleicht und bei Übereinstimmung einen Takt an den Postscaler weiter gibt. Wenn der Postscaler überläuft wird ein Timer2 Interrupt ausgelöst.
Der Timer2 wird vom CCP-Modul für die PWM verwendet und ist somit nicht immer frei zur verfügung.
Timer2 Codebeispiel
Links zu Application Notes und weiterführenden Informationen zum Timer2
- Microchip Timer2 Manual PDF
- AN549 Using the CCP Module(s)
- AN600 Air Flow Control Using Fuzzy Logic
- AN643 Adaptive Differential Pulse Code Modulation using the PIC16/17
- Erklärung zum Timer2 auf der Homepage von Sprut
- Timer2 auf PIC-Projekte.de
AD-Wandler
AD-Wandler Hardware
AD-Wandler Codebeispiel
Links zu Application Notes und weiterführenden Informationen zum AD-Wandler
Comparator
Comparator Hardware
Comparator Codebeispiel
Links zu Application Notes und weiterführenden Informationen zum Comparator-Modul
CCP-Modul (Compare/Capture/PWM)
Compare Hardware
Compare Codebeispiel
PWM Hardware
Capture Hardware
Capture Codebeispiel
PWM Codebeispiel
Links zu Application Notes und weiterführenden Informationen zum CCP-Modul
EUSART
EUSART Hardware
EUSART TX Codebeispiel
EUSART RX Codebeispiel
Links zu Application Notes und weiterführenden Informationen zum EUSART-Modul
- Microchip USART Manual PDF
- AN547 Serial Port Utilities Implementing Table Read and Table Write
- AN964 Using the EUSART on the PIC16F688
SSP-Modul (Synchronous Serial Port)
SPI Hardware
SPI Codebeispiel
...
I2C Hardware
I2C Codebeispiel
...
Links zu Application Notes und weiterführenden Informationen zum SSP-Modul
- Microchip SSP Manual PDF
- Microchip MSSP Manual PDF
- AN578 Use of the SSP Module in the IIC Multi-Master Environment
- AN554 Software Implementation of I²C Bus Master
- AN734 Using the PICmicro SSP for Slave I2C Communication
- AN736 An I2C Network Protocol for Environmental Monitoring
- IIC auf PIC-Projekte.de
- SPI auf PIC-Projekte.de
Links
- Homepage Microchip
- Microchip Mid-Range Reference Manual PDF
- Produktseite PIC16F690
- Datenblatt PIC16F690 PDF
- HiTech Software
- Sprut -> DIE deutschsprachige Seite zum Thema PIC
- PIC-Projekte.de PIC-Tutorial, Tutorial zum Programmieren in C, viele Projekte mit Codeerklärungen und ein deutsches PIC-Forum
- ...
Siehe auch
TODO
- Den Code auf dieser Seite "einklappbar" machen, sodass nicht ewig weit gescrollt werden muss
- Die Bilder neu malen wegen eventuellem Copyright-Schnickschnack
- Timer1
- Codebeispiel mit externer Taktquelle
- Codebeispiel mit externem Quarz
- Codebeispiel mit T1G als Zählbeginn/ende
- Codebeispiel mit Comparator als Zählbeginn/ende
- Codebeispiel mit Synchronem Zähler
- Timer2
- Erklärung Hardware
- Codebeispiel
- AD-Wandler
- Erklärung Hardware
- Codebeispiel
- Codebeispiel mehrere Kanäle
- CCP-Modul
- PWM
- Erklärung Hardware
- Codebeispiel
- Komparator
- Erklärung Hardware
- Eusart
- Erklärung Hardware RX
- Erklärung Hardware TX
- Codebeispiel RX
- Codebeispiel TX
- Codebeispiel RX-TX
- SPI
- Erklärung Hardware
- Codebeispiel senden
- Codebeispiel empfangen
- I2C
- Erklärung Hardware
- Codebeispiel senden
- Codebeispiel empfangen
- Brown Out Reset
- Erklärung Hardware
- Codebeispiel
- EEPROM
- Erklärung Hardware
- Codebeispiel