Forum: Mikrocontroller und Digitale Elektronik ATmega128 double Werte in Flash (PROGMEM)


von R. F. (firo)


Lesenswert?

Hallo zusammen,

nachdem ich nun den ganzen Tag damit rumhantiert und etliche Threads die
einige verwandte Suchbegriffe enthalten gelesen habe, brauche ich immer
noch Hilfe.
So weit ich weiß sind auf einem AVR (ATmega128) die Gleitpunktwerte
float und double 32-bit breit, richtig?
Wenn ich nun z.B.
1
const double testpgm PROGMEM = 1.23456789;
2
3
int main(void)
4
{
5
char buffer[32];
6
double testme;
7
8
...
und später in main:
1
testme = (double) pgm_read_dword(&testpgm);
2
dtostre(testme,buffer,8,1);
3
uart_puts(buffer);
4
uart_puts("\r\n");
nutze, bekomme ich irgendweine, nicht passende Zahl ausgegeben, z.B.
1.06731900e+09.

Ich benutze AVR Studio 4(.13.528) mit WinAVR-20070525
1
-Wall -gfwarf-2 -DF_CPU=1843200UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums

Ich bedanke mich recht herzlich für eure Hilfe und bitte um Nachsicht,
wenn ich wichtige Informationen unterschlagen habe, ist hier mein erster
Thread :-/ Ich reiche die dann umgehend nach.

Schönen Gruß

wald-vor-lauter-bäumen-nicht-mehr-seh

von Uhu U. (uhu)


Lesenswert?

pgm_read_dword gibt vermutlich 2 Byte zurück - richtig?

Sieh mal nach welchen Wert sizeof(double) zurück gibt. Ich würde wetten, 
daß es > 2 ist.

Dein Programm macht dann folgendes:

  - es liest 2 Byte an der Adresse &testpgm aus dem Programmspeicher
  - wandelt den unsigned (?) Wert, den pgm_read_dword zurückgibt in
    double um
  - weist das Ergebnis an test zu.

Ich vermute, daß das, was du willst, viel einfacher zu erreichen ist:

  testme = testpgm;

Der Compiler sollte dafür sorgen, daß der richtige Code dafür erzeugt 
wird.

von Johannes M. (johnny-m)


Lesenswert?

Uhu Uhuhu wrote:
> pgm_read_dword gibt vermutlich 2 Byte zurück - richtig?
Nö. Ein dword ist ein "double word", also zwei Worte. Ein Wort sind 2 
Bytes, also 16 Bit. Demnach ist ein Doppelwort 32 Bits breit.

> Sieh mal nach welchen Wert sizeof(double) zurück gibt. Ich würde wetten,
> daß es > 2 ist.
Ja, 4...

von R. F. (firo)


Lesenswert?

Hallo,

der Ansatz war also komprimiert: float / double = 32 Bit und doppelwort 
= 32 Bit.
Daher auch dword...

Weiterhin möchte ich eigentlich eine Tabelle von Faktoren (700 Stück) 
Typ double im Flash ablegen, weil ich das E²PROM noch für andere 
Tabellen (Die veränderbar sein müssen) brauche. Diese Tabelle ist eine 
konstante Struktur, da es sich um die relative spektrale Empfindlichkeit 
einer BPX61 handelt, da diese in der Hardware "fest" eindesigned ist, 
braucht man diese nicht ändern können.

Also, wo mache ich den Denkfehler, weil ich habe es getestet mit int8_t, 
mit int16_t und int32_t, jeweils mit pgm_read_byte, pgm_read_word, 
pgm_read_dword. Allerdings ist dann die Konvertierung nicht mit dtostre 
gewesen, sondern mit itoa und 2x ltoa, mal gucken ... aber daran sollte 
es nicht liegen, oder?

Danke für diese und die zukünftigen Antworten

Grüße

PS: Jetzt habe ich mal mit
1
double testpgm PROGMEM = 1.2345;
2
3
int main(void)
4
{
5
   char buffer[64];
6
...
7
8
testme = (double) pgm_read_dword(&testpgm);
9
10
sprintf(buffer,"%e",testme);
11
12
uart_puts(buffer);
13
uart_puts("\r\n");

und bekomme ein '?' raus aaaarrrggghhh

ratlos

von holger (Gast)


Lesenswert?

>sondern mit itoa und 2x ltoa, mal gucken ... aber daran sollte
>es nicht liegen, oder?

Genau daran liegt es. itoa() ist für integer, ltoa
ist für long, ???toa() ist für double. Oder
nimm sprintf().

von Uhu U. (uhu)


Lesenswert?

Was gibt pgm_read_dword zurück? int?

Wenn ja, ist es kein Wunder daß du was falsches herausbekommst, denn 
dann interpretiert der Compiler das Bitmuster, das zurück kommt als int 
und ruft intern die int -> double Wandlung. Das Bitmuster im 
Programmspeicher ist aber schon double-Format.

Versuchs mal mit einem union aus einem int und einem double, weise dem 
int das Ergebnis von pgm_read_dword zu und hole den double-Wert aus der 
double-Komponente.

Hast du die Einfachlösung testme = testpgm; mal probiert?

von R. F. (firo)


Lesenswert?

Hallo,

das war missverständlich von mir:
itoa habe ich angewendet, wenn auch über main ein int8_t als PROGMEM 
Konstante deklariert war,
ltoa für int16_t (wo ich sehe, dass es wohl auch mit itoa gehene müsste) 
und für int32_t.

(Zwischenzeitlich noch ein PS im Beitrag darüber angefügt)

Laut AVR-Libc:
1
8-bit types.
2
typedef signed char   int8_t
3
typedef unsigned char   uint8_t
4
5
16-bit types.
6
typedef int   int16_t
7
typedef unsigned int   uint16_t
8
9
32-bit types.
10
typedef long   int32_t
11
typedef unsigned long   uint32_t
12
13
64-bit types.
14
typedef long long   int64_t
15
typedef unsigned long long   uint64_t

mit
1
dtostre()
 ging es, mit
1
sprintf()
 nicht. Das mit den 32-Bit Integer sollte zeigen, dass damit das dword 
lesen wohl geht.

Ich frage mich ob das ablegen der Werte nicht funktioniert oder das 
zurücklesen oder ich beim casting was verkehrt mache, ich bin mit dem 
Latein am ende...

LG

PS: @ uhu dword sollte nach dem test int32_t liefern, d.h. casting auf 
double sollte meiner theorie nach funktionieren :-(

von Uhu U. (uhu)


Lesenswert?

> PS: @ uhu dword sollte nach dem test int32_t liefern, d.h. casting auf
> double sollte meiner theorie nach funktionieren :-(

Nein. Siehe Beitrag "Re: ATmega128 double Werte in Flash (PROGMEM)"

von holger (Gast)


Lesenswert?

>und bekomme ein '?' raus *aaaarrrggghhh*

Du hast vergessen printf mit float Support zu linken !
Wie ging das noch ?

von R. F. (firo)


Lesenswert?

Danke für den Tipp,

eine gute, eine schlechte Nachricht:
zuerst die "gute" testme = testpgm liefert in verbindung mit dtostre() 
das richtige ergebnis.

Bleibt dazu die Frage, liegt das jetzt im RAM oder im Flash? (Weil im 
nächsten Schritt wenn es läuft will ich ja 700 * 4 Byte ablegen!)

Andererseits liefert sizeof(pgm_read_dwort(&testpgm)) = 4.
Also war die Überlegung an sich richtig, oder nicht?
Wäre ja super lustig, wenn ich sachen ins Flash lege ohne sie mit 
Flash-Routinen lesen zu müssen verwirrt

@ holger: Habe es zwischendurch auch komplett auf double umgestellt 
(weil ja der Rest auf dem AVR mit der Bitbreite stimmt - ging auch 
nicht)

(BTW: Aber wie geht das mit dem float Support)

von holger (Gast)


Lesenswert?

Da ist es wieder ;)

 -Wl,-u,vfprintf -lprintf_flt -lm

von R. F. (firo)


Lesenswert?

@ uhu

dword liefert mir auf jeden fall 4 Byte = double word = 2 x 16 bit = 32 
bit

http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html#gbb68859ac5dfa6a09ac048b4037a83b6

von Mini Double (Gast)


Lesenswert?

Ihr Experten.
Seit wann ist ein Double nur 4Byte groß ??
Wenn Float schon 4Byte hat und double "double" heißt....

Zudem würde ein einfacher Cast mit Zeigerspielerei ausreichen.

float f = * (float*)(& ( BitMuster_4_Byte_groß ) );

für double... sollte man jetzt nachrechnen oder mal nachschauen wie groß 
Double nun wirklich ist. -> dann findet man auch schnell heraus, was an 
obriger Zeile geändert werden müsste, um sie für Double gängig zu 
machen.

von Jörg X. (Gast)


Lesenswert?

im AVR-GCC-Tutorial wird sowas (ähnliches) erwähnt:
1
typedef union{
2
int32_t i;
3
double f;
4
}int32todbl;
5
...
6
int32todbl tmp;
7
tmp.i =  pgm_read_dword(&testpgm);
8
testme = tmp.f;
9
dtostre(testme,buffer,8,1);
hab's aber nicht getestet

> Seit wann ist ein Double nur 4Byte groß ??
ist beim AVR-GCC so
hth. Jörg

von Johannes M. (johnny-m)


Lesenswert?

@Mini Double:
double ist im AVR-GCC nur 32 Bit breit. Das stimmt schon so. Es gibt 
noch keine Implementierung für 64-Bit-Gleitkommawerte. Deshalb sind 
float und double gleich lang.

von R. F. (firo)


Lesenswert?

Zuerst vielen Dank an alle Helfer:

das mit dem union funktioniert scheinbar, jetzt kann ich mal gucken ob 
das auch gleich noch mit der Tabelle hinhaut.

also zusammenfassend:
1
typedef union{
2
int32_t i;
3
double f;
4
}int32todbl;
5
6
double testpgm PROGMEM = 1.2345;
7
8
int main(void)
9
{
10
int32todbl temp;
11
...
12
13
temp.i = pgm_read_dword(&testpgm);
14
15
dtostre(temp.f,buffer,8,1);
16
17
uart_puts(buffer);
18
uart_puts(" PGM \r\n");
liefert jetzt 1.2345000e+00 auf dem Terminal.

Ich muss zu meiner Schande gestehen, dass ich diesen Part des Tutorials 
heute mindestens 5 mal aufhatte und trotzdem irgendwie nicht hinter den 
tieferen Sinn des Union gekommen war und es einfach nicht ausprobiert 
hatte.

von Uhu U. (uhu)


Lesenswert?

R. Fischer wrote:
> Wäre ja super lustig, wenn ich sachen ins Flash lege ohne sie mit
> Flash-Routinen lesen zu müssen *verwirrt*

Wieso - du benutzt doch einen C-Compiler. Der ist clever genug zu 
erkennen, wo die Variable liegt und erzeugt - hinter den Kulissen - den 
richtigen Code.

Jetzt schreibst du einfach

  const double testpgm[700] PROGMEM = { <700 konstante double-werte> };

und dann adressierst du das Zeug so:

  double v25 = testpgm[25];

da mußt du keine großen Verrenkungen machen... Du kannst auch mit den 
testpgm[i] direkt rechen - umkopieren ist unnötig.

von R. F. (firo)


Lesenswert?

Hallo Uhu,

leider funktioniert dass so leider nicht, habe es extra getestet.
Ich habe in einer Datei "constants.h" einen Eintrag der lautet:
1
static const double Srelativ[] PROGMEM =
2
{
3
#include "BPX61res.txt"
4
};

Und dann gibt es in "functions.c" eine Funktion die folgendes tut:
1
double  Srel(uint16_t lambda)
2
{
3
  typedef union {
4
  int32_t i;
5
  double f;
6
  } int32todbl;
7
8
  uint16_t i;
9
  int32todbl ret;
10
11
  if ((lambda >= LAMBDAmin) && (lambda <= LAMBDAmax))
12
  {
13
    i = lambda-LAMBDAmin;
14
15
    ret.i = pgm_read_dword_near(&Srelativ[i]);
16
//    ret.i = Srelativ[i];
17
  }
18
  else
19
  {
20
    ret.f = -1.0;
21
  }
22
23
  return ret.f;
24
}

wenn ich dann in "main.c" sowas zum Test mache:
1
  for(j=LAMBDAmin; j<LAMBDAmax; j++)
2
  {
3
    dtostrf(Srel(j),3,8,buffer);
4
    uart_puts(buffer);
5
    uart_puts("\r\n");
6
  }

Funktioniert es nur mit pgm_read_dwort(...), mit der von Dir 
vorgeschlagenen Variante ergibt es nur noch Murks

von Uhu U. (uhu)


Lesenswert?

Aber

  testme = testpgm;

hat funktioniert, oder habe ich da was falsch verstanden?

Wenn das so ist, dann muß der Array-Zugriff eigentlich auch 
funktionieren.

von Mini Double (Gast)


Lesenswert?

Wenn douible und float gleich lang sind, warum verwendet man dann 
double, anstatt float? Wenn es sich tatsächlich um einen 4Byte-Double 
handelt, existiert kein Unterschied und der Bregriff double führt nur in 
die Irre.

Naja egal.
Sorry für man daherschwätzen ;-)

Trotzdem funktioniert die Pointer-Lösung auch ;-)

MFG

von Uhu U. (uhu)


Lesenswert?

Es ist den Compilerbauern freigestellt, wie sie die Typen double und 
float implementieren. Daß double mehr Genauigkeit bringen muß, ist nicht 
vorgeschrieben.

Dasselbe gilt für int und log - die sind auf 32-Bit-Rechnern 
üblicherweise auch gleich.

von R. F. (firo)


Lesenswert?

Hallo nochmal, war nachher etwas abwesend ;)

@ Uhu: Ja, ist richtig verstanden - Unterschied ist aber, dass die 
angesprochene Lösung nur innerhalb eines Moduls/Datei funktioniert und 
ich eine gewisse Struktur im Projekt nicht missen möchte (constants.h, 
main.c, functions.c, etc.).

Gute Nacht und abermals vielen Dank an alle Beteiligten :-)

