Forum: Mikrocontroller und Digitale Elektronik LED Laufllicht in Assembler


von Stephan W. (stipo)


Lesenswert?

Hallo zusammen,
ich bin fleißig dabei mich durch das Tutorial zu arbeiten.
Dabei habe ich ein LED Lauflicht programmiert, was soweit funktioniert.

Folgendes soll der Code machen:
-- 5 LED ansteuern als Lauflicht
-- über einen Taster den Speed einstellen 1-9
-- Anzeige der Speed auf einem 7-Seqment

Folgendes macht der Code:
-- Die 5 LED werden als Lauflicht angezeigt
-- Tastendruck wird erkannt und Speed erhöht (die Zahl des Speed, ist 
aber umgekehrt und wird dadurch langsamer. Soll aber im moment egal 
sein)
-- Anzeige des Speed als Zahl

Was der Code noch falsch macht:
-- Die Tasten lassen sich nicht entprellen
-- Der Speed kann auch größer werden als 9 (es fehlt/funktioniert nicht 
die begrenzung zwischen 1-9)

Ich packe mal den Code rein.

Folgende Fragen hab ich:
-- Warum funktioniert der Code nicht mehr, wenn ich in der Interrupt 
Methode mit push das temp Register weg sichere?
-- Was muss ich ändern, das bei einem Tastendruck nur eine 
Flanke/Entprellen funktioniert. Wenn ich den Code auskommentiere, dann 
erkennt er mir keinen Tastendruck mehr.
-- Was könnte ich noch verbessern an dem Code ( Tips zum nachlesen )
-- Was ist totaler schwachsinn und sollte ich total anders lösen.

Nur keine Scheu zeigen. Haut rein mit der Kritik. Kann das vertragen und 
dadurch den Stil verbessern.

Danke an alle.

Grüße
Stephan
1
/*
2
 * LED_Lauflicht.asm
3
 *
4
 *  Created: 17.09.2011 22:55:06
5
 *   Author: stephan
6
 */ 
7
8
 .include "m8def.inc"
9
10
 ; definiere Register
11
 .def  zero    = r1
12
 .def  temp    = r16      ; Ein Temporäres Register
13
 .def  temp2    = r26      ; Ein Temporäres Register
14
 .def  lauflicht  = r17      ; Das Register, für das Lauflicht
15
 .def  SubCount  = r18      ; Timer Interrupt zwischen Variable
16
 .def  key_now    = r19      ; Der aktuelle Tasterstatus
17
 .def  speed    = r20      ; Die Geschwindigkeit der Lauflicht
18
 .def  key_old    = r2      ; Der davor Tasterstatus
19
20
 ; definiere Ports
21
 .equ  led_port  = PORTB      ; Der Port, an dem die LED angeschlossen sind
22
 .equ  led_ddr    = DDRB      ; Die Datenrichtung der LED
23
24
 .equ  taster_port  = PORTC      ; Der Port an dem die Taster angeschlossen sind
25
 .equ  taster_ddr  = DDRC      ; Die Datenrichtung der Taster
26
 .equ  taster_in  = PINC      ; Eingabe der Taster
27
28
 .equ  seq_port  = PORTD      ; Der Port an dem die Anzeige dran ist
29
 .equ  seq_ddr    = DDRD      ; Die Datenrichtung der Anzeige
30
31
 ; definiere Interrupt
32
 .org  0x0000
33
    rjmp main          ; Reset Handler
34
 .org  OVF0addr
35
    rjmp timer0_overflow    ; Timer Overflow Handler
36
37
; Der Einstiegspunkt in die Anwendung
38
 main:
39
    rcall  initialize      ; Initialisiere
40
    rjmp  loop        ; Springe in den Programmablauf
41
42
; Die Verarbeitungsschleife des µC
43
; ################################
44
loop:
45
    ; Lauflicht ausgeben
46
    rcall  ausgabe_led        ; Gehe zur Ausgabe des Lauflicht
47
48
    rcall  ausgabe_speed      ; Gibt den Speed auf der 7-Seqment Anzeige aus
49
        
50
    ; Tasten auswerten
51
    in    key_now, taster_in    ; Hole Tasten
52
    ;mov    temp2, key_now    ; erstmal in temp sichern
53
    ;eor    key_now, key_old  ; mit dem vorherigen zustand vergleichen XOR
54
    ;mov    key_old, temp2    ; und den jetzigen Zustand für den nächsten
55
                  ; Schleifendurchlauf als alten Zustand merken
56
    ;breq  loop        ; Das Ergebnis des XOR auswerten:
57
    ;and    temp2, key_now    
58
    ;brne  loop
59
            
60
    cpi    key_now, 0b00111110  ; Taste 1 gedrückt
