AVR Checkliste

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

Diese Seite soll als erste Anlaufstelle dienen, wenn der AVR-Mikrocontroller mal wieder nicht so will wie er soll. Es wird versucht, die Standardfehler und Probleme aufzulisten und zu erklären, was man dagegen tun kann.

Hardware

Pinkompatibilität

Wenn man einen anderen Controller als den im Schaltplan benutzt, sollte man daran denken, dass nur wenige AVR-Controller vollständig pinkompatibel und damit untereinander austauschbar sind. Manchmal liegen gar die am dringendsten benötigten Funktionen (ISP-Programmierung) bei anderen Controllern auf anderen Pins. Deshalb unbedingt vorher die Belegungen anhand der Datenblätter vergleichen!

Fuses

Die AVR-Controller haben 'Fuses' (deutsch: 'Sicherungen'), die das Verhalten des Prozessors auf grundlegender Ebene bestimmen. Ein häufiger Fehler ist beispielsweise, dass über die Fuse "CKSEL" (clock select) die falsche Taktquelle gewählt wurde. Die meisten AVRs können mit dem internen Oszillator (internal R/C), mit einem externen Oszillator (external clock), mit einem Quarz (external crystal) oder mit einem Resonator (external R/C) betrieben werden. Wenn die Einstellung über die Fusebits nicht dazu passt (z. B. external clock statt external crystal), fehlt dem Controller unter Umständen der Systemtakt und er läuft nicht an, oder man bekommt auf der seriellen Schnittstelle Nonsens, oder die Timer bzw. Zeitschleifen im Programm laufen zu langsam oder zu schnell.

Auch die Fuse "JTAGEN" (JTAG enable) verdient besondere Beachtung: Wenn sie eingeschaltet ist, ist auf einem bestimmten Port (z. B. PORTC beim ATMega32) die JTAG-Unterstützung aktiviert. Dann funktionieren vier Bits dieses Ports nicht wie gewohnt, da sie für die JTAG-Schnittstelle reserviert werden.

In dem zum STK500 gehörenden Entwicklungstool 'AVR Studio' gibt es in der Programmer-Dialogbox einen Tab 'Fuses', der controllerabhängig den Status der Fusebits anzeigt und Änderungen ermöglicht.

Siehe auch: AVR Fuses.

ISP-Programmieradapter

Bei ISP-Programmieradaptern für den Parallelport kann es zu Inkompatibilitäten mit manchen Ports kommen. Tritt das Problem auch auf, wenn der ISP-Adapter an einem anderen Rechner angeschlossen ist? Funktioniert es vielleicht mit einer anderen Software? Siehe auch: AVR In System Programmer.

Dauert bei einem seriellen Programmer mit eigenem Controller (STK500, Atmel AVRISP etc.) das Programmieren sehr lange, oder ist fehlerbehaftet, kann das an einer zu hohen ISP-Taktrate liegen. Sie darf 1/4 des Controllertaktes (F_CPU) nicht übersteigen. Abhilfe: ISP Taktrate heruntersetzen auf maximal 1/4 FCPU.

Beim Programmieren mit dem usbasp Programmieradapter muss die Geschwindigkeit richtig eingestellt werden. Bei einem AVR der auf den eingebauten 1 MHz läuft muss mit langsamer Geschwindigkeit (Jumper zu) programmiert werden.

Programmieren via SPI und angeschlossene SPI-Hardware

Wenn Peripherie über SPI angebunden ist, muss sich diese zum Programmierzeitpunkt auf dem Bus neutral verhalten. Normalerweise bedeutet dies, dass die Slave-Select/Chip-Select-Leitungen der Peripherie auf high gezogen werden müssen, wenn die Chips keinen internen Pull-Up haben. Werte von 10 kΩ bis 100 kΩ für normale Anwendungen haben sich bewährt.

Spannungsversorgung

Der Pin AVCC ist der Versorgungsanschluss für den AD-Wandler (ADC, analog digital converter) und den zugehörigen Port. Er ist nicht an allen AVRs vorhanden; wenn er aber existiert, so muss er auf jeden Fall angeschlossen sein, auch wenn der AD-Wandler nicht benutzt wird. Wird der AD-Wandler verwendet, sollte zur Verbesserung der Genauigkeit der AVCC-Pin über einen Lowpass-Filter angeschlossen werden (siehe Datenblatt).

Oft funktioniert die Programmierung des Controllers auch, wenn Vcc oder GND nicht richtig angeschlossen ist. Zur Sicherheit kann man mit einem Messgerät direkt an den Anschlüssen des AVRs kontrollieren (VCC-GND, AVCC-GND), ob die Verbindungen korrekt sind. Es empfiehlt sich, vor dem Einsetzen bzw. Einlöten des Controllers die Versorgungsanschlüsse nochmals zu prüfen, um sicherzustellen, dass man den IC nicht durch eine zu hohe Spannung aufgrund eines Fehlers in der Versorgung zerstört.

