Ihr solltet unbedingt auch mal einen Blick in das Handbuch zum XC8-Compiler werfen. Mircochip hat eine neue Serie von Compilern veröffentlicht (2012). Dabei handelt es sich um drei verschiedene, wobei es sich in diesem Artikel um den relevanten für den Hobbybereich drehen wird: Der XC8.

• XC8   Unterstützung für: PIC10, 12, 16 und 18
• XC16  Unterstützung für: PIC24 und dsPIC
• XC32  Unterstützung für: PIC32

Hier ist die Zahl im Namen des Compilers XC# ausschlaggebend dafür, für welchen PIC Typen der Compiler gedacht ist. Und zwar beziehen sich die Zahlen auf die Datenbreite der Controller.

8  Bit Datenbreite  PIC10, 12, 16 und 1816 Bit Datenbreite  PIC24 und dsPIC 
• 32 Bit Datenbreite  PIC32

Neu ist hier, dass es nun einen Compiler gibt, der die gesamten PIC Typen mit einer 8 Bit Datenbreite bedient. Zuvor gab es lediglich den MCC18 Compiler, welcher lediglich für PIC18 Typen war und diverse Third Party Compiler. Nun gibt es alles aus eigenem Hause.

Bibliotheken

Anders als noch beim C18-Compiler, werden die Bibliotheken für den PIC-Mikrocontroller nicht gesondert geladen, stattdessen wird nur eine einzige Bibliothek für alle PIC-Typen verwendet (siehe hierzu auch Seite 25 im XC8 User Guide). Bitte beachtet, dass ihr die xc.h immer einbinden müsst.

#include <p32xxxx.h>

// So war es beim MCC18 Compiler
#include <p30fxxxx.h>
#include <p33Fxxxx.h>
#include <p24Fxxxx.h>
#include "p30f6014.h"

// Und so ist es nun beim XC8
#include <xc.h>

Konfiguration

Nachfolgend einmal dargestellt wie die Konfiguration eingestellt werden kann:

#pragma config WDT=ON, WDTPS = 0x1A

Sehr komfortabel geht das setzen der Konfigurationsbits über die IDE, siehe:

Interrupt service Routine (ISR)

Ein kurzes Code-Beispiel soll das Prinzip für das Schreiben einer Interrupt-Service-Routine verdeutlichen. Das Schlüsselwort low_priority leitet die Service-Routine für jene Interrupts mit niedriger Priorität ein.

void interrupt isr_HIGH_Prio(void)
{
   // ...
}

/* Beziehungsweise ein Interrupt mit niedriger Priorität */

void interrupt low_priority isr_LOW_Prio(void)
{
   // ...
}

Compiler Libraries

Natürlich hat man auch mit dem XC8 Compiler die Möglichkeit die mitgelieferten Bibliotheken des Compilers zu nutzen um Beispielsweise die I2C Funktionen des PIC komfortabel nutzen zu können. An dieser Stelle möchte ich nun kurz zeigen, wie Ihr dazu vorgehen müsst. Zuerst wird die folgende Headerdatei eingebunden:

#include <plib.h>

Ein Blick in diese Headerdatei zeigt, dass diese Headerdatei alle Peripherie-Headerdateien einbindet. Nun muss man noch deutlich machen, welche man davon nutzen möchte. Das macht man mit einem Makro.

  1. Die Headerdatei plib.h einbinden
  2. Diese Headerdatei öffnen (in MPLABX Str gedrückt halten und auf den Namen der Datei klicken)
  3. Die gewünschte Peripherie Headerdatei öffnen (z.B.: i2c.h)
  4. Die in dieser Datei eingebundene Headerdatei pconfig.h öffnen
  5. In der Datei pconfig.h mit Textsuche nach dem verwendeten PIC suchen (Verwendet: PIC18F2320 suchen nach: 18F2320)
  6. Nun das notwendige Makro suchen, kopieren und vor dem Einbinden der plib.h einfügen

Hier ein Beispiel um bei einem PIC18F2320 die i2c.h Datei einzubinden:

#define I2C_V1 // Erst das Makro setzten, ..

#include <plib.h> // ..dann die Headerdatei einbinen!

