Bauen und Programmieren von AVR-Mikrocontrollern am Beispiel von ATMEL ATTiny 2313 und ATMega 8


Start AVR-KursStartseiteserielles ISP-Interface

20. Gern gemachte Fehler
fme = frequently made errors



Arbeiten mit Konstanten

Die Lade-, Vergleichs- und Arithmetikbefehle für Konstanten funktionieren nur für die Universalregister R16 bis R31 (0x10 bis 0x1F). Für die unteren Register R0 bis R15 muss man zum Laden den Umweg über ein oberes Register gehen. Vergleichs- und Arithmetikbefehle für die unteren Register arbeiten auch nur mit einem anderen Register, niemals aber mit Konstanten. Es gibt übrigens keinen Additionsbefehl für Konstanten (für eine Bytebreite). Konstrukte dieser Art müssen durch die Subtraktion der konstanten Gegenzahl umschrieben werden. Die Additions- und Subtraktionsbefehle für Konstanten in Wortbreite (= 2 Byte = 16 Bit) arbeiten nur für die obersten 4 Registerpaare (R24:R25; R26:R27; R28:R29 und R30:R31) und nur für vorzeichenlose Konstanten von 0 bis 63. Dabei ist immer die durch 2 teilbare Registernummer im Befehl als Parameter zu nennen.

Der CLR-Befehl funktioniert für alle allgemeinen Register R0 bis R31.

geht geht nicht Ersatz
LDI        R16,0x4A LDI      R5, 0x4A

LDI     R16, 0x4A
MOV  R5, R16

CPI       R16, 0xF8 CPI     R5, 0xF8 LDI     R16, 0xF8
CP     R5, R16
ANDI    R16, 0b10011101 ANDI  R5, 0b10011101 LDI     R16, 0b10011101
AND  R5, R16
... ... ...
SUBI   R16, 135 SUBI  R5, 135 LDI     R16, 135
SUB   R5, R16
ADD   R16, R17 ADDI  R16, 209 SUBI  R16, -209    oder

LDI     R17, 209
ADD  R16, R17
CLR    R10 - -
ADIW  R24, 42 ADIW   R12, 42
ADIW   R28, 86
ADIW   R27, 0x08
-
SBIW   R30, 1 SBIW   R0, 75 -

 

 

Bitte ein Bit - oder ein Byte?

Bei manchen Befehlen ist als Parameter die Nummer eines Bits gefragt, bei anderen der Wert eines Bytes. Leider ist die Syntax zum Teil verwirrend. Besonders bei den rot hinterlegten Befehlen ist Vorsicht geboten. Während bei SBI und CBI der Assembler wenigstens registriert, wenn der Parameter mit der Bitnummer größer 7 ist, gibt es bei SBR und CBR keinerlei Fehlermeldung. Der Befehl
SBR R16, 5
wird klaglos geschluckt, jedoch an Stelle von:
SBR R16, 0b00100000

interpretiert als:
SBR R16, 0b00000101

und das ist total daneben!

Zu Fehlermeldungen (unzulässiger Bereich) führen auch Bit-Befehle, die I/O-Register im oberen Bereich (ab 0x20) ansprechen. Die Register von 0x20 bis 0x3F müssen daher mit Byteoperationen bedient werden.

Bitorientierte Befehle

SBI: Set Bit in IO-Register
CBI: Clear Bit in IO-Register
SBI PORTD,5 Setze das Bit Nummer 5 in Port D auf 1
Es kann nur ein Bit pro Befehl beeinflusst werden
SBRC: Skip next command if Bit in Register clear
SBRS: Skip next command if Bit in Register set

SBRC R16, 3
RJMP weiter
OUT PORTD, R16

Wenn im Register R16 das Bit Nummer 3 gelöscht ist, wird der RJMP-Befehl übersprungen und mit dem Ausgabebefehl weiter gemacht.
SBIC Skip next command if Bit in IO-Register clear
SBIS Skip next command if Bit in IO-Register set
SBIS DDRD, 3
RJMP weiter
OUT PORTD, R16
Wenn in DDRD das Bit Nummer 3 gesetzt ist, wird der RJMP-Befehl übersprungen und mit dem Ausgabebefehl weiter gemacht.

