Forum: Mikrocontroller und Digitale Elektronik [AVR-ASM] Strings aus Flash über UART "printen"


von Marek N. (Gast)


Lesenswert?

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

von spess53 (Gast)


Lesenswert?

Hi

Ja das geht. Ich habe so eine Routine für LCD-Ausugaben. Aber wozu erst 
in das RAM kopieren?

MfG Spess

von Hannes L. (hannes)


Lesenswert?

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

...

von spess53 (Gast)


Lesenswert?

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

von Hc Z. (mizch)


Lesenswert?

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.

von Hc Z. (mizch)


Lesenswert?

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.

von Marek N. (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.