Grüss euch, Ich habe flgendes Problem: Ich habe ein C-Programm für den 10 Bit AD-Wandler des C517A von Siemens erstellt. Das Programm soll einfach die Spannung, die am AD-Wandler liegt, in Volt auf einem LC-Display ausgeben. Ich dachte mir: Nichts einfacheres, man nimmt einfach einen float und rechnet Spannung = (Uref / 1023) * (float)Messwert, wobei Uref = 5V und Messwert die Zahl ist, die der AD-Wandler liefert. Nun sollte man ja eigentlich das richtige resultat in Volt haben - weit gefehlt. Konvertiert man nämlich den float-Wert in einen String (mittels sprintf), dann meldet das LC-Display nachher fröhlich <NO FLOAT> Und das ist ja nicht unbedingt das gewünschte Resultat. Im SDCC-manual heisst es, float werde unterstützt, aber ich kann keine floats ausgeben! Kann mir jemand von euch vielleicht sagen, was ich tun muss, um a) den AD-Wandler-Wert korrekt umzurechnen, und b) diesen umgerechneten Wert dann auf dem LC-Display auszugeben?? Vielen dank schon im Voraus für eure hilfe. grüsse tobias
>b) diesen umgerechneten Wert dann auf dem LC-Display auszugeben
Bastel dir sprintf bzw. ftoa (float to ascii) selber.
Die Konvertierungsmethode ist ja nichts weiter als eine Ansammlung von
Divisionen (für jede Stelle eine...).
Die kann man auch durch eine Reihe von Schleifen realisieren:
unsigned char Ausgabestring[8];
Tausender = 0;
while (floatzahl > 999.99)
{
Tausender++;
floatzahl -= 1000.0;
}
Ausgabestring[0]='0'+Tausender;
Hunderter = 0;
while (floatzahl > 99.99)
{
Hunderter++;
floatzahl -= 100.0;
}
.
.
.
Ausgabestring[7]='\n';
sollte so gehen...
moin moin,
>Spannung = (Uref / 1023) * (float)Messwert
immer wieder der Denkfehler, 10Bit sind 1024 Zustände...
also
Spannung = (Uref / 1024) * (float)Messwert
mfg
Pieter
@pieter, ich weiss, dass 2^10 = 1024 ist, aber ich habe mir überlegt, dass der AD-wandler ja eigentlich nur 1023 verschiedene spannungen unterscheiden kann (0 V habe ich nicht mitgezählt). @inoffizieller WM-Rahul (komplizierter name): hast du einen kompletten code für diese ausgabefunktion? scheint mir reichlich kompliziert. eigentlich müsste sdcc solche funktionen integriert haben, aber wie aktivier ich sie?
moin moin, Konto auf 0 ist auch ein (schlechter) Zustand! Float ist zu aufwendig. Besser so: URef AD_Wert 5 AD_Wert Um= --------------- = ------------- = (5 MUL AD_Wert) DIV 1024 1024 1024 Das geht also in Integer zu rechnen, ist so einfacher und schneller. Sieh auch mal hier: http://www.mikrocontroller.net/forum/read-1-372794.html#new Mit Gruß Pieter
Verwende Integer-Arithmetik: Du willst bei 1023 5.000 angezeigt bekommen *). Also multiplizierst Du 1023 mit 4,88758553274, um auf 5000 zu kommen (oder mit 48,875855327, für 50000). Dies Zahl gibst Du aus und gibst bei der Ausgabe ein Komma an die richtige Stelle. Um mit 4,8875 zu multiplizieren, rechnest Du mal 4,8875855*65536 = 320312,8054 (also 320313) und streichst die letzten 16 Bits des Ergebnisses (dividierst durch 65536 oder zur Not shift right 16). Am effektivsten: Du greifst auf die oberen 16 Bits des 32-Bit-Ergebnisses zu. Die Multiplikation hat 10x19=29 Bits. In Assembler brauchst Du 6 MULs und ein paar ADD / ADC s. Wenn Du die unteren 16 Bits weglässt, bleiben 13 (0..8191) übrig, davon nutzt Du den Bereich 0..5000 . Hier ein Beispiel: Eingangsspannung ist 1,234V, der ADC gibt 253 aus (1,234/5*1024=252,7232). 253*320312=81038936, 81038936/65536=1236. Bei der Ausgabe der Integer-Zahl 1236 ein Komma eingefügt, schon steht 1,236 in der Anzeige. Der µC kann diese Integer-Berechnungen in wenigen Taktzyklen erledigen. Für Floats bräuchte er vermutlich 100 mal länger, vom Speicherbedarf ganz abgesehen. So was ähnliches (Temperaturausgabe eines DS1624) hatten wir hier: http://www.mikrocontroller.net/forum/read-1-424385.html#426232 *) Ob Du mit 1023 oder 1024 rechnest, hängt davon ab, ob Du bei voller Eingangsspannung 4,995 oder 5,000 angezeigt haben willst.
@Pieter: zur berechnung des AD-Wertes: wir haben beide recht, wie ich grade aus dem post von Profi erfahren habe. ich habe mir bis jetzt immer überlegt, dass: 1. die grösste zahl, die der 10-Bit AD-Wandler liefert 1023 ist ( =2^10-1), 2. dass bei einer eingangsspannung von 5V = Vref das resultat des AD-Wandlers ebenfalls 1023 ist. folglich muss ich die 5 V durch 1023 teilen, um bei einer multiplikation mit 1023 wieder 5 zu erreichen. es ist jedoch naheliegender (logischer), aber das endresultat stimmt nicht haargenau (was es zwar auf keinen fall jemals wird ;)) @Profi: du machst deinem namen alle ehre. ich hab nur einen kleinen teil deiner überlegungen verstanden... gibts dazu vielleicht irgendwo noch eine detailiertere beschreibung, wie man solche aufgaben mit integer-arithmetik löst? grüssse tobias
moin moin, @Tobias also, Du gehst davon aus, der 10Bit ADU hat eine Auflösung von 2^10-1 = 1023. Dann nimm mal einen 1Bit ADU. 2^1-1 = 0. Du bekommst also NIE einen Spannungswert angezeigt. Mit Gruß Pieter
@Pieter: das mit dem 1 Bit ADU stimmt, da ahst du recht. ABER: Wenn du dem ADU Vin=Vref einspeist, dann zeigt er seine höchste Zahl an, die er darstellen kann, oder? also ein 8 Bit ADU mit Vref=5V und Vin=5V würde 255 anzeigen. einer mit 10 Bit und Vref=5V und Vin=5V würde 1023 anzeigen. nun ist es ja so, dass wnn ich 5 Volt einspeise, und den ADU-Wert umrechne, ich auch 5 Volt bekommen will, deshalb die division durch 2^Anzahl_Bit-1. beim 1 Bit ADU bin ich mir nicht so sicher, aber ich würde jetzt mal sagen, das ist ein sonderfall. (Du kannst ja direkt rechnen: Wert_ADU * Vref; wenn der ADU 1 liefert und Vref=5V sit, ergibt das dann auch 5) Denn wer hat schon einen 1 Bit ADU?? ;) aber du hast natürlich recht, aus diesem gesichtspunkt wäre wieder deine rechnung die richtige. und mittlerweile bin ich schon ein bisschen verwirrt: was ist richtig? -> ich konsultier gleich wikipedia ;) gruss
>was ist richtig? -> ich konsultier gleich wikipedia ;)
Das Datenblatt zu studieren reicht auch...
Wenn man nur mit 10bit breiten Zahlen rechnen würde, könnte man gar
nicht durch 1024 teilen...(nur um die Diskussion noch mal anzufachen)
Der Code ist doch schon recht umfangreich.
Du musst einfach nur die jeweilige Stelle von deiner Zahl
subtrahieren.
Das kann man dann bis zur xten Nachkommastelle treiben, wobei es da
dann immer ungenauer wird.
@inoffizieller...usw.: ja, aber es ist ja nur der ADU, der 10 Bit liefert. wenn du das ganze dann umrechnest, und du arbeitest auf einem 8 Bit mikrocontroller, dann wirst du für das resultat sowieso eine 16 Bit zahl nehmen müssen (oder, wie profi schon gesagt hat: 24, 32... Bit). da kann man dann locker durch 1023 teilen. ist allerdings dann etwas mühsamer mit nur 8 bit, als wenn man durch 1024 teilt und einfach schieben kann. da taucht nämlich schon die nächste frage auf: wenn ich mit 8 Bit arbeite und habe eine 16 Bit zahl - sagen wir in den registern R0 und R1 - wie teile ich die dann durch 1023? (in assembler!) der div-befehl kann ja nur 8/8 Bit rechnen. ist das etwa der grund, warum der C517A eine multiplizier/dividier-einheit on-chip hat, um mit 32 und 16 bit rechnen zu können? (wie man die bedient ist mir allerdings bis jetzt auch verschlossen geblieben ;))
> ABER: Wenn du dem ADU Vin=Vref einspeist, dann zeigt er seine > höchste Zahl an, die er darstellen kann, oder? Ja, aber wenn du die Spannung etwas ernidrigst, zeigt er immer noch seine höchste Zahl an. Du musst dich vor der Vorstellung trennen, dass der Zahlenwert aus dem ADC ein punktuelles Ergebnis ist. Der ADC verwendet Bereiche: Jede Spannung von 0 bis x0 wird vom ADC mit 0 quittiert. Jede Spannung von x0 bis x1 liefert eine 1. Jede Spannung von x(n-1) bis x(n) liefert MAX. Wieviele solcher Bereiche gibt es? MAX verrät es uns. Es gibt MAX + 1 solcher Bereiche. Sagen wir mal MAX wäre 4 (dann können wir einfacher rechnen). Weiters sei ARef gleich 5 Volt. Wie sehen dann die Bereiche aus: Wert vom ADC Spannungsbereich 0 0 - 1 1 1 - 2 2 2 - 3 3 3 - 4 4 4 - 5 Wenn dir also der ADC als Ergebnis eine 4 liefert, dann liegt die tatsächliche Spannung iregendwo im Bereich 4 - 5 Volt. D.h. aus der Formel: Wert = ARef * ADC / 5 kriegst du immer den unteren Grenzwert des Bereiches der Größe ARef / ( MAX + 1) in dem die Spannung liegt. Lass uns mal was probieren: Rechnen wir mal ARef * ADC / 4 0 0 1 1.25 2 2.5 3 3.75 4 5 Die Zahlen die hier rauskommen, haben nichts mehr mit den Messbereichen zu tun. Ein Wert von 2 würde ja bedeuten, dass die Spannung irgendwo zwischen 2.5 und 3.75 Volt gewesen war. Das war sie aber nicht. Bei 3.2 Volt würde der ADC dir schon 3 melden! Die letztere Formel würde überhaupt nur dann einen Wert von 4 liefern, wenn die Spannung genau 5 Volt beträgt. Das entspricht aber nicht der Realität. Auch bei 4.6 Volt würde der ADC eine 4 zurückliefern!
moin moin, lese mal das Datenblatt zum 80C552, der hat auch 10Bit ADU http://www.datasheetarchive.com/datasheet.php?article=630376 1Bit ADU-> Transistor if Ube<0.6V then Uc>0 else Uc=0. In meinem Labornetzteil benutze ich MAX186. Lade das Datnblatt und sieh wie der die 12Bit macht. http://www.datasheetarchive.com/datasheet.php?article=2189149 Mit Gruß Pieter
@Tobias, Lösungsvorschlag .... die X te bei 5 Volt Ref. hast Du ja 4,88 mV pro LSB. Also bringt der A/D bei 1 Volt CCh. Jetzt könntest du zB. vom Ergebnis so oft CCh subtrahieren und eine Variable dabei hochzählen bis Du einen OV hast. Dann CCh addieren und Du hast die Ganzen Volt in der Variablen die hochgezählt wurde. Für den Rest der kleiner als 1 Volt ist,einfach ne Tabelle nehmen und die gerundeten Werte da ablegen. Hat den Nachteil das dieser Weg bei Änderung von Vref angepasst werden muß.( aus CCh wird bei Vref 4,096V dann FAh ).
@karl heinz: nun bin ich überzeugt. ich muss also durch 1024 teilen und nicht durch 1023.
@stephan: das ist auch eine gute idee. nun habe ich ca. 3 verschiedene lösungswege, das zu machen :D und ich frage mich, was am besten ist: a) integer-arithmetik (z.b. 16 bit) b) tabelle (nicht so elegant) c) dein lösungsvorschlag mit dem subtrahieren nur mal so als anmerkung: ich will keine besonders schnellen berechnungen, sie müssen auch nicht 100%ig genau sein. aber mein codespeicher ist begrenzt, deshalb kann ich nicht beliebig grosse funktionen basteln. schliesslich soll ja noch ein haufen anderer rkam reinpassen ;) welches ist jetzt die eleganteste methode? wie rechnet eigentlich ein digitales multimeter? ich hab zwar keine ahnung davon, aber ich denk mal, da ist auch ein ADC drinn, dessen wert umgerechnet werden soll?
Das hängt von Dir und Deiner Programmierkunst ab. Tabelle geht am schnellsten macht aber bei der erstellung Arbeit. Und braucht Speicher Integer hat zur Folge das Du eben etwas mehr rechnen mußt. Da ohne Komma gerechnet wird sind die Zahlen etwas größer, da ja das Komma nachher nur gesetzt wird. Aus 1558 wird 15,58. Ist allerdings auch die sauberste Lösung die auch mit anderen zB. 12 oder 14 Bit Wandlern gehen würde, wenn sie 16 Bit rechnet !! Meine Methode ist ne Zwischenlösung. Etwas Rechnen und den Rest aus der Tabelle. Damit bleibt die Tabelle wesentlich kleiner und der Rechenaufwand ist wesentlich geringer. Deinen Weg mußt Du selbst finden. Wenn nicht gehe alle 3 Wege und lerne dabei und wähle für Später deinen aus.
@Pieter: > Dann nimm mal einen 1Bit ADU. > 2^1-1 = 0. ??? Also 2^1-1 ist bei mir immer noch 1 und nicht 0! Markus_8051
@Markus: uhps, stimmt :D da bin ich ebenfalls in pieters fettnäpfchen getreten ;) @stephan: ich hab das ganze jetzt mal mit integer gemacht. klappt ganz schön, ausser dass ich bei printf() keine möglichkeit gefunden habe, irgendwo mitten in die int-zahl ein komma zu setzen. gibts da vl. schon was vorgefertigtes? sonst muss ich eine solche funktion selber basteln. ist zwar kein grosses problem, aber es ist mühsam und braucht viel zusätzliche rechenzeit. etwas vorgefertigtes wäre da sicher idealer. gruss
Sorry ich nix verstehen von "C " ! Aber letztendlich ist es doch nur eine Frage der ASCI darstellung. Du mußt doch eh alles in ASCI wandeln.Dann steht auf dem Display 1588 und auf Position XY ungelößt schreibst Du ein Komma. Dann steht da 15,88. Die Zahl 1588 ( zB. Dein Ergebnis) mußt Du doch eh zerlegen.
moin moin, sollte doch in der Hilfe stehen, wie %W.De Scientific notation x.xxxx e nnn. float, double %W.Df Fixed format xx.xxxx. float, double %W.Dg Variable `W' digits wide,`D' decimals xxxx.xx Mit Gruß Pieter
@pieter: danke. woher hast du das? übrigens: sry dass ich mich so lange nicht gemeldet habe, ich war leider kurz abwesend. grüsse
ALSO Wenn du Gleitkommas im printf haben willst. Dann musst du beim sdcc die compiler option large-model benutzten. Wenn es immernoch nicht klappt, dann schaue mal im printf.c source code. Ich glaube das man dort ein #define ändern muss, und neu compilieren. Auf jedem fall muss du den source dann code ändern. Am besten ich gebe dir meins
hallo toto, danke für den tipp mit --model-large. aber komischerweise funktioniert mein programm überhaupt nicht mehr, wenn ich model-large einsetze. ein teil des programmes wird zwar ausgeführt, aber dann passiert einfach nichts mehr. komische sache das, model-large... gruss
Model Large unterstellt ja externen RAM Bereich etc, die Frage ist, wie sieht deine Hardware aus ??
meine hardware sieht folgendermassen aus: Adresse 0000h - 07FFh: EPROM mit monitprorogramm 8000h - FFFFh: RAM 32k zum speichern von programm + daten internes RAM ist 256 Byte gross + 2k erweitertes internes RAM (kann über SFR-Bits eingeschaltet werden). reichen die angaben?? gruss
Ja, jetzt noch einen Blick ins SDCC Manual, Seite 32. Und da schaust du dir mal die Compiler Optionen an, die da z.B. wären: --xdata-loc --xram-size --code-loc --code-size Setze da mal deine Angaben rein und neuer Versuch.
meinst du das klappt, wenn ich die angaben änbdere? das einzige, was ich jeweils angebe, ist --code-loc 0x8000 und --xram-size 0x8000. das reicht also nicht? gruss
xdata location fehlt doch. Bitte lese dir das Kapitel mal durch. RAM Größe kennt er nun, nur wo es zu finden ist ...? Und was meinst du wo er deine Variablen ablegt?
@joe: stimmt, du hast recht. ich probier das bei gelegenheit nochmal, mit den richtigen angaben dazu. grüsse & schönen abend
Lass von dir hören, ist auch für andere von Interesse. Am besten du liest dir die Optionen mal gesamt durch, da läßt sich einiges optimieren.
ja mach ich, ich meld mich morgen abend wieder. bis dann, gruss
toto schrieb: > ALSO > > Wenn du Gleitkommas im printf haben willst. > Dann musst du beim sdcc die compiler option large-model benutzten. > Wenn es immernoch nicht klappt, dann schaue mal im printf.c source > code. > Ich glaube das man dort ein #define ändern muss, und neu compilieren. > Auf jedem fall muss du den source dann code ändern. Sorry, dass ich den alten Thread nochmal aufwärme, aber meine Frage passt hier ganz gut rein: Im SDCC-Manual steht folgendes: > The default printf() implementation in printf_large.c does not support > float (except on ds390), only <NO FLOAT> will be printed instead of the > value. To enable floating point output, recompile it > with the option -DUSE_FLOATS=1 on the command line. Use --model-large > for the mcs51 port, since this uses a lot of memory. Wie ist das genau gemeint mit dem neu kompilieren? Muss dann da eine Lib erzeugt werden (wie geht das?) oder wie weiß dann der SDCC, dass die neu kompilierte printf verwendet werden soll? Oder muss ich die Sourcedatei von printf ändern und in mein SDCC-Projekt mit aufnehmen? Danke.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.