Hello World ;-)
Ich habe mal eine Grundsatzfrage. Und zwar habe ich bisher 8051 in
Assembler programmiert und beschäftige mich nun mit den Kuriositäten der
AVRs.
Bisher habe ich das so gehandhabt, dass wenn ich Strings aus dem Flash
über den UART ausgeben wollte, z.B. für interaktive Benutzerdialoge, ich
mir "Printmakros" geschrieben habe, an die als Parameter der
entsprechende String übergeben wurde. Die Zeichenkette stand also in der
Mitte des Programms, dort wo sie eben ausgegeben werden sollte. Das
Makro rief dann eine Funktion auf, die den String Byte für Byte auf
einen reservierten RAM-Bereich kopiert hat. Die Anfangsadresse des
Strings wusste ich ja aus dem Stack durch den Funktionsaufruf. Die
Kopierfunktion hat alle Bytes bis zur Terminierung "\0" in den Ram
kopiert. Währenddessen habe ich einen Zähler, auch als Pointer auf den
RAM incrementiert. Die Addition des Pointerwertes auf die
Rücksprungaddresse vom Stack lieferte mir die neue Rücksprungaddresse,
so dass nach der Rückkehr von der Funktion das Datenfeld, (welches als
Befehle interpretiert sicher Kuddel-Muddel anrichten würde) überprungen
wurde und das Programm an der Stelle NOP weitergemacht hat. Anschließend
konnte ich gemütlich die Bytes aus dem RAM per Interrupt über den UART
ausgeben, während der Controller schon eine andere Stelle des Flashs
beackern konnte.
Mit diesem Verfahren konnte ich gute Erfahrungen sammeln, auch wenn es
durch die Funktionsaufrufe etwas Overhead gab. (Für Interessierte der
Link zum "Kommandozeileninterpreter":
http://www2.ant.uni-bremen.de/~niemie08/projects/monitor_935.html)
Mein "Problem" ist, dass im Buch von Günter Schmitt
"Microcomputertechnik mit Controllern der Atmel AVR-RISC-Familie" die
Strings als Datenfelder am Ende des Programms zusammenhängend abgelegt
sind, und die Referenzierung über Labels ins Z-Register läuft. Da würde
ich bei einigen Dutzend Strings den Überblick verlieren!
Meine konkrete Frage nun: Darf ich das so handhaben, wie ich es bisher
beim 8051 gemacht habe, oder gibt es mit den AVRs elegantere Methoden
als die von G. Schmitt?
Beste Grüße, Marek
spess53 schrieb:> Hi>> Ja das geht. Ich habe so eine Routine für LCD-Ausugaben. Aber wozu erst> in das RAM kopieren?
Z.B. Ringbuffer, um blockierendes Programmieren zu vermeiden. In der
Zeit (zwischen den UDRE-Interrupt-Aufrufen) in der man die Bytes
rausschaufelt, kann die Mainloop zur Tagesordnung zurück kehren. Mach'
ich bei Text-LCD meist auch so, organisiere das aber nicht als Ring,
sondern als Bildschirmspeicher.
Übrigens: Ich habe die Strings (aber meist für LCD, seltener für UART)
meist am Programmende oder einer separaten Include-Datei und verliere
dabei dank sprechender Labelnamen nicht den Überblick. An Ort und Stelle
geht es zwar auch, kostet dann aber einen zusätzlichen Sprung über den
Text hinweg.
>> MfG Spess
...
Hi
>An Ort und Stelle geht es zwar auch, kostet dann aber einen zusätzlichen >Sprung
über den Text hinweg.
Das mache ich in der Routine über den Stack. Also in der Art:
call print_str
.db "Hello World",0
und hier gehts weiter
MfG Spess
Der AVR schreibt die Rücksprungadresse als Wortadresse ins Stack. Um
sie für lpm passend zu bekommen, musst Du sie verdoppeln, da Du dafür
Byteadressen brauchst. Um die tatsächliche Rücksprungadresse zu
bekommen, musst Du nach der Zeichenausgabe ggfs. auf die nächste
gerade Adresse gehen und wieder halbieren. Im Programmcode darf nach
dem String die .align-Direktive (bei gcc/gas) nicht fehlen.
Ganz 1:1 kannst Du es vom 8051 also nicht übernehmen, aber gehen tut's.
Ich habe das auch schon öfter gemacht.
Und weil's so schön ist, ein funktionierendes Beispiel (für gas (GNU
Assembler)):
1
;;; print string immediately following the call (in program space),
2
;;; then return to the caller's caller
3
.global iPdata
4
iPdata: pop ZH
5
pop ZL
6
add ZL,ZL
7
adc ZH,ZH
8
rjmp 12f
9
pdlub: rcall putTxChar
10
12: lpm r24,Z+
11
tst r24
12
brne pdlub
13
ret
Und ein Aufruf sieht so aus:
1
curHome: rcall iPdata
2
.byte 0x1b,'[','H',0
3
.align 1
Das Errechnen der Rücksprungadresse habe ich mir hier gespart, da der
Caller sowieso auch gleich zurückkehren würde. Solange das gegeben ist,
kann man einfach zwei Ebenen zurückspringen, was kompakter ist. Im
obigen Beispiel wird also curHome() (von C) oder rcall curHome (aus ASM)
aufgerufen und kehrt dorthin zurück.
Guten Abend,
na da bin ich ja beruhigt, dass einige andere auch eine ähnliche Lösung
dafür erarbeitet haben.
Meine Intention damals war eben wirklich, dass das Hauptprogram eben
schon was anderes machen kann, oder in den Stand-by geht, während die
einzelnen Buchstaben relativ langsam über den UART oder LCD
hinauströpfeln.
Nach einigem Überlegen, bin ich nun doch zu der Entscheidung gekommen,
dass ich, entgegen meiner ursprünglichen Absicht die Strings doch hinten
zusammenhängend in einem Datenfeld anordne, um sie dann per Label über
Z-Register aufzurufen. Das hat den Vorteil, dass ich ggf. bei Änderungen
alle Strings auf einmal sehen und bearbeiten kann, und nicht erst
Dutzende Print-Aufrufe im ganzen Quelltext durchsuchen muss. Durch das
Einbinden einer eigenen Datei könnte man sogar einfach verschiedene
Sprachen (Deutsch, Enlisch, Spanisch, Franzöisch) der Menüs einbinden...
Vielen Dank für das positive Feedback :-)
Beste Grüße, Marek