Forum: Projekte & Code Long(Int) -> ASCII Umwandlung


von J.St. (Gast)


Lesenswert?

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

von Richard (Gast)


Lesenswert?

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.

von J.St. (Gast)


Lesenswert?

Hallo,
ich benutze diese Funktion in einem MSP430F437.

J.St.
www.SynaSys.de

von Peter D. (peda)


Lesenswert?

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

von Peter Fleury (Gast)


Lesenswert?

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.

von Thorsten (Gast)


Lesenswert?

Stimmt, nur verschwenden dieses Standardfunktionen i. d. R. massenhaft
Speicherplatz. Sie lassen sich wesentlich effizienter codieren.

von Stefan (Gast)


Lesenswert?

@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.

von Thorsten (Gast)


Lesenswert?

Oh sorry ! Du hast natürlich recht, ich hab vor dem posten mal wieder
nicht nachgedacht. Es war in der Tat printf. Peinlich ...

von kamil (Gast)


Lesenswert?

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

von kamil (Gast)


Lesenswert?

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

von Sebastian (Gast)


Lesenswert?

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

von Reiner (Gast)


Lesenswert?

@Sebastian

Die xtoa() funktionen sind in stdlib.h (WINAVR)

von Sebastian (Gast)


Lesenswert?

@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

von Sebastian (Gast)


Lesenswert?

Meinte natürlich itoa() ...

von Sebastian (Gast)


Lesenswert?

@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

von Peter D. (peda)


Lesenswert?

@Sebastian

hier findest Du die Antwort:

http://www.schellong.de/c.htm


Peter

von Sebastian (Gast)


Lesenswert?

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?

von Stefan Kleinwort (Gast)


Lesenswert?

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

von Stefan Kleinwort (Gast)


Lesenswert?

Übrigens - in einer halben Stunde hat man bei einem C-Buch doch
gerademal die Struktur des Inhaltsverzeichnisses verstanden ;-)

Stefan

von Sebastian (Gast)


Lesenswert?

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

von kay knofe (Gast)


Lesenswert?

hat jemand sowas vielleicht schon kurz(!!!) und überzeugend als asm??
oder mal hier irgentwo im forum gesehen??

von Andreas S. (andreas) (Admin) Benutzerseite


Lesenswert?

Suche nach bin2bcd.

von Stefan Kleinwort (Gast)


Lesenswert?

@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

von Marco Schwalm (Gast)


Angehängte Dateien:

Lesenswert?

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

von Sebastian (Gast)


Lesenswert?

@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. :-)

von Oliver Keller (Gast)


Angehängte Dateien:

Lesenswert?

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

von Josef (Gast)


Lesenswert?

//***********************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

von da_marshal (Gast)


Angehängte Dateien:

Lesenswert?

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

von Michael (Gast)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

@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

von Michael (Gast)


Lesenswert?

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.

von da_marshal (Gast)


Lesenswert?

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

von Michael (Gast)


Lesenswert?

@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.

von Peter D. (peda)


Lesenswert?

"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

von Thorsten (Gast)


Lesenswert?

> Abgesehen davon würde ich einen PC nie in Assembler programmieren

Es sei denn, es geht um wirklich zeitkritische Anwendungen.

von da_marshal (Gast)


Lesenswert?

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
Noch kein Account? Hier anmelden.