Hallo, hier eine C-Funktion für die Umwandlung von "long" bzw. "int"-Werten in ASCII-Zeichen (z.B. für LCD-Ausgabe). //-------------------------------------------------------------------- void toolsLongToASCII (unsigned char *str, unsigned long value) { unsigned char i,j=0; float a,b,e; for (i=7;i>0;i--) { a = pow (10.0,(float)i); b = a / 10.0; e = (value % (unsigned long)a); e /= b; str[j++] = e + 48; } } //. Funktionsaufruf im Programm ...................................... uchar chrASCII[]="0000000"; toolsLongToASCII (chrASCII,value); //-------------------------------------------------------------------- J.St. www.SynaSys.de
Hallo J.St., das ist ja eine sehr schöne Routine. Läuft sie nur auf einen P4 oder auch auf einem µC ? Wenn ja, hast Du diese Routine auch in Assembler zur Verfügung ? P.S.: wenn i auch float wäre, könnte man die Konvertierung (float) i sparen.
Ich denke auch, diese Routine ist eher für einen Pentium gedacht. Ohne Notwendigkeit ein long nach float zu konvertieren ist nur sinnvoll, wenn die CPU eine FPU hat oder wirklich fast nur am Schlafen ist. Soll der MC aber nebenbei noch andere Sachen machen, könnte die Rechenzeit bestimmt besser genutzt werden. Außerdem ist long 10-stellig, bzw. int 5-stellig, warum gibst Du dann 7 Stellen aus ? Auch ist bei float die Mantisse nur 24 bit, d.h. Du kannst damit long Zahlen garnicht vollständig darstellen. Auch wird deshalb ungern int nach float gewandelt, weil es dabei schnell zu Rundungsfehlern kommen kann (die berüchtigte 9-er Periode). Zu den Guten Sitten bei C gehört es auch, einen String immer mit dem 0-Byte als letztes abzuschließen. Peter
Für die Umwandlung von int, unsigned int, long und unsigned long gibts es standard C Funktionen: itoa(), ltoa(), utoa(), ultao(). Ob MSP430 diese Funktionen auch hat weiss ich nicht, aber unter WinAVR würde man dass wie folgt implementieren: unsigned int val = 123456; /* Zahl zum konvertieren */ char buffer[11]; ultoa( val, buffer, 10 ); Die Variable buffer muss mind. 11 Char gross sein, um die grösste unsigned long int Zahl inklusive Null-Terminator darzustellen.
Stimmt, nur verschwenden dieses Standardfunktionen i. d. R. massenhaft Speicherplatz. Sie lassen sich wesentlich effizienter codieren.
@Torsten: Bist Du Dir da sicher? Im Gegensatz zu anderen (gekauften) Compilern kannst Du bei gcc ja den Quelltext anschauen. Ich habe bei ltoa 53 Assembler-Befehle gezählt, das sind ca. 120 Bytes. Viel besser geht es wohl kaum. Dein Argument stimmt allerdings bei printf und ähnlichen Kalibern.
Oh sorry ! Du hast natürlich recht, ich hab vor dem posten mal wieder nicht nachgedacht. Es war in der Tat printf. Peinlich ...
hallo, ich hab gerade erst angefangen mit dem proggen von µCs. hab hier einen ATmega16 den ich über ISP mit ponyprog schreibe. den code erzeuge ich derzeit mit Codevison. aber jetzt stellt sich das problem, das ich keine int zahlen direkt ausgeben kann auf einem display. in C geht das ja einfach mit printf("zahl %i"); aber mit codevision geht das nicht, wenn man die #include <lcd.h> drin hat. die umwandlung die hier beschrieben wird, wäre ja schon mal eine lösung, aber die funktion ultoa gibts natürlich nicht im codevision oder welche headerdatei muss ich includen? grüsse
hallo, ich sollte doch vorher lesen und dann denken und dann fragen ;-) die funktion für umwandlung von int in string ist itoa. ultoa steht wohl für unsigned long ;-) grüsse
Hi Leute! Ich habe jetzt ein vergleichbares Problem: Ich möchte einen int in einen char* umwandeln, um den Wert auf einem LCD auszugeben. Eine Routine LCDOut(char* str), die einen String (nullterm.) ausgeben kann, habe ich mir schon zusammengebastelt. Aber beim Konvertieren scheitere ich. Ich lese überall was von itoa() oder IntToStr() usw. Wo finde ich die? Die stdio.h und die string.h vom MSP430 stellen diese Funktionen jedenfalls nicht zur Verfügung. Oder ich bin zu blöd, die richtige Lib zu finden ... Danke für Tips! Ciao, Sebastian
@Reiner Danke für die Antwort, aber ich habe vergessen zu erwähnen, dass ich den MSP430F149 mit der IAR Embedded Workbench benutze. Und da finde ich kein itoa.h in der stdlib.h. :-( Error[e46]: Undefined external "itoa" referred in test1
@Jens: Ich habe es jetzt endlich geschafft, deinen Code mal zu kompilieren und er läuft auch. ABER nur, wenn ich den String mit char myString[]="0000000"; initialisiere. Wenn ich hingegen char* myString="0000000"; verwende, bleibt der String nach der Funktion leer. Ist das nicht unlogisch?! grübel Ciao, Sebastian
Hm, hab's gerade mal eine halbe Stunde lang gelesen ... Das ist mir zu hoch. Könnte mal jemand freundlicherweise zusammenfassen, warum das bei mir nicht klappt?
Hier definierst Du die Variable mystring als ein Array of char mit 7 Elementen. Das Array wird mit dem String "0000000" initialisiert: char myString[]="0000000"; Hier definierst Du einen Pointer und initialisierst ihn mit der Adresse des Strings "0000000". Der String "0000000" wird gleichzeitig angelegt - und zwar als Konstante, i.d.R. also im Flash. Eine Änderung des "Stringinhalts" muss deshalb fehlschlagen: char* myString="0000000"; Sinnvoller ist die Deklaration: char mystring[7] = "0000000"; oder noch besser: char myString[8] = "0000000"; In letzterer Definition steht als letztes Zeichen noch die binäre NULL im String, das ist das String-Ende-Zeichen bei den meisten String-Funktionen. Stefan
Übrigens - in einer halben Stunde hat man bei einem C-Buch doch gerademal die Struktur des Inhaltsverzeichnisses verstanden ;-) Stefan
Ah, danke. So hatte ich das noch gar nicht gesehen. Ich war bis jetzt immer davon ausgegangen, dass das beides absolut genau dasselbe ist, also beides einen String anlegt und ihn mit sieben Nullen initialisiert. Dann hab ich das damals in den Informatik-Vorlesungen irgendwie falsch verstanden ... Einen "echten" String-Typ gibt es in C doch gar nicht, oder? Meinen Funktionen übergebe ich jedenfalls immer einen char*, wenn ich mit Strings arbeiten will, also void foobar(char* str) { //meinefunktion } Ist schon so lange her, dass ich das letzte mal mit C gearbeitet habe ... :-) > Übrigens - in einer halben Stunde hat man bei einem C-Buch doch > gerademal die Struktur des Inhaltsverzeichnisses verstanden ;-) Naja, der o.a. Link ist jedenfalls IMHO schon ein ziemlicher Hammer. ;-) Und ich bin ja auch nicht grundsätzlich blöd, ich stelle mich nur manchmal so an. :-) Ich habe es sogar schon ganz alleine geschafft, anhand der Datasheets des HD44870 ein paar Funktionen zu schreiben, die mir beliebige Strings auf einem LCD-Display ausgeben und selbständig CR+LF machen, wenn sie am Zeilenende sind. Und das Display initialisieren und löschen können sie natürlich auch. Und die Temperatur messen und ausgeben hab ich jetzt auch geschafft. stolzbin :-) Falls jemand Interesse hat ... ;-) Ciao, Sebastian
hat jemand sowas vielleicht schon kurz(!!!) und überzeugend als asm?? oder mal hier irgentwo im forum gesehen??
@Sebastian: Tschuldige für die späte Antwort, war wohl die Email-Benachrichtigung ausgeschaltet. >Ah, danke. So hatte ich das noch gar nicht gesehen. Ich war bis jetzt >immer davon ausgegangen, dass das beides absolut genau dasselbe ist, >also beides einen String anlegt und ihn mit sieben Nullen >initialisiert. Dann hab ich das damals in den Informatik-Vorlesungen >irgendwie falsch verstanden ... Auf diesen Monster-Maschinen (PCs) funktioniert auch beides genau gleich. Bei PC-C-Compilern werden Konstanten auch im RAM angelegt, und können deshalb (bei den meisten) auch nachträglich geändert werden. Bei Microcontrollern ist i.d.R. konstant aber wirklich konstant, weil Flash. >Einen "echten" String-Typ gibt es in C doch gar nicht, oder? Meinen >Funktionen übergebe ich jedenfalls immer einen char*, wenn ich mit >Strings arbeiten will, also > >void foobar(char* str) { //meinefunktion } Du übergibst hier die Adresse des Strings (oder Char-Arrays). Alles andere macht ja auch wenig Sinn, das würde ja bedeuten, dass der komplette String kopiert werden müsste. >> Übrigens - in einer halben Stunde hat man bei einem C-Buch doch >> gerademal die Struktur des Inhaltsverzeichnisses verstanden ;-) >Naja, der o.a. Link ist jedenfalls IMHO schon ein ziemlicher Hammer. >;-) Und ich bin ja auch nicht grundsätzlich blöd, ich stelle mich nur >manchmal so an. :-) Wollte damit nur ausdrücken, dass ein C-Buch für die allermeisten eine anspruchsvolle Lektüre ist :-)) Viele Grüße, Stefan
Hallo erst mal. Habe mich auch mal darüber geärgert, dass sprintf mehr als 1.5 kB frisst. So habe ich dann mal eine Routine geschrieben, welche einen Formatstring aus dem Flash-Rom liest, diesen in ein Ram-Array schreibt, und ein %-Zeichen im Formatstring als Platzhalter für eine uint16_t-dezimal-Umwandlung dient. Die Unterroutine ist in Assembler gecoded, benutzt eine Schleife (keine Divisionen) und ist auf einem AtMega8 92 Byte gross. Falls das jemand benutzen sollte, so kann er ja mal posten, ob die Routine bei ihm funktioniert. Gruß Marco
@Stefan: Danke für die Antwort, so hatte ich es mir auch in etwa gedacht ... @all: Wenn es irgendjemanden interessieren sollte, ich habe auf meiner Homepage www.mathar.com mal ein Tutorial zu meinen bisherigen MCP430-Experimenten geschrieben. :-)
Hi freaks ;-) mich hat die Größe von sprintf ebenfalls gestört und sowas wie dtostr() hat der MSPGCC nicht. Deshalb habe ich mir als Ersatz die angehängte DecToSting C-Funktion geschrieben. Ich habe auch eine minimale Formatierung implementiert: Führende Nullen oder Leerzeichen und Anzahl der auszugebenen Stellen sind einstellbar. Die Funktion ist ziemlich universell gehalten: Kein Assembler und ohne "besondere" mathematische Funktionen (die der MSPGCC ja eh nicht onboard hat :). Sie sollte also recht portabel sein. An Kommentar zum code bin ich übrigens sehr interessiert. Bis auf, die nicht ganz so schöne ;), Zeile 36 bin ich mit der Funktion soweit recht zufrieden. Gruss, oli
//***********************Zahl in BCD umrechnen u.via Uart ausgeben void Convert_Int_Bcd (int Zahl) { int Help; Help = Zahl / 1000; put_char (Help +0x30) ; //an Uart Zahl = Zahl - (Help * 1000); Help = Zahl / 100; put_char (Help +0x30) ; Zahl = Zahl - (Help * 100); Help = Zahl / 10; put_char (Help +0x30) ; Zahl = Zahl - (Help * 10); Help = Zahl; put_char (Help +0x30) ; } geht schnell, ist übersichtlich, lässt sich leicht erweitern und braucht nicht allzuviel Code, wenn man schon Matheroutinen im Programm hat. Nachteil: nicht so elegant ;-) Josef
so um mal auf das grund thema zurückzukommen: eine ulong2ascii routine zuschreiben fand ich mehr als einfach, als ich mich vor ein par wochen angefangen hatte mich damit zu beschäftigen. ich wollt jetzt mal hier im net nahc einigen anderen routinen suchen, die noch kleiner als meine sind... und naja, dass hier gesagt wurde, dass es mit 120 bytes kaum besser geht hab ich mich sehr gewundert: also meine zugegebenermaßen nicht sehr aufwendig programmierten ersten version lagen schon unter der hälfte, d.h. bei 55-60 bytes! nach sehr viel zeit die mit optimierungen der routine verbracht habe bin ich jetzt an einen punkt angelangt, an dem ich es nun wirklich um kein byte kleiner krieg: volle 20 byte bei 12 prozessorbefehlen!!! ok, genug der schwafelei, kommen wir mal zum code (im nasm syntax verfasst): bits 32 cpu 386 %macro pushb 1 db 06ah, %1 %endmacro ;start pushb 10 pop ecx pushb 0d0h loop_1: xor edx, edx div ecx push edx test eax, eax jne loop_1 loop_2: pop eax add al, 030h stosb jne loop_2 ;ende ;;;;;;;;;;;;;;;;;;;;;;;;;; so das war's schon. also zur "nutzung" meiner routine: input: eax = ulong der in einen asciiz string kovertiert werden soll edi = pointer zum buffer (min. 11 bytes wie wir wissen) direction-flag = 0 (sollte der normal fall sein) output: der string im buffer benutzt werden: eax, edx, ecx, flags und maximal 44 bytes auf dem stack so siehts nach der routine aus: eax = 11111111 11111111 11111111 00000000b edx = 00000000 00000000 00000000 0000????b ecx = 00000000 00000000 00000000 00001010h edi = edi+n (wobei n=anzahl der geschriebenen bytes ) zero-flag = 1 carry-flag = 1 parity-flag = 1 signed-flag = 0 overflow-flag = 0 auxiliary-flag = 0 direction-flag = 0 so, dass ist dann wohl eine der kleinsten routinen, und wenn einer auf schnelligkeit aus ist und nicht auf die anzahl der bytes sollte er sich mal http://www.df.lth.se/~john_e/gems/gem003e.html angucken. (das ist aber natürlich nicht von mir, ich hab bis jetzt noch nicht ganz da durchgeblickt, aber das ist mindestens 4-5 mal so schnell, glaub ich) hoffe mein zeug und die andere routine werden irgendwem helfen, der das hier liest. wenn wer fragen dazu hat, bugs findet( dürfte schwer sein ;) ), oder eine 18/19 byte version hat würd ich mich freuen wenn er mir ne mail schickt ( an da_marshal[AT]web.de weil die andere is eher nur eine spam addresse ) [im datei anhang findedt ihr die routine mit beschreibung in englisch (man ich mag die sprache echt total)] ach und wenn einer von euch das teil bei sich in einem program implementiert, oder wo anders publiziert bitte ich darum mein pseudonym (da_marshal) als autor zu vermerken (nur für ein bißchen würde auf dieser welt :) ) ..and i wish you a happy coding
Also ich finde die Routinen ganz schön lang und langsam. Es geht doch viel einfacher: .set regs 64:32 .set dcu MP33C400 start: movi dx1:32,12345678 ! Zahl laden cv2pck dx1 ! Zahl konvertieren retx dx1:64 ! Ergebnis als packed BCD Letzlich wird nur ein Befehl benötigt; es muß eben nur der richtige sein.
@Michael, Thema verfehlt, Note 6 setzen. ASCII war gefragt. ASCII != packed BCD !!! Packed BCD kommt aus der Ursteinzeit der Rechentechnik, da leistete man sich noch den Luxus überflüssige Hardware zu verschwenden und an einen 8-Bit Port 2 * SN7447 7-Segment-Dekoder anzuschließen. Heutzutage sind Mikrokontroller aber so billig, da erhöht jeder zusätzliche IC die Gesamtkosten drastisch. Ich habe das packet BCD Format noch nie verwendet, ich wandele immer direkt in das benötigte Ausgabeformat. Peter
Ich habe ganz bewußt das Format packed-BCD gewählt; wer ASCII braucht, kann sich das einfach anpassen. Viel wichtiger finde ich, daß das Ergebnis im Register bleibt und keine RAM verschwendet wird.
ja, das die verdammt langasm ist weiß ich (aber wenigstens schneler als _itoa). ich wollt das teil ja nur so klein wie möglich kriegen, aber das mit dem ram muss ich nicht verstehen, oder? maximal 44 bytes, wird schon keiner sterben. und ich wollte mich auch auf die, wenn man sie so nennen mag, 'normalen/standard' befehle zurückgreifen. wenn du's schneller haben willst kannste du ja einfach die division mit reciprokaler multiplication ersetzten. is dann zwar auch nich so schnell wie die routine im gem, aber doch schon einiges schneller und nur einwenig größer.statt: loop_1: xor edx, edx div ecx push edx test eax, eax jne loop_1 kann man dann ja das hier schreiben: loop_1: mov ecx, edx mov eax, 0cccccccdh mul ecx shr edx, 3 lea eax, [edx+edx*4] shl eax, 1 sub ecx, eax push ecx test eax, eax jne loop_1 immer nur noch 32 bytes. eine andere 'verschönerung' die ich vorgenommen hab ist das ich "push byte 0d0h" und "and al, 030h" durch "push byte 030h" "xor al, 030h" ersetzt habe(weil eax dann nach dem aufruf auf 0 gesetzt ist; find ich irgednwie schöner. aber ich muss sagen mit bcds hab ich mich auch mal kurz beschäfftigt, aber genutzt hab ich die nicht wirklich. @michael: hast du eine schnellere bzw. kleinere routine als meine, dann poste die mal, würd mich interessieren wenn ja (ulong2ascii dann natürlich). sonst für leute die noch an anderen sachen zum thema coding/optimierung interresiert sind kann ich nur die vorher schon erwähnte seite http://www.df.lth.se/~john_e/ und noch www.hugi.de (bes. coding constest und die special edition) empfehlen! marcel
@Marcel: Ich hatte doch geschrieben, daß eine entsprechende CPU (DPU) die Wandlung kurz und schnell erledigt: cv2pck braucht 6 Takte. Bei 400MHz sind das gerade 15ns :-) Meine 'Kritik' richtete sich gegen den 386er, mit dem AVRs oder PICs oder MSPs nicht zu vergleichen sind. Und dabei auf die paar Fitzel-Byte Codegröße zu achten, ist schon ein wenig abwegig.
"Bei 400MHz sind das gerade 15ns :-)" Super, dann muß man bloß noch 199.999.985ns nutzlos verwarten, damit ein Mensch Zeit hat, die Zahl auch abzulesen. Aber ich denke auch, daß ein Mikrokontrollerforum nicht der rechte Platz für stromfressende Boliden ist. Hier sind eher CPUs gemeint, die mit <20mA zufrieden sind. Abgesehen davon würde ich einen PC nie in Assembler programmieren, das bringt heutzutage nichts, da die 640kB Speicherbarierre inzwischen längst Geschichte ist. Gibts überhaupt noch Speicherriegel <128MB zu kaufen ? Peter
> Abgesehen davon würde ich einen PC nie in Assembler programmieren
Es sei denn, es geht um wirklich zeitkritische Anwendungen.
in assembler auch für den pc zu programmieren findet ich nich wirklich abwägig. bei der programmierung eines z.b. komprimierungsalgorithmus ist es schon nicht schlecht hier und da ein paar gute optimierungen selbst zu schreiben. da kann man schon etwas zeit sparen. also ich finde das bringt auch was bei 'großen' cpus, also die von pcs, auch schon gut was
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.