61
    breq  geschwindigkeit    ; springe zu Methode geschwindigkeit
62
63
    rjmp  loop
64
65
; Ändert die Geschwindigkeit der Lauflicht
66
; ########################################
67
geschwindigkeit:
68
    inc    speed        ; speed +1
69
    ldi    temp2, 9
70
    cpc    speed, temp2      
71
    brne  return
72
    ldi    speed, 5    
73
    reti
74
75
; Gibt das LED Lauflicht auf den Ausgang 
76
; ######################################
77
ausgabe_led:
78
    ldi    ZL, LOW(Codes*2)  ; die Startadresse der Tabelle in den
79
    ldi    ZH, HIGH(Codes*2)  ; Z-Pointer laden
80
81
    mov    temp, lauflicht    ; die wortweise Adressierung der Tabelle
82
    add    temp, lauflicht    ; berücksichtigen
83
84
    add    ZL, temp      ; und ausgehend vom Tabellenanfang
85
    adc    ZH, zero      ; die Adresse des Code Bytes berechnen
86
87
    lpm              ; dieses Code Byte in das Register r0 laden
88
89
    out    led_port, r0    ; und ausgeben
90
        
91
    reti
92
93
; Gibt den Speed als Zahl auf das 7-Sequment
94
; ##########################################
95
ausgabe_speed:
96
    ldi    ZL, LOW(Codes_Seqment*2)  ; die Startadresse der Tabelle in den
97
    ldi    ZH, HIGH(Codes_Seqment*2)  ; Z-Pointer laden
98
99
    mov    temp2, speed        ; die wortweise Adressierung der Tabelle
100
    add    temp2, speed        ; berücksichtigen
101
102
    add    ZL, temp2          ; und ausgehend vom Tabellenanfang
103
    adc    ZH, zero          ; die Adresse des Code Bytes berechnen
104
105
    lpm                  ; dieses Code Byte in das Register r0 laden
106
107
    out    seq_port, r0        ; und ausgeben
108
    reti
109
110
; Initialisiere den Controller
111
; ###########################
112
initialize:
113
114
    ; Stackpointer initialisieren
115
    ldi    temp, HIGH(RAMEND)
116
    out    SPH, temp
117
    ldi    temp, LOW(RAMEND)
118
    out    SPL, temp
119
120
    ; LED als Ausgang festlegen
121
    ldi    temp, 0xFF      
122
    out    led_ddr, temp    ; Datenrichtung festlegen auf Ausgang
123
    ldi    temp, 0xFF      
124
    out    led_port, temp    ; LED Ausgänge ausschalten
125
126
    ; Taster als Eingänge festlegen
127
    ldi    temp, 0x00      
128
    out    taster_ddr, temp  ; Datenrichtung festlegen auf Eingang
129
    ldi    temp, 0xFF      
130
    ;out    taster_port, temp  ; Internen PullUp aktivieren
131
132
    ; 7 Sequemt Anzeige festlegen
133
    ldi    temp, 0xFF      
134
    out    seq_ddr, temp    ; Datenrichtung festlegen auf Ausgang
135
    out    seq_port, temp    ; Ausgänge ausschalten
136
        
137
    ; Timer Overflow Interrupt
138
    ldi    temp, (1<<CS02) ;| (1<<CS02)  ; Teiler für Timer Interrupt setzen
139
    out    TCCR0, temp
140
141
    ldi    temp, (1<<TOIE0)  ; Interrupt bei Timer Overflow
142
    out    TIMSK, temp
143
144
    ; Register vorbelegen
145
    clr    SubCount      ; SubCount leeren
146
    ldi    lauflicht, 0
147
    ldi    speed, 1      ; starte mit geschwindigkeit 9
148
    mov    zero, lauflicht
149
      
150
    ; Tasten auslesen
151
    in    temp, taster_in    ; Hole Tastenstatus
152
    mov    key_old, temp    ; speicher als alten Status
153
    
154
    sei              ; Allgemein Interrupt aktivieren
155
    
156
    reti
157
    
158
; Der Timer Overflow Interrupt
159
; ############################
160
timer0_overflow:
161
    ;push  temp      ; Register sichern
162
163
    inc    SubCount    ; wenn das nicht der 15. Interrupt
164
    cp    SubCount, speed  ; ist, dann passiert nix
165
    brne  return
166
167
                ; Überlauf
168
    clr    SubCount    ; SubCount zurücksetzen
169
    
170
    inc    lauflicht    ; decrementiere lauflicht
171
    cpi    lauflicht, 6
172
    brne  return
173
174
    ldi    lauflicht, 0    
175
    ;pop    temp      ; Register zurück laden
176
    reti
177
178
; allgemeiner Return nach entscheidungen
179
; ######################################
180
return:
181
    ; hier wird nix gemacht, außer sauber aus einer Methode zurück springen