Die Skip-Befehle können logischerweise nur bitorientiert arbeiten, weil keinen Sinn macht, mehrere gesetzte Bits auf einmal abzufragen. Allerdings geht das nur bei den unteren IO-Registern 0x00 bis 0x1F und bei allen allgemeinen Registern.

Byteorientierte Befehle

SBR: Set Bits in Register
CBR: Clear Bits in Register
SBR R16, 0b00100000
CBR R17, 0b01100100


Setze das Bit Nummer 5 in Register R16 auf 1
Setze das Bit Nummer 5 in Register R17 auf 0
Mit einem Befehl können mehrere Bits gesetzt/rückgesetzt werden.
  IN         R16, TCCR1B
ANDI   R16, 0b11111000
ORI      R16, 1<<CS11
OUT    TCCR1B, R16
Mit einem Befehl können mehrere Bits gesetzt/rückgesetzt werden.

Byteorientierte Befehle müssen zum Setzen und Rücksetzen von Bits in allen allgemeinen und den höheren IO-Registern (0x20 bis 0x3F) verwendet werden.

Falsche Logik
Die Befehle SBRC, SBRS, SBIC, SBIS werden meistens in Verbindung mit einem Sprungbefehl RJMP eingesetzt. Mit der ganzen Springerei und Überspringerei kann sich recht schnell ein Logikfehler einschleichen. Solche Fehler kann man nur vermeiden, wenn man die mit diesem Konstrukt verbundene Wenn-Dann-Sonst-Struktur gut durchdenkt und jeden Fall konkret durchspielt.

Die folgenden Ablaufdiagramme zeigen die Verwendung der Skip-Befehle im Zusammenhang mit der einseitigen und zweiseitigen Alternative.

Auf den SBRS-Befehl folgt immer die RJMP-Anweisung, die im linken Fall den Dannteil überspringt. Im rechten Fall überspringt die RJMP-Anweisung den Dann-Teil zum Sont-Teil, der in die Marke "weiter" mündet. Der Dannteil wird abgearbeitet, wenn der SBRS-Befehl die erste RJMP-Anweisung skipt (=überspringt). Am Ende des Dann-Teils muss ein unmittelbarer Sprung hinter den Sonst-Teil zur Marke "weiter" erfolgen.

 

Register sichern
Dass die in einer Prozedur verwendeten Register auf dem Stack gesichert werden müssen, sieht jeder ein. Dass dieses besonders bei Interruptserviceroutinen (ISR) nötig ist leuchtet auch ein. Was man gerne vergisst, ist das Statusregister SREG, in dem die Bits untergebracht sind, welche bei arithmetischen, logischen und Vergleichsoperationen gesetzt werden um den weiteren Programmablauf zu steuern. Wird dieses Register in einer ISR benutzt, die z. B. einen Vergleichsbefehl (CP, CPI, TST, etc)enthält, dann werden im SREG Bits gesetzt oder rückgesetzt und in diesem veränderten Zustand an das aufrufende Programm übergeben, wenn die ISR zu ende ist. Folgt jetzt im aufrufenden Programm ein Branch-Befehl (BREQ, BRNE, BRSH ...) dann kann durch ein falsch gesetztes Statusbit das Programm in eine nicht gewünschte Richtung gelenkt werden. Deshalb ist es nötig, dass auch das SREG auf den Stack gerettet wird, falls die Procedur Statusbits verändert. Das ist der Fall, wenn in der Procedur logische, arithmetische oder Vergleichsbefehle explizit (AND, OR, ADD, SBC, CP) oder verdeckt (TST) ausgeführt werden. Natürlich verändern auch Befehle wie CLC, CLR, SEN, SEZ, INC, DEC das Statusregister. Keine Beeinflussung erfolgt durch Lade- und Speicherbefehle (LD, ST, MOV..)

