Hallo,
ich glaube, dass ich ein Problem mit dem Speicherbereich des ATmega1284
habe: Zur Ausgabe von Zeichen auf ein Display habe ich die unteren 128
Zeichen der Ascii-Tabelle in ein Array im Flash abgelegt:
const Tu8 u8_FONT5[ZEICHENANZAHL_FONT5][BYTEANZAHL_FONT5] PROGMEM ={...}
Für verschiedene Fonts habe ich insgesamt 3 Tabellen abgelegt. Alle
Routinen zur Ausgabe funktionieren auch. Da ich aber nun auch
Sonderzeichen wie Ä, Ö,Ü etc. ausgeben möchte, habe ich jeweils eine
zweite Tabelle mit den hinteren 128 Zeichen der Ascii-Tabelle abgelegt.
Dadurch stoße ich jedoch an die Grenze des Speicherbereichs:
Beim Compilieren:
Device: atmega1284p
Program: 117566 bytes (89.7% Full)
(.text + .data + .bootloader)
Data: 11372 bytes (69.4% Full)
(.data + .bss + .noinit)
Dadurch funktionieren auch nicht mehr alle Routinen. Wenn ich nur die
erste Hälfte der großen Font benutze, geht wieder alles. Auch wenn ich
beide Hälften der größten Font benutze und die Tabellen der beiden
kleinen Fonts auskommentiere, funktioniert alles.
Der Flash wird dann natürlich weniger beschrieben, z.B. bei
Program: 91454 bytes (69.8% Full)
(.text + .data + .bootloader)
funktioniert alles.
Darf ich gar nicht die kompletten 128 KB des ATmega1284P beschreiben?!
Ein Hinweis, wo das im Datenblatt steht, wäre nett.
Gruß
Andreas
Andreas H. schrieb:> Dadurch stoße ich jedoch an die Grenze des Speicherbereichs:
An welche "Grenze"?
> Dadurch funktionieren auch nicht mehr alle Routinen.
Wie kommst du zu diesem "Dadurch"?
Nein, dein Speicher ist gut ausgenutzt, aber nicht voll. Das
Problem sitzt irgendwo anders, möglicherweise irgendwo ganz
anders.
Allerdings: die ganzen *_P-Funktionen funktionieren nur, solange
sich die progmem-Daten, die sie lesen sollen, innerhalb der ersten
64 KiB des Flashs befinden, da sie einen normalen Zeiger
benutzen, der nur 16 bit breit ist. Der Linkerscript tut zwar sein
bestes, progmem-Daten daher direkt hinter die Tabelle mit den
Interruptvektoren zu pflanzen (und jeglichen ausführbaren Code erst
danach), aber trotzdem ist natürlich insgesamt nur Platz für knapp
64 KiB an progmem-Daten.
Wenn du mehr adressieren willst, musst du die umständlicheren *_far-
Routinen zum Zugriff benutzen, die mit einer 32-bit-Zahl statt eines
Zeigers für die Adresse arbeiten.
Vielen Dank für die Antwort!
Ich denke, dass du damit direkt ins Schwarze getroffen hast. Ich habe
meine Tabellen mal um die ersten 32 Zeichen der ASCII-Tabelle gekürzt,
da ich diese nie benutzen werde. Dadurch habe ich die Größe aller
Tabellen auf 61138 Byte gedrückt und alles funktioniert auch so wie es
soll. Wenn ich diese wieder aufblähe, dann komme ich sicherlich über die
64kB.
Ich habe dazu noch zwei Frage:
- Ich muss nur beim Zugriff auf das Array die Routine
"pgm_read_byte_far()" statt "pgm_read_byte()" benutzen und die Adresse
vorher in eine 32-Bit-Variable casten? Ich weiß ja nicht, ob evtl. noch
weitere Texte oder Ähnliches hinzukommt und dann komme ich evtl. wieder
in den Bereich >64kB. So wäre ich mir dann ganz sicher, dass das unter
allen Umständen funktioniert?
- Gibt es eine grobe Abschätzung, was für einen Laufzeitverlust man
zwischen dem 16-Bit- und dem 32-Bit-Zugriff hat?
Danke schonmal im Voraus...
Andreas H. schrieb:> - Ich muss nur beim Zugriff auf das Array die Routine> "pgm_read_byte_far()" statt "pgm_read_byte()" benutzen und die Adresse> vorher in eine 32-Bit-Variable casten?
Ja. Im Zweifelsfalle mal den vom Compiler generierten Assemblercode
begutachten.
> - Gibt es eine grobe Abschätzung, was für einen Laufzeitverlust man> zwischen dem 16-Bit- und dem 32-Bit-Zugriff hat?
Du kannst dir im Headerfile die Implementierung der beiden Funktionen
ansehen (ist inline assembler). Der Unterschied ist nicht sehr groß.
Für die _P-Funktionen gibt's halt nur viel mehr an Vorgefertigtem,
sowas wie strlen_P() usw., das gibt's für die 32-(eigentlich 24-)Bit-
Versionen nicht, dort kann man nur je ein oder mehrere Bytes in den
SRAM lesen.
Andererseits, wenn man das Problem kennt und ein Auge drauf hat,
dass alles unter 64 KiB bleibt, dann kann man sicher auch mit den
einfacheren Implementierungen leben.
Darf ich fragen, was das für Fonts sind und wie du die aufgebaut hast?
3 Fonts a' 256 Zeichen macht 768 Zeichenbeschreibungen.
Da du mehr als 64k verbrutzels, bedeutet das, das du pro Zeichen
65535 / 768 = 85
85 Bytes pro Zeichen verbraten hast. Und das kommt mir dann doch bei
aller Liebe etwas viel vor. Ein normales Zeichen in einer 8*5 LCD Matrix
dargestellt, lässt sich in 5 Bytes beschreiben. OK, bei einem Grafik-LCD
können das dann schon mal ein paar mehr sein, weil man ja auch größere
Schriftarten haben will und ein wenig detailierter ausgeführte Zeichen
hat. Aber 85 Bytes pro Zeichen?
Karl Heinz Buchegger schrieb:> Darf ich fragen, was das für Fonts sind und wie du die aufgebaut hast?
Mein Display hat 4 Graustufen, das heißt ich habe 2 Bit Informationen
für 1 Pixel gespeichert. Die Höhe der 3 Fonts beträgt 8, 12 und 24
Pixel. Die Zeichen selber sind unterschiedlich breit, das heißt ich habe
noch jeweils eine weitere Tabelle in der die Länge jedes einzelnen
Zeichens steht (max. 13, 19, 27 Pixel). Die Tabelle für die Zeichen wäre
noch weiter optimierungsfähig, da die Breite des zweidimensionalen
Arrays sich am längsten Zeichen orientiert und somit der nichtbenutze
Bereich der anderen Zeichen mit 0x00 aufgefüllt ist. Da die Routinen
bisher aber alle so geschrieben sind und ich ja noch ein bisschen
Speicher habe, wollte ich das erstmal so lassen, falls es geht. Die
Alternative wäre dann aus dem zweidimensionalen Array ein großes Array
zu machen, und ein weiteres Array, dass je nach ASCII-Zeichen
entscheidet an welche Stelle ich im Array springen muss. Wegen der
Übersicht habe ich mich aber erstmal für die erstere Variante
entschieden.
... und so kommt man dann eben auf 64kB Tabellen :/
Ich komm mit dem Wechsel zu der 32-Bit-Routine aber noch net ganz hin:
Wenn ich
u8_Data = pgm_read_byte(&u8_FONT5[u8_Zahl - 32][u8_Zaehlarray +
u8_Page]);
durch
u8_Data = pgm_read_byte_far(&u8_FONT5[u8_Zahl - 32][u8_Zaehlarray +
u8_Page]);
ersetze spuckt er mir folgende Warnung aus:
../Display.c:381: warning: cast from pointer to integer of different
size
Andreas H. schrieb:> u8_Data = pgm_read_byte_far(&u8_FONT5[u8_Zahl - 32][u8_Zaehlarray +> u8_Page]);
Ich habe bei der Suche einen Makro von Carlos Lamas auf avrfreaks.net
gefunden:
1
#define GET_FAR_ADDRESS(var) \
2
({ \
3
uint_farptr_t tmp; \
4
\
5
__asm__ __volatile__( \
6
\
7
"ldi %A0, lo8(%1)" "\n\t" \
8
"ldi %B0, hi8(%1)" "\n\t" \
9
"ldi %C0, hh8(%1)" "\n\t" \
10
"clr %D0" "\n\t" \
11
: \
12
"=d" (tmp) \
13
: \
14
"p" (&(var)) \
15
); \
16
tmp; \
17
})
Der sollte dir helfen. Wahrscheinlich sollten wir den in die
avr-libc mit aufnehmen.
Hm, auf dieses Makro bin ich auch schon gestoßen, allerdings bekomme ich
anstatt der Warnung nun eine Fehlermeldung:
../Display.c:385: error: lvalue required as unary '&' operand
Ich kann mit der Fehlermeldung aber auch net so viel anfangen :(
Oder ich wende das Makro falsch an?!
u8_Data = pgm_read_byte_far(GET_FAR_ADDRESS(&u8_FONT5[u8_Zahl -
32][u8_Zaehlarray + u8_Page]));
wenn ich das "&" weglasse so wie hier:
u8_Data = pgm_read_byte_far(GET_FAR_ADDRESS(u8_FONT5[u8_Zahl -
32][u8_Zaehlarray + u8_Page]));
erhalte ich folgende Fehlermeldungen insgesamt drei mal:
C:\Projekte\F120neu\trunc\src\default/../Display.c:385: undefined
reference to `r30'
:(
Andreas H. schrieb:> u8_Data = pgm_read_byte_far(&u8_FONT5[u8_Zahl - 32][u8_Zaehlarray +> u8_Page]);>> ersetze spuckt er mir folgende Warnung aus:> ../Display.c:381: warning: cast from pointer to integer of different> size
Der Compiler bringt das nicht so recht auf die Reihe. Probier mal eine
zusätzliche Klammer:
Andreas H. schrieb:> wenn ich das "&" weglasse so wie hier:>> u8_Data = pgm_read_byte_far(GET_FAR_ADDRESS(u8_FONT5[u8_Zahl -> 32][u8_Zaehlarray + u8_Page]));>> erhalte ich folgende Fehlermeldungen insgesamt drei mal:
Hat als eindimensionales Array ok getestet, das Makro kann keine
zweidimensionalen Arrays aufdröseln.
Der Adressoperator & wird vom Makro eingefügt, der muss trotzdem raus.
Andreas schrieb:> Der Compiler bringt das nicht so recht auf die Reihe. Probier mal eine> zusätzliche Klammer:u8_Data = pgm_read_byte_far(&(u8_FONT5[u8_Zahl -
32][u8_Zaehlarray + u8_Page]));
> Bei mir funktioniert es so.
Hast Du da nicht was übersehen ?
> Bei mir funktioniert es so.
Es lebe der Unterschied zwischen compilieren und funktionieren ;D
Jörg Wunsch schrieb:> Du musst die Adressrechnung selbst machen:>> u8_Data = pgm_read_byte_far(GET_FAR_ADDRESS(u8_FONT5) + irgendwas +> irgendwasanderes * XXXX);
Okay, das wars. Jetzt gehts mit folgendem Ausdruck:
u8_Data = pgm_read_byte_far(GET_FAR_ADDRESS(u8_FONT5) + 162 * (u8_Zahl -
32) + u8_Zaehlarray + u8_Page);
Super, vielen Dank für die große Hilfe! Alleine hätte ich das nie und
nimmer hinbekommen...
Andreas schrieb:> Nein. Was soll ich Deiner Meinung nach übersehen haben?
Wir sprachen über ein Makro um Adressen über 64k korrekt zu extrahieren,
um sie dann pgm_read_byte_far zu übergeben.
Und was glaubst Du wohl wird Deine Zeile hier mit Progmem-Daten machen,
die jenseits der 64k liegen ?
> u8_Data = pgm_read_byte_far(&(u8_FONT5[u8_Zahl - 32][u8_Zaehlarray +> u8_Page]));
Da wird schon eine Adresse übergeben, nur zeigt die irgendwohin, aber
nicht dahin wo sie soll.
Jörg Wunsch schrieb:> Ich habe bei der Suche einen Makro von Carlos Lamas auf avrfreaks.net> gefunden:
OK, und wenn man nochmal in der aktuellen avr-libc nachsieht statt
in einer veralteten Version :), dann gibt's dort ganz offiziell
inzwischen einen Makro pgm_get_far_address(), der genau das tut.
(Carlos Lamas ist im SVN-Log auch als Autor benannt.)
MWS schrieb:> Und was glaubst Du wohl wird Deine Zeile hier mit Progmem-Daten machen,> die jenseits der 64k liegen ?
Ich glaube nicht. Meine Tabelle liegt an der Adresse 0x1c320 (habe
gerade extra für Dich im lss-File nachgesehen) und jeder einzelne Wert
wird mit
1
wert=pgm_read_byte_far(&(tabelle[i]));
korrekt gelesen.
> Da wird schon eine Adresse übergeben, nur zeigt die irgendwohin, aber> nicht dahin wo sie soll.
Eben nicht. Es wird die korrekte Adresse geladen. Das Macro
GET_FAR_ADDRESS wird dafür nicht benötigt. Jedenfalls nicht in der
Compilerversion 4.5.1
Andreas schrieb:> Eben nicht. Es wird die korrekte Adresse geladen. Das Macro> GET_FAR_ADDRESS wird dafür nicht benötigt. Jedenfalls nicht in der> Compilerversion 4.5.1
Na, warum soll's nicht möglich sein, aber stell doch mal Deinen Testcode
ein, damit's von jemand mit der entsprechenden Version überprüfbar ist.
Nicht dass Du aus Versehen 'nen Treffer hattest :D
Andreas schrieb:> Eben nicht. Es wird die korrekte Adresse geladen. Das Macro> GET_FAR_ADDRESS wird dafür nicht benötigt. Jedenfalls nicht in der> Compilerversion 4.5.1
Wenn, dann ist das Zufall, dass es funktioniert. Gegeben sei:
(Compiler ist auch hier ein GCC 4.5.1, und er spuckt natürlich
auch die Warnung aus, die oben schon genannt worden ist.)
Das Ergebnis ist:
1
...
2
.global getcharfromstring
3
.type getcharfromstring, @function
4
getcharfromstring:
5
/* prologue: function */
6
/* frame size = 0 */
7
/* stack size = 0 */
8
.L__stack_usage = 0
9
subi r24,lo8(-(string))
10
sbci r25,hi8(-(string))
11
clr r26
12
sbrc r25,7
13
com r26
14
mov r27,r26
15
/* #APP */
16
; 8 "foo.c" 1
17
out 59, r26
18
movw r30, r24
19
elpm r24, Z+
20
21
; 0 "" 2
22
/* epilogue start */
23
/* #NOAPP */
24
ret
25
...
Es ist deutlich zu sehen, dass RAMPZ (Register 59) von r26 geladen
wird, das wiederum einen Fantasiewert bekommt und nichts, was
irgendwie dem tatsächlichen Bit 16 von "string" entspricht.
Gegenprobe:
Auch hier wiederum gut zu erkennen, dass RAMPZ, wenn auch ein wenig
umständlich, seinen Wert aus den Bits 16 ... 23 der Adresse von
"string" bekommt (die vom Linker einzutragen ist).
Hallo,
da bin ich wieder :( Ich brauche nochmal eure Hilfe. Ich habe im Flash
ebenfalls Strings abgespeichert und würde gerne von euch wissen, ob ich
beim Zugriff hier Probleme bekommen könnte bezüglich des
Speicherbereichs >64kB, vor allem weil es weiter oben heisst, dass die
Funktion strlen_P() evtl. da nicht mitmacht.
Ich musste mich auch erst selbst in das Thema "Strings im Flash"
einlesen und habe das ganze dann wie in
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Array_aus_Strings_im_Flash-Speicher
realisiert:
Hier mal die betreffenden Auszüge aus meinem Code:
Hallo,
nur um nochmal die letzte Frage aufzurufen. Ich habe die
Compilerreihenfolge der Arrays geändert. Dann habe ich mir das .lss-File
angeguckt und nun ist das Array "TextArray" direkt hinter den
Interruptvektoren. Und dieses Array ist auf jeden Fall kleiner als 64kB.
Also brauche ich den Zugriff darauf ja nicht zu ändern, und für den
Zugriff auf die anderen Arrays habe ich ja die Lösung mit dem Makro
genommen.
So sollte ich ja dann auf der sicheren Seite sein, oder?!
Andreas H. schrieb:> So sollte ich ja dann auf der sicheren Seite sein, oder?!
Ja, klingt so. Ganz sicher würdest du gehen, wenn du die Flash-
Daten, bei denen dir die Lage egal ist, über
1
__attribute__((section("yoursectionname")))
in eine x-beliebige Section packst und dann mit einem custom linker
script diese Section hinter die .progmem* packst.