Frage:
Ganzzahl zu ASCII in C18
user17592
2013-04-13 11:50:48 UTC
view on stackexchange narkive permalink

Ich schreibe Code für einen PIC18F46K22 mit dem C18-Compiler. Ich möchte den Wert einer Ganzzahl \ $ n \ $ in ASCII über den USART auf meinen PC schreiben.

Für \ $ n<10 \ $ ist es einfach:

  Write1USART (n + 0x30); // 0x30 = '0'  

Dies würde für \ $ 10 \ le {} n \ le100 \ $ funktionieren:

  Write1USART ((n / 10) ) + 0x30); Write1USART ((n% 10) + 0x30);  

Aber dies ist wahrscheinlich nicht der schnellste Weg.

Also Gibt es irgendwo da draußen eine eingebaute Funktion oder eine Funktion, die ich einfach verwenden könnte, anstatt meine eigene zu rollen?

Meinen Sie etwas, das die volle Größe eines Int erreichen könnte?
@Kortuk ja, oder noch besser, ein "unsigned long".
Nur ein Vorschlag: Verwenden Sie anstelle von "n + 0x30" mit dem Kommentar "0x30 =" 0 "" n + "0". Sowohl C als auch C ++ erfordern, dass die Ziffern "0" - "9" benachbarte ansteigende Werte haben, sodass "n +" 0 "immer funktioniert und klarer ist.
Fünf antworten:
Anindo Ghosh
2013-04-13 14:19:00 UTC
view on stackexchange narkive permalink

Der C18-Compiler unterstützt die Number-to-ASCII-Familie von Standard-C-Funktionen in stdlib.h : itoa () , ltoa () , ultoa () und so weiter.

Je nachdem, welchen Compiler / welche stdlib.h Sie haben, lautet der relevante Funktionsprototyp:

  extern char * itoa (char * buf, int val, int base); // signiertes intextern char * utoa (char * buf, vorzeichenloses val, int base); // unsigned int  

oder

  extern char * itoa (char * buf, int val); // signiertes intextern char * utoa (char * buf, vorzeichenloses val); // unsigned int  

Wenn Sie nach einer relativ robusten integrierten " Standard " C-Methode zum Konvertieren Ihrer Zahlen in ASCII-Zeichenfolgen suchen, sind diese Die Funktionen xtoa () sollten verwendet werden.

Wenn Sie andererseits gezwungen sind, einige zusätzliche Zyklen oder Speicherbytes aus dem endgültigen Code herauszupressen, sind einige der anderen Antworten auf Ihre Frage der richtige Weg.

Diese Funktionen sind nicht Standard C. `itoa` stammt möglicherweise aus dem alten Borland C, hat es aber nie in ISO C geschafft. Wenn sie da sind, benutze sie.
user17592
2013-04-13 11:50:48 UTC
view on stackexchange narkive permalink

Ich habe selbst eine Funktion erstellt:

  void writeInteger (vorzeichenlose lange Eingabe) {vorzeichenloser langer Start = 1; vorzeichenloser langer Zähler; while (Start * 10 < = Eingabe) Start * = 10; für (Zähler = Start; Zähler > = 1; Zähler / = 10) Write1USART (((Eingabe / Zähler)% 10) + 0x30);}  
