Forum: Mikrocontroller und Digitale Elektronik Assembler-Kode Optimierung


von Horst W. (-sub-)


Lesenswert?

Hallo,

ich bin noch ziemlich "jung" was das programmieren von µC angeht.
Ich habe nun ein Programm fertig (es funktioniert auch) und ich wollte 
mal einen "älteren" fragen, ob und wie man diesen Kode optimieren kann.
Ich möchte dabei aber meine Kode-Struktur (also Timer, Unterprogramme, 
Interrupt etc.) beibehalten. Alles was ich will ist weniger Kode-Zeilen.
Währe nett wenn sich das mal jemand angucken könnte und Vorschläge 
machen könnte wie ich das eleganter programmieren kann.

Vielen Dank im Voraus für eure Mühe

1
;**************************************************************************
2
  .include "m8def.inc"    ;Definitionen für ATMega8
3
;**************************************************************************
4
5
.def  temp  = r16
6
.def  temp1 = r17
7
.def  temp2 = r18
8
.def  temp3 = r19
9
.def  timer = r20
10
.def  zaehler = r21
11
12
.org  0x0000 
13
  rjmp  main  ; Reset Handler
14
15
.org OVF0addr
16
    rjmp    timer0_overflow       ; Timer Overflow Handler
17
18
main:
19
20
  ldi    temp, low(ramend)
21
  out    spl, temp
22
23
  ldi    temp, high(ramend)
24
  out    sph, temp
25
26
  ldi     temp, 0b00000101      ; clkI/O/1024 (From prescaler)
27
    out     TCCR0, temp
28
29
    ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow
30
    out     TIMSK, temp
31
32
    ldi    temp, 0xFF
33
  out    DDRD, temp  ; Datenregister D = Ausgang
34
35
    ldi    temp, 0x3f
36
  out    DDRC, temp  ; Datenregister C = Ausgang
37
38
    ldi    temp, 0x00
39
  out    DDRB, temp  ; Datenregister B = Eingang
40
41
  ldi    zaehler, 0x00  ; Zähler löschen
42
  ldi    timer, 0x00    ; Timer löschen
43
  out    PORTD, temp    ; PORTD löschen
44
  out    PORTC, temp    ; PORTC löschen
45
46
anfang:
47
48
  cli          ; Globalen Interrupt ausschalten
49
50
  in    temp, PINb  ; Port B einlesen
51
  andi    temp, 1    ; mit 1 ver-unden
52
  cpi    temp, 1    ; Taste1 gedrückt?
53
  brne  hochzaehlen  ; wenn ja, dann dann zu hochzaehlen
54
  in    temp, PINb  ; Port B einlesen
55
  andi    temp, 16  ; mit 16 ver-unden
56
  cpi    temp, 16  ; Taste2 gedrückt?
57
  breq  anfang    ; wenn nein, dann zurück zu anfang
58
    
59
runterzaehlen:
60
61
  rcall  runter
62
  rjmp  weiter  
63
64
hochzaehlen:
65
66
  rcall  hoch
67
68
weiter:
69
70
  ldi    temp, 0    ; 0 nach temp laden
71
  out    tcnt0, temp  ; Counter löschen
72
73
  sei
74
75
warten:
76
  
77
  cpi    timer, 3  ; timer auf 3 testen
78
  brne  warten    ; nein!
79
  ldi    timer, 0  ; timer löschen
80
81
z1:
82
83
  cpi    zaehler, 0x01  ; Zähler auf 1 testen
84
  brne  z2        ; Zähler größer als 1
85
  ldi    temp2, 0x01    ; 1 nach temp2 schreiben
86
  out    PORTc, temp2  ; Kontroll LED an
87
  ldi   temp1,0b01000001 ; 1
88
  out    PORTD,temp1    ; 1 auf 7-Seg. ausgeben
89
90
z2:
91
92
  cpi    zaehler,2
93
  brne  z3
94
  ldi    temp2, 0x02    ; 2 nach temp 2 schreiben
95
  out    PORTc, temp2  ; Kontroll LED an
96
  ldi   temp1,0b00110111  ; 2
97
  out   PORTD,temp1   ; 2 auf 7-Seg. ausgeben
