Forum: Mikrocontroller und Digitale Elektronik Assambler Erweitertes Lauflicht


von ac93 (Gast)


Lesenswert?

Hi,

ich brauche mal eine erklärung.

Ich habe mal dieses Programm geschrieben:

.include "tn13def.inc"

.def ref = r16

rjmp Schleife

Schleife:

  rcall walkcenter

rjmp Schleife


walkcenter:

  rcall led1
  rcall led12
  rcall led2
  rcall led11
  rcall led3
  rcall led10
  rcall led4
  rcall led9
  rcall led5
  rcall led8
  rcall led6
  rcall led7
  ret

Led1:
    ldi ref,17
    out ddrb,ref
    ldi ref,1
    out portb,ref
    rcall Delay
    ret

Led2:
    ldi ref,17
    out ddrb, ref
    ldi ref,16
    out portb,ref
    rcall Delay
    ret

Led3:
    ldi ref,18
    out ddrb,ref
    ldi ref,2
    out portb,ref
    rcall Delay
    ret

Led4:
    ldi ref,18
    out ddrb,ref
    ldi ref,16
    out portb,ref
    rcall Delay
    ret

Led5:
    ldi ref,20
    out ddrb,ref
    ldi ref,4
    out portb,ref
    rcall Delay
    ret

Led6:
    ldi ref,20
    out ddrb,ref
    ldi ref,16
    out portb,ref
    rcall Delay
    ret

Led7:
    ldi ref,9
    out ddrb,ref
    ldi ref,1
    out portb,ref
    rcall Delay
    ret

Led8:
    ldi ref,9
    out ddrb,ref
    ldi ref,8
    out portb,ref
    rcall Delay
    ret

Led9:
    ldi ref,10
    out ddrb,ref
    ldi ref,2
    out portb,ref
    rcall Delay
    ret

Led10:
    ldi ref,10
    out ddrb,ref
    ldi ref,8
    out portb,ref
    rcall Delay
    ret

Led11:
    ldi ref,12
    out ddrb,ref
    ldi ref,4
    out portb,ref
    rcall Delay
    ret

Led12:
    ldi ref,12
    out ddrb,ref
    ldi ref,8
    out portb,ref
    rcall Delay
    ret


Delay:
    ldi r17,75

DelayA:
    ldi r18,255

DelayB:
    dec r18
    brne DelayB
    dec r17
    brne DelayA
    ret


Nun meine Frage zum Ziel:

Ich will, dass led1 und led12 gleichzeitig an gehen. Die leds sind in 
einer Multiplex schaltung aufgebaut.

genauso sollen led2 und led11, usw gleichzeitig an gehen, sodass sich 
led6 und led7 in der "mitte" treffen.

ich bei diesem programm geht erst 1, dann 12, dann 2, dann 11 usw. an. 
Da das aber nicht sonderlich mein ziel erfüllt suche ich hier nach einer 
Lösung.

ich habe schon mehreres ausprobiert mit
z.b.

ldi r17,20
rcall led1
rcall led12
dec r17
brne....

in der hoffnung dass so led1 und led12 abwechselnd schnell an und aus 
gehen, sodass es aussieht als würden sie "dauerleuchten".

dann habe ich noch probiert, im selben system immer led1 und led2, led2 
und led3, led3 und led4 usw. als lauflicht zu programmieren.
Das habe ich bis jetzt leider auch nicht hinbekommen.

Kann mir jemand helfen und erklären was der Chip bei jedem Befehl macht?

Danke schonmal im Vorraus.

: Verschoben durch User
von Christian (Gast)


Lesenswert?

Ein Schaltplan wäre nicht schlecht..

von Karl H. (kbuchegg)


Lesenswert?

ac93 schrieb:

> Nun meine Frage zum Ziel:
>
> Ich will, dass led1 und led12 gleichzeitig an gehen. Die leds sind in
> einer Multiplex schaltung aufgebaut.


ehe du das tust, solltest du dir erst mal darüber Gedanken machen, wie 
du dein Programm massiv vereinfachen kannst.

