| Weitere Artikel aus dem Elo-Magazin |
Ausgangsports
von B. Kainka, aus dem Lernpaket Mikrocontroller
Damit sichtbar etwas passiert, soll nun ein Port als Ausgang eingerichtet und eingeschaltet werden. Alle Hardware-Aktionen werden über Register im Controller gesteuert. Das Datenblatt tiny13.pdf befindet sich auf der CD und gibt dazu umfassende Auskunft. Hier geht es um zwei Register, das Datenrichtungsregister DDRB und das Ausgaberegister PORTB.
Damit der Assembler weiß, welches Register an welcher Adresse liegt, muss die Datei "tn13def.inc" in das Projekt eingefügt werden. Dazu dient die Compiler-Anweisung .include "tn13def.inc". Die Datei liegt im Verzeichnis C:ProgrammeAtmelAVR ToolsAvrAssemblerAppnotes und wird automatisch gefunden. Sie kann mit einem Editor angesehen werden und enthält neben vielen anderen die folgenden beiden Zeilen:
PORTB
=$18.equ
DDRB
=$17Ganzen Artikel lesen...

Der Assembler setzt also z.B. die Adresse $18 (Hexadezimal 18 = dezimal 26) an jeder Stelle ein, die im Listung das Wort PORTB enthält. Allgemein ist Groß- und Kleinschreibung freigestellt.
Erweitern Sie nun den Quelltext LED.asm wie im folgenden Listing. Übersetzen Sie es dann neu und laden Sie die Datei LED.hex mit der Upload-Funktion in LPmikro.exe. Am Port PB3 soll eine LED mit Vorwiderstand 1 kΩ angeschlossen sein. Sobald das Programm geladen und gestartet ist, leuchtet die LED. Falls es noch Probleme mit dem Editieren oder Assemblieren gibt oder Sie aus anderen Gründen auf ein fertiges Projekt zurückgreifen möchten, können Sie auch die Datei LED2.hex aus dem Projektverzeichnis LED2 verwenden, die schon fertig übersetzt vorliegt.
LED
mit
Vorwiderstand
an
PB3
.include
"tn13def.inc"
rjmp
AnfangAnfang:

sbi
ddrb,3
;Datenrichtungsbit
sbi
portb,3
;PB3=1,
einschaltenSchleife:

rjmp
Schleife
Das Programm schaltet den Anschluss PB3 ein. Zum besseren Verständnis sollen die einzelnen Zeilen nun genauer erklärt werden. Am Anfang steht ein Kommentar, der nicht mit übersetzt wird und nur zur besseren Lesbarkeit des Quelltextes dient. Kommentare werden mit einem Semikolon eingeleitet und dürfen an beliebiger Stelle im Text stehen. Danach folgt die schon erwähnte Compiler-Anweisung zur Einbinden der Datei "tn13def.inc".
Das Programm enthält zwei Adressmarken (Label) „Anfang" und „Schleife". Der erste echte Assemblerbefehl im Quelltext lautet „rjmp Anfang". „rjmp" steht für Relative Jump, also für einen Sprung mit einer relativen Adresse, d.h. mit der Angabe einer Sprungweite. Das Programm verzweigt also nach dem Start zur Adresse „Anfang". Im Prinzip könnte man diese Zeile entfernen ohne die Funktion des Programms zu verändern. Allerdings ist der erste Sprung wichtig für die korrekte Funktion des Upload-Programms. Es setzt nämlich hier beim Hochladen einen Sprung auf das Bootprogramm ein und vermerkt den wirklichen Anfang des Programms an anderer Stelle. Alle Programme im Lernpaket beginnen deshalb mit einem Sprungbefehl.
Alle Assembler-Befehle sind in der englischen Hilfe zum AVR Studio aufgelistet. Markieren Sie den Befehl „sbi" mit der Maus und drücken Sie F1. Es erscheint ein Hilfefenster mit einer genauen Definition. Außerdem erhalten Sie einen vollständigen Überblick über alle verfügbaren Assemblerbefehle und ihre Funktion.
Die Hilfe im AVR Studio
Der Befehl "SBI - Set Bit in I/O Register" schaltet ein Bit im angegebenen Register ein. Er wird hier zweimal verwendet. Zuerst wird das Bit 3 in DDRB eingeschaltet um die Ausgabe-Datenrichtung einzuschalten, dann das Bit 3 in PORTB um den Portzustand auf Eins zu schalten. Damit wird die LED eingeschaltet.
Am Ende des Programms finden Sie wieder eine Endlosschleife mit einem Sprung zur Label „Schleife". Dies ist wichtig, um ein definiertes Ende des Programms zu erreichen. Es muss verhindert werden, dass der Controller am Ende noch Befehle ausführt, die als Reste früher geladener Programme im Speicher liegen. Ohne die Schleife am Ende könnten zufällige Aktionen ablaufen, die nicht vorhersagbar sind.
Wenn Sie sehen wollen, was der Assembler aus Ihrem Quelltext gemacht hat, reicht ein Blick in das Programmfenster im Uploadbereich von LPmikro. Sie sehen hier acht hexadezimal dargestellte Bytes. Jeweils zwei Bytes gehören zu einem Befehl, weil AVR-Controller die Befehle zusammen mit ihren Argumenten jeweils mit 16 Bit codieren.
00
000000
00
C0
BB
9A
C3
9A
FF
CF0000
Die übertragenen Bytes
Alternativ kann man sich auch die Datei LED.hex in einem Editor anschauen. Sie enthält die selben Bytes zusammen mit einem Rahmen, Adresse und Prüfbyte.
:0800000000C0BB9AC39AFFCFB8
:00000001FF
Inhalt der Datei LED.hex
Bits und Bytes
Oft ist es nötig, nicht nur ein einzelnes Bit eines Ausgangsports einzuschalten, sondern gleich mehrere. In diesem Fall übergibt man dem Register PORTB acht Bits als ein Byte, also eine Zahl Bereich 0 bis 255. Der Befehl OUT (Store Register to I/O Location, Registerinhalt in IO-Adresse speichern) kopiert den Inhalt eines Registers, das zuvor mit der gewünschten Zahl geladen wurde. Der Mikrocontroller besitzt 32 Register R0 bis R31. Nur die Register R16 bis R31 erlauben das direkte Laden mit einer konstanten Zahl. Dazu dient der Befehl LDI (Load Immediate, direktes Laden). Zahlen können unterschiedlich angegeben werden:

10,
255
Hexadezimal:
0x0a,
0xff
Alternative
Schreibweise:
Hexadezimal:
$0a,
$ff
Binär:


0b00001010,
0b11111111
Das Programm LED3.asm soll die Ausgänge PB3 und PB4 einschalten. Bit 3 hat die Wertigkeit 2^3=8, Bit 4 entsprechend 2^4=16. Die Summe beider Wertigkeiten ist 24. Das Programm muss also die Zahl 24 in DDRB und PORTB schreiben. Alternativ zur Dezimalzahl kann auch die Binärzahl 0b00011000 oder die Hexadezimalzahl 0x18 bzw. $18 verwendet werden.
LED
mit
Vorwiderstand
an
PB3
oder
PB4
.include
"tn13def.inc"
rjmp
AnfangAnfang:

ldi
r16,0b00011000
out
ddrb,r16
;Datenrichtung
ldi
r16,0x18
;0x18
=
0b00011000
=24
out
portb,r16
;PB3=1
und
PB4=1Schleife:

rjmp
Schleife
Einschalten mehrerer Ausgänge
4.4 Ein Blinkprogramm
Als nächste Programmierübung soll die LED am Port PB3 immer wieder ein- und ausgeschaltet werden. Zum Ausschalten wird der neue Befehl CBI (Clear Bit in IO Register, ein Bit im angegebenen Register löschen). Warteschleifen sollen den Vorgang etwas verlangsamen. Man erkennt im Listing deutlich die beiden Warteschleifen mit den Sprungmarken Warten1 und Warten2.
LED
mit
Vorwiderstand
an
PB3
.include
"tn13def.inc"rjmp
AnfangAnfang:

sbi
ddrb,3
;DatenrichtungsbitSchleife:

sbi
portb,3
;PB3
=
1
ldi
r16,255
;r16
laden
Warten1:

dec
r16
;r16
-
1

brne
Warten1

cbi
portb,3
;PB3
=
0
ldi
r16,255
;r16
laden
Warten2:

dec
r16
;r16
-1

brne
Warten2
rjmp
Schleife
Das Programm Blink1.asm
Eine Warteschleife verwendet jeweils ein allgemeines Arbeitsregister (hier Register R16) des Controllers. Zunächst wird es mit einem Startwert geladen. Da bei einem 8-Bit-Mikrocontroller nur Bytes verarbeitet werden können, ist der größte mögliche Wert 255. Achtung, der Befehl LDI (Load Immediate, direktes Laden) ist nicht auf alle Register anwendbar, sondern nur auf die Register R16 bis R31. Mit ldi r16, 255 wird also die Zahl 255 ins Register R16 geladen. In der Warteschleife zählt der Befehl DEC (Decrement, Verkleinern) jeweils um Eins zurück. Der bedingte Sprungbefehl BRNE (Branch if Not Equal, Verzweige, wenn nicht gleich Null) verzweigt immer wieder zum Anfang der Schleife, solange der Inhalt des Registers noch größer als Null ist. Sobald das Register 255 mal verkleinert wurde und den Wert Null enthält, unterbleibt der Sprung, sodass der auf die Schleife folgende Befehl ausgeführt wird. In diesem Programm gibt es zwei Warteschleifen, jeweils direkt nach dem Einschalten und nach dem Ausschalten des Ports.
Laden Sie das Programm Blink1.hex in den Controller. Verwenden sie eine LED mit Vorwiderstand wie in Abb. 3.2. Die Hoffnung auf ein sichtbares Blinken wird zunächst enttäuscht. Die LED leuchtet dauerhaft, wenn auch etwas schwächer als gewohnt. Tatsächlich blinkt die LED noch viel zu schnell. Wenn Sie jedoch die Platine schnell hin- und her bewegen, können Sie ein Flackern sehen.
Unterprogramme
Das Blinkprogramm soll mit einer doppelten Warteschleife langsamer laufen. Dabei sollen nicht nur 255 Schleifendurchläufe sondern bis zu 255*255 = ca. 65000 Durchläufe verwendet werden. Dazu braucht man zwei Schleifen, die ineinander geschachtelt sind. In der äußeren Schleife wird der Zähler der inneren Schleife immer wieder neu mit seinem Startwert geladen.
Eine solche geschachtelte Schleife wird wieder zweimal gebraucht. In solchen Fällen lagert man Programmteile gern in Unterprogramme aus, um sie nur einmal schreiben zu müssen. Das Unterprogramm wird von beliebigen Stellen aus mit RCALL (RCALL - Relative Call to Subroutine, Relativer Aufruf eines Unterprogramms) angesprungen. Der Controller merkt sich dabei die Adresse, von der aus gesprungen wurde in einem besonderen Speicherbereich, dem so genannten Stapel (Stack, siehe Kap. 8.1). Am Ende des Unterprogramms muss RET (Return from Subroutine, Rücksprung vom Unterprogramm) stehen. Dann wird die Rücksprungadresse vom Stapel genommen, und das Programm führt die Anweisung nach dem RCALL-Befehl aus.
Blinker
mit
Unterprogramm
.include
"tn13def.inc"rjmp
AnfangAnfang:

ldi
r16,0x18
;PB4
und
PB4
out
ddrb,r16
;DatenrichtungSchleife:

ldi
r16,8

;8
=
0x08
out
portb,r16
;PB3
=
1,
PB4
=
0
rcall
Warten
;Unterprogrammaufruf
ldi
r16,16
;16
=
0x10
out
portb,r16
;PB3
=
0,
PB4
=
1
rcall
Warten
;Unterprogrammaufrufrjmp
SchleifeWarten:


Ldi
r16,250

Warten1:
;äußere
Schleife
Ldi
r17,250Warten2:
;innere
Schleife
dec
r17
brne
Warten2
dec
r16
brne
Warten1

ret


;Rücksprung
Verwendung eines Unterprogramms
Mit der doppelten Warteschleife tut das Programm genau das, was es soll: Die LED blinkt nun deutlich sichtbar. Sie können mit den Startwerten der inneren und der äußeren Zählschleife experimentieren und das Programm mit unterschiedlichen Geschwindigkeiten laufen lassen. Eine gute Übung ist auch die Erweiterung auf eine dreifach geschachtelte Warteschleife.
In der Schleife setzt das Programm abwechselnd das Bit 3 und das Bit 4 in PORTB. Deshalb blinkt nicht nur der Ausgang PB3, sondern er wechselt sich mit PB4 ab. Es entsteht also ein Gegentaktblinker. Sie können wahlweise den Ausgang PB3 oder PB4 anschließen oder auch eine zweite LED blinken lassen, die allerdings nicht im Material des Lernpakets enthalten ist.
4.6 Geschwindigkeitstest
Die Ausführungsgeschwindigkeit ist letztlich vom internen Taktoszillator des ATtiny13 abhängig. Sie können Rückschlüsse auf die Genauigkeit des Oszillators ziehen, wenn Sie ein Programm mit genau bekanntem Zeitbedarf schreiben. Das Blinkprogramm soll nun so beschleunigt werden, dass genau zehn Prozessortakte für einen vollständigen Durchlauf benötigt werden.
Das Programm erklärt sich selbst. Neu ist nur der Befehl NOP (No Operation, Nichts tun), der genau einen Prozessortakt benötigt. Der Prozessortakt ist bei AVR-Controllern gleich dem Befehlstakt. Die meisten Befehle benötigen also nur einen Takt. Ausnahmen bilden Sprungbefehle und Bitmanipulationen wie SBI und CBI. Die Hilfe gibt für jeden befehl an, wie viele Taktzyklen verbraucht werden. Sie sind im Listing als Kommentare angegeben. Insgesamt verbraucht die Schleife genau 10 Takte.
Clock/10
an
PB3
.include
"tn13def.inc"rjmp
AnfangAnfang:

sbi
ddrb,3
Loop10:

sbi
portb,3
;2

nop
;1

nop
;1

nop
;1

nop
;1

cbi
portb,3
;2

rjmp
loop10
;2


;Summe
10
Takte
Rechteckausgabe mit 120 kHz
Eine angeschlossene LED zeigt nun etwa halbe Helligkeit, weilt die Ausgangsfrequenz sehr hoch ist. Mit einem Oszilloskop oder einem Frequenzzähler kann aber nun die Genauigkeit des internen Oszillators überprüft werden. Wegen der verwendeten Taktrate von 1,2 MHz sollte am Ausgang P3 ein Signal mit 120 kHz erscheinen.
Es geht aber auch ohne teure Messgeräte. Schließen Sie ein 20 cm langes Stück Draht als Sendeantenne an. Das Signal ist dann auf einem gewöhnlichen Mittelwellenradio zu empfangen. Möglich ist der Empfang auf allen ungeraden Vielfachen der Ausgangsfrequenz. Da Sie mit einem Takt von 1,2 MHz arbeiten und damit 120 kHz ausgeben, ist das Signal z.B. bei 5 * 120 kHz = 600 kHz zu empfangen.
Da es sich um ein unmoduliertes Signal handelt, hört man meist nichts, erkennt aber an der Feldstärkeanzeige oder an einem geringeren Rauschen das Signal. Falls zufällig ein Mittelwellensender im gleichen Bereich empfangen werden kann, kommt es zu einer Interferenz beider Signale, die man im Normalfall als Pfeifen hört. Hier ist es eher ein etwas verrauschtes Zwitschern, weil der RC-Oszillaltor nicht die Stabilität eines Quarzoszillators erreicht. Die Genauigkeit ist fast immer besser als 3 %.