98
99
z3:
100
101
  cpi    zaehler,3
102
  brne  z4
103
  ldi    temp2, 0x03    ; 3 nach temp 2 schreiben
104
  out    PORTc, temp2  ; Kontroll LED an
105
  ldi   temp1,0b01100111  ; 3
106
  out   PORTD,temp1   ; 3 auf 7-Seg. ausgeben
107
108
z4:
109
110
  cpi    zaehler,4
111
  brne  z5
112
  ldi    temp2, 0x04    ; 4 nach temp 2 schreiben
113
  out    PORTc, temp2  ; Kontroll LED an
114
  ldi   temp1,0b01001101  ; 4
115
  out   PORTD,temp1   ; 4 auf 7-Seg. ausgeben
116
117
z5:
118
119
  cpi    zaehler,5
120
  brne  z6
121
  ldi    temp2, 0x05    ; 5 nach temp 2 schreiben
122
  out    PORTc, temp2  ; Kontroll LED an
123
  ldi   temp1,0b01101110  ; 5
124
  out   PORTD,temp1   ; 5 auf 7-Seg. ausgeben
125
126
z6:
127
128
  cpi    zaehler,6
129
  brne  z7
130
  ldi    temp2, 0x06    ; 6 nach temp 2 schreiben
131
  out    PORTc, temp2  ; Kontroll LED an
132
  ldi   temp1,0b01111110  ; 6
133
  out   PORTD,temp1   ; 6 auf 7-Seg. ausgeben
134
135
z7:
136
137
  cpi    zaehler,7
138
  brne  z8
139
  ldi    temp2, 0x07    ; 7 nach temp 2 schreiben
140
  out    PORTc, temp2  ; Kontroll LED an
141
  ldi   temp1,0b01001011  ; 7
142
  out   PORTD,temp1   ; 7 auf 7-Seg. ausgeben
143
144
z8:
145
146
  cpi    zaehler,8
147
  brne  z9
148
  ldi    temp2, 0x08    ; 8 nach temp 2 schreiben
149
  out    PORTc, temp2  ; Kontroll LED an
150
  ldi   temp1,0b01111111  ; 8
151
  out   PORTD,temp1   ; 8 auf 7-Seg. ausgeben
152
153
z9:
154
155
  cpi    zaehler,9
156
  brne  z0
157
  ldi    temp2, 0x09    ; 9 nach temp 2 schreiben
158
  out    PORTc, temp2  ; Kontroll LED an
159
  ldi   temp1,0b01001111  ; 9
160
  out   PORTD,temp1   ; 9 auf 7-Seg. ausgeben
161
162
z0:
163
164
  cpi    zaehler,10
165
  brne  ende
166
  ldi    temp2, 0x0a    ; 10 nach temp 2 schreiben
167
  out    PORTc, temp2  ; Kontroll LED an
168
  ldi   temp1,0b01111011  ; 0
169
  out   PORTD,temp1    ; 10 auf 7-Seg. ausgeben
170
171
ende:
172
  
173
  rjmp   anfang
174
175
timer0_overflow:            ; Timer 0 Overflow Handler
176
177
  inc   timer      ; Timer inkrementieren
178
  reti          ; return from interrupt
179
180
hoch:
181
182
  cpi    zaehler,10    ; Zähler = 10?
183
  brne  inkrement    ; Nein!
184
  ldi    zaehler,0    ; Ja!, dann Zähler = 0
185
186
inkrement:
187
188
  inc   zaehler      ; Zähler inkrementieren
189
  ret            ; return
190
191
runter:
192
193
  cpi    zaehler,0    ; Zähler = 0
194
  brne  dekrement    ; Nein!
195
  ldi    zaehler, 11    ; Ja!, dann Zähler = 11
196
  
197
dekrement:
198
 
199
  dec   zaehler      ; Zähler dekrementieren
200
201
  cpi    zaehler,0    ; Zähler = 0
202
  brne  dekrement1    ; Nein!
203
  ldi    zaehler, 10    ; Ja!, dann Zähler = 10
204
205
dekrement1:
206
207
  ret            ; return


Grüße

-sub-

von D. W. (dave) Benutzerseite


Lesenswert?

Das mit den 10 Ziffern kannste als Lookup-Table realisieren.

