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
>Mich würde es interessieren wie ihr solche Strings verwaltet
printf("-> Please Select ");
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 | ;... |
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
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
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 |
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 :-)
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
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 ;)
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!
ich speichere alle Strings als zusammenhaengenden Block, in ASM als .DB , wobei jeweils das erste Byte die Laenge des Strings bedeutet ab.
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.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.