Die VCC-Leitung eines Controllers zu unterbrechen (aus welchen Gründen auch immer, z. B. um ihn "komplett abzuschalten") ist nicht ratsam. Falls noch irgendein Pin von einer externen Quelle auf H-Pegel gehalten wird, oder ein Pin über einen Widerstand mit VCC verbunden ist, wird der Controller laufen, weil er über diesen Pin mit Strom versorgt wird. Das ist möglich, weil der Strom über die interne Clamp-Diode des Pins nach VCC fließen kann (und das auch tut). Jeder Pin hat je eine interne Clamp-Diode gegen GND und eine gegen VCC zum Schutz vor zu niedrigen bzw. zu hohen Eingangsspannungen.

Reset-Pin

Der Reset-Pin am AVR ist 'active-low', d. h. wenn man ihn mit GND (Masse, ground) verbindet, wird der Controller zurückgesetzt gehalten. Zwar haben die meisten(!) AVRs einen internen Pullup-Widerstand, der den Reset-Pin gegen VCC "zieht", dieser ist jedoch relativ hochohmig (ca. 50 kΩ, vgl. Datenblatt) und reicht in extrem stark gestörter Umgebung nicht aus, um den Reset-Pin sicher "hochzuhalten". Als Mindestbeschaltung empfiehlt sich daher ein externer Pullup-Widerstand (typisch 10 kΩ), der den Reset-Pin mit VCC verbindet. Er sollte nicht kleiner als 4,7 kΩ sein, da der Programmieradapter sonst eventuell den Reset-Pin während des Programmiervorgangs nicht sicher auf "low" ziehen kann. Mit einem Oszilloskop kann man gut überprüfen, ob der Pegel am Reset-Pin sauber zwischen H und L wechselt. Zusätzlich sollte man auch noch einen Kondensator 47 nF oder 100 nF zwischen Reset-Pin und GND anordnen. Dieses RC-Glied sorgt dafür, dass der Controller beim Einschalten der Versorgungsspannung für eine definierte Zeitspanne im Reset gehalten wird. Im laufenden Betrieb sorgt der Kondensator dafür, dass der Reseteingang unempfindlich gegenüber Spikes und Glitches wird. Er sollte deshalb unmittelbar in Pin-Nähe beim Prozessor untergebracht werden. Achtung: Wenn debugWIRE möglich sein soll, muss dieser Kondensator weggelassen werden.

Atmel empfiehlt zusätzlich noch zum Schutz vor Überspannungen eine externe Diode nach VCC ("Clamp-Diode"), da für den Reset-Pin keine interne vorhanden ist. Mit dieser Diode funktionieren jedoch manche Programmieradapter nicht. Allerdings schützt sie den Controller davor, in den High-Voltage-Programming Mode zu schalten, zum Beispiel als Folge von Störspannung wegen EMV, und dadurch (schlimmstenfalls) den Programmspeicher zu überschreiben.

Programmierung nur einmal möglich?

Es kann vorkommen, dass ein AVR nur einmal programmiert werden kann, danach geht es nicht mehr (z. B. wird der Typ nicht mehr erkannt). Erst nachdem die Versorgungsspannung getrennt und wieder verbunden wurde, geht es erneut. In diesem Fall ist meistens der Reset-Pin kurzgeschlossen, daher lässt sich der AVR einmal programmieren.

Bei korrekter Beschaltung weist der Reset-Pin eine Spannung nahe VCC auf (z. B. 4.9 V). Ist die Spannung nahe bei GND, ist der Pin kurzgeschlossen und verursacht das beschriebene Problem.

Abblockkondensatoren

Abblockkondensatoren dienen dazu, impulsartige Einbrüche der Betriebsspannung, die durch schnelle Schaltvorgänge verursacht werden können, zu minimieren. Sie verhindern unkontrollierte Brownouts, welche durch diese Spannungseinbrüche auftreten können. Diesen Zweck erfüllen sie optimal, wenn folgende Regeln eingehalten werden:

  • Ein Abblockkondensator sollte möglichst dicht am IC sitzen.
  • Eine niederimpedante Anbindung sollte gegeben sein – Stegleitungen sind deshalb zu vermeiden. Dann muss die Versorgungsspannung erst durch das Pad des Abblockkondensators fließen und wird dann weiter zum IC gereicht.
  • Jedes IC in einer Schaltung sollte einen Abblockkondensator besitzen.
  • Bei ICs mit mehreren Anschlüssen für VCC sollte jeder VCC-Pin mit einem eigenen Abblockkondensator beschaltet werden (z. B. AVRs in SMD-Bauform wie dem ATmega16A also mit vier Kondensatoren). Dabei ist darauf zu achten, dass jeder Abblockkondensator mit einem eigenen Via auf die niederimpedante (heißt Hochfrequenz gut leitende) großflächige, nicht unterbrochene Massefläche kontaktiert wird.
  • Die Länge der GND-Leitung vom Kondensator zum IC ist genau so wichtig wie die Länge der VCC-Leitung.
  • Es sollten keramische Kondensatoren mit einer Kapazität von maximal 100 nF verwendet werden. Größere Kondensatoren, etwa 10 µF-Elkos, sind für diese Aufgabe nicht geeignet (und auch kein Dutzend µF-Keramikondensatoren), weil sie "zu langsam" sind. Ihre Impedanz ist bei hochfrequenten Spannungen zu groß – ein schneller, hochfrequenter Spannungsimpuls kann so nicht abgeleitet werden.