182
    reti
183
184
; Die Codetabelle für das LED Lauflicht
185
; #####################################
186
Codes:              
187
    .db    0b11111111    ; 0
188
    .db    0b11111110    ; 1
189
    .db    0b11111100    ; 2
190
    .db    0b11111000    ; 3
191
    .db    0b11110000    ; 4
192
    .db    0b11100000    ; 5
193
    .db    0b11000000    ; 6
194
    .db    0b10000000    ; 7
195
    .db    0b00000000    ; 8
196
197
; Die Codetabelle für die 7 Seqment Anzeige
198
; #########################################
199
Codes_Seqment:
200
    .db    0b11000000    ; 0: a, b, c, d, e, f
201
    .db    0b11111001    ; 1: b, c
202
    .db    0b10100100    ; 2: a, b, d, e, g
203
    .db    0b10110000    ; 3: a, b, c, d, g
204
    .db    0b10011001    ; 4: b, c, f, g
205
    .db    0b10010010    ; 5: a, c, d, f, g
206
    .db    0b10000010    ; 6: a, c, d, e, f, g
207
    .db    0b11111000    ; 7: a, b, c
208
    .db    0b10000000    ; 8: a, b, c, d, e, f, g
209
    .db    0b10010000    ; 9: a, b, c, d, f, g

von Mark L. (m2k10) Benutzerseite


Lesenswert?

Einfach mal ein paar Sachen:
'reti' ist für den Rücksprung aus IRQs, für 'normale' subroutinen 
solltest du 'ret' nehmen (Ist hier egal, aber besser direkt nicht 
angewöhnen).
'cpc' verwendet auch das Carry-Flag, 'cp' wäre hier richtig.
mit 'push temp' sicherst du nicht das SREG, sondern nur temp. push temp, 
in temp, SREG und umgedreht vor dem 'reti' würde das machen, aber dann 
musst du auch bei 'return:' einfügen.

Mark

von spess53 (Gast)


Lesenswert?

HI

>return:
>    ; hier wird nix gemacht, außer sauber aus einer Methode zurück springen
>    reti


Eben nicht. Du springst aus deiner Interrupt-Routine dorthin. Wenn du 
vorher temp gepusht hast stimmt der Stack nicht mehr -> Nirvana. 
Außerdem sicherst du SREG nicht. Wird noch lustige Effekte geben.

MfG Spess

von Stephan W. (stipo)


Lesenswert?

Danke schonmal, für die Tips
Mark L. schrieb:
> Einfach mal ein paar Sachen:
> 'reti' ist für den Rücksprung aus IRQs, für 'normale' subroutinen
> solltest du 'ret' nehmen (Ist hier egal, aber besser direkt nicht
> angewöhnen).
Habe ich geändert und merke mir das.
> 'cpc' verwendet auch das Carry-Flag, 'cp' wäre hier richtig.
> mit 'push temp' sicherst du nicht das SREG, sondern nur temp. push temp,
> in temp, SREG und umgedreht vor dem 'reti' würde das machen, aber dann
> musst du auch bei 'return:' einfügen.
Das versuche ich gerade. Lese da aber nochmal den Teil im Tutorial 
durch. Hab dann wohl etwas übersehen dabei.

von Stephan W. (stipo)


Lesenswert?

Auch Dir Danke schonmal.
spess53 schrieb:
> HI
>
>>return:
>>    ; hier wird nix gemacht, außer sauber aus einer Methode zurück springen
>>    reti
>
>
> Eben nicht. Du springst aus deiner Interrupt-Routine dorthin. Wenn du
> vorher temp gepusht hast stimmt der Stack nicht mehr -> Nirvana.
> Außerdem sicherst du SREG nicht. Wird noch lustige Effekte geben.

Ich habe das mal so umgeschrieben. Wäre das so dann besser? Bin mir da 
nun nicht ganz sicher. In der Return Methode schreibe ich dann SREG und 
temp auch wieder zurück.
1
; Der Timer Overflow Interrupt
2
; ############################
3
timer0_overflow:
4
    push  temp      ; Register sichern
5
    in    temp, SREG
6
7
    inc    SubCount    ; wenn das nicht der 15. Interrupt
8
    cp    SubCount, speed  ; ist, dann passiert nix
9
    brne  return
10
11
                ; Überlauf
12
    clr    SubCount    ; SubCount zurücksetzen
13
    
14
    inc    lauflicht    ; decrementiere lauflicht
15
    cpi    lauflicht, 6
16
    brne  return
17
18
    ldi    lauflicht, 0    
19
    out    SREG, temp
20
    pop    temp      ; Register zurück laden
21
    reti