Dieser Code wird [hier] diskutiert (http://electronics.stackexchange.com/q/65475/17592).
abdullah kahraman
2013-04-13 12:47:00 UTC
view on stackexchange narkive permalink

Sie können eine Funktion ausprobieren, die die Brute-Force-Methode zum Konvertieren in einen String verwendet. Die folgende Funktion verwendet weder den Moduloperator noch die Multiplikation. Es wird eine Zeichenfolge zurückgegeben.

  / * * Erstellen Sie eine Funktion, die eine Zeichenfolge zurückgibt. * Es akzeptiert 'inputValue', das bis zu 255 beträgt, aber Sie können es zu einem int oder longint machen ... * ... nachdem Sie einige Änderungen an der Funktion vorgenommen haben. * inputValue: 5 7 6 * Ziffern: 1. 2. 3. * / vorzeichenloses Zeichen * returnString (vorzeichenloses Zeichen inputValue) {statisches vorzeichenloses Zeichen verarbeitetString [4]; // Eine dreistellige Zeichenfolge zurückgeben. vorzeichenloses Zeichen firstDigitCounter = 0; // Brute-Force-Zähler für die erste Ziffer. vorzeichenloses Zeichen secondDigitCounter = 0; // Brute-Force-Zähler für die zweite Ziffer. if (inputValue > 99) // Wenn wir eine dreistellige Nummer haben, {while (inputValue > 99) // Bis unsere Nummer 3-stellig ist, d. h. größer als 99, {inputValue - = 100; // Subtrahiere 100 und .. firstDigitCounter ++; // .. erste Ziffer erhöhen. } while (inputValue > 9) // Bis unsere Zahl 3-stellig ist, d. h. größer als 9, {inputValue - = 10; // Subtrahiere 10 und .. secondDigitCounter ++; // .. zweite Ziffer erhöhen. } // Jetzt haben wir den 'inputValue' als einzelne Ziffer belassen. verarbeiteteString [0] = firstDigitCounter + 0x30; // Erste Ziffer verarbeitetString [1] = secondDigitCounter + 0x30; // Zweite Ziffer verarbeitetString [2] = inputValue + 0x30; // Dritte Ziffer verarbeitetString [3] = '\ 0'; // String terminator. } else // Wenn wir eine zweistellige Zahl haben, {while (inputValue > 9) // Bis unsere Zahl 3-stellig ist, d. h. größer als 99, {inputValue - = 10; // Subtrahiere 10 und .. secondDigitCounter ++; // .. zweite Ziffer erhöhen. } processString [0] = secondDigitCounter + 0x30; // Zweite Ziffer verarbeitetString [1] = inputValue + 0x30; // Dritte Ziffer
ProcessedString [2] = '\ 0'; // String terminator. } return processString; // Gib die verarbeitete Zeichenfolge zurück.}  

Pastebin des obigen Codes.

Ich bin mit einem Dinosaurier festgefahren, der nur Assembler hat, und habe das Problem aufgegeben und Daten in hexadezimaler Form gesendet. Also +1 für eine Prozedur, die keine Multiplikationen oder vor allem Divisionen verwendet. Es lässt sich jedoch nicht einfach für 4- oder 5-stellige Zahlen skalieren. Irgendwelche Ideen, wie man es auf größere Zahlen skaliert?
@BobbiBennett Ich verwende normalerweise Hex auf meinen Geräten. Wenn die Ausgabe hübsch aussehen soll, lasse ich das meinen Computer tun. Ich habe auch Mikrocomputer verwendet, die die direkte Multiplikation nicht unterstützen könnten. Eine Division würde mehr als eine Millisekunde dauern. In diesem Fall ist dies der einzige Weg.
jippie
2013-04-13 12:48:18 UTC
view on stackexchange narkive permalink

Ich habe zuvor sprintf (); verwendet. Abgesehen davon, dass es beim Formatieren praktisch ist, bin ich mir nicht ganz sicher, ob es schnell ist und einen geringen Platzbedarf hat. Es kommt mit.

  #include <stdio.h>const uint8_t stringLength = 16; char string [stringLength] = {0}; flüchtige uint32_t-Messung = 12345; sprintf (string, "Gemessen:% lu Millisekunden \ n ", Messung); uint8_t charCounter = 0; while ((charCounter < stringLength) und (string [charCounter]! = 0x00)) {serialByteOut (string [charCounter]); // Sende ein einzelnes Zeichen charCounter ++;}  

Wobei Messung eine 32-Bit-Ganzzahl ist, die in einem ISR aktualisiert wurde, den ich drucken möchte, und string ist der Ausgabepuffer. % lu gibt an, dass eine lange vorzeichenlose Ganzzahl gedruckt werden soll und \ n eine neue Zeile ist.

Die Verwendung ist weitgehend dieselbe wie für printf (); Die Dokumentation ist umfangreich und kann auf verschiedenen Websites leicht im Internet gefunden werden: http://linux.die.net/man/3/sprintf

Ich bin auf diesem Stapel, um zu lernen, auch wenn es aus meinen eigenen Fehlern ist. Warum die Abwahl?
Normalerweise benutze ich auch sprintf (), aber es ist keineswegs klein. Ich erinnere mich nicht an tatsächliche Zahlen, aber Sie werden eine signifikante Zunahme der Codegröße feststellen. Aber Silizium ist billig :-)
Marquis of Lorne
2013-04-13 13:06:12 UTC
view on stackexchange narkive permalink

Sie könnten dies versuchen:

  void writeInteger (unsigned i) {if (i > 9) writeInteger (i / 10); write1USART (i% 10 + '0');}  
Dieser Code funktioniert (in ASCII), ist aber in zweierlei Hinsicht unnötig niedlich: (1) Verwenden von “| "0" anstelle von "+" 0 "verschleiert die auszuführende Operation (obwohl sie für ASCII" 0 "funktioniert). (2) Der rekursive Aufruf ist bei Mikrocontrollern, die häufig mit sehr kleinen Stapelgrößen arbeiten, nicht ideal. Ein Anrufer könnte eine grobe Überraschung erleben, wenn er am Ende 9 Rekursionsstufen auf seinem Stapel hat.
@microtherion ASCII wurde in der Frage angegeben, aber die Verwendung von '0' ist nicht 'niedlich', sondern eine akzeptierte Methode zum Isolieren von Zeichensatzunterschieden. Wenn der Compiler BCD verwenden würde, würde der Code auch in BCD funktionieren. Es ist 0x30, das nur in ASCII funktioniert. Verwenden von | anstelle von + wird die Tatsache ausgedrückt, dass wir das Zonenbit setzen und keine magische arithmetische Berechnung durchführen. Die Rekursion kann nicht mehr als zehnmal wiederholt werden, es sei denn, ein vorzeichenloses int hat 64 Bit, was uns aus dem Bereich der Mikroprozessoren herausführt, und die zehnmaligen verwenden hier nicht wesentlich mehr Speicher als andere Lösungen.
@EJP, Es ist nicht die "0", gegen die ich Einwände habe, sondern die |. Die Operation, die wir ausdrücken, lautet "Ordne eine Ziffer einem Bereich zusammenhängender Zeichen zu", daher ist + dafür vollkommen klar und funktioniert in Fällen, in denen die LSBs der "Null" dieses Bereichs nicht 0 sind (z. B. für einige der numerischen Darstellungen in Unicode). | ist weniger klar und weniger allgemein.
Da für die Rekursion nicht „wesentlich“ mehr Speicher verwendet wird, werden für eine 32-Bit-Rekursion bei einer vorzeichenlosen 32-Bit-Rekursion je nach Adressgröße des Mikrocontrollers wahrscheinlich 60 bis 80 Byte RAM verwendet. Eine iterative Lösung verwendet <20 Bytes. Bei einigen MCUs mit nur 128 Byte RAM kann die Verschwendung von 40 Byte ** erheblich sein.
@microtherion Ich nehme an, es sollte wirklich entweder + '0' oder | sein 0x30. Die Rekursionskosten werden in gewissem Maße durch die winzige Größe der Methode ausgeglichen, aber Grenzen sind Grenzen und müssen auf jeden Fall eingehalten werden.


Diese Fragen und Antworten wurden automatisch aus der englischen Sprache übersetzt.Der ursprüngliche Inhalt ist auf stackexchange verfügbar. Wir danken ihm für die cc by-sa 3.0-Lizenz, unter der er vertrieben wird.
Loading...