Bildlich gesprochen hast du gerade die Addition entdeckt und willst 
jetzt Flugzeugtriebwerke berechnen, wobei du alle Multiplikationen durch 
entsprechend viele Additionen ersetzt, weil das das einzige ist, was du 
hinkriegst.

AVR-Tutorial


Dein Lauflicht kann MASSIV vereinfacht werden!
Zum einen sind die LED immer an den gleichen Ports. Es besteht daher 
überhaupt keine Notwendigkeit immer das DDRB umzustellen. Die 
Einstellung der DDR Register erfolgt im Normalfall am Anfang des 
Programms und bleibt dann immer gleich. Nur in ein paar Ausnahmefällen 
ist man gezwungen, einen Portpin mal von Eingang auf Ausgang oder vice 
versa umzustellen. Aber soweit bist du noch lange nicht.

Zum anderen fehlt dir zum vernünftigen Multiplexen noch mindestens eine 
andere Zutat: nämlich Timer und deren Interrupt Routine.
Auch wenn man Multiplexen ohne hinbekommt, sinnvoll ist es nicht.
Die Timerinterruptroutine geht ständig alle LED durch und schaltet sie 
ganz kurz ein. Schnell genug dass wir das nicht sehen.

Davon losgelöst, kommt dann deine spezielle Lauflichtstrategie mit den 
wandernden LED ins Spiel.

Du musst hier die Dinge trennen. Auf der einen Seite ist der Multiplex, 
der dir erlaubt, jede beliebige Kombination deiner LED aufleuchten zu 
lassen auch wenn das zunächst hardwaretechnisch gar nicht möglich ist.

Und auf der anderen Seite steht dann die darauf aufbauende Anwendung 
eines Lauflichts, die einfach nur sagt: diese und jene LED jetzt 
einschalten bzw. ausschalten und sich nicht mehr darum zu kümmern hat, 
mit welchem Trick (nämlich Multiplex) das realsiert ist.

Link-Tip: AVR-Tutorial

von Oberlehrer (Gast)


Lesenswert?

Es heisst: Assembler

von oldmax (Gast)


Lesenswert?

Hi
Ich vermisse die Zuweisung vom Stack. Vermutlich hast du noch gar nicht 
bemerkt, das dein Programm nicht so läuft, wie es soll, sondern irgend 
was macht....
Grundsätzlich: in Assembler muß der Stack gesetzt werden. Hochsprachen 
wie BASCOM und C erledigen das von selbst, aber nicht Assembler. Auch 
die Include-Datei wird das nicht einhalten. Lies mal die Tutorials 
durch.
Zum Programm, du kannst es so machen, klar. Aber K.H.B. Hat recht, es 
geht wesentlich einfacher. Und nun bist du dran, zu erkennen, wie du 
dein Programm anders aufbaust. Zuerst einmal optische Ordnung.
Du kannst mit Kommentarzeilen vieles deutlich machen. Z.B.
;***********************************
;* Dies ist das Hauptprogramm      *
;***********************************

;***********************************
;* Ausgabe am Port X               *
;***********************************

Delay's solltest du dir gar nicht erst angewöhnen. Zumindest nicht 
solche, die dein Programm aufhalten. Dafür gibt es Timer, aus deren 
Interrupt verschiedene Zeiten abgeleitet werden können.
So kannst du Bspw. im Programm für eine Zeitverzögerung ein Bit setzen 
und eine Variable auf einen Wert. In der Timer-ISR fragst du ab, ob das 
Bit gesetz ist und zählst die Variable herunter. In deinem Programm 
kontrollierst du den Counter auf 0 und gesetztem Bit. Wenn die Zeit 
abgelaufen ist, stößt du die Aktion an, die dann ausgeführt werden soll 
und setzt das Bit für die Zeit zurück.
Angenommen, dein Timer ISR bringt jede ms einen Interrupt.
Du brauchst ein paar Variable:
ms0, ms1,ms2,sekunde, minute.... was auch immer.
1
Timer_ISR:
2
    Push ....           ; alle Register sichern, da Interrupt
3
    LDS RegX, ms0       ; ms laden
