Wenn der PIC nicht "mit-arbeitet" ...
Einen PIC zu benutzen ist einfach: man schreibt ein bißchen Code, brennt ihn in den Flash-ROM, setzt den PIC in die Anwenderschaltung, legt Spannung an, und er funktioniert — meistens jedenfalls...
Für alle Anhänger von Murphys Gesetz folgen ein paar Tips, was alles schief gehen könnte.
Der PIC funktioniert überhaupt nicht
Mögliche Ursachen:
- Der PIC steckt nicht richtig in der Fassung.
Das klingt vielleicht blöd, aber es ist nicht unmöglich, ein PIC verkehrt herum einzusetzen, ein Anschlußbein zu verbiegen, oder eine Fassung zu erwischen, die nicht richtig Kontakt gibt.
- Fehlende Betriebsspannung.
- Oszillator-Ausfall.
Mein Favorit.
- Ein typischer Anfängerfehler besteht darin zu vergessen, das Konfigurationswort einzustellen. Dadurch wird im PIC der RC-Oszillator ausgewählt, und der funktioniert offensichtlich nicht, wenn die äußere Beschaltung fehlt. Um diesen Fehler zu vermeiden, sollte man immer das Konfigurationswort im Quelltext festlegen.
- Ein RC-Oszillator kann instabil werden, wenn R und C außerhalb des empfohlenen Bereiches liegen (3kΩ < R < 100kΩ, C > 20pF).
- Falls man einen Quarz einsetzt: für Frequenzen oberhalb 4 MHz sollte der HS-Mode gewählt werden.
- Die meisten PIC 12Fxxx/16Fxxx haben einen eingeschränkten Oszillatorfrequenzbereich für Betriebsspannungen Vdd unterhalb von 4,5 Volt.
Wenn Geschwindigkeit und Genauigkeit keine Rolle spielen, würde ich die Verwendung des internen Oszillators empfehlen. Das vereinfacht das Leiterplattenlayout und gibt zwei zusätzliche Anschlüsse.
- Der PIC bleibt im RESET-Zustand.
Neben der simplen Ursache, daß der /MCLR-Anschluß "versehentlich" auf "Low" gezogen wird, gibt es noch andere, nicht so leicht erkennbare Situationen, die einen PIC vom Laufen abhalten:
- Falls "Brown-out-Reset" aktiv ist, dann bleibt der PIC im RESET-Zustand solange Vdd unterhalb eines bestimmten Niveaus liegt. Diese "Brown-out"-Spannung ist spezifisch für jeden Controllertyp. Man kann die "PIC Info"-Funktion von 'PicProm' nutzen, um den Wert herauszufinden.
- Die interne "Power-on-Reset"-Schaltung mancher PIC erwartet, daß Vdd von einem Wert nahe Vss zu steigen beginnt; andernfalls blockiert sie.
Das kann kritisch sein, wenn man eine energiesparende Schaltung aus- und wieder einschaltet. Ein 100µF-Pufferkondensator beispielsweise kann mehrere Minuten benötigen, bis er vollständig entladen ist, da der PIC bei sehr niedrigen Spannungen kaum noch Strom verbraucht.
- Negative Spannungsimpulse am /MCLR-Anschluß können einen "Latch-up" verursachen. Falls der Anschluß niederohmig angesteuert wird, z.B. mit einer Taste, dann sollte man einen Reihenwiderstand von 100Ω vorsehen.
- Mit Spannungen deutlich oberhalb Vdd am /MCLR-Anschluß schaltet der PIC in den Programmiermodus.
- Der Debugger ist aktiviert.
Wenn die Debugger-Logik aktiviert ist, dann führt der PIC die erste Anweisung aus und hält dann an. Deshalb muß man den PIC nach dem Debuggen der Software erneut im normalen Modus programmieren, bevor man ihn in der Anwenderschaltung nutzt.
- Der PIC scheint tot zu sein.
Nach meiner Erfahrung sind PIC nicht so einfach kaputt zu bekommen. Selbst nach einem Verpolen der Betriebsspannung besteht noch Hoffnung.
Bevor man ihn zu einer hübschen Skulptur umarbeitet, kann man versuchen, den PIC durch mehrfaches Löschen wiederzubeleben.
Der PIC funktioniert nicht wie erwartet
Mögliche Ursachen:
- Falsche Register-Bank ausgewählt.
Der Maschinencode der PIC 12Fxxx/16Fxxx kann nur die unteren 7 Bits der Registeradresse aufnehmen. Um mehr als 128 Register zu ermöglichen, sind sie in zwei bzw. vier Bänken organisiert. Wenn man Assemblercode schreibt, muß man folglich bei jedem Register-Zugriff darauf achten, daß auch die entsprechende Register-Bank im STATUS-Register ausgewählt ist.
Falls MASM ein "List file" erzeugt, kann der Editor von 'PicProm' sowohl die HEX-Datei als auch die zugehörigen Quelltextzeilen darstellen, was eine visuelle Überprüfung des erzeugten Codes ermöglicht.
![[Editor]](pe2.png)
(Mit MASM kann man zwar vor jedem Registerzugriff den Pseudo-Befehl "banksel" verwenden, der die nötigen Befehle erzeugt, um die Bank-Bits richtig zu setzen, jedoch würde ich diese Methode aus zwei Gründen nicht empfehlen: erstens läßt sich anscheinend keine Sprungmarke in der gleichen Zeile setzen, und zweitens generiert diese Pseudo-Anweisung immer Maschinencode — selbst dann, wenn die Bank-Bits schon richtig eingestellt sind. Das bläht die Programmgröße unnütz auf und kostet wertvolle Ausführungszeit.)
- Falsche Speicher-Seite ausgewählt.
Der Maschinencode der PIC 12Fxxx/16Fxxx kann nur die unteren 11 Bits einer Programmspeicher-Adresse aufnehmen, wodurch sich ein direkt adressierbarer Bereich von 2 KWords ergibt. Falls die Programmgröße diese 2 KWords übersteigt, muß man vor jedem "goto"- oder "call"-Befehl sicher stellen, daß die entsprechende Speicherseite im Register PCLATH eingestellt ist.
- Der Watchdog-Timer setzt den PIC immer wieder zurück.
Bei eingeschaltetem Watchdog-Timer — was der Default-Zustand ist — muß man "clrwdt"-Befehle so innerhalb des Quelltextes verteilen, daß bei normaler Programmausführung wenigstens alle 10 ms einer von ihnen abgearbeitet wird.
Bevor man den Watchdog-Timer im Konfigurationswort deaktiviert, sollte man sich klar machen, daß er die einzige Möglichkeit darstellt, mit der ein PIC "sich selbst" befreien kann, falls er durch eine Software-Fehlfunktion in einer Endlos-Schleife "hängt".
- Die PORTs wurden nicht richtig initialisiert.
Nach dem Power-on-Reset können manche PORT-Anschlüsse als Analogeingänge konfiguriert sein, und sie liefern beim Lesen immer '0'. Das ist vom Controllertyp abhängig. Hier hilft ein Blick ins Datenblatt für die richtige PORT-Initialisierung.
- Befehl mit einem PORT als Ziel.
Wenn man die Daten eines PORTs modifiziert, dann ist zu beachten, daß der Befehl den aktuellen – externen – Zustand aller Anschlüsse liest und das Ergebnis in alle Ausgangspuffer des PORTs schreibt. Dies kann zu unerwarteten Ergebnissen führen. Einige Beispiele:
- Es ist eine gute Praxis, den Ausgangspuffer zu setzen, bevor man einen Anschluß als Ausgang konfiguriert — jedoch aufgepaßt beim Umschalten mehrerer Anschlüsse!
Nehmen wir an, man möchte RB0 und RB1 als Ausgang und auf "Low" setzen, und die Anschlüsse werden gerade durch interne oder externe Widerstände auf "High" gezogen.
| bcf | PORTB,0 | ; Ausgangspuffer RB0='0' |
| bcf | PORTB,1 | ; Ausgangspuffer RB1='0', aber RB0='1' |
| bsf | STATUS,RP0 | ; Bank 1 |
| bcf | TRISB,0 | ; RB0 als Ausgang konfigurieren |
| bcf | TRISB,1 | ; RB1 als Ausgang konfigurieren |
Der zweite Befehl, "bcf PORTB,1", liest den ganzen PORTB, löscht Bit 1 und schreibt das Ergebnis in die Ausgangspuffer. Da RB0 immer noch Eingang ist, reflektiert er den anliegenden "High"-Pegel (= '1').
Um dies zu vermeiden, muß man die Ausgangspuffer von RB0 und RB1 mit dem gleichen Befehl setzen:
| movlw | 0FCh | |
| andwf | PORTB | ; Ausgangspuffer RB0='0' und RB1='0' |
| bsf | STATUS,RP0 | ; Bank 1 |
| andwf | TRISB | ; RB0 und RB1 als Ausgang konfigurieren |
Übrigens, falls sich die Interrupt-Routine auf den PORTB auswirkt, dann sollte man während obiger Befehlsfolge die Interrupts deaktivieren.
- Falls ein Ausgang kapazitiv belastet wird – z.B. mit einer längeren Leitung –, dann kann das Umschalten des Anschlusses einige Zeit beanspruchen.
Nehmen wir an, RB2 und RB3 sind als Ausgang konfiguriert, beide auf "low" gesetzt, und sie sollen auf "high" umgeschaltet werden:
| bsf | PORTB,2 | ; Ausgangspuffer RB2='1' |
| bsf | PORTB,3 | ; Ausgangspuffer RB3='1', aber eventuell RB2='0' |
Der erste Befehl schreibt die Ausgangspuffer von PORTB am Ende des Q4-Taktzyklus, und der zweite Befehl liest den tatsächlichen Zustand aller Anschlüsse zu Beginn des nächsten Q2-Taktzyklus. Falls RB2 es nicht schafft, die Leitung in weniger als einer Oszillatorperiode umzuladen, dann kann der zweite Befehl noch ein "low" einlesen und dies dann auf den Ausgangspuffer von RB2 übertragen, wodurch der gewünschte Zustand überschrieben wird.
Deshalb sollte man die Ausgangspuffer von RB2 und RB3 mit dem gleichen Befehl setzen:
| movlw | 00Ch | |
| iorwf | PORTB | ; Ausgangspuffer RB2='1' und RB3='1' |
- Bei älteren PIC ist der RA4-Anschluß ein "Open drain"-Ausgang. Falls der Ausgangspuffer von RA4 auf '1' gesetzt ist, aber der Anschluß extern auf "Low" gezogen wird, dann hat jeder Befehl, der einen anderen Anschluß von PORTA modifiziert, den Nebeneffekt, den Ausgangspuffer von RA4 auf '0' zu schalten, wodurch der Anschluß nun durch den PIC auf "Low" gehalten wird.
- Falls ein Anschluß als analoger Eingang konfiguriert ist, dann setzt jeder Befehl, der einen anderen Anschluß des jeweiligen PORTs modifiziert, den Ausgangspuffer dieses Anschlusses auf '0', da der Eingang beim Lesen immer '0' liefert.
Die 'Falle' der analogen Anschlüsse liegt darin, daß das Löschen des TRIS-Bits (nur) die Ausgangsstufe aktiviert, während der Eingang im analogen Mode verbleibt, d.h., man erhält eine Kombination von digitalem Ausgang und analogem Eingang.
Damit ein Ausgang zuverlässig funktioniert, sollte man den Anschluß als digital konfigurieren oder ein Schattenregister für diesen PORT verwenden (siehe nächster Punkt).
Der Innenwiderstand der Ausgangstreiber hängt von der Temperatur, Vdd und der externen Last ab. Aufgrund der Sättigung der Ausgangsstufen kann im schlimmsten Fall schon die Injektion weniger Milliampere den Zustand eines Anschlusses von "High" nach "Low" kippen.
(Leider zeigen neuere Datenblätter nicht mehr den ganzen Arbeitsbereich der Ausgangstreiber. Man vergleiche die Diagramme "VOH vs. IOH" für den 16F88x, Abbildung 18-28, mit denen für den 16F87xA, Abbildung 18-17.)
Falls die Schaltung in einer gestörten Umgebung arbeitet, dann können Transienten den Weg zu den Port-Anschlüssen finden. Und ein Störimpuls, der ausgerechnet während eines Port-Zugriff auftritt, liefert eine schöne Bestätigung für Murphys Gesetz...
Um "merkwürdigem" Verhalten in "freier Wildbahn" vorzubeugen, ist es ratsam, Schattenregister zu verwenden. Jede Manipulation der Port-Bits wird zunächst im Schattenregister vorgenommen und das Ergebnis dann in das jeweilige Port-Register übertragen, z.B.:
| bsf | Port_B,4 | ; Schattenregister RB4='1' |
| bcf | Port_B,7 | ; Schattenregister RB7='0' |
| movf | Port_B,w | ; lese Schattenregister und ... |
| movwf | PORTB | ; ... setze alle Ausgangspuffer |
Auf diese Weise spielt der externe Zustand der Port-Anschlüsse keine Rolle.
- Fehlende Hauptschleife.
Einfach gesagt: einmal eingeschalten arbeitet der PIC die Befehle im Programmspeicher ab, bis er wieder ausgeschalten wird. Es gibt keinen "Halt"-Befehl, und die "END"-Anweisung am Ende eines Assemblercode-Blockes hat keine Bedeutung bezüglich der Programmabarbeitung.
Folglich muß das Programmm eine (nie endende) Hauptschleife enthalten oder — falls das Programm nur einmal nach dem Einschalten laufen soll — wenigstens mit einem "goto $"-Befehl enden, um die Abarbeitung von leerem Programmspeicher zu verhindern.
- Stack-Überlauf.
Die PIC 12Fxxx/16Fxxx haben nur 8 Stackniveaus für "call"-Befehle und die Interrupt-Routine. Falls das Programm einen zu starken Gebrauch von verschachtelten Unterprogrammen macht, dann kann ein Stacküberlauf auftreten — und für diesen Fall ist keine Behandlung vorgesehen. Das Programm stürzt einfach ab.
Aus diesem Grund analysiert 'PicProm' jede HEX-Datei beim Laden und bringt eine Warnung, falls ein Stacküberlauf bei der Programmausführung auftreten könnte.
- Fehlender Abblock-Kondensator.
Ein Abblock-Kondensator von 100nF sollte so dicht wie möglich an die Vdd- und Vss-Anschlüsse angebracht werden. Meine bevorzugte Methode für PDIP-Gehäuse besteht darin, einen kleinen SMD-Kondensator direkt zwischen die Anschlüsse zu löten.
zurück zur Hauptseite