Hi,
ich habe gestern angefangen auf einem atmega32 etwas mit Assembler zu
experimentieren. Ich hab zuvor bereits ein bisschen auf dem atmega32
programmiert aber immer nur in C, möchte mich jetzt aber intensiver mit
der Plattform beschäftigen und deswegen jetzt in Assembler
programmieren.
Mit Hilfe des Datenblatts von Atmel geht das eigentlich auch ganz gut,
jetzt habe ich aber ein Problem, bei dem ich leider auf dem Schlauch
sehe.
Im Grunde möchte ich nur einen Wert aus dem SRAM also .data auslesen und
diesen Mittels UART auf der Seriellen Schnittstelle ausgeben. Die
Kommunikation über UART funktioniert mit konstanten Werten auch ohne
Probleme. Versuche ich aber einen aus dem SRAM ausgelesen Wert zu
senden, kommt immer ein falscher Wert auf meinem Terminal heraus.
Ich denke, dass das ganze wahrscheinlich ein grundlegender Fehler ist,
dass ich irgendetwas nicht beachtet habe oder so. Da die meisten
Assembler Beispiele und Erklärungen sind ja auch leider für den Atmel
eigenen Assembler und lassen sich speziell in diesem Bereich scheinbar
nicht direkt auf den avr-gcc übertragen, den ich verwende.
Die UART Implementierung erfolgt in folgender Datei:
1
#define __SFR_OFFSET 0x00
2
#include <avr/io.h>
3
4
.text
5
.global init_usart, usart_transmit_byte
6
7
init_usart:
8
ldi r16, lo8(51)
9
out UBRRL, r16
10
ldi r16, hi8(51)
11
out UBRRH, r16
12
13
ldi r16, (1<<RXEN) | (1<<TXEN) | (1<<RXCIE)
14
out UCSRB, r16
15
16
ldi r16, (1<<URSEL)|(1<<USBS)|(3<<UCSZ0)
17
out UCSRC, r16
18
ret
19
20
21
;; Parameter:
22
;; r16 <- byte zum senden
23
24
usart_transmit_byte:
25
sbis UCSRA, UDRE
26
rjmp usart_transmit_byte
27
out UDR, r16
28
ret
Da ja bei avr-objcopy auch angegeben wird, was letztendlich in das Hex
File rein soll und ich mich damit auch noch nicht wirklich auskenne,
hier auch noch mein Makefile:
Also wie gesagt, mit konstanten Werten wie dem EOL und CR funktioniert
es ohne Probleme, erst wenn ich auf den Speicher zugreifen will kommt
anstellen des ":" ein "*", irgendwas läuft da also scheinbar beim
Speicherzugriff falsch. Ich bin eigentlich davon ausgegangen, dass ich
im .data Bereich einfach Daten definieren kann, die dann beim Start in
den SRAM geladen werden und auf die ich dann über das Label zugreifen
kann. Scheinbar scheint das ja leider nicht so einfach zu sein ^^.
Ich würde mich sehr freuen, wenn mir jemand dabei helfen könnte. Wenn
irgendwelche anderen Informationen noch nötig sind werde ich die
natürlich auch gerne nachreichen.
Gruß,
toti
toti schrieb:> die dann beim Start in> den SRAM geladen werden
Wer sollte das bei einem Assembler-Programm tun? Bei C: ja, das macht
der automatisch dazugelinkte Startup-Code.
Konrad S. schrieb:> toti schrieb:>> die dann beim Start in>> den SRAM geladen werden>> Wer sollte das bei einem Assembler-Programm tun? Bei C: ja, das macht> der automatisch dazugelinkte Startup-Code.
Ich muss zugeben, dass ich mir das auch nicht ganz beantworten konnte.
Heißt das, dass ich diesen Prozess von Hand anstoßen muss oder geht man
dabei ganz anders vor?
MWS schrieb:> Das bedeutet, was im SRam drin sein soll, muss man da per Programm> reinladen. Alternativ Daten per LPM aus dem Flash lesen.
Das klingt so ganz einleuchtend, das einzige Problem ist nur, dass ich
nichts dazu finden kann, wie beim gnu assembler festlegen kann, dass die
Daten im flash gespeichert werden sollen. Ich finde da leider nur
Beschreibungen für den Atmel Assembler.
Ergänze Main.S um ".global __do_copy_data", und entferne deinen eigenen
Reset-Vector. Das Initialisieren des Stack-Pointers kannst du dir dann
auch sparen (das passiert schon im Startup-Code, den du im Augenblick
einfach überspringst).
Außerdem fehlt beim Linken die -mmcu Option.
PS: Ach, mir fällt gerade auf, dass dein Reset-Vector eh wirkungslos
ist, da das .org eh nicht das macht was du denkst.
Stefan Ernst schrieb:> Ergänze Main.S um ".global __do_copy_data", und entferne deinen eigenen> Reset-Vector. Das Initialisieren des Stack-Pointers kannst du dir dann> auch sparen (das passiert schon im Startup-Code, den du im Augenblick> einfach überspringst).>> Außerdem fehlt beim Linken die -mmcu Option.>> PS: Ach, mir fällt gerade auf, dass dein Reset-Vector eh wirkungslos> ist, da das .org eh nicht das macht was du denkst.
mhhh, ok, ich hab den Reset-Vector gelöscht und .global __do_copy_data
hinzugefügt aber leider verstehe ich das mit dem Startup-Code jetzt
nicht genau. Von wo aus wird der aufgerufen und warum überspringe ich
den Momentan?
Torsten W. schrieb:> leider verstehe ich das mit dem Startup-Code jetzt> nicht genau. Von wo aus wird der aufgerufen
Der wird automatisch hinzugelinkt (sofern du das nicht unterbindest) und
ein Sprung darauf als Reset-Vektor eingetragen.
Torsten W. schrieb:> warum überspringe ich> den Momentan?
Weil du direkt nach main springst. Aber (wie gesagt) da habe ich mich
geirrt. Dein rjmp main landet eh nicht im Reset-Vektor, sondern ist
einfach nur toter Code. Die Module werden gelinkt, da kannst du keine
absoluten Adressen mit .org vorgeben. Das sind dann nur Modul-relative
Adressen.
Ahh ok, also nach dem Entfernen der .org _VECTORS_SIZE anweisung
funktioniert es jetzt, aber wirklich befriedigend ist das nicht, weil
ich es nicht ganz verstehe ^^. Könntest du mir dazu irgendeine Literatur
empfehlen, um das ganz genau nachzulesen. Ich wäre auch generell an
Tutorials für den GNU Assembler interessiert, weil ich da wie gesagt
keine gefunden hab.
Torsten W. schrieb:> wie beim gnu assembler festlegen kann
Zum gnu Assembler kann ich nicht viel sagen, aber ich nehme an, so wie
Du test_byte angelegt hast, landete das tatsächlich im Flash. Das
hättest Du dann per Z-Pointer und LPM in's SRam laden können. Was ich
für den Beginn auch mal selbst machen, bevor ich das automatisiert
stattfinden lassen würde.
MWS schrieb:> Zum gnu Assembler kann ich nicht viel sagen, aber ich nehme an, so wie> Du test_byte angelegt hast, landete das tatsächlich im Flash.
Ne, das landet schon im RAM (aber natürlich steht der
Initialisierungswert im Flash).
Stefan Ernst schrieb:> Was genau?
Was genau .global __do_copy_data bewirkt. Handelt es sich dabei um das
ganz am Anfang angesprochene Ladescript, was in den Code mit rein
gelinkt wird und dann zu beginn die Daten vom Flash in den SRAM kopiert?
Ich hab jetzt mal versucht einen ganzen String auszugeben, bekomme dabei
aber auch nur Müll heraus. Definiert hab ich den folgendermaßen:
1
.data
2
test_byte: .byte 0x3A
3
text_tmp: .string "Das ist ein test"
Ausgeben möchte ich den dann mit folgender Prozedur:
1
usart_transmit_string:
2
.next: lpm r16, Z+
3
cpi r16, 0x20 ;; Ausgeben bis zum Leerzeichen
4
breq .done
5
rcall usart_transmit_byte
6
rjmp .next
7
.done: ret
Und aufgerufen hiermit:
1
ldi ZL, lo8(text_tmp)
2
ldi ZH, hi8(text_tmp)
3
rcall usart_transmit_string
Ich möchte jetzt nicht jede Kleinigkeit hier vorschnell fragen, aber das
wundert mich jetzt ein bischen, weil das einzelne Byte funktioniert.
Torsten W. schrieb:> Was genau .global __do_copy_data bewirkt. Handelt es sich dabei um das> ganz am Anfang angesprochene Ladescript, was in den Code mit rein> gelinkt wird und dann zu beginn die Daten vom Flash in den SRAM kopiert?
Ja, damit wird sicher gestellt, dass diese Kopier-Routine Bestandteil
des Startup-Codes wird.
Torsten W. schrieb:> Ich möchte jetzt nicht jede Kleinigkeit hier vorschnell fragen, aber das> wundert mich jetzt ein bischen, weil das einzelne Byte funktioniert.
Und wo bei dem einzelnen Byte hast du lpm verwendet?
Stefan Ernst schrieb:> Wenn das funktioniert hat, hattest du schlicht Glück.> test_byte und text_tmp sind RAM-Adressen.
mhhh ok, also das heißt ja dann ich müsste sie einfach mittel
1
lds r16, test_byte
oder
1
ldi ZH, hi8(test_byte)
2
ldi ZL, lo8(test_byte)
3
ld r16, Z
laden können, da ja durch das .global __do_copy_data die
Speicherbereiche im SRAM ja durch den Startup-code initialisiert worden
sein müssten oder? Ich bekomme in diesem Fall aber wieder ein * statt
des Doppelpunkts.
Stefan Ernst schrieb:> Torsten W. schrieb:>> Ich bekomme in diesem Fall aber wieder ein * statt>> des Doppelpunkts.>> Hast du die Linker-Kommandozeile korrigiert?
Ich bin jetzt ein bisschen verwirrt. Ich hatte dem Linker bis jetzt als
Parameter "-m avr5" übergeben und dabei kam ja nicht das gewünschte
Ergebnis heraus. Sowohl beim Byte als auch bei dem String wurden falsche
Werte ausgegeben. Jetzt habe ich das Linken mal nicht direkt über avr-ld
ausgeführt sondern mittel avr-gcc und als Parameter -mmcu=atmega32
übergeben, wie beim Compilen. Und jetzt funktioniert das Ganze. Was mich
jetzt aber wirklich irritiert ist, dass wenn ich nach dem erfolgreichen
Linken wieder auf avr-ld umstelle und "-m avr5" oder auch gar nichts
übergebe, funktioniert es auch. Wenn ich dann aber im .data bereich
etwas verändere, z.B. einen Anderen String davor packe, dann werden
teile von dem ausgegeben. Ich vermute jetzt, dass der Linker auf die
Informationen vom letzten linken zugreift, wenn ich ihm nicht die
richten Parameter übergebe?! Liegt da irgendwo eine Temporäre Datei rum,
auf die avr-ld dann zugreift oder woran liegt das?
Konrad S. schrieb:> Lass mal den avr-gcc linken und gib ihm die Option -v mit. Dann siehst> du, wie der Linker aufgerufen wird.
Der Linker wird folgendermaßen aufgerufen:
ich gehe mal davon aus, dass collect2 und avr-ld identisch sind oder? Da
collect2 mir bei einem Start mit -v folgendes angibt:
1
/usr/libexec/gcc/avr/4.5.3/collect2 -v
2
collect2 version 4.5.3 (GNU assembler syntax)
3
/usr/bin/avr-ld -v
4
GNU ld (GNU Binutils) 2.22
Der Unterschied müsste dann ja daran liegen, dass ich beim direkten
Aufrufe von avr-ld die crtm32.o nicht übergebe und deswegen irgendwas im
Programm falsch läuft. Ich verstehe jetzt aber immer noch nicht, warum
es mit avr-ld auch direkt geht, wenn ich vorher einmal mit avr-gcc
linken lasse. Selbst wenn ich dazwischen ein make clean mache.
Torsten W. schrieb:> Der Unterschied müsste dann ja daran liegen, dass ich beim direkten> Aufrufe von avr-ld die crtm32.o nicht übergebe
Und was ist mit -lgcc und insbesondere -lc? Hast du die etwa bei deinem
eigenen avr-ld Aufruf auch mit drin?
Torsten W. schrieb:> Ich verstehe jetzt aber immer noch nicht, warum> es mit avr-ld auch direkt geht, wenn ich vorher einmal mit avr-gcc> linken lasse. Selbst wenn ich dazwischen ein make clean mache.
Ich wette du hast dem AVR zwischendurch den Saft nicht abgedreht.
Vermutlich landet bei deinem eigenen Aufruf kein __do_copy_data in Code.
Es funktioniert aber nach einem "richtigen" Durchlauf trotzdem, weil im
RAM immer noch das richtige drin steht (ein Reset oder Chip-Erase löscht
nicht das RAM).
Stefan Ernst schrieb:> Und was ist mit -lgcc und insbesondere -lc?
Huch, genau das hab ich grad eben noch bei meinem vorherigen Post
hinzugefügt. Genau als ichs abgeschickt habe, hab ich die Mail von
deinem Post bekommen und mein Post wurde wohl irgendwie nicht
gespeichert. Hört sich jetzt wie ne doofe Ausrede an ^^.
Stefan Ernst schrieb:> Vermutlich landet bei deinem eigenen Aufruf kein __do_copy_data in Code.> Es funktioniert aber nach einem "richtigen" Durchlauf trotzdem, weil im> RAM immer noch das richtige drin steht (ein Reset oder Chip Erase löscht> nicht das RAM).
Hui, da muss ich mir jetzt aber selber vorn Kopf hauen, daran hab ich
gar nicht gedacht. Aber es stimmt natürlich, ist ja auch total
einleuchtend, bin ich aber so alleine nicht drauf gekommen.
Vielen Dank auf jeden Fall für die vielen und besonders schnellen
Antworten, das hat mir sehr geholfen.