Forum: Mikrocontroller und Digitale Elektronik Wie am Besten Text-Strings in ASM verwalden?


von Daniel (Gast)


Lesenswert?

Hallo,

ich programmiere schon länger an einem kleinen AVR-Projekt mit LCD- und 
UART-Ausgaben (in Assembler, Mega16) und mit der Zeit sammlen sich auch 
etliche Text-Strings für LCD-Ausaben bzw. Debug-Informationen via UART 
an.

Momentan rufe ich jede einzelnen String mit einer Unterroutine aus dem 
Hauptprogramm auf, wie zum Beispiel "rcall LCD_Select"


Die Unterroutine sieht dann wie folgt aus:


LCD_Select:
  ldi ZL, LOW(text_Select*2)   ; Adresse des Strings in den
  ldi ZH, HIGH(text_Select*2)  ; Z-Pointer laden
  rcall lcd_flash_string
  ret

text_Select:  .db "-> Please Select ",0


Die Funktion "lcd_flash_string" stammt hier aus dem Tutorial und schiebt 
den String einfach auf das LCD (siehe unten).

Das funktioniert so auch wunderbar, nur werden die Anzahl der 
Textstrings im Laufe der Zeit immer mehr und das Programm wird so leider 
unübersichtlich und kostet auch recht viel Speicherplatz. Ich habe dann 
also für jeden Text einen seperaten Aufruf zum Setzen des Z-Pointers.
Mich würde es interessieren wie ihr solche Strings verwaltet, gibt es 
spezielle Tricks? Vielleicht kann ich da noch was optimieren.
Es handelt sich hier nur um statischen Text, alle Variablen werden nur 
bei Bedarf vom Programm einzeln ausgegeben.



Hier noch die "lcd_flash_string"-Routine:

 lcd_flash_string:
           push  temp1
           push  ZH
           push  ZL
 lcd_flash_string_1:
           lpm   temp1, Z+
           cpi   temp1, 0
           breq  lcd_flash_string_2
           rcall  lcd_data
           rjmp  lcd_flash_string_1
 lcd_flash_string_2:
           pop   ZL
           pop   ZH
           pop   temp1
           ret


Gruß Daniel

von holger (Gast)


Lesenswert?

>Mich würde es interessieren wie ihr solche Strings verwaltet

printf("-> Please Select ");

von Klaus 2. (klaus2m5)


Lesenswert?

per Macro:
1
.macro   PrintStr ;@0 = message pointer
2
   ldi   zl,low(@0*2)
3
   ldi   zh,high(@0*2)
4
   rcall prtstr
5
.endmacro
6
.macro   PrintStr_far ;@0 = message pointer
7
   ldi   zl,low(@0*2)
8
   ldi   zh,high(@0*2)
9
   call  prtstr
10
.endmacro
11
12
; Print String (called by PrintStr Macro)
13
prtstr:
14
   do    print_string
15
      lpm   a,z+
16
      tst   a
17
      exiteq print_string
18
      rcall PrintChr
19
   loop  print_string
20
   ret
21
22
; Messages
23
;...
24
load_abort:    .db   " Load aborted",0
25
err_chksum:    .db   " Checksum failed",0,0
26
err_nonhex:    .db   " Non-Hex data in record",0
27
err_func:      .db   " Invalid function or count in record",0,0
28
;...
29
30
; printing a message
31
;...
32
   tst   b
33
   ifne  eep_save_error_checksum
34
      PrintStr err_chksum     ;" Checksum failed"
35
      rjmp  eep_write_err
36
   end   eep_save_error_checksum
37
;...

von Klaus 2. (klaus2m5)


Lesenswert?

Text aus laufendem Code ausgeben:
1
; Print String from instruction stream
2
;    Warning: works only on lower 64kB flash and
3
;             only for AVRs with 128kB max flash
4
prtstr:
5
   pop   zh          ;get calling PC = word pointer to string
6
   pop   zl
7
   lsl   zl          ;z=z*2 (word to byte pointer conversion)
8
   rol   zh
9
   do    print_string
10
      lpm   a,z+        ;get text
11
      tst   a           ;stop on null
12
      exiteq print_string
13
      rcall PrintChr
14
   loop  print_string
15
   lsr   zh          ;z=z/2 (byte to word pointer conversion)
16
   ror   zl
17
   ifcs  print_string_odd
18
      adiw  z,1         ;correction to even # of bytes
19
   end   print_string_odd
20
   ijmp              ;return after end of string
21
22
; printing a message
23
;...
24
   tst   b
25
   ifne  eep_save_error_checksum
26
      rcall prtstr
27
      .db   " Checksum failed",0
28
      rjmp  eep_write_err
29
   end   eep_save_error_checksum
30
;...
do..exit..loop und if..else..end Macros gibt es hier: 
http://www.mikrocontroller.net/articles/AVR_Assembler_Makros#SAM_.28Structured_Assembly_Macros.29

: Bearbeitet durch User
von chris (Gast)


Lesenswert?

txt_out0:
  ;falls LCD muss hier die Adresse hin
  ldi     ZH,high(out0*2)
        ldi     ZL,low(out0*2)
        rcall     txt_out
  ret

;Hier nur die Zeichenausgabe mit der Stoppbedingung
txt_out:
       lpm    temp1,z+  ;!!! muss der .db *2 genommen werden
       cpi    temp1,$ff
       breq    txt_out_end
       rcall    lcd_data  ;z.b.: ldi ZH,high(out0*2)
       rjmp    txt_out
txt_out_end:
      ret

;hier so wie du es auch hast über ne DB mit stoppbedingung
out0:  .db "   AVR-Atmega8  ",$ff

von Peter D. (peda)


Lesenswert?

Man kann den String direkt nach dem Call plazieren:
1
.def  a0    = r20    ;working registers
2
;...
3
;-------------------------------------------------------------------------
4
;      send zero terminated string after call
5
;-------------------------------------------------------------------------
6
puts:
7
  pop  zh      ;get address after call
8
  pop  zl
9
  lsl  zl      ;*2 to get byte pointer
10
  rol  zh
11
  rjmp  _tou2
12
_tou1:  rcall  putchar
13
_tou2:  lpm  a0, Z+
14
  tst  a0      ;check zero byte
15
  brne  _tou1
16
  lsr  zh      ;/2 to get word pointer
17
  ror  zl
18
  ijmp
19
;-------------------------------------------------------------------------
20
;...
21
  rcall  puts
22
  .db  "Hallo Welt", 0x0D, 0x0A, 0
23
  rcall  puts
24
  .db  "Noch ein Text", 0x0D, 0x0A, 0

von Daniel (Gast)


Lesenswert?

Okay, danke für die vielen Antworten.

Werde mir die Macro-Prorammierung mal näher anschauuen, damit habe ich 
bis jetzt noch überhaupt keine Erfahrung.

Mich würde auch interessieren, ob ihr bei euren Projekte mit LCD-Ausgabe 
auch so viele Text-Strings habt? Bis wieviel Zeichen schickt ihr die 
Zeichen einzeln (nacheinander) zum LCD bzw. ab wieviel Zeichen verwendet 
ihr einen kompletten String aus dem Flash? Habe momentan ca. 
30verschiedene, konstante Textausgaben, die ich je nach Ereignis auf ein 
2zeiliges Display ausgebe.Ich suche immernoch einen für mmich optimalen 
Programmierstil, deswegen die Frage. Das ist zwar nur eine 
Hobby-Basteiel aber man will sich ja auch verbessern :-)

von chris (Gast)


Angehängte Dateien:

Lesenswert?

Daniel schrieb:
> ob ihr bei euren Projekte mit LCD-Ausgabe
> auch so viele Text-Strings habt?

ja, datei is net vollständig was die prgrammierung des lcds angeht aber 
so zu deiner frage

von Moritz A. (moritz_a)


Lesenswert?

Daniel schrieb:
> Bis wieviel Zeichen schickt ihr die
> Zeichen einzeln (nacheinander) zum LCD bzw. ab wieviel Zeichen verwendet
> ihr einen kompletten String aus dem Flash?

Ab 3 Zeichen hole ich es aus dem Flash, die Überlegung war wenn ich mich 
recht erinnere: Flash-Adresse sind 2 Byte, also lohnt es sich ab >2 
Zeichen nicht mehr einen char-Pointer zu übergeben und sich das RAM mit 
dem String zu befüllen, sondern die Flash-Adresse.

Ob das so 1:1 von Assembler auf C übertragbar ist musst du wissen ;)

von Klaus 2. (klaus2m5)


Lesenswert?

Peter Dannegger schrieb:
> Man kann den String direkt nach dem Call plazieren:
1
> puts:
2
>   pop  zh      ;get address after call
3
>   pop  zl
4
>   lsl  zl      ;*2 to get byte pointer
5
>   rol  zh
6
>   rjmp  _tou2
7
> _tou1:  rcall  putchar
8
> _tou2:  lpm  a0, Z+
9
>   tst  a0      ;check zero byte
10
>   brne  _tou1
11
>   lsr  zh      ;/2 to get word pointer
12
>   ror  zl
13
>   ijmp

Klever, bei ungerader Anzahl der Bytes im String springt der IJMP auf 
das letzte Wort im String, das ist jedoch durch das automatische Padding 
des Assemblers 0x0000 und wird als NOP decodiert. Das spart die 
Korrektur auf die nächste gerade Byteadresse, benötigt aber zwingend 
0x00 als Ende-Marker.

Es geht immer noch einen Tick schlanker!

von und (Gast)


Lesenswert?

ich speichere alle Strings als zusammenhaengenden Block, in ASM als .DB 
, wobei jeweils das erste Byte die Laenge des Strings bedeutet ab.

von Peter D. (peda)


Lesenswert?

und schrieb:
> wobei jeweils das erste Byte die Laenge des Strings bedeutet

Das wär mir zu umständlich, immer erst die Zeichen abzählen zu müssen.
Außerdem braucht man dann noch ein zusätzliches Register, um die Länge 
zu vergleichen.

Die C-Entwickler waren nicht doof und haben sich schon was dabei 
gedacht, einen String einfach mit 0 enden zu lassen.

von Klaus 2. (klaus2m5)


Lesenswert?

Peter Dannegger schrieb:
> Die C-Entwickler waren nicht doof und haben sich schon was dabei
> gedacht, einen String einfach mit 0 enden zu lassen.

Da gehörte nun wirklich nicht viel dazu und sie waren auch nicht die 
Ersten, die das so gemacht haben.

von Nosnibor (Gast)


Lesenswert?

Der String direkt hinterm Aufruf ist elegant und bequem, aber nur, wenn 
man den String nur einmal braucht. Ich habe meist eine Tabelle mit den 
Anfangsadressen der Strings, so daß ich nur ein Byte brauche, um einen 
String zu bezeichnen. Das zahlt sich aus, wenn komplexere 
Datenstrukturen Strings benutzen.

Und gerade bei Strings fürs LCD kann es hilfreich sein, wenn die 
Ausgabefunktion das erste Byte des Strings als Befehl statt Daten ans 
Display schickt. Oft muß man sowieso vorher den Cursor positionieren, 
das hat man dann mit nur einem zusätzlichen Byte erledigt.

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.