PIC18 Tutorial – Interrupt, Konfig und ICSP

Oftmals kommt es vor, dass der PIC auf ein bestimmtes Ereignis sofort reagieren soll. Nun verwendet man hierfür als Anfänger gerne mal das so genannte “Polling”. Der Mikrocontroller soll, sobald an einem bestimmten Eingang eine Zustandsänderung eintrifft (zum Beispiel durch einen betätigten Taster oder Schalter) eine LED eingschalten werden. Hierfür gibt es zwei Lösungsmöglichkeiten: Eine sehr leichte “Quick & Dirty” und eine elegante und Rechenzeit optimierte. In diesem Artikel zeige ich euch die Vorteile von Interrupts und wie ihr sie speziell mit PIC-Mikrocontrollern verwenden könnt um eure Programme wesentlich effizienter zu gestalten.

Polling

Beim sogenannten Polling fragt der Mikrocontroller immer wieder eine Bedingung ab, solange bis diese wahr wird und er eine entsprechende Aktion ausführt. An unserem Beispiel würde dies bedeuten, dass der Controller fortwährend den Eingangspegel seines I/O kontrolliert um zu prüfen ob der Taster betätigt wurde, der letztendlich bewirken soll, dass eine LED eingeschaltet wird. Dieser Vorgang ist sehr einfach und dementsprechend auch super simpel zu programmieren. Der nachfolgende Programmablaufplan (PAP) zeigt das Polling-Verfahren:



Dieses Verfahren hat jedoch einen entscheidenden Nachteil: Während der Mikrocontroller immer wieder prüft ob sich der Eingangspegel des I/O geändert hat, kann er stattdessen keine anderen Aufgaben mehr ausführen. Das Pollen ist also einfach aber auch sehr ineffizient, da es eine Menge an Rechenzeit vergeudet. Wie man dieses Problem wesentlich besser lösen kann wollen wir uns im Folgenden anschauen.

Interrupt

Mit einem Interrupt wäre dieses Problem deutlich eleganter gelöst. Wir setzten gewisse Steuerbits, welche bewirken, dass sobald sich der Zustand am Eingang ändert, der PIC seinen aktuellen Befehl noch ausführt und im Anschluss sofort zu einer bestimmten Adresse springt und den Code abarbeitet den er dort findet. Es wird also im Prinzip ein Unterprogramm automatisch aufgerufen. Der große Vorteil daran ist, dass wir während wir auf die Zustandsänderung am Eingang warten trotzdem parallel noch weiter arbeiten können! Die nachfolgende Grafik sollte das beschriebene Verhalten verdeutlichen:



Erläuterung des PAP: Wenn ihr euch den letzten PAP zum Interrupt anseht, könnt ihr feststellen, dass der normale Programmablauf durch das Auftreten eines Interrupts unterbrochen wird. An unserem Beispiel hätte ein Druck auf einen Taster den Interrupt ausgelöst und innerhalb der Interrupt-Service-Routine würde nun unsere LED eingeschaltet werden ohne, dass wir ständig überprüfen mussten ob der Taster gedrückt wurde. Im Anschluss wird der normale Programmablauf fortgesetzt.

Interrupt-Quellen

Der PIC kann natürlich nicht nur beim Ändern des Zustandes an einem Eingang einen Interrupt auslösen. Es gibt diverse Möglichkeiten, die einen Interrupt auslösen können. Ihr könnt die Interrupt Quellen des PIC im Kapitel Device Overview oder im Kapitel Interrupts (siehe Interrupt Logic) im Datenblatt nachlesen. Auf der nachfolgenden Grafik sind die Quellen aufgelistet, die einen entsprechenden Interrupt auslösen können:

Quelle: Datenblatt des PIC18F13K22

Interrupt on change

Dieser Interrupt wurde oben schon angesprochen. Er löst aus, sobald an einem Pin ein Zustandswechsel auftritt, dabei unterscheidet man zwischen fallender und steigender Flanke des Signales. Wenn ein solches Ereignis eintritt wird das zugehörige INTxIF Bit gesetzt, dass, wenn zusätzlich INTxIE gesetzt wurde zu einem Interrupt führen kann (sofern wie bei allen Interrupts die globalen Enable Bits GIEH bzw. GIEL gesetzt wurden). Beim dieser Interrupt-Quelle gilt es schon beim Schaltungsdesign darauf zu achten, dass nur einige wenige Pins für diesen Interrupt geeignet oder besser gesagt vorgesehen sind. Ihr erkennt diese an der Doppelbelegung mit INTx wobei x mit einer entsprechenden Zahl zu ersetzen ist (z.B. INT1).

