mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Assembler-Kode Optimierung


Autor: Horst Wohlers (-sub-)
Datum:

Bewertung
0 lesenswert
nicht 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

;**************************************************************************
  .include "m8def.inc"    ;Definitionen für 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  ; Zähler löschen
  ldi    timer, 0x00    ; Timer löschen
  out    PORTD, temp    ; PORTD löschen
  out    PORTC, temp    ; PORTC löschen

anfang:

  cli          ; Globalen Interrupt ausschalten

  in    temp, PINb  ; Port B einlesen
  andi    temp, 1    ; mit 1 ver-unden
  cpi    temp, 1    ; Taste1 gedrückt?
  brne  hochzaehlen  ; wenn ja, dann dann zu hochzaehlen
  in    temp, PINb  ; Port B einlesen
  andi    temp, 16  ; mit 16 ver-unden
  cpi    temp, 16  ; Taste2 gedrückt?
  breq  anfang    ; wenn nein, dann zurück zu anfang
    
runterzaehlen:

  rcall  runter
  rjmp  weiter  

hochzaehlen:

  rcall  hoch

weiter:

  ldi    temp, 0    ; 0 nach temp laden
  out    tcnt0, temp  ; Counter löschen

  sei

warten:
  
  cpi    timer, 3  ; timer auf 3 testen
  brne  warten    ; nein!
  ldi    timer, 0  ; timer löschen

z1:

  cpi    zaehler, 0x01  ; Zähler auf 1 testen
  brne  z2        ; Zähler größer 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    ; Zähler = 10?
  brne  inkrement    ; Nein!
  ldi    zaehler,0    ; Ja!, dann Zähler = 0

inkrement:

  inc   zaehler      ; Zähler inkrementieren
  ret            ; return

runter:

  cpi    zaehler,0    ; Zähler = 0
  brne  dekrement    ; Nein!
  ldi    zaehler, 11    ; Ja!, dann Zähler = 11
  
dekrement:
 
  dec   zaehler      ; Zähler dekrementieren

  cpi    zaehler,0    ; Zähler = 0
  brne  dekrement1    ; Nein!
  ldi    zaehler, 10    ; Ja!, dann Zähler = 10

dekrement1:

  ret            ; return



Grüße

-sub-

Autor: D. W. (dave) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Horst Wohlers (-sub-)
Datum:

Bewertung
0 lesenswert
nicht 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-

Autor: Hannes Lux (hannes)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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.
setseg:                 ;UP, setzt Segmente am Display
 ldi zl,low(bimu*2)         ;Z-Pointer auf Anfang
 ldi zh,high(bimu*2)        ;der Tabelle mit Bitmustern
 add zl,wl                  ;Zahlenwert addieren
 adc zh,null                ;evtl. Übertrag auch
 lpm wl,z                   ;Bitmuster aus Flash holen
 out segport,wl             ;und an Segment-Port ausgeben 
 ret                        ;zurück...


bimu:                   ;Bitmuster für Display-Segmente
.db 0b01000001,0b01011111   ;0, 1
.db 0b00110001,0b00010101   ;2, 3
.db 0b00001111,0b10000101   ;4, 5
.db 0b10000001,0b01011101   ;6, 7
.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...

...

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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-Tutori...

Autor: Horst Wohlers (-sub-)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

Sehr schön ich danke euch!

Grüße

-sub-

Autor: W. Bl (wb1)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Horst Wohlers (-sub-)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

@ W. Bl

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

Grüße

-sub-

Autor: Uwe (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.