PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Float in Char?



Henry
26.05.2006, 00:56
Hallo,

ich habe mich jetzt ein paar Stunden durch das Forum gesucht und bin leider nicht fündig geworden. Daher habe ich mich entschlossen doch mal direkt nachzufragen.

Das man einen Integer mit:


itoa(messwert0, ausgabe0, 10);
in ein Char umwandeln kann habe ich bereits gefunden.
nun möchte ich mit meinem Integer eine Berechnung durchführen:


spannung = messwert0 * 0,0048828;
um es dann auf mein LCD zu bringen muss ich es danach wieder in ein Char (oder String) wandeln und das ergebnis auf z.B. 2 Stellen nach dem Komma runden.
Gibt es da auch so eine einfache Funktion, und welche datei muss ich einbinden?

Danke schon einmal

izaseba
26.05.2006, 01:48
Warum muß das unbedingt Float sein ?
Geht es hier um 5/1024 ?????
Da fühlt sich der AVR garnicht wohl...

Ich gib Dir ein Tip

(ADCergebnis*320313)/65536

Du bekommst eine Zahl zwischen 0 und 5000.
Jetzt gilt es sie in ASCII umzuwandeln und nach der ersten Zahl ein Komma einzuschmuggeln fertig, ohne Float.

Gruß Sebastian

ogni42
26.05.2006, 12:07
sprintf ist Dein Freund. Du musst alledings ein char-Array nehmen, da Du ja pro stelle des Wertes einen Character brauchst

izaseba
26.05.2006, 12:42
sprintf ist Dein Freund
Naja, das schon, nur wie groß wird Dein Programm mit dem Float/stdio.h Gespann?

Da find ich schon Festkommarechnen efizenter.

Gruß Sebastian

Henry
26.05.2006, 13:30
Hallo,

danke euch beiden erst einmal für die Antworten.
Ich habe es gerade mal mit dem Beispiel von izaseba versucht. Funktioniert soweit gnz gut, nur wenn ich das richtig sehe müßte dann ja die Auflösung 1mV betragen.
habe ich nun im Display eine 4 angezeigt sollte das dann ja 4mV entsprechen. Messe ich aber am eingang nach sind es in Wirklichkeit 19mV (also +14mV) habe ich 1001 in der Anzeige sind es 1,023V (+22mV).
Nicht das das jetzt ein riesen Fehler währe (die Toleranz des Meßgerätes ist ja auch noch dabei).
Wäre das mit der Floatvariante ungefähr genau so hoch von der Differenz her?
Wie setze ich denn das Komma bei deiner Variante (izaseba)? So das dann bei 4 -> 0,004 angezeigt würde?

izaseba
26.05.2006, 14:12
Ne,ne,
also die Genauigkeit bei dieser Rechnung beträgt etwa 0,008%.
Ich hab mir das nicht selber so ausgedacht, es kommt davon, daß ich gerne mit Assembler programmiere, und da muß man sich solcher Tricks behelfen :-)

Also, Kaffe ist da ich kann beginnen:

Du hast als Referenz 5 V richtig ?

Die Zahl 320313 kommt so zustande

Da es blöd ist mit Komma zu rechnen (5V/1023) multipliziert man das ganze mit 65536(0xFFFF) und da wir jetzt milivolt haben möchten nehmen wir nicht 5V sondern 5000
(5000*65536)/1023 = 320312,8055
Wir runden auf auf 320313 und hier ist unsere minimale Ungenauigkeit.

Wenn Du jetzt den ADC Wert damit Multiplizierst und anschließend noch durch 65536 teilst, was dann nur heißt die 2 unteren Bytes wegzulassen erhälst Du Deine Spannung in milivolt.
Beispiel
(512 * 320313)/65536 -> 2502 der Rest wird abgeschnitten

Gcc ist so klever,daß er in der tat die zwei untere Bytes einfach wegläßt, und nicht anfängt wild durch 65536 zu teilen :-)

so, das ganze ist hier (http://www.avr-asm-tutorial.net/avr_de/rechnen/fpconv.html#bsp2)
nochmal nachzulesen, es gibt da Leute die schlauer sind als ich und sich das ganze so schön ausgedacht haben.....

Wie kriegt man jetzt ein Komma dahin?
indem man itoa im Eigenbau benutzt :-)

Kommt wieder aus dem Assembler:

Von der Ausgangszahl wird solange 1000 abgezogen, bis die Zahl kleiner als 1000 ist. man muß sich nur merken, wie oft mal 1000 abgezogen hat.



uint8_t zaehler = 0;
while (deinadcergebnis >= 1000) {
zaehler++;
deinadcergebnis -=1000;
}


