Forum: Mikrocontroller und Digitale Elektronik Anfänger bitte um Erklärung von Codebeispiel


von Markus (Gast)


Lesenswert?

Hi Allz,

arbeite mich seit einigen Wochen in die Hard- und Software rund um den
Atmel ein. Nachfolendes Codebeispiel hab ich im Web
(http://www.avr-asm-tutorial.net/) gefunden:

(Bemerkungen stammen von mir)
.include "2313def.inc"
.DEF  mp = R16
.DEF  z1 = R0
rjmp  main
main:
ldi mp,LOW(RAMEND)  ;Stack
out SPL,mp      ;einrichten
;ldi mp,HIGH(RAMEND);haben aber
;out SPH,mp      ;8bit
ldi mp,0      ;laden 0 in R16
mov z1,mp      ;und ab nach R0 -> R0-R15 kann man nicht direkt
addressieren
ldi mp,0x05      ;Wert 5
out tccr0,mp    ;'in Timer schreiben ->Takt / 1024
ldi mp,0b00000010  ;LED an Pin15 als
out ddrb,mp      ;Ausgang schalten
ldi mp, 0x00    ;LED
out portb, mp    ;ist aus

loop:
in mp,TCNT0      ;lade in R16 Inhalt von TCNT0 (TimerControl Register 0)
cpi mp,0      ;vergleiche ob 0
brne loop      ;wenn nicht loope
rcall IncZ1      ;ab nach IncZ1
rcall Display    ;ab nach Display


warte:
IN mp,TCNT0      ;lade in R16 Inhalt von TCNT0 (TimerControl Register 0)
cpi mp,0      ;vergleiche ob 0
breq warte      ;Schleife warte bis Wert 0 hat
rjmp loop      ;ab nach loop

IncZ1: INC z1    ;Wert in Z1 um eins erhöhen
ret

Display:
mov mp,z1      ;Wert von z1 nach mp kopieren
com mp        ;bitweise invertieren - aus 1 wird 0 ...
out portb,mp    ;schreiben wir in PortB
ret

Was ich nicht verstehe ist das Schleifenkonstrukt. Bis zur Subroutine
loop ist mir eigentlich alles klar. In Loop wird gewartet bis im
TimerControlRegister 0 der Wert 0 erreicht wird. Dann wird Z1 in der
Subroutine um 1 erhöht. Frage 1: Warum? Anschliessend wird die LED in
der Display Routine ein- bzw. ausgeschaltet. Frage 2: Warum wird in der
Display Routine der Inhalt von z1 in mp kopiert? Invertierung und
Ausgabe sind wieder klar. So, jetzt läuft das Programm in die warte
Routine. Hier wird wieder auf den Timer 0 gewartet, oder? Diesmal halt
anders umgesetzt, ich vermute mal zu Lernzwecken um die
unterschiedlichen Methoden zu demonstrieren. Anschliessend wieder nach
loop springen. Versteh ich das richtig: Nach jeder Änderung in der
Display Routine wird 3 X Taktfrequenz/1024 gewartet?

Fragen über Fragen, ich hoffe jemand liest sich diesen Roman überhaupt
durch.

Gruss

von Rufus T. Firefly (Gast)


Lesenswert?

Erste Anmerkung:
loop ist keine Subroutine, sondern nur ein Sprunglabel.
Das BASIC-Äquivalent ist Ziel einer GOTO-Anweisung.

IncZ1 und Display sind Subroutinen (die nämlich werden mit rcall
aufgerufen) und enden folglich mit ret.
Das BASIC-Äquivalent ist das Ziel einer GOSUB-Anweisung.

von Santa Klaus (Gast)


Lesenswert?

Hallo,

>Was ich nicht verstehe ist das Schleifenkonstrukt. Bis zur Subroutine
>loop ist mir eigentlich alles klar. In Loop wird gewartet bis im
>TimerControlRegister 0 der Wert 0 erreicht wird. Dann wird Z1 in der
>Subroutine um 1 erhöht. Frage 1: Warum?

Zweck dieses Programm ist es, die LED blinken zu lassen.  Dazu wird Z1
fortwährend hochgezählt ("inkrementiert"): 0, 1, 2, 3 ... 254, 255,
0, 1, 2 ... ohne Ende wiederholt (Sprung von 255 auf 0 klar?).  Aus
diesem Dauer-Inkrementieren kann man nun leicht ein Blinken
(An-Aus-An-Aus...) "ableiten", indem man einfach nur ein einzelnes
Bit von Z1 betrachtet: Jedes der acht Bits von Z1 "blinkt" nämlich!
Aber nicht alle gleich schnell, sondern jedes Bit eins weiter links
blinkt mit der halben Frequenz. In dem Codebeispiel wurde über die
Zeilen

ldi mp,0b00000010  ;LED an Pin15 als
out ddrb,mp      ;Ausgang schalten

das Bit Nummer 1 ausgewählt (an Pin 1 ist dann die LED angeschlossen).
Ersetzen der "0b00000010" durch z. B. "0b01000000" und Umstecken der
LED auf Pin 6 würde zu einem 32 mal langsameren Blinken führen (32 klar?
Von 1 bis 6 sind 5 Schritte und 2^5 = 32).

>Anschliessend wird die LED in
>der Display Routine ein- bzw. ausgeschaltet.

Korrekt; die LED zeigt den Zustand des Bits Nr. 1 der Variablen Z1 an.

>Frage 2: Warum wird in der
>Display Routine der Inhalt von z1 in mp kopiert? Invertierung und
>Ausgabe sind wieder klar.

Von mp wird das Einerkomplement gebildet (d. h. alle Bits von mp werden
getoggelt).  Diese Operation möchte man nicht auf Z1 durchführen, weil
man Z1 unverändert lassen will.  Deshalb dupliziert man den Inhalt in
von Z1 in mp und kann dann mit mp machen, was man will (sonst müßte man
die Änderung an Z1 wieder rückgängig machen -> fehleranfällig -> nicht
gut).

>So, jetzt läuft das Programm in die warte
>Routine. Hier wird wieder auf den Timer 0 gewartet, oder? Diesmal
>halt anders umgesetzt, ich vermute mal zu Lernzwecken um die
>unterschiedlichen Methoden zu demonstrieren.

Nein, nicht zu Lernzwecken.  Der Timer läuft mit einem Vorteiler von
1024.  Wenn das Progamm aus der Displayroutine "zurückgekehrt" ist,
hat der Timer deshalb immer noch den Stand Null!  Er wird ihn auch noch
für einige Zeit (geschätzt 1000 Taktzyklen) lang behalten, bis er
endlich auf 1 springt.  Eben dieses Springen auf die 1 wird in der
Warteroutine abgewartet.  Also: Der Zähler zählt nach dem Muster

0, 0, 0, ..., 0, 1, 1, 1, ..., 1, 2, 2, 2, ... 2, 3, 3, 3 .......
^

(wobei die "..." für je 996 mal die Zahl stehen; von einer Zahl zur
nächsten = ein Quarztakt)

aber der Z1-Zähler soll nur beim Auftreten der ERSTEN Null (die mit
"^" markierte) inkrementiert werden, und nicht beim Auftreten ALLER
Nullen.  Um dies zu erreichen, ist die "warte"-Schleife notwendig.

von Markus (Gast)


Lesenswert?

@Santa Klaus:
Vielen Dank für deine Ausführungen, das meiste hab ich nun kapiert!
Lediglich die Ausführung der zweiten "warte" Schleife ist mir noch
nicht so klar. Könntest du nochmal bitte auf deine Ausführungen
eingehen:

>Nein, nicht zu Lernzwecken.  Der Timer läuft mit einem Vorteiler von
>1024.  Wenn das Progamm aus der Displayroutine "zurückgekehrt" ist,
>hat der Timer deshalb immer noch den Stand Null!  Er wird ihn auch
>noch
>für einige Zeit (geschätzt 1000 Taktzyklen) lang behalten, bis er
>endlich auf 1 springt.  Eben dieses Springen auf die 1 wird in der
>Warteroutine abgewartet.  Also: Der Zähler zählt nach dem Muster
>
>0, 0, 0, ..., 0, 1, 1, 1, ..., 1, 2, 2, 2, ... 2, 3, 3, 3 .......
>^
>
>(wobei die "..." für je 996 mal die Zahl stehen; von einer Zahl zur
>nächsten = ein Quarztakt)

Danke für deine Bemühungen und die Geduld!

von Santa Klaus (Gast)


Lesenswert?

Mhh, arbeitest Du vielleicht mit dem AVR Studio? Dann wäre es am
einfachsten, wenn Du einfach mal im Simulator verfolgen würdest, wie
sich das mit der Zählerei verhält.  Du würdest die Sache dann sofort
verstehen (kannst ja die warte-Schleife auskommentieren und gucken, was
passiert).  Kannst Du diesen Vorschlag realisieren?

PS: Den Simulator brauchst Du eh, wenn Du ernsthaft programmieren
willst.  Da wäre das direkt eine gute Gelegenheit, sich mit diesem in
allen "Krisensituationen" äußerst nützlichen Werkzeug vertraut zu
machen.

von Markus (Gast)


Lesenswert?

Hi,

ich werd mir das Studio mal besorgen und sehen wie weit ich komme.

Danke für den Tip.

Gruss

von Markus (Gast)


Lesenswert?

@Santa Klaus:

Danke nochmal, ich glaub jetzt sitzt es!

Gruss

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.