Einfach die 10 Bitmuster für die Anzeigen in den Flash speichern oder in 
den RAM laden und dann mit nem Zeiger aufrufen.

Modularisieren ist wichtig, finde ich, und gerade, wenn es die 9 ist, 
wird es schneller sein.

von Horst W. (-sub-)


Lesenswert?

David W. wrote:
> Das mit den 10 Ziffern kannste als Lookup-Table realisieren.
>
> Einfach die 10 Bitmuster für die Anzeigen in den Flash speichern oder in
> den RAM laden und dann mit nem Zeiger aufrufen.
>
> Modularisieren ist wichtig, finde ich, und gerade, wenn es die 9 ist,
> wird es schneller sein.

Hallo,

könntest du das bitte mit einem kleinen Beispiel erklären.

Grüße

-sub-

von Hannes L. (hannes)


Angehängte Dateien:

Lesenswert?

Horst Wohlers wrote:
> ...
> könntest du das bitte mit einem kleinen Beispiel erklären.

Ich versuche es mal...

Der Wert der Ziffer (0..9) steht in Register wl, das Register null 
enthält immer 0. Der Datenbereich (auch Tabelle genannt) bimu enthält 
die Bitmuster für die Siebensegmentanzeige. Die Bitmuster sind hier 
etwas verworren und auch Low-aktiv, was mit einfacherer Hardware zu tun 
hat.

Zuerst setzt man den Z-Pointer auf den Beginn der Tabelle, wobei wegen 
der byteweisen Adressierung beim Befehl LPM (im AVR-instruction-set 
nachlesen) das Doppelte der Adresse gebraucht wird. Das Label "bimu:" 
ist dabei nur ein Platzhalter für die tatsächliche Speicheradresse und 
wird vom Assembler beim Assemblieren mit dem tatsächlichen Adresswert 
(16-Bit-Zahl) "gefüllt". Der Pointer zeigt nun auf das erste Element der 
Tabelle. Wir brauchen aber das Element (das Byte mit dem Bitmuster), das 
unserem Ziffernwert entspricht. Also erhöhen wir den Pointer um den 
Ziffernwert durch eine Addition. Bei der Addition des L-Bytes könnte ein 
Überlauf (in ZL) erfolgen, der das Carry-Flag setzt. Damit der Übertrag 
auch in ZH landet, wird zu ZH der Wert 0 mit Carry addiert. Nun zeigt 
der Z-Pointer auf das korrekte Byte. Der Befehl LPM liest dieses Byte 
(aus dem Programmspeicher, also Flash) nach WL und gibt es am Port aus.
1
setseg:                 ;UP, setzt Segmente am Display
2
 ldi zl,low(bimu*2)         ;Z-Pointer auf Anfang
3
 ldi zh,high(bimu*2)        ;der Tabelle mit Bitmustern
4
 add zl,wl                  ;Zahlenwert addieren
5
 adc zh,null                ;evtl. Übertrag auch
6
 lpm wl,z                   ;Bitmuster aus Flash holen
7
 out segport,wl             ;und an Segment-Port ausgeben 
8
 ret                        ;zurück...
9
10
11
bimu:                   ;Bitmuster für Display-Segmente
12
.db 0b01000001,0b01011111   ;0, 1
13
.db 0b00110001,0b00010101   ;2, 3
14
.db 0b00001111,0b10000101   ;4, 5
15
.db 0b10000001,0b01011101   ;6, 7
16
.db 0b00000001,0b00000101   ;8, 9

Um die Routine besser nachvollziehen zu können hänge ich mal den 
kompletten Quelltext an. Da sind dann auch die Deklarationen zu sehen, 
also die Namensvergabe für die Register und Konstanten.

Viel Spaß damit...

...

von Karl H. (kbuchegg)


Lesenswert?

Horst Wohlers wrote:
> David W. wrote:
>> Das mit den 10 Ziffern kannste als Lookup-Table realisieren.
>>
>> Einfach die 10 Bitmuster für die Anzeigen in den Flash speichern oder in
>> den RAM laden und dann mit nem Zeiger aufrufen.
>>
>> Modularisieren ist wichtig, finde ich, und gerade, wenn es die 9 ist,
>> wird es schneller sein.
>
> Hallo,
>
> könntest du das bitte mit einem kleinen Beispiel erklären.

Guckst du hier
http://www.mikrocontroller.net/articles/AVR-Tutorial:_7-Segment-Anzeige

von Horst W. (-sub-)


Lesenswert?

Hallo,

Sehr schön ich danke euch!

Grüße

-sub-

von W. B. (wb1)


Lesenswert?

Dein Code mit Zeigern

;*********************************************************************** 
***
  .include "m8def.inc"    ;Definitionen fr ATMega8
;*********************************************************************** 
***

.def  temp  = r16
.def  temp1 = r17
.def  temp2 = r18
.def  temp3 = r19
.def  timer = r20
.def  zaehler = r21

.org  0x0000
  rjmp  main  ; Reset Handler

.org OVF0addr
    rjmp    timer0_overflow       ; Timer Overflow Handler

main:

  ldi    temp, low(ramend)
  out    spl, temp
  ldi    temp, high(ramend)
  out    sph, temp
  ldi     temp, 0b00000101      ; clkI/O/1024 (From prescaler)
    out     TCCR0, temp
    ldi     temp, 0b00000001      ; TOIE0: Interrupt bei Timer Overflow
    out     TIMSK, temp
    ldi    temp, 0xFF
  out    DDRD, temp  ; Datenregister D = Ausgang
    ldi    temp, 0x3f
  out    DDRC, temp  ; Datenregister C = Ausgang
    ldi    temp, 0x00
  out    DDRB, temp  ; Datenregister B = Eingang
  ldi    zaehler, 0x00  ; Zhler lschen
  ldi    timer, 0x00    ; Timer lschen
  out    PORTD, temp    ; PORTD lschen
  out    PORTC, temp    ; PORTC lschen

anfang:

  cli          ; Globalen Interrupt ausschalten
  in    temp, PINb  ; Port B einlesen
  sbrc temp,0
; sbrc = ueberspringe den nächsten Befehl wenn bit 0 =low
  rjmp runterzaehlen
; +++++++++++++++
;  andi    temp, 1    ; mit 1 ver-unden
;  cpi    temp, 1    ; Taste1 gedrckt?
;  brne  hochzaehlen  ; wenn ja, dann dann zu hochzaehlen
; +++++++++++++++++++++
  sbrc temp1,4
  rjmp anfang
; ++++++++++++++++
;  in    temp, PINb  ; Port B einlesen
;  andi    temp, 16  ; mit 16 ver-unden
;  cpi    temp, 16  ; Taste2 gedrckt?

;  breq  anfang    ; wenn nein, dann zurck zu anfang
; +++++++++++++++++++
runterzaehlen:

  rcall  runter
  rjmp  weiter

hochzaehlen:

  rcall  hoch

weiter:

  ldi    temp, 0    ; 0 nach temp laden
  out    tcnt0, temp  ; Counter lschen

  sei
; Tabellenzeiger laden
  ldi zh, high(segmenttabelle*2)
  ldi zl,low(segmenttabelle*2)
warten:

  cpi    timer, 3  ; timer auf 3 testen
  brne  warten    ; nein!
  ldi    timer, 0  ; timer lschen

z1:
  add zl,zaehler
  lpm temp2,z
  out portc,zaehler
  out portd,temp2
;  cpi    zaehler, 0x01  ; Zhler auf 1 testen
;  brne  z2        ; Zhler grer als 1
;  ldi    temp2, 0x01    ; 1 nach temp2 schreiben
;  out    PORTc, temp2  ; Kontroll LED an
;  ldi   temp1,0b01000001 ; 1
;  out    PORTD,temp1    ; 1 auf 7-Seg. ausgeben

;z2:

;  cpi    zaehler,2
;  brne  z3
;  ldi    temp2, 0x02    ; 2 nach temp 2 schreiben
;  out    PORTc, temp2  ; Kontroll LED an
;  ldi   temp1,0b00110111  ; 2
;  out   PORTD,temp1   ; 2 auf 7-Seg. ausgeben

;z3:

;  cpi    zaehler,3
;  brne  z4
;  ldi    temp2, 0x03    ; 3 nach temp 2 schreiben
;  out    PORTc, temp2  ; Kontroll LED an
;  ldi   temp1,0b01100111  ; 3
;  out   PORTD,temp1   ; 3 auf 7-Seg. ausgeben

;z4:

;  cpi    zaehler,4
;  brne  z5
;  ldi    temp2, 0x04    ; 4 nach temp 2 schreiben
;  out    PORTc, temp2  ; Kontroll LED an
;  ldi   temp1,0b01001101  ; 4
;  out   PORTD,temp1   ; 4 auf 7-Seg. ausgeben

;z5:

;  cpi    zaehler,5
;  brne  z6
;  ldi    temp2, 0x05    ; 5 nach temp 2 schreiben
;  out    PORTc, temp2  ; Kontroll LED an
;  ldi   temp1,0b01101110  ; 5
;  out   PORTD,temp1   ; 5 auf 7-Seg. ausgeben

;z6:

;  cpi    zaehler,6
;  brne  z7
;  ldi    temp2, 0x06    ; 6 nach temp 2 schreiben
;  out    PORTc, temp2  ; Kontroll LED an
;  ldi   temp1,0b01111110  ; 6
;  out   PORTD,temp1   ; 6 auf 7-Seg. ausgeben

;z7:

;  cpi    zaehler,7
;  brne  z8
;  ldi    temp2, 0x07    ; 7 nach temp 2 schreiben
;  out    PORTc, temp2  ; Kontroll LED an
;  ldi   temp1,0b01001011  ; 7
;  out   PORTD,temp1   ; 7 auf 7-Seg. ausgeben

;z8:

;  cpi    zaehler,8
;  brne  z9
;  ldi    temp2, 0x08    ; 8 nach temp 2 schreiben
;  out    PORTc, temp2  ; Kontroll LED an
;  ldi   temp1,0b01111111  ; 8
;  out   PORTD,temp1   ; 8 auf 7-Seg. ausgeben

;z9:

;  cpi    zaehler,9
;  brne  z0
;  ldi    temp2, 0x09    ; 9 nach temp 2 schreiben
;  out    PORTc, temp2  ; Kontroll LED an
;  ldi   temp1,0b01001111  ; 9
;  out   PORTD,temp1   ; 9 auf 7-Seg. ausgeben

;z0:

;  cpi    zaehler,10
;  brne  ende
;  ldi    temp2, 0x0a    ; 10 nach temp 2 schreiben
;  out    PORTc, temp2  ; Kontroll LED an
;  ldi   temp1,0b01111011  ; 0
;  out   PORTD,temp1    ; 10 auf 7-Seg. ausgeben

;ende:

  rjmp   anfang

timer0_overflow:            ; Timer 0 Overflow Handler

  inc   timer      ; Timer inkrementieren
  reti          ; return from interrupt

hoch:

  cpi    zaehler,10    ; Zhler = 10?
  brne  inkrement    ; Nein!
  ldi    zaehler,0    ; Ja!, dann Zhler = 0

inkrement:

  inc   zaehler      ; Zhler inkrementieren
  ret            ; return

runter:

  cpi    zaehler,0    ; Zhler = 0
  brne  dekrement    ; Nein!
  ldi    zaehler, 11    ; Ja!, dann Zhler = 11

dekrement:

  dec   zaehler      ; Zhler dekrementieren

  cpi    zaehler,0    ; Zhler = 0
  brne  dekrement1    ; Nein!
  ldi    zaehler, 10    ; Ja!, dann Zhler = 10

dekrement1:

  ret            ; return

.org 0x100
segmenttabelle:
.db 
0b01111011,0b01000001,0b00110111,0b01100111,0b01001101,0b01101110,0b0111 
1110,0b01001011,0b01111111,0b01001111

von Horst W. (-sub-)


Lesenswert?

Hallo,

@ W. Bl

Tja was soll ich sagen... Vielen Dank für Deine Mühe

Grüße

-sub-

von Uwe (Gast)


Angehängte Dateien:

Lesenswert?

Hi!
Die Variante im Anhang ist noch etwas kürzer, aber nur weil dein 
"rauf/runter" irgendwie nicht ganz ok war und Pins direkt abfragbar 
sind.

Viel Erfolg, Uwe

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.