In dieser ISR wird zunächst der Inhalt von Register temp1 auf dem Stack gesichert um dann den Inhalt von SREG aufzunehmen. Dann wird temp1 mit dem Inhalt des Statusregisters ein weiteres Mal auf den Stack gesichert, danach temp2. Am Ende der ISR bei ana_comp_exit werden die Register in genau der umgekehrten Reihenfolge restauriert.

 

Parameter zuviel

Ein netter Fehler, der gleich einen ganzen Rattenschwanz an Fehlermeldungen der folgenden Art hervorruft ist in diesem Programmtext versteckt.:

Nach dem Assemblieren tauchen folgende Meldungen auf:

Die Ursache ist der CLI-Befehl. Dieser Befehl existiert zwar, er löscht aber das globale Interruptflag und erwartet keinen Parameter. An seiner Stelle sollte eigentlich der ähnlich geschriebene Befehl CBI stehen, der das Löschen eines Bits in einem IO-Register zur Folge hat und daher die zwei Parameter testport und testpin erwartet.

Der entscheidende Hinweis auf die Fehlerursache steht ganz am Anfang der 29 Einträge enthaltenden Fehlerliste: "Wrong number of operands".

Sensibilisiert durch den Hinweis ruft man die zugehörige Programmzeile auf und erkennt hoffentlich sehr schnell den Schreibfehler.

 

CLR, SET und SER

Den CLR-Befehl habe ich schon weiter oben betrachtet. Er tut das, was der Name sagt für alle allgemeinen Register, er löscht alle Bits, setzt sie also auf 0.

Ganz daneben liegt der, welcher meint, der SET-Befehl würde nun alle Bits setzen. Tatsächlich wird genau ein Bit gesetzt und zwar ist das das T-Bit im Statusregister (SREG). Dieses T-Bit dient zusammen mit den Befehlen BLD und BST zum Transfer eines Bit von einem Register in ein anderes.

Das Gegenstück zum CLR-Befehl ist der SER-Befehl (Set Bits in Register), der aber bereits wieder eine Extrabehandlung braucht, weil er nur für die oberen allgemeinen Register (R16 bis R31) funktioniert.

 

Keine Reaktion beim Einlesen von einem Port

An einen Port angeschlossene Leitungen werdn mit einem IN-Befehl abgefragt, das ist klar. Essentiell ist aber auch, dass das Input-Register des Ports PINX (X steht für den Kennbuchstaben des Ports: B oder D beim Tiny 2313) eingelesen wird. Liest man z. B. von der Adresse PORTD statt von PIND, dann erhält man als Eingabewert das Byte, das im Ausgangsregister des Ports steht.

 

Merkwürdige Portausgabe

Wieso schafft die Leitung eines Ports es , meine LED, die ich über ein Darlingtonarray ansteuere, zum Leuchten zu bringen. Ich teste den Port ohne Beschaltung - aus: 0V - ein: 5V alles OK ~?:-(( Ich schließe den Darlingtoneingang an und messe - aus: 0V - ein: 2V - Mist, kapute Portleitung?   :-((

Nein, ganz gemeiner Fehler! Das scheinbar korrekte Verhalten ohne Belastung des Ausgangs kommt daher, dass der Port als Eingang programmiert war und mit dem Einschalten des Portbits lediglich der interne Pullupwiderstand eingeschaltet wurde. Bei externer Belastung sinkt die Leerlaufspannung von 5V daher auf den niedrigeren Wert 2V, der zum Durchschalten des Darlingtontransistors nicht mehr reicht. Die LED bleibt dunkel. Nach dem Setzen des korrespondierenden Bits im DDR war die Leitung nun wirklich als Ausgang programmiert und die LED reagierte wie vorgesehen. ;-))

Weitere primitive Fehler

Was laufend vorkommt, wenn man im entscheidenden Moment abgelenkt ist oder wird:

 

 


Start AVR-KursAn den Seitenbeginnnicht verfügbar