Falls Ihr diese Fehlermeldung bekommt: Can’t open include file “adc.h”: No such file or directory

Bitte wie folgt vorgehen:

  1. Rechtsklick auf euer Projekt und im Kontextmenü Properties auswählen
  2. Im Fenster Categories unter den Einstellungen für den XC8 auf XC8 Compiler klicken
  3. Nun im Fenster rechts auf den kleinen Button bei Include directories klicken
  4. Hier nun einen neuen Pfad anlegen. Dieser sollte ähnlich wie dieser aussehen:
..\..\..\..\Program Files (x86)\Microchip\xc8\v1.20\include\plib

Der Compiler sollte nun wissen, wo er nach den entsprechenden Headerfiles zu suchen hat und folglich keine Fehlermeldung mehr ausgeben.

Interrupts

Hier ein kleines Beispiel zum Verwalten von Interrupts mit Hilfe von vordefinierten Makros:

ADIE = 1; // A/D interrupts will be used

PEIE = 1; // all peripheral interrupts are enabled
ei(); // enable all interrupts
// ...
di(); // disable all interrupts

Interrupts dürfen nicht während der Abarbeitung einer ISR wieder freigegeben werden! Dies erledigt der Compiler selbstständig.

Warteschleifen

Selbstverständlich sind auch weiterhin fertig eingebaute Warteschleifen vorhanden. Diese werden nun etwas komfortabler genutzt. Wie das von statten geht, möchte ich euch hier kurz zeigen; Zunächst müsst ihr (wie immer) die xc.h Datei in eurer Projekt eingebunden haben. Im Anschluss müsst ihr noch mitteilen, mit welche Frequenz der PIC getaktet wird. Ist dies getan, können die Warteschleifen generiert werden. Siehe hierzu auch Seite 232 im XC8 User Guide.

Hier ein entsprechendes Codebeispiel:

#include <xc.h>

#define _XTAL_FREQ 4000000 /*Clock frequency in Hz*/

//...
__delay_ms(x); // request a delay in milliseconds
__delay_us(x); // request a delay in microseconds
//...

Bei der Verwendung der Delay-Funktionen muss darauf geachtet werden, dass je nach eingestellter Frequenz (XTAL-Makro) der Übergabewert nicht zu groß werden darf. Der Compiler macht euch jedoch mit einem Error darauf Aufmerksam, falls es so sein sollte. Das einfachste wäre wohl, eine Funktion zu schreiben, die die gewünschte Wartezeit entgegennimmt und entsprechend häufig die Delay-Funktion aufruft. Ganz allgemein sollte ein Programm ohnehin immer mit so wenig Warteschleifen wie möglich auskommen, da hierbei wertvolle Rechenzeit des Controllers “verbrannt” wird.

Zugriff auf den EEPROM-Speicher

Mitunter ist es erforderlich, dass Daten auch nach einem möglichen Verlust der Versorgungsspannung des Controllers erhalten bleiben. Dazu müssen diese Daten in das EEPROM gespeichert werden. Beim Umgang mit dem EEPROM müssen allerdings auch ein paar Dinge beachtet werden. Das Beschreiben einer EEPROM-Zelle dauert wesentlich länger im Vergleich zu normalen Speicherzellen. Davon bekommt der Programmierer jedoch in der Regel nichts mit, da das Abspeichern lediglich vom Programmierer angestoßen wird und dann im Hintergrund abläuft. Weitaus wichtiger ist die Angabe der Lebenszeit einer EEPROM-Zelle. Solch eine Zelle kann (garantiert) 100.000 mal beschrieben werden. Es sollte also genau kalkuliert werden, welche Daten in das EEPROM kommen und ob es wirklich notwendig ist. Falls man sich dazu entschließt Daten im EEPROM abzulegen, dann sollte man sich der 100.000 garantierten Schreibzyklen bewusst seien und kalkulieren ob diese ausreichen werden.

Beispiel am XC8:

#include <xc.h> 
int main ()
{
   volatile unsigned char value = 0x09;
   unsigned char address = 0xE5;
   
   // Writing value 0x9 to EEPROM address 0xE5 
   eeprom_write(address, value);

   // Reading the value from address 0xE5
   value = eeprom_read (address);
}

5 Responses

Leave a Comment