PIC Codebeispiele

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

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

Pinbelegungen PIC12F683/16F684/16F690/12F1822/16F1825/16F1829

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

Hardware Timer0 und WDT im PIC16F690

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


Timer1

Timer1 Hardware

Hardware Timer1 im PIC16F690

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


Timer2

Timer2 Hardware

Hardware Timer2 im PIC16F690

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


AD-Wandler

AD-Wandler Hardware

Hardware AD-Wandler im PIC16F690

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

Hardware PWM im PIC16F690

PWM Codebeispiel

Links zu Application Notes und weiterführenden Informationen zum CCP-Modul


EUSART

EUSART Hardware

Hardware EUSART im PIC16F690
Hardware EUSART im PIC16F690

EUSART TX Codebeispiel

EUSART RX Codebeispiel

Links zu Application Notes und weiterführenden Informationen zum EUSART-Modul


SSP-Modul (Synchronous Serial Port)

SPI Hardware

Hardware SPI im PIC16F690

SPI Codebeispiel

...

I2C Hardware

Hardware I2C im PIC16F690

I2C Codebeispiel

...

Links zu Application Notes und weiterführenden Informationen zum SSP-Modul



Links

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