Timer Interrupt

Dieser Interrupt wird ausgelöst, sobald ein Timer überläuft. Sprich, sobald der Zähler wieder bei 0 anfängt zu zählen, wird das TMRxIF Flag des Timers, der gerade übergelaufen ist gesetzt und der Interrupt ausgelöst, wenn der Interrupt über das TMRxIE Bit enabled wurde (und wie bei allen Interrupts die globalen Enable Bits GIEH bzw. GIEL gesetzt wurden). Timer Interrupts sind sehr nützlich um beispielsweise Warteschleifen zu erzeugen, bei denen der Controller nicht stillsteht, sondern anderweitige Aufgaben durchführen kann. Der Timer-Interrupt ist wahrscheinlich der wichtigste Interrupt überhaupt.

EUSART

Auch das serielle Interface kann einen Interrupt auslösen. Wenn das Modul ein Datenpaket empfängt und dieses in den Buffer lädt, kann ein Interrupt ausgelöst werden, so dass der Controller die empfangenen Daten sofort auslesen kann. Beim Eintreffen eines Datenpaketes wird das RXxIF Bit gesetzt. Wenn nun zusätzlich das RXxIE Bit gesetzt wurde, kann das Modul einen Interrupt auslösen (sofern wie bei allen Interrupts die globalen Enable Bits GIEH bzw. GIEL gesetzt wurden). Darüber hinaus kann auch ein TX-Interrupt ausgelöst werden: Wenn der Sendebuffer leer ist, da die Daten vollständig gesenet wurden, wird das TXxIF Bit gesetzt. Wenn zusätzlich das TXxIE Bit gesetzt wurde, kann dies zu einem Interrupt führen (siehe wieder GIEH bzw. GIEL). Der Programmierer kann so schnell auf einen leeren Sendebuffer reagiere und weitere Daten aussenden.

Auswahl weiterer Interruptquellen

Anbei eine Auswahl weiterer Interruptquellen ohne Anspruch auf Vollständigkeit und ohne detaillierter Beschreibung. Der nachfolgend aufgeführten Interruptquellen sind nicht zwingend bei jedem PIC vorhanden.

  • ADC – Fertigstellen einer A/D Umsetzung
  • MSSP – Datenübertragung abgeschlossen / Buskollision aufgetreten
  • CCP – Ein Capture/Compare Ereignis ist aufgetreten
  • OSC – Wenn der ext. Takt versagt (wechsel auf int. Takt)
  • EEPROM – Schreiboperation zum EEPROM abgeschlossen
  • HLVD – Unterspannung erkannt

Konfiguration von Interrupts

Natürlich funktionieren die Interrupts nicht “einfach so” ohne jegliches Zutun. Interrupts sind im Normalfall deaktiviert bzw. verboten. Damit man einen Interrupt benutzen kann, muss man das entsprechende Enable-Bit setzten, welches den Interrupt zulässt. Außerdem muss man eine globale Freigabe für Interrupts freigeben, man muss das GIE Bit setzten. Dieses Bit ist das Global-Interrupt-Enable-Bit und verbietet beim Zustand 0 alle Interrupts. Das bedeutet jedoch nicht, dass im Umkehrschluss, wenn dieses Bit gesetzt ist, dass alle Interrupts erlaubt sind. Jeder Interrupt hat ein weiteres/eigenes Enable Bit (z.B. für TIMERx TMRxIE).

Außerdem hat jeder Interrupt ein eigenes Flag, welches das Auftreten eines Interrupts signalisiert. Zum Beispiel wird bei einem TIMERx Überlauf das Bit TMRxIF Bit gesetzt, somit weiß der PIC, dass der TIMERx einen Interrupt auslösen möchte, dieses wird aber nur dann wirklich ausgelöst, wenn das zugehörige Enable Bit TMRxIE Bit ebenfalls auf 1 gesetzt ist und zusätzlich das GIE Bit Interrupts global überhaupt zulässt.