Quarz oder Quarzoszillator?

Die neueren AVRs haben einen internen Oszillator, der im Auslieferungszustand über die entsprechenden Fuses eingeschaltet ist. In diesem Fall muss kein externer Quarz mehr angeschlossen werden. Man kann in den Fuse-Bits aber einstellen, dass man einen externen Taktgenerator (external clock, z. B. Quarzoszillator) oder externen Quarz (external crystal) verwenden möchte.

Werden seriell Daten ohne Clockleitung (also asynchron) übertragen, ist eine externe genauere Taktquelle meistens unverzichtbar. Zumindest sinnvoll sind sie bei UARTs (obwohl es auch ohne noch gehen mag). USB, CAN oder Ethernet funktionieren nicht ohne präzisen externen Takt. I2C/TWI oder SPI sind synchrone Übertragungsverfahren und brauchen keinen genauen Takt.

Dann, oder wenn der AVR keinen internen Takt hat, wird an die entsprechenden Pins des Controllers ein Quarz (external crystal, Bauelement mit zwei Pads) oder Quarzoszillator (external clock, Bauelement mit vier Pads) angeschlossen. Im Falle eines Quarzes werden die XTAL-Pins mit den beiden Anschlüssen des Quarzes und jeweils mit einem Kondensator (ca. 12 bis 22 pF) (vgl. Datenblatt ATMega 8) gegen Masse angeschlossen. Im Falle eines Quarzoszillators reicht es aus, den Taktausgang mit dem XTAL1-Pin zu verbinden, und den XTAL2-Pin unbeschaltet zu lassen. Die Fuses sind entsprechend einzustellen.

Ground-Anschlüsse

Bei ICs mit mehreren Masse-Anschlüssen (GND, ground) müssen immer alle Anschlüsse beschaltet werden. Siehe http://www.mikrocontroller.net/forum/read-1-107259.html

Eingänge

Taster müssen:

  • einen Pullup-Widerstand besitzen, so sie active-low betrieben werden, d.h. wenn beim Tastendruck der Pin mit GND (Masse) verbunden wird (dies ist die übliche Anschaltung). Man kann einen externen Pull-Up Widerstand (typ. 10 kOhm) benutzen oder den internen aktivieren (DDR als Eingang also "0", PORT auf "1").

Will man einen Taster active-high betreiben, soll also bei Tastendruck eine "1" in PIN gelesen werden, ist ein externer Pull-Down-Widerstand (typ. 10 kOhm) gegen GND anzuschließen, denn interne Pull-Downs sind nicht verfügbar.

Symptome: Aufgrund des Prellens bekommt man bei einem Tastendruck statt eines Signals mehrere, und beim fehlenden Pullup fängt man sich Störungen (z. B. das 50 Hz-Netzbrummen) ein, da der Pin nicht auf einem "definierten Pegel" liegt, wenn der Taster nicht geschlossen ist. Soll z. B. bei einem Tastendruck eine LED angehen, dann leuchtet die LED durch das Netzbrummen plötzlich mit 50 Hz anstatt aus zu sein.

  • active low: ein Anschluss des Tasters an den Port-Pin, den anderen Taster-Anschluss an GND; internen Pull-Up-Widerstand aktivieren oder externen Widerstand zwischen Port-Pin und VCC.
  • active high: Taster zwischen Port-Pin und VCC; externen Widerstand zwischen Port-Pin und GND.

Ausgänge

Man sollte darauf achten, dass "kritische" Ausgänge, d.h. Ausgänge, über die nicht "nur" eine LED geschaltet wird, einen definierten Zustand haben, wenn der Portpin auf "Eingang" und damit hochohmig geschaltet ist. Dadurch wird sichergestellt, dass beim Einschalten nicht kurz ein Verbraucher geschaltet wird (z. B. "Zucken" eines Motors). Dies kann man bewerkstelligen, indem man extern Widerstände (auch hier Pull-Up bzw. Pull-Down genannt, typ. 10 kOhm) an den Ausgangs-Pins vorsieht, die den Ausgang auf den gewünschten Zustand ziehen.

Besonderheiten bei ATmega128 und seinen Derivaten im 64-Pin-Gehäuse