4
    Inc RegX            ; erhöhen
5
    CPI RegX,10         ; Grenzwert abfragen
6
    BREQ Set_ms1        ; eine Dekade weiter wg. Überlauf
7
    STS ms0, RegX       ; zurückschreiben
8
    RJMP End_Timer      ; und fertig
9
Set_ms1:                ; nächste Dekade
10
    CLR RegX
11
    STS ms0, RegX       ; zuerst Millesekunden auf 0 und ablegen
12
    LDS RegX, ms1       ; nächste Dekade Laden
13
    Inc RegX            ; erhöhen
14
    CPI RegX,10         ; Grenzwert abfragen
15
    BREQ Set_ms2        ; eine Dekade weiter, wg. Überlauf
16
    STS ms1, RegX
17
    RJMP End_Timer 
18
Set_ms2:
19
    ....
20
21
End_Timer:
22
   POP .....
23
RETI
Je nachdem welche Zeitbasis du brauchst, setzt du einen Aufruf in den 
entsprechenden Abschnitt und bearbeitest dort das Bit. Nennen wir mal 
die Variable dazu Time_Ctrl
1
Time_Ctrl:    .Byte      ; Bit 0 Pause angefordert und läuft= 1 
2
                         ; Bit 1 Zeit ist abgelaufen
3
                         ; ..... weitere Steuerbits
4
Time_Val:     . Byte
Im Programm setzt du Time_Val auf einen Wert und Bit 0 von Time_Ctrl.
In der Timer_ISR rufst du ein Unterprogramm auf, in dem du das Bit 0 
prüfst. Ist es "1" decrementierst du die Time_Val und prüfst, ob sie 0 
ist. Wenn ja, löscht du Bit 0 und setzt Bit 1 von Time_Ctrl.
Im Hauptrogramm prüfst du nur Bit 1. ist es gesetzt, aufruf eines 
Unterprogrammes mit der Aktion, und anschließendem löschen von Bit 1. 
Damit wird dieses Programm nur bearbeitet, wenn die Zeit abgelaufen ist.
Beachte aber: alle Programmteile, die aus einer Interruptroutine 
aufgerufen werden , müssen die verwendeten Register sichern.
 Der Vorteil: Du kannst jede Zeitverzögerung zeitlich definieren und 
dein Programm macht auch weiterhin seinen Job schnell und reagiert auf 
andere Bedingungen.
Gruß oldmax

von spess53 (Gast)


Lesenswert?

Hi

>Grundsätzlich: in Assembler muß der Stack gesetzt werden. Hochsprachen
>wie BASCOM und C erledigen das von selbst, aber nicht Assembler.

Stimmt nicht ganz. Bei neueren AVRs wird der Stackpointer nach RESET 
automatisch auf RAMEND initialisiert. Allerdings schadet es nicht den 
Stackpointer manuell zu setzen.

MfG Spess

von Thomas Forster (Gast)


Lesenswert?

spess53 schrieb:
> Bei neueren AVRs wird der Stackpointer nach RESET
automatisch auf RAMEND initialisiert.

Ich habe gerade mal beim ATmega48/88 nachgelesen. Dort ist das noch 
nicht zu finden. Welche neueren AVRs meinst du?

Thomas

von spess53 (Gast)


Lesenswert?

Hi

>Ich habe gerade mal beim ATmega48/88 nachgelesen. Dort ist das noch
>nicht zu finden. Welche neueren AVRs meinst du?

Z.B. auch ATmega48/88

Im Datenblatt zu finden unter:

AVR CPU Core->Stack Pointer->Initial Value

MfG Spess

von Thomas F. (igel)


Lesenswert?

>>Ich habe gerade mal beim ATmega48/88 nachgelesen. Dort ist das noch
>>nicht zu finden. Welche neueren AVRs meinst du?

>Z.B. auch ATmega48/88

>Im Datenblatt zu finden unter:

>AVR CPU Core->Stack Pointer->Initial Value


Tatsächlich, Danke.
Das Kleingedruckte habe ich nicht gelesen  ;-)
Werde das demnächst mal ausprobieren.

Thomas

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.