von Karl H. (kbuchegg)


Lesenswert?

Die andere Alternative anstatt über eine unionn zu gehen,
wäre pgm_read_block zu benutzen.
Das Problem: auch wenn die Funktion pgm_read_dword() heist,
so steckt doch die implizite Annahme dahinter, dass ein dword
eine ganze Zahl darstellt.
Mit pgm_read_block wirst du diese Annahme los: read_block liest
Bytes aus einem Speicherbereich und legt die Bytes in einem
anderen Speicherbereich (der float Variablen ab). Und zwar ohne
Ansehen oder Interpretation dieser Bytes.

von Johannes M. (johnny-m)


Lesenswert?

@Karl Heinz:
Kann es sein, dass es pgm_read_block in der aktuellen AVR-libc-Version 
nicht mehr gibt? Das wäre nämlich schon weiter oben mein Vorschlag 
gewesen, allerdings habe ich (weil sich so was ja schon mal ändert) in 
die libc-Doku geschaut und pgm_read_block nicht mehr gefunden... (Oder 
bin ich nur blind?) Dafür eben jetzt pgm_read_dword, das afair in 
früheren Versionen nicht existierte.

von R. F. (firo)


Lesenswert?

Hallöchen,

hört sich interessant an, leider finde ich weder im Tutorial noch auf 
http://www.nongnu.org/avr-libc/user-manual/group__avr__pgmspace.html 
diese Funktion. Gibt es ein pgm_read_block überhaupt?

