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
constdoubletestpgmPROGMEM=1.23456789;
2
3
intmain(void)
4
{
5
charbuffer[32];
6
doubletestme;
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
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
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.
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...
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
>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().
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?
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 :-(
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)
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.
@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.
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
typedefunion{
2
int32_ti;
3
doublef;
4
}int32todbl;
5
6
doubletestpgmPROGMEM=1.2345;
7
8
intmain(void)
9
{
10
int32todbltemp;
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.
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.
Aber
testme = testpgm;
hat funktioniert, oder habe ich da was falsch verstanden?
Wenn das so ist, dann muß der Array-Zugriff eigentlich auch
funktionieren.
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
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.
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 :-)
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.
@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.
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.
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.
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?
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 :-)