Bei den PIC18F-Typen werden verschiedene Interrupt Prioritäten unterschieden (Low & High). Diese haben auch zwei unterschiedliche Interrupt-Vektoren. Daher gibt es bei den PIC18F-Typen auch zwei globale Enable-Bits. Einmal das GIEL und das GIEH Bit. Ist das GIEL Bit auf disable, so können keine Interrupts, welche als “Low priority” deklariert sind ausgeführt werden – “high priority” Interrupts hingegen schon, sofern das GIEH Bit auf enable steht. Ist hingegen das GIEH Bit aus disable, können weder Low noch High- priorisierte Interrupts ausgeführt werden. Wir stellen also fest, dass das GIEH Bit übergreifend auch die niedrig priorisierten Interrupts sperren kann aber nicht umgekehrt! Für verschiedene Interrupt Prioritäten müsst Ihr das Bit IPEN im Register RCON setzten.

Die Priorität eines Interrupts wird mit dem zugehörigen Priority-Bit eingestellt (z.B. TMRxIP für die Priorität eines TIMERx Interrupts). Diese sind auf verschiedene Register verteilt. Man findet jedoch mit Hilfe des Datenblatts sehr schnell heraus, wo das entsprechende Bit zu finden ist. Wird ein Priority-Bit gesetzt, so hat der zugehörige Interrupt eine hohe Priorität. Wird das Bit hingegen gelöscht, so ist die Priorität niedrig. Ein Interrupt mit einer niedrigen Priorität kann von einem Interrupt mit hoher Priorität unterbrochen werden! Umgekehrt st das logischerweise nicht möglich.

Das Konfigurationswort

Das Konfigurationswort eines PIC gibt spezielle Einstellungen an, welche für den PIC zum Arbeiten unabdingbar sind. Das entscheidende hierbei ist, dass der PIC dieses Wort (auch als Fuses bezeichnet) nicht selber verändern kann. Wenn die Fuses einmal beim Brennen gesetzt sind bleiben sie so. Man legt durch die Fuses grundlegende Hardware Einstellungen fest, welche angegeben werden müssen. Man kann das Konfigurationswort entweder manuell in den Programmcode eingeben, oder aber setzt die gewünschten Option in der IDE (siehe meinen Artikel zu MPLABX IDE).

Erklärungen des Codes

Hier sind einmal die für den Einstieg in die PIC µC Welt wichtigsten Einstellungsmöglichkeiten in einer Tabelle aufgelistet:

FUNKTION KURZBESCHREIBUNG
Taktgenerator Gibt an mit welcher Frequenzkategorie der PIC arbeitet
Power up Timer Kann einen verzögerten Start des PIC hervorrufen (sinnvoll)
Watchdog Timer Kann einen Reset auslösen, wenn der PIC abstürzt
Code protection Wer seinen Code vor Spionage schützen möchte
MCLR Hier kann die Reset Funktion deaktiviert werden (bringt einen I (Input) Pin mehr
LV Programming Kann eine Programmierung mit 5V ermöglichen (kostet einen IO-Pin)
Brown out Reset Kann einen Reset auslösen, wenn die Betriebsspannung einbricht

Taktgenerator

Bei dieser Option müsst ihr den richtigen Taktbereich auswählen, damit der PIC weiß, mit welcher Frequenz gearbeitet wird. Ihr findet die Entsprechende Einstellung im Datenblatt. Hier ein Überblick:

LP 32 kHz – 200 kHz Quarz, Keramikresonator, Extern
XT 100 kHz – 4 MHz Quarz, Keramikresonator, Extern
HS 4 MHz – 20 MHz Quarz, Keramikresonator, Extern
RC 30 kHz – 4 MHz Widerstand – Kondensator Kombi

Natürlich kann der PIC auch alternativ mit dem internen Takt betrieben werden (sofern euer PIC diess Feature bereitstellt). In der Konfiguration direkt über MPLABX würde dann etwas wie “INTOSC” in der Auswahl auftauchen.

Power up Timer

Im Normalfall beginnt der PIC Mikrocontroller sofort nach Zuschaltern der Spannungsversorgung mit seiner Arbeit.  Durch Aktivieren des Power up Timers kann an dieser Stelle eine 72ms Verzögerung aktiviert werden. Das ist oftmals nützlich, wenn anderweitige Schaltkreise im System etwas länger brauchen. Ich würde daher empfehlen diese Option i.d.R. immer zu nutzen, sofern kein triftiger Grund dagegen spricht. Durch Aktivieren des Power up Timers kann mehr oder weniger die Stabilität des Gesamtsystems erhöht werden.

Watchdog Timer

Der Watchdog Timer ist ein Hardwaretimer, welcher den PIC resetet sobald der Timer überläuft. Ist diese Funktion aktiviert, muss der Timer in regelmäßigen Abständen gelöscht werden. Wozu das ganze? Nun man stelle sich vor, dass sch der PIC bei der Abarbeitung einer Aufgabe festgefahren hat. Dies mag durch einen Fehler im Programm oder eine andere Ursache aufgetreten sein. Wie dem auch sei .. angenommen der PIC kommt von alleine nun nicht mehr aus diesem Zustand heraus. Bei kritischen Programmen (z.B. Programmen mit Sicherheitsaspekten für Menschen) ist diese Situation zwingend zu lösen. In diesem Fall würde der PIC nun also nicht mehr an die Stelle im Programm gelangen, an der er den Watchdog-Timer zurücksetzen würde. Folglich wird es relativ schnell zu einem Überlauf kommen, der PIC wird resettet und aus seiner Ausweglosen Situation befreit und kann mit der normalen Arbeit fortfahren/neu beginnen .. die Sicherheit für Menschen ist somit hoffentlich wieder hergestellt.

Code protection

Diese Funktion schützt euren Programmcode vor Diebstahl. Wenn ihr ein Programm geschrieben habt, welches Ihr nicht veröffentlichen möchtet, könnt ihr mit dieser Funktion den Code schützen. Ist die Code-Protection, die im Übrigen getrennt für verschiedene Bereiche des Speichers eingestellt werden kann, hingegen nicht aktiviert, kann eurer Programm ohne größeren Aufwand ausgelesen werden. Je nachdem unter welche Lizenz eurer Programm steht, ist das dann gegebenenfalls nicht in eurem Interesse 😉

MCLR (Master clear reset)

Oftmals ist es ja überhaupt nicht notwendig, dass ein PIC per Tastendruck resetet werden kann. Daher kann der MCLR Pin (RA5) auch als Eingang (und zwar nur als Eingang) benutzt werden. Wenn Knappheit an GPIOs herrscht ist dies oftmals ein gern genutztes oder besser gesagt nicht genutztes Feature, das einem einen zusätzlichen Eingang schenkt.

LV Programming

Diese Funktion ist für Einsteiger weniger Interessant. Sie ermöglicht das Brennen mit 5 anstatt 12V Programmierspannung. Geht aber auf Kosten von einem GPIO Pin. Für das Low Voltage Programming LVP wird zusätzlich der GPIO PGM benötigt, der dann im normalen Programm nicht mehr genutzt werden kann. Ich würde euch empfehlen beim normalen Programmieren mit 12V zu bleiben und daher das LVP zu deaktivieren.

Brown out Reset

Der Brown out Reset schützt den PIC davor fehlerhaft zu arbeiten, wenn es zu Spannungseinbruch der Versorgungsspannung kommt. Der Brown out Reset überprüft ständig die Betriebsspannung des PIC. Sobald diese für mindestens 0,1ms unterhalb von ..V fällt, wird ein Reset des PIC ausgelöst, sobald die Spannung wieder im sicheren Bereich ist und 72ms vergangen sind. Bei PIC18F ist die Schwelle bei dem der Reset ausgelöst wird zwischen 2 und 4,5V einstellbar.

Gut zu wissen

Hier entsteht eine kleine Sammlung mit Dingen die immer wieder falsch gemacht werden bzw. zu Problemen führen. Für Hinweise per Mail oder als Kommentar unter diesem Tutorial bin ich immer dankbar.

Was ist Fosc, Tosc und Tcy

\text{F}_{\text{osc}} ist die Taktfrequenz der Signalquelle, die wir an unseren PIC anschließen (zum Beispiel ein Quarz). \text{T}_{\text{osc}} ist dementsprechend die Periodendauer der Frequenz \text{F}_{\text{osc}}. Man errechnet \text{T}_{\text{osc}} mit 1 / \text{F}_{\text{osc}}. Da ein PIC zum Bearbeiten eines Befehls 4 Taktzyklen (  4 \cdot \text{T}_{\text{osc}} = \text{T}_{\text{cy}} ) benötigt, errechnet sich \text{T}_{\text{cy}} (also die Zeit zum Bearbeiten eines Befehls) durch \frac{1}{ \text{F}_{\text{osc}} \text{/} 4} = 4 \cdot \text{T}_{\text{osc}}.

Beispiel: An einem PIC ist ein 4 MHz Quarz angeschlossen.


\text{F}_{osc} = 4\,\text{MHz}

\text{T}_{\text{osc}} =\frac{1}{\text{f}_{\text{osc}}} = \frac{1}{4\,\text{MHz}} = 250\,\text{ns}


Durch die Tatsache, dass der PIC vier Taktzyklen zum Abarbeiten eines Befehls benötigt, ist der interne Befehlstakt 4\,\text{MHz} \text{/} 4 =1\,\text{MHz}. Also benötigt der PIC zum abarbeiten eines Befehls:


\text{T}_{\text{cy}} = 4 \cdot \text{T}_{\text{osc}} = 4 \cdot 250\,\text{ns} = 1\,\mu\text{s}

Offene Pins am Controller

Wenn es vorkommt, dass ihr nicht alle Pins des Mikrocontrollers braucht, dann habt ihr zwei Möglichkeiten: Ihr schaltet die Pins in der Software auf Ausgang und weist ihnen idealerweise den Wert 0 zu (in diesem Fall keine externe Beschaltung vorsehen um die Gefahr eines Kurzschlusses zu vermeiden) oder ihr legt die nicht verwendeten Pins (Eingänge) über entweder externe oder interne (falls vorhanden) Pull-up/down-Widerstände auf ein definiertes Potential.

Nicht verwendete Eingänge sollten jedenfalls niemals offen gelassen werden. Sie können zum Schwingen führen und verursachen unter Umständen einen erhöhten Stromverbrauch. Siehe hierzu auch folgende Artikel [1], [2 siehe Unused Port Pins] und [3 siehe 2.3.2 Dynamische Verluste…]

Achtung bei RA4

Immer wenn ihr den GPIO RA4 eines PIC als Ausgang benutzen wollt müssen bei euch die Alarmglocken angehen (dies bezieht sich lediglich auf PIC16F), denn dieser Pin ist bei vielen PIC16F Typen nur ein Open Drain Pin und ist somit nicht in der Lage, ohne einen extra Pull up Widerstand, sein Potential allein auf 5V zu ziehen.

In circuit serial programming

Wer kennt das nicht: Für die Entwicklung einer Software muss der PIC mehrmals geflasht und anschließend die Software in der Schaltung getestet werden. Es ist nervtötend andauernd den PIC immer wieder aus der Fassung der Schaltung zu entnehmen, dann in die Fassung des Brenners zu stecken und dann wieder anders herum. Es gibt hierfür eine Lösung und die nennt sich In Cuircuit Serial Programming oder kurz ICSP. Das bedeutet der PIC wird in seiner fertigen Schaltung gebrannt ohne ihn entnehmen zu müssen. Damit die ICSP Funktion benutzt werden kann müssen ein paar Vorkehrungen getroffen werden:

Es werden folgende Leitungen benötigt:

  • Vpp – Programmierspannung
  • Vdd – Versorgungsspannung
  • Vss – Masse/Ground
  • PGD – Datenleitung
  • PGC – Taktleitung
  • PGM – wird lediglich für LVP benötigt

Hier seht ihr die ICSP Beschaltung am Beispiel des PICKit3 von Microchip:


Schlusswort

Ich hoffe, dass ich euch mit diesem kleinen Tutorial den Einstieg in die PIC-Mikrocontroller-Welt etwas versüßen konnte. Gerne könnt ihr eure Meinung und Kritik als E-Mail oder direkt als Kommentar unter dem/den Artikel(n) oder alternativ im Forum abladen. Ich freue mich jedenfalls über jede Rückmeldung – egal über welchen Kanal. Viel Spaß beim Arbeiten mit PIC-Mikrocontrollern 😉

Siehe auch

Leave a Comment