Schönen Gruß

von Karl H. (kbuchegg)


Lesenswert?

Johannes M. wrote:
> @Karl Heinz:
> Kann es sein, dass es pgm_read_block in der aktuellen AVR-libc-Version
> nicht mehr gibt?

Mein Fehler.
Ich hab das mit der EEProm Funktion eeprom_read_block() verwechselt.
Was es aber gibt, ist memcpy_P().

  double testpgm PROGMEM = 1.2345;
  double Result;

  memcpy_P( &Result, &testpgm, sizeof( double ) );

muesste das Gewünschte leisten.

von R. F. (firo)


Lesenswert?

Hallo K-H,

stimmt - auch nochmal getestet. Aber die Lösung mit der union ist 
"didaktisch" sinnvoller, Dein Vorschlag eher kompakter :-)

von Karl H. (kbuchegg)


Lesenswert?

R. Fischer wrote:
> Aber die Lösung mit der union ist "didaktisch" sinnvoller,

Dir ist schon klar, dass diese Art der Verwendung einer
union vom C-Standard her eigentlich nicht gedeckt ist.
Auch wenn sie praktisch überall funktioniert.

von R. F. (firo)


Lesenswert?

Hallo nochmal,

leider nicht, bis jetzt -> ist gespeichert.
Mit der union ist das kritisch, weil?
(die Anordnung darin nicht spezifiziert ist? (es könnte also ein 
Compiler, etc. einfach das 1. Byte des float ans ende legen, dass 4. in 
die Mitte und den Rest in die Lücken)). Wie kann man sich das 
erklären/merken?

von Karl H. (kbuchegg)


Lesenswert?

R. Fischer wrote:
> Hallo nochmal,
>
> leider nicht, bis jetzt -> ist gespeichert.
> Mit der union ist das kritisch, weil?

Nein. In der Praxis funktioniert das auf allen
bekannten Compilern.

Es ist nur so, dass laut Sprachnorm es nicht gestattet
ist, aus einer Union den Wert über einen anderen Member
zu lesen als er geschrieben wurde. Alles was davon abweicht,
wird vom Standard als 'undefined behaviour' gebrandmarkt.
'undefined behaviour' bedeutet: Alles Mögliche kann passieren,
inklusive: funktioniert so wie erwartet.

Wie gesagt: Mir ist kein Compiler bekannt, bei dem das nicht
funktionieren würde. Aber der Makel der 'undefined behaviour'
bleibt trotzdem :-)

> (die Anordnung darin nicht spezifiziert ist? (es könnte also ein
> Compiler, etc. einfach das 1. Byte des float ans ende legen, dass 4. in
> die Mitte und den Rest in die Lücken)).

Könnte er im Prinzip. Wird er aber nicht machen :-)

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.