ISP-Adapter

Der ATmega64 und der ATmega128 sowie alle vom ATMega128 abgeleiteten AVRs im 64-Pin-Gehäuse (ATMega641/1281/2561, AT90PWM2/3/2B/3B sowie AT90CAN32/64/128, Ausnahmen sind nur AT90USB64/128, die eine ganz andere Pinbelegung haben) haben besondere Fallstricke, über die man bei nicht ausreichendem Datenblattstudium leicht stolpert.

  • Der erste betrifft den Anschluss der ISP-Signale (MISO, MOSI, SCK, RESET). Dieser erfolgt nicht wie bei den meisten anderen AVR-Controllern an den gleichnamigen Pins, sondern an PDI, PDO, SCK und RESET. Die Pinzuordnung ist:
  • MOSI → PE0 (Pin 2)
  • MISO → PE1 (Pin 3)
  • SCK → PB1 (Pin 11)
  • RESET → RESET (Pin 20)
PEN (Pin 1) hat für normale ISP-Adapter keine Bedeutung und kann offen gelassen oder direkt mit Vcc verbunden werden. Die Benutzung der Pins PDI und PDO anstelle von MOSI und MISO gilt für praktisch alle ATmega128-Derivate im 64-Pin-TQFP-Gehäuse, darunter AT90CAN32/64/128 und ATmega641/1281/2561. Im Zweifelsfall im Datenblatt (Pin Configuration) nachsehen, ob PE0 und PE1 mit "PDI" bzw. "PDO" beschriftet sind.
  • Die zweite kleine Gemeinheit betrifft die M103C-Fuse (ATmega103 Compatibility Mode, nur bei den ATmega-Typen, nicht beim AT90CAN32/64/128). Diese ist bei fabrikneuen Atmega64/128 gesetzt und sorgt dafür, dass sich diese wie ein Atmega103 verhalten.
    • Dies hat zur Folge, dass ein für den ATmega64 oder ATmega128 geschriebenes Programm beim ersten RET abstürzt, da der SRAM an einer anderen Stelle endet als erwartet und somit der Prozessor keine gültige Rücksprungadresse vom Stack holen kann.
    • Außerdem funktionieren einige IO-Pins an PORTC, PORTF und PORTG anders.
    • Für weitere Infos bzgl. TWI, UART, Timer, Bootloader und Kalibrierung des internen RC-Oszillators unbedingt das Datenblatt lesen.

Lötstellen

"Kalte", d. h. schlechte Lötstellen verraten sich durch ihre matte Oberfläche (bei bleihaltigem Lot). Beschädigte Lötstellen erkennt man oft an einem Riss, der sich kreisförmig um die Mitte des Lötpunktes herum gebildet hat. Solche Stellen verursachen oft erst bei mechanischer Beanspruchung Probleme.

Bei kleinen Abständen (SMD-Bauteile) müssen besonders Verbindungen zwischen benachbarten Lötungen kontrolliert werden.

Bei Lochrasterplatinen kann man mit einem spitzen Messer (Bastelmesser) die Zwischenräume von möglichen leitenden Verbindungen befreien.

Software

Mainschleife vorhanden?

Insbesondere beim Testen von Interrupts kann es passieren, dass man keine eigentliche Programmschleife vorsieht, weil ja alles in den Interrupts passiert. Das führt dazu, dass der Controller nach Abarbeiten aller Befehle in

int main(void) {}

seine Arbeit einstellt und daher auch keine Interrupts mehr funktionieren. Demnach sollte bei rein Interrupt-basierten Programmen die Funktion mindestens so aussehen:

int main(void)
{
    // hier Interrupts definieren
    // und einschalten
    while (1){}
}

Alle Interruptvektoren definiert?

Wenn man irgendwelche Interruptvektoren verwendet, sollte man alle definieren, auch die nicht benutzten. Passiert es dann aufgrund eines Fehlers, dass ein Interrupt unbeabsichtigt ausgeführt wird, so führt der Controller dann eine definierte Aktion aus. Benutzt man eine Hochsprache wie C oder Basic, nimmt einem der Compiler diese Arbeit ab.

Außerdem sollte man immer im Datenblatt des Controllers nachsehen, wie die Interrupttabelle aufgebaut ist. Die kleinen AVRs verwenden ein RCALL zum Anspringen der Interrupts, welches 1 Wort lang ist. Die grossen AVRs mit mehr als 8 KiBi FLASH verwenden ein CALL, welches 2 Worte lang ist. Verwendet man nun den falschen Befehl, verschieben sich die Einsprungadressen und das Chaos ist perfekt.

In einem komplett interruptlosen Programm kann man auf die Interrupttabelle selbstverständlich verzichten (erkennbar daran, dass nirgendwo im Code Interrupts mittels Assembler-Befehl SEI eingeschaltet werden).

