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