Forum: Compiler & IDEs Strings im oberen Flash-Bereich (> 64K)


von Günter R. (galileo14)


Lesenswert?

Wenn man für einen ATmega128 einen Bootloader programmiert und dieser 
soll etwas am LCD-Display anzeigen, so müssen die Strings ja im oberen 
64K-Flashbereich liegen; die normalen "_p"-Funktionen sind dann ja nicht 
anwendbar, bzw. Konstrukte der Art "PutTextOnLCD(PSTR("blablah"));", da 
ja schon das PSTR-Makro nur bis 64K geht.

Ich hatte vor längerer Zeit lange herumgebastelt, um dafür eine Lösung 
zu finden, und ich fand folgendes heraus:

#define PSTRX(s) (__extension__({static const char c[] PROGMEM = (s); 
(uint32_t)(c);}))

void PutTextOnLCD(uint32_t s)

{
  char cc;
  ...
  cc=pgm_read_byte_far(s);
  ..
}

// Verwendung:

PutTextOnLCD(PSTRX("Hallo!"));

Ich hatte also ein eigenes PSTR-Makro gebastelt, das hier PSTRX ("X" für 
"eXtended") heißt.

PSTR und PSTRX machen ja nichts anderes, als daß sie einen String unter 
einem nach außen unbekannten Namen im Flash ablegen und dessen Adresse 
abliefern.

Diese Lösung habe ich jahrelang unter einer 2006er WinAVR-Version 
problemlos (und warnungslos) angewandt. Als ich kürzlich auf 
WinAVR-20090313 umgestiegen bin, kommen jetzt plötzlich Warnungen der 
Art

"cast from pointer to integer of different size"

die sich auf das PSTRX-Makro beziehen. Das Programm funktioniert 
durchaus, aber die Warnung stört mich, und sie zeigt auch, daß der 
Compiler nicht ganz zufrieden ist.

Hat jemand eine Idee, wo man drehen müßte? Oder wie man das ganze anders 
(aber ebenso elegant) löst?

Günter

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Günter R. wrote:

> Hat jemand eine Idee, wo man drehen müßte?

Nö, auf Anhieb nicht.  Aber bist du dir sicher, dass das wirklich
funktioniert?  Wenn ich das compiliere, dann generiert er
mir:
1
        ldi r22,lo8(c.1607)
2
        ldi r23,hi8(c.1607)
3
        clr r24
4
        sbrc r23,7
5
        com r24
6
        mov r25,r24
7
        call PutTextOnLCD

c.1607 ist dabei der Label für das anonyme String-Objekt.  Es
ist gut zu sehen, dass nur dessen untere 16 bits benutzt werden,
während die oberen 16 bits vorzeichenerweitert aus den unteren
als 0 oder 0xffff eingefügt werden.  Das ist sicher nicht das,
was du wolltest.

von Günter R. (galileo14)


Lesenswert?

Hallo Jörg,

kenne mich im AVR-Assembler nicht so aus (nach 8080/Z80/8051/TLCS-900 
habe ich Assembler nicht "weitergepflegt - wäre aber wohl doch nützlich 
:|). Trotzdem habe ich in meinem Listing genau die Assembler-Folge 
gefunden, die Du oben hingeschrieben hast.

Es ist also wohl so, daß der Adressoperator nur 16 Bits weitergibt? Und 
daß durch den uint32_t-Cast wohl 0xffff auf die oberen Adressen 
erweitert wird? Dadurch würde das Programm funktionieren, wenn auch 
vielleicht nur "zufällig" (soetwas ist immer schlecht, das weiß ich, und 
geht mir auch immer gegen den Strich).

Die Methode erfordert, daß man 32-Bit-Adressen bekommt. Kann man dann 
möglicherweise in AVR-C gar keine 32-Bit-Adressen eines Datenobjekts 
ermitteln (Datenobjekte, die im Flash liegen, somit natürlich in einem 
anderen Adreßraum)?