22
23
; allgemeiner Return nach entscheidungen
24
; ######################################
25
return:
26
    ; hier wird nix gemacht, außer sauber aus einer Methode zurück springen
27
    out    SREG, temp
28
    pop    temp
29
    reti

von spess53 (Gast)


Lesenswert?

Hi

>Ich habe das mal so umgeschrieben. Wäre das so dann besser?

Nicht wirklich. Mach es doch einfach so:
1
    ....
2
    ldi    lauflicht, 0 
3
4
return:
5
    out    SREG, temp
6
    pop    temp      ; Register zurück laden
7
    reti

Oder willst du bei jeder Änderung mehrere Stellen umschreiben?

MfG Spess

von Stephan W. (stipo)


Lesenswert?

spess53 schrieb:
> Hi
>
>>Ich habe das mal so umgeschrieben. Wäre das so dann besser?
>
> Nicht wirklich. Mach es doch einfach so:
>
>
1
>     ....
2
>     ldi    lauflicht, 0
3
> 
4
> return:
5
>     out    SREG, temp
6
>     pop    temp      ; Register zurück laden
7
>     reti
8
>
>
> Oder willst du bei jeder Änderung mehrere Stellen umschreiben?
>
> MfG Spess

Verstehe ich jetzt nicht ganz. Hab mal meine Gedanken in den Code mit 
rein geschrieben
1
; Der Timer Overflow Interrupt
2
; ############################
3
timer0_overflow:
4
    push  temp      ; Register sichern
5
    in    temp, SREG
6
7
    inc    SubCount    ; wenn das nicht der 15. Interrupt
8
    cp    SubCount, speed  ; ist, dann passiert nix
9
    brne  return <- Hier springe ich doch in return, weil der vergleich nicht gleich war.
10
11
                ; Überlauf
12
    clr    SubCount    ; SubCount zurücksetzen
13
    
14
    inc    lauflicht    ; decrementiere lauflicht
15
    cpi    lauflicht, 6
16
    brne  return <- Hier springe ich auch zurück, weil der Zähler noch nicht die 6 erreicht hat.
17
18
    ldi    lauflicht, 0
19
20
<< Hier muss ich doch genauso temp und SREG zurück schreiben, weil wenn die obigen beiden vergleiche ( == ) waren, dann springen die doch nie in return. Spätestens da wäre dann doch der Fehler wieder vorprogrammiert? >>    
21
    out    SREG, temp
22
    pop    temp      ; Register zurück laden
23
    reti
24
25
26
; Return Methode, um damit sauber aus einer Interrupt Routine zu springen
27
; #######################################################################
28
return:
29
    ; Hier wird noch SREG und temp zurück geschrieben,
30
    ; was vorher in der Interrupt Routine gesichert wurde.
31
    out    SREG, temp
32
    pop    temp
33
    reti

von spess53 (Gast)


Lesenswert?

HI

Du hast zwei identische Codeteile. Wozu?

Entweder wird (in meiner Variante) zum Label return gesprungen oder der 
Code nach

ldi    lauflicht, 0

ausgeführt.

MfG Spess

von Stephan W. (stipo)


Lesenswert?

Jetzt verstehe ich gerade garnichts mehr.

Heist das, der führt den Code dann von oben nach unten aus, bis der 
irgendwann ein ret oder reti findet, egal ob da eine Sprungmarke wie 
return: noch dazwischen steht?

von spess53 (Gast)


Lesenswert?

Hi

>Heist das, der führt den Code dann von oben nach unten aus, bis der
>irgendwann ein ret oder reti findet, egal ob da eine Sprungmarke wie
>return: noch dazwischen steht?

Bingo. Ein Label beeinflusst den Programmablauf nicht.

MfG Spess

von Stephan W. (stipo)


Lesenswert?

spess53 schrieb:
> Hi
>
>>Heist das, der führt den Code dann von oben nach unten aus, bis der
>>irgendwann ein ret oder reti findet, egal ob da eine Sprungmarke wie
>>return: noch dazwischen steht?
>
> Bingo. Ein Label beeinflusst den Programmablauf nicht.
>
> MfG Spess

Okay, dann ist das klar :)
Man man, wenn man aus der OOP Welt zurück in die Basic (Schneider) Welt 
versetzt wird, muss man sich da erstmal wieder total umgewöhnen.

Dann danke ich mal, das kann der entscheidende Hinweis sein, das ich den 
Programmcode mit anderen Augen ansehe.

Grüße
Stephan

von spess53 (Gast)


Lesenswert?

Hi

>Okay, dann ist das klar :)
>Man man, wenn man aus der OOP Welt zurück in die Basic (Schneider) Welt
>versetzt wird, muss man sich da erstmal wieder total umgewöhnen.

Der umgedrehte Weg hat auch seine Tücken.

MfG Spess

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.