jetzt zaehler mit 0x30 verodern und in ein chararray rein.
als nächstes ein komma in dein chararray.
Jetzt das gleiche mit 100 und 10 und den Rest, der dann übrigbleibt ergibt die einer Stelle, die ohne Schleife mit 0x30 verodert werden kann.
zum Schluß noch ein '\0' in chararray, und die Zahl ist fertig zum absenden,anzeigen.

So Kaffee leer :-)

Gruß Sebastian

SprinterSB
26.05.2006, 19:48
spannung = messwert0 * 0,0048828;

Falls du je auf die Idee kommen solltest, Ausdrücke wie den obigen (evtl auch in Integer-Berechnungen) zu verwenden:

Diese Zeile gibt einen Compilerfehler, weil 8 keine gültige Oktalziffer ist.

Die Zeile ist nämlich gleichbedeutend mit
spannung = 048828;

Die Zahl rechts wird, da sie mit 0 beginnt, als Oktal genommen und das gibt einen Fehler.

Was du vermutlich meintest, ist

spannung = messwert0 * 0.0048828;

Also:
spannung = messwert0 * 0,0011;

würde dir spannung auf 9 setzen, unabhängig von messwert0 und du würdest dir den Wolf suchen...

Henry
26.05.2006, 19:58
@izaseba:
Danke für die ausführliche Erklärung. Werde mir das noch einmal genau durchlesen um es zu verinnerlichen

@SprinterSB:
oh je, was man nicht alles beachten muss.
ich fange aber auch erst an ;)

SprinterSB
26.05.2006, 20:34
Wie kriegt man jetzt ein Komma dahin?
indem man itoa im Eigenbau benutzt :-)

Kommt wieder aus dem Assembler:

Von der Ausgangszahl wird solange 1000 abgezogen, bis die Zahl kleiner als 1000 ist. man muß sich nur merken, wie oft mal 1000 abgezogen hat.



uint8_t zaehler = 0;
while (deinadcergebnis >= 1000) {
zaehler++;
deinadcergebnis -=1000;
}


Diese Lösung für Division mit Rest ist wohl die kürzeste (was Flash-Verbrauch angeht). Man kann sich aber auch in der libgcc2 bedienen bei Division mit Rest:


#include <inttypes.h>

/* Struktur definieren und Funktion bekannt machen */
typedef struct
{
uint32_t quot; /* Quotien */
uint32_t rem; /* Rest */
} udiv32_t;

extern udiv32_t udiv32 (uint32_t, uint32_t) __asm__("__udivmodsi4");

...
{
udiv32_t qrem;

qrem = udiv32 (spannung_mV, 1000);

spannung_vorkomma = qrem.quot;
spannung_nachkomma = qrem.rem; /* von 0...999 */
}


Etwas mehr zu tippen aber ne gute Ecke schneller ;-)

izaseba
26.05.2006, 22:13
Hallo Sprinter,

Schön die Division mit Rest, nur sagmal muß ich jetzt die sources haben um das nachzugucken, oder gibt es eine andere Möglichkeit in der Bibliothek zu lesen *ganz rot werde, weil dumm frage...*

Gruß Sebastian

SprinterSB
29.05.2006, 14:08
Die libgcc2 gehört zu gcc dazu, im Gegensetz etwa zur libc oder libm, die "eigenständige" Bibliotheken sind.

In der libgcc2 sind einfache Sachen -- wie etwa obige Division (mit Rest) -- implementiert, die zu komplex sind, um sie direkt in der jeweiligen Maschinenbeschreibung (hier avr) zu codieren.

Das Pattern im betreffenden Fall ist "udivmodsi4". Veröffentlich in Headern sowie global deklariert in der libgcc2 sind leider nur signed Division+Rest 16/16 und 32/32 resp. die passenden Structs div_t und ldiv_t.

Warum entsprechende Routinen für unsigned und 8/8 nicht offengelegt werden ist mir schleierhaft, immerhin sind alle in der libgcc2 implementiert...

Gerade die unsigned 8/8 Division (udivmodqi4) spart deutlich Bandbreite, etwa wenn man Daten für die Ausgabe nach Dezimalstring konvertieren will.

Wie die Prototypen zu sein haben hab ich tatsächlich in der Quelle zur libgcc2 (./gcc/config/avr/libgcc2.S) nachschauen müssen.

Die Divisions-Funktionen dort sind
__(u)divmodqi4: (un)singned Division mit Rest 8/8
__(u)divmodhi4: (un)singned Division mit Rest 16/16
__(u)divmodsi4: (un)singned Division mit Rest 32/32

Übrigens steht "si" immer für 32 Bit (single integer), "hi" für 16 Bit ("half int") und "qi" für 8 Bit ("quarter of an int"). "di" ist "double int" (64), "df" ist double (double float), "sf" ist "single float" (32), etc.

Weitere Beispiele sind im [wiki="avr-gcc"]-Artikel unter "Optimierungen"