Oft hilft es einen Catch All Interruptvektor zu definieren um herauszufinden ob ein Fehler (z.B. ein Neubeginn des Programms) vorliegt.

#include <avr/interrupt.h>

ISR (BADISR_vect)
{
    // Hier eine Fehlerausgabe definieren.
    // Z.B. UART-Ausgabe oder ein PIN toggeln
}

Dann findet man solche Fehler recht schnell.

Alle Konfigurationsregister korrekt initialisiert?

Alle benötigten Konfigurationsregister (auch "I/O-Register" genannt) eines Mikrocontrollers müssen korrekt initialisiert werden. Bei Fehlfunktionen sollten diese Konfigurationen noch einmal mit dem Datenblatt abgeglichen werden. Manchmal ist es auch sinnvoll, bestimmte Funktionen explizit abzuwählen. Ein Beispiel hierfür ist der Analog-Komparator des AVR: Schaltet man diesen ab, wenn man ihn nicht benötigt, kann man dadurch ein wenig Strom sparen. Wenn man besonders "sauber" programmieren möchte, initialisiert man Konfigurationsregister immer, d.h. auch wenn sie nicht im Programm verwendet werden. Dies verhindert mögliche zufällige Fehlkonfigurationen.


Stackpointer initialisiert? (Nur in Assembler relevant)

Fehlerbeschreibung: Das Programm lief, bis ein "rcall" oder ein Interrupt eingefügt wurde. Danach ging plötzlich gar nichts mehr.

Wahrscheinliche Ursache: Der Stack ist ein spezieller Bereich im RAM, der von Sprungbefehlen und Interruptaufrufen dazu verwendet wird, die Rücksprungadresse ins Hauptprogramm zu speichern. Da der Stack bei den AVRs nach "unten" wächst, d.h. in Richtung Anfang des RAMs, wird er üblicherweise ans Ende des RAMs gelegt. Der Stack muss vor der ersten Benutzung, am besten direkt am Anfang des Programms, initialisiert werden (sonst ist nach einem Sprung in ein Unterprogramm die Rücksprungadresse undefiniert und das Programm wird an einer unvorhersehbaren Stelle weiter ausgeführt, was in der Regel einen Absturz zur Folge hat). "Den Stack initialisieren" bedeutet, den Stackpointer auf den gewünschten "Startwert" zu setzen. Meistens wird dafür die letzte RAM-Adresse gewählt.

Die Initialisierung unterscheidet sich geringfügig je nach verwendetem AVR. Bei den alten AT90xxxx und den ATtinys ist sie mit zwei Zeilen erledigt:

 
       ldi r16, RAMEND   ;Die Adresse der letzten Stelle im RAM in r16 laden
       out SPL, r16      ;Die Adresse in das Register SPL ausgeben


Da die ATmegas mehr RAM haben, reicht das 8 bit-Register SPL nicht mehr, und es ist ein zweites (SPH) dazugekommen. Die Initialisierung des Stacks erfordert hier vier Zeilen:

 
       ldi r16, LOW(RAMEND)    ;Untere 8 bit des 16 bit-Wertes RAMEND laden
       out SPL, r16
       ldi r16, HIGH(RAMEND)   ;Obere 8 bit laden
       out SPH, r16


16bit-Register in richtiger Reihenfolge geladen/gelesen? (Nur in Assembler relevant)

Bei den 16-bit Registern (z. B. Timer1) genau die Hinweise im Kapitel "Accessing 16-bit Registers" beachten!

So muss z. B. das High-Byte VOR dem Low-Byte geschrieben werden, weil mit dem Schreiben des Low Byte das gesamte Register "übernommen" wird. Beim Lesen muss zuerst das Low-Byte gelesen werden, dann das High-Byte. Es wird dadurch garantiert, dass sich zwischen den beiden einzelnen Befehlen nicht noch das Register weiter verändert, z. B. beim 16bit Counter. Möglich wird das durch ein "temporary Register", von dem es aber nur eines gibt - was beachtet werden muss, da zwischen diesen beiden einzelnen Lese-/Schreib-Befehlen kein Interrupt auftreten darf, der selber das temporary Rgegister benutzt.

In C oder Basic kümmert sich der Compiler um das Problem - man liest oder beschreibt dann einfach z. B. TCNT1 anstatt TCNT1H und TCNT1L einzeln.


Flag richtig gelöscht?

Ein beliebter Fehler ist, dass man überliest, dass gesetzte Flags (z. B. die Interrupt-Flags) durch beschreiben mit einer '1' und nicht mit einer '0' gelöscht werden!

Beispiel:

  • das Flag ist zuerst '0'
  • das Ereignis (z. B. Zählerüberlauf, also Bit TOV0 in TIFR) tritt auf und setzt das jeweilige Bit im Register auf '1'
  • wir haben auf die '1' gewartet und wollen das Bit nun wieder löschen und müssen dafür eine '1' (eins!) in das Register schreiben, keine '0'. Trotzdem wird das Bit dadurch '0' z. B.:
    TIFR = (1<<TOV0);

Das ganze ist kein Fehler, sondern hat seine guten Gründe.

Wenn Interrupts zugelassen sind (z. B. in TIMSK) und die Interruptroutine aufgerufen wird, werden diese Flags zumeist automatisch gelöscht.

Der UART-Empfangs-Interrupt (RXC in UCSRA) jedoch nicht! Der wird gelöscht durch Auslesen des Empfangsregisters (UDR). Hier liegt wiederum ein beliebter Stolperstein: Man prüft oft zuerst in der Interruptroutine die Fehlerflags (z. B. Parity) und macht dann manchmal den Fehler das empfangene Zeichen im Fehlerfall nicht auszulesen. Das führt dann zum Absturz (der IRQ wird immer wieder aufgerufen) - man muss also immer das UDR lesen auch wenn etwas Fehlerhaftes drinsteht.

Zusätzlich ist zu beachten, dass in C die Interrupt-Flags nicht mit einer Veroderung des Registers (z. B. TIFR |= (1 << TOV0)) gelöscht werden sollten, da bei dieser Operation alle anderen evtl. gesetzten Flags im betreffenden Register ebenfalls gelöscht werden. Es ist die Schreibweise von oben zu benutzen (also TIFR = (1 << TOV0))!


Alle Interrupt-Variablen als volatile bezeichnet?

Variablen können im C-Quelltext mit dem Schlüsselwort volatile ausgezeichnet werden: Der Ausdruck

volatile int i

versieht die Integervariable i mit dem Attribut volatile. Dadurch wird der Compiler angewiesen, diese Variable nicht zu optimieren, sondern im Speicher abzulegen und zu beachten, dass sie von anderen Prozessen geändert werden kann. Normale Variablen können durch den Compiler auf Register reduziert werden, oder die Daten werden einmalig aus dem Speicher geladen und erst am Ende des Programmes wieder zurückgeschrieben. Das kann Probleme bereiten, wenn beispielsweise in einer Interruptroutine der Inhalt der Variablen verändert wird. Darum müssen alle globalen Variablen, die in einer ISR (interrupt service routine) geändert werden, als volatile gekennzeichnet werden.

Wo nötig Variablenzugriff atomar gestaltet?

Der Zugriff auf Variablen, die größer als 8 Bit sind und sowohl in der Hauptfunktion ("main") als auch in einer (oder mehrerer) ISR genutzt werden, muss atomar erfolgen. Eine Verletzung dieser Regel kann zu schwer nachvollziehbaren Problemen führen.

Programmierfrequenz passend?

Sollte ein Programmieren nicht möglich sein erst einmal die Programmiergeschwindigkeit bei AVR dude mit der Option -B bitclock anpassen wobei bitclock die Perioden dauer in Millisekunden (floating-point Zahl) ist. Normalerweise -B 1 für Controller mit >=4MHz daher ist bei der Erstprogrammierung ein höherer Wert zu versuchen.

Serielle Verbindungen

I²C/TWI

Sind die Leitungen SCL und SDA mit einem Pullup-Widerstand ausgestattet? Die I²C-Bus-Leitungen SCL und SDA müssen über einen Pullup-Widerstand mit einem Wert von 4,7 kΩ bis 10 kΩ mit der Versorgungsspannung (+5 V) verbunden sein.

- Bei manchen ATmegas muss der Wert in TWBR >10 sein auch wenn es nicht im Datenbuch steht.

UART/USART

Übertragungsprobleme durch falschen oder ungenauen Takt

  • Der interne Oszillator ist recht ungenau und nicht temperaturstabil. Daher kann die Umgebungstemperatur auch den USART-Takt verändern. Für serielle, asynchrone Kommunikation per UART sollte man deshalb immer einen Quarz oder einen Oszillator verwenden, egal bei welcher Baudrate: 3% Fehler sind immer 3% Fehler, egal ob bei 1200 oder 9600 Baud. Falls doch der interne Oszillator verwendet wird: Wurde er für die richtige Frequenz und Betriebsspannung kalibriert?
  • Nicht mit allen Quarzen kann man alle Baudraten genau genug erzeugen. Deswegen gibt es Baudratenquarze wie z. B. 3,6864 MHz. Näheres steht im Datenblatt.
  • Geschieht die Konfiguration des U(S)ART automatisch durch die Programmiersprache (z. B. in BASCOM, C), dann muss dort der Takt genau angegeben werden. Setzt man z. B. einen 3,6864 MHz-Quarz ein, darf man dort nicht einfach "4 MHz" angeben, weil dann die Abweichung mit ca. 8% zu hoch wäre. Damit funktioniert die Übertragung nicht mehr.
  • Im AVR-Studio kann man die Taktfrequenz sowohl über ein #define F_CPU im Quelltext als auch über das Optionsmenu oder das selbsterstellte Makefile einstellen. Es darf nur eine Variante verwendet werden!
  • Wenn ein externer Oszillator oder Quarz angeschlossen ist: Sind die Fuses des Controllers so gesetzt, dass er auch verwendet wird?
  • Ist die Fuse CLKDIV nicht programmiert?
  • Es taucht auch des öfteren das Missverständnis auf, dass man nur F_CPU verändern muss, um den µC mit einem anderen Takt laufen zu lassen. Dem ist nicht so. Der µC-Takt wird durch die Fuses bzw. den eventuell angeschlossenen Quarz oder Quarzoszillator bestimmt. F_CPU dient nur dazu, die dadurch festgelegte Frequenz im C-Programm zur Verfügung zu haben. F_CPU ist nur eine Information, mit der man bei der Programmierung arbeiten kann. Aber ein anderer F_CPU-Wert führt nicht dazu, dass der µC dann magisch auf diese Frequenz umgestellt wird.
  • Um zu prüfen, ob der externe Quarz auch wirklich verwendet wird, kann man mittels _delay_ms() testweise eine LED im Sekundentakt blinken lassen. Den Unterschied zwischen Quarz und internem RC-Oszillator kann man bei größeren Frequenzunterschieden leicht sehen. Ebenso sieht man, ob versehentlich die CLKDIV Fuse gesetzt ist: die Blinkfrequenz ist dann achtmal langsamer. Allerdings muss man auch hier aufpassen. Siehe AVR-GCC-Tutorial: Warteschleifen.
  • Erscheinen im Terminalprogramm kryptische Zeichen anstatt ordentlichen Buchstaben (z. B. ü statt A), liegt das zu 99,9% an einer falsch eingestellten/erzeugten Baudrate im Mikrocontroller. Meistens wiederum liegt dies daran, dass der µC nicht mit der vermeintlichen Taktrate läuft und daher die Baudratenparameter falsch berechnet werden.
  • URSEL-Bit in UCSRC: Bei der Initialisierung der UART beachtet, wenn das für den verwendeten AVR notwendig ist, siehe AVR-GCC-Tutorial/Der UART: Die UART-Register.
  • In den Tutorials für Assembler und GCC wird die Nutzung recht gut erklärt und mit Beispielen erläutert.
Testprogramm

Im folgenden Programm muss angepasst werden:

  • Die vermeintliche Taktfrequenz F_CPU
  • der Port und der Pin, an dem eine LED angeschlossen ist.

Wird das Programm laufen gelassen, muss die LED 1 Sekunde ein/1 Sekunde aus sein. Stimmen die Zeiten nicht, ist das ein deutlicher Hinweis darauf, dass die tatsächlich vom µC verwendete Taktfrequenz nicht mit der in F_CPU angegebenen übereinstimmt. Das Programm muss mit aktivierter Optimierung -Os compiliert werden.

// Testprogramm für CPU Takt
// Hier die vermeintliche Taktrate des µC eintragen
// Im Beispiel hier: 1MHz

#define F_CPU 1000000

#include <avr/io.h>
#include <util/delay.h>

// Hier die tatsächlich verwendeten Parameter angeben

#define LED_PORT    PORTB
#define LED_DDR     DDRB
#define LED_PIN     PB0

int main()
{
   LED_DDR |= 1 << LED_PIN;

   while (1)
   {
      LED_PORT ^= 1 << LED_PIN;
      _delay_ms(1000);
   }

   return 0;
}

Sonstige Fehlerquellen bei UART/USART

  • Funktioniert die Datenübertragung zum PC nur solange der Programmieradapter eingesteckt ist, deutet dies auf ein Problem mit der Masse hin (z. B. GND-Anschluss am RS232-Stecker nicht belegt oder dergleichen).
  • Der Pegelwandler/Inverter (z. B. MAX232) muss mit Kondensatoren für die internen Ladungspumpen beschaltet werden. Beim MAX232 sind dies mindestens vier Kondensatoren ≥ 1 µF (Polung beachten!), beim MAX202, MAX232A und MAX3232 vier Kondensatoren ≥ 100 nF. Hinzu kommt der obligatorische Abblockkondensator von 100 nF keramisch zwischen VCC und GND des ICs. Das jeweilige Datenblatt gibt Auskunft über die Beschaltung bei "typischer Anwendung".
  • TX/RX vertauscht? Modem- oder Nullmodemkabel?
  • Bei Kommunikation mit PC: Ist die serielle Schnittstelle richtig konfiguriert? Nicht nur Baudrate, Stopp- und Parity-Bits, sondern auch Handshakeverhalten muss stimmen (kein Hardwarehandshake bei ausschließlicher Verwendung der Pins RX/TX).
  • Wird das Signal eventuell durch zusätzliche Chips invertiert? (EEPROM von FT232 falsch Programmiert)
  • Die Hardware und Verkabelung kann man schnell prüfen, indem man den Pin R1OUT bzw. R2OUT mit T1IN bzw. T2IN am MAX232 miteinander verbindet (je nachdem welches Pinpaar verwendet wird). Dann sollte man auf dem PC ein Terminalprogramm wie Hyperterminal starten und nach dem Eingeben von Zeichen diese sofort angezeigt bekommen, egal mit welcher Baudrate. Lediglich die Flusssteuerung muss auf "Kein" eingestellt werden.
  • Man muss aber beachten, dass T1IN bzw. T2IN für diesen Test nicht mit dem Mikrocontroller verbunden sein darf, weil sonst zwei Ausgänge miteinander verbunden sind, siehe Ausgangsstufen Logik-ICs. Wenn der µC gesockelt ist, kann man ihn dafür einfach aus dem Sockel nehmen und die RX/TX Pins direkt im µC-Sockel mit einem Stück Draht verbinden.
  • Ist der UART TXD-Pin als Output konfiguriert?
  • Liegt Spannung an Pin 15 und 16 an? (Falsche Zeichen kommen an, aber in der richtigen Reihenfolge. Beim Test durch Kurzschluss von T1IN und R1OUT kommt nichts an)

SPI (Hardware)

  • Master Mode: SS Pin als Ausgang oder auf High gelegt? (siehe hier: [1] )
  • SPI zu schnell
  • Hängen andere SPI-Devices am Bus, die undefiniertes CS haben?
  • Sind die DDR-Register der SPI-Ports richtig gesetzt?

Analog-Digital-Wandler

  • Sind die Pullup-Widerstände deaktiviert? (im allgemeinen)
  • (im speziellen) Bei einige Controllern liegt der JTAG mit auf den ADC-Pins. Die betroffenen ADC-Kanäle werden erst richtig funktionieren, wenn der JTAG deaktiviert ist. Das geht per Fuse oder - wenn man an die Fuses nicht mehr herankommen sollte, weil man mit einem Bootloader arbeitet - auch per Software:
/* File jtag_utils.h           *
 * Author N.G. (newgeneration) */
#ifndef JTAG_UTILS_H_
#define JTAG_UTILS_H_

#include <avr/io.h>
#include <util/atomic.h>

// Check for the JTAG-disable-Bit (JTD)
#if defined(JTD)

// Es gibt (mindestens) zwei Moeglichkeiten, in welchem Register
// das JTD-Bit steckt: bei aelteren AVRS im MCUCSR (MCU Control 
// and Status Register), bei neueren im MCUCR (MCU Control Register)
#ifdef MCUCSR
#define JTD_REGISTER MCUCSR
#else
#define JTD_REGISTER MCUCR
#endif

static inline void jtag_disable() __attribute__((always_inline));
static inline void jtag_enable() __attribute__((always_inline));

static inline void jtag_disable() 
{
    // Auszug aus dem Datenblatt:
    /* In order to avoid unintentional disabling or enabling    *
     * of the JTAG interface, a timed sequence must be followed *
     * when changing this bit: The application software must    *
     * write this bit to the desired value twice within four    *
     * cycles to change its value.                              */
    
    // Damit das Bit auch wirklich innerhalb von 4 Zyklen zwei mal
    // gesetzt wird, werden die Interrupts verboten und es wird 
    // inline-assembler verwendet. Damit ist man unabhängig von der
    // Optimierung des Compilers.
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) 
    {
        uint8_t tmp;
        __asm__ __volatile__ (
            "in %[tmp], %[reg]   \n\t"
            "ori %[tmp], %[jtd]  \n\t"
            "out %[reg], %[tmp]  \n\t"
            "out %[reg], %[tmp]  \n\t" :
            [tmp] "=&d" (tmp) :
            [reg] "I" (_SFR_IO_ADDR(JTD_REGISTER)), [jtd] "M" (1 << JTD)
        );
    }
}

static inline void jtag_enable() 
{
    ATOMIC_BLOCK(ATOMIC_RESTORESTATE) 
    {
        uint8_t tmp;
        __asm__ __volatile__ (
            "in %[tmp], %[reg]   \n\t"
            "andi %[tmp], %[jtd] \n\t"
            "out %[reg], %[tmp]  \n\t"
            "out %[reg], %[tmp]  \n\t" :
            [tmp] "=&d" (tmp) :
            [reg] "I" (_SFR_IO_ADDR(JTD_REGISTER)), [jtd] "M" ((uint8_t)~(1 << JTD))
        );
    }
}

#undef JTD_REGISTER

#else /* !defined(JTD) */
#warning "Has this device a JTAG-Interface?"
#error   "The device does not support JTAG disabling in software!"
#endif /* defined(JTD) */

#endif /* defined(JTAG_UTILS_H_) */

Weblinks