Günter

von Peter (Gast)


Lesenswert?

Ich hatte da einst ein Makro verwendet, um eine 24-Bit Addresse (bzw 32 
Bit) von einem progmem Pointer zu erhalten. Eventell hilft Dir dieses 
makro weiter...

Beispiel: UART_puts_PF(FAR(*PSTR(pgmemstr)));
1
//----------------------------------------------------------
2
// Macros to access strings defined in PROGMEM above 64kB
3
//----------------------------------------------------------
4
#define FAR(var)                                      \
5
({                                                    \
6
    uint_farptr_t tmp;                                \
7
                                                      \
8
    __asm__ __volatile__(                             \
9
                                                      \
10
            "ldi    %A0, lo8(%1)"           "\n\t"    \
11
            "ldi    %B0, hi8(%1)"           "\n\t"    \
12
            "ldi    %C0, hh8(%1)"           "\n\t"    \
13
            "clr    %D0"                    "\n\t"    \
14
        :                                             \
15
            "=d" (tmp)                                \
16
        :                                             \
17
            "p"  (&(var))                             \
18
    );                                                \
19
    tmp;                                              \
20
})

von Günter R. (galileo14)


Lesenswert?

Hallo, Peter,

Dein Makro sieht sehr gut aus, funktioniert perfekt, und spart noch 
Platz gegenüber meiner ursprünglichen (unperfekten) Lösung.

Vielen Dank!

Gruß, Günter

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Mag das vielleicht mal jemand als offiziellen Patch für avr-libc
einreichen?

von Peter (Gast)


Lesenswert?

Gern geschehen, es freut mich, dass das Makro auch noch mit aktuellem 
AvrGcc funktioniert.

@Jörg: Ich will mich da nicht mit fremden Lorbeeren schmücken, ich hatt 
das Makro einst ebenfalls in einem Forum erhalten. Ist aber schon 3 
Jahre her und ich weiss nicht mehr woher. Ich habe seinerseits 
vorgeschlagen das Makro in die avrlibc aufzunehmen "pgmspace.h" oder 
direkt auf geeignete Weise in die xyz_P() Funktionen zu integrieren, 
stiess aber damals auf kein "Gehöhr". Das Problem taucht aber immer 
wieder in den Foren auf.

Ich weiss nicht, wie und wo man sowas als offiziellen Patch oder 
Erweiterung einreichen kann, aber ich denke das ganze ist bei Dir, als 
"Vater" der avrlibc gut aufgehoben... ;o)

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Peter wrote:

> Ich weiss nicht, wie und wo man sowas als offiziellen Patch oder
> Erweiterung einreichen kann,

https://savannah.nongnu.org/patch/?group=avr-libc

Je einfacher es zu integrieren ist (Patch als Ausgabe von diff -u
zwischen alter und neuer Datei liefern, Dokumentation nicht
vergessen -- kann man per copy&paste von den existierenden Einträgen
nehmen), um so größer ist die Chance, dass es beim nächsten Release
berücksichtigt werden kann.  Ich habe für die Vorbereitung eines
Releases immer einen endlichen Zeitfonds, in dem kann ich entweder
dann z. B. einen Patch integrieren, der mich 4 Stunden kostet (die
sind mit allen Tests schnell zusammen) oder 8 Patches, die mich eine
halbe Stunde jeweils kosten.

> aber ich denke das ganze ist bei Dir, als
> "Vater" der avrlibc gut aufgehoben... ;o)

Nein, er ist noch nicht ,,bei mir''.  Dafür muss er in einem
Patchtracker landen (d. h. bei obiger URL eingereicht sein).  Ich kann
beim nächsten Release unmöglich noch alle Forums-Diskussionen des
letzten halben Jahres durchsehen (nach welchem Stichwort denn
überhaupt?) nach irgendwelchen Vorschlägen.  Das ist kein Unwillen
oder gar Desinteresse, aber anders lässt sich das nicht organisieren.

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.