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
Hi Ja das geht. Ich habe so eine Routine für LCD-Ausugaben. Aber wozu erst in das RAM kopieren? MfG Spess
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
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.