Ganzzahl in ASCII in C18

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

Für n < 10 , es ist einfach:

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

Das würde funktionieren 10 n 100 :

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

Aber das ist wahrscheinlich nicht der schnellste Weg.

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 statt n + 0x30mit einem Kommentar . Sowohl C als auch C++ erfordern, dass die Ziffern - benachbarte ansteigende Werte haben, also immer funktionieren und klarer sind. 0x30 = '0'n + '0'09n + '0'

Antworten (5)

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

Je nachdem, welchen Compiler / stdlib.h Sie haben, wäre der relevante Funktionsprototyp:

extern char *   itoa(char * buf, int val, int base); // signed int
extern char *   utoa(char * buf, unsigned val, int base); // unsigned int

oder

extern char *   itoa(char * buf, int val); // signed int
extern char *   utoa(char * buf, unsigned val); // unsigned int

Wenn Sie nach einem relativ robusten, eingebauten " Standard "-C-Weg suchen, um Ihre Zahlen in ASCII-Strings umzuwandeln, sollten Sie diese xtoa()Funktionen verwenden.

Wenn Sie andererseits gezwungen sind, ein paar zusätzliche Zyklen oder Bytes an Speicher aus dem endgültigen Code herauszuholen, sind einige der anderen Antworten auf Ihre Frage der richtige Weg.

Diese Funktionen sind kein Standard-C. itoaVielleicht stammt es aus dem alten Borland C, aber es hat es nie in ISO C geschafft. wenn sie da sind, benutze sie.

Sie könnten eine Funktion ausprobieren, die die Brute-Force-Methode zum Konvertieren in eine Zeichenfolge verwendet. Die folgende Funktion verwendet weder den Modulo-Operator noch die Multiplikation. Es gibt eine Zeichenfolge zurück.

/*
 *  Create a function that will return a string.
 *  It accepts 'inputValue' that is up to 255, but you can make it an int or longint...
 *  ...after you make some edits in the function.
 *  inputValue:   5       7       6
 *  Digits:      1st     2nd     3rd
 */
unsigned char* returnString(unsigned char inputValue)
{
    static unsigned char processedString[4]; // Return a string of 3 digits.
    unsigned char firstDigitCounter = 0; // Brute-force counter for first digit.
    unsigned char secondDigitCounter = 0; // Brute-force counter for second digit.
    if (inputValue > 99) // If we have a 3 digit number,
    {
        while (inputValue > 99) // Until our number is 3 digits, i.e. bigger than 99,
        {
            inputValue -= 100; // Subtract 100 and..
            firstDigitCounter++; //.. increment first digit.
        }
        while (inputValue > 9) // Until our number is 3 digits, i.e. bigger than 9,
        {
            inputValue -= 10; // Subtract 10 and..
            secondDigitCounter++; //.. increment second digit.
        }

        // Now, we have left the 'inputValue' as a single digit.

        processedString[0] = firstDigitCounter + 0x30; // First digit
        processedString[1] = secondDigitCounter + 0x30; // Second digit
        processedString[2] = inputValue + 0x30; // Third digit
        processedString[3] = '\0'; // String terminator.
    }
    else // If we have a 2 digit number,
    {
        while (inputValue > 9) // Until our number is 3 digits, i.e. bigger than 99,
        {
            inputValue -= 10; // Subtract 10 and..
            secondDigitCounter++; //.. increment second digit.
        }
        processedString[0] = secondDigitCounter + 0x30; // Second digit
        processedString[1] = inputValue + 0x30; // Third digit
        processedString[2] = '\0'; // String terminator.
    }
    return processedString; // Return the processed string.
}

Pastebin des obigen Codes.

Ich stecke mit einem Dinosaurier fest, der nur Assembler hat, und habe das Problem aufgegeben, Daten in Hex zu senden. Also +1 für ein Verfahren, das keine Multiplikationen oder, was noch wichtiger ist, Divisionen verwendet. Aber es lässt sich nicht leicht für 4- oder 5-stellige Zahlen skalieren. Irgendwelche Ideen, wie man es auf größere Zahlen skalieren kann?
@BobbiBennett Ich verwende im Allgemeinen Hex auf meinen Geräten. Wenn ich möchte, dass die Ausgabe hübsch aussieht, lasse ich das meinen Computer tun. Ich habe auch Mikrocomputer verwendet, die das Multiplizieren nicht direkt unterstützen konnten, eine Division würde mehr als eine Millisekunde dauern, in diesem Fall wird dies der einzige Weg sein.

Ich habe sprintf();vorher verwendet. Abgesehen davon, dass es mit der Formatierung bequem 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 };

volatile uint32_t measurement = 12345;
sprintf( string , "Measured: %lu milliseconds\n" , measurement );

uint8_t charCounter = 0;
while ( ( charCounter < stringLength ) and ( string[ charCounter ] != 0x00 ) ) {
    serialByteOut( string[ charCounter ] );                         // Send a single character
    charCounter++;
}

Wo measurementwird eine 32-Bit-Ganzzahl in einer ISR aktualisiert, die ich drucken möchte, und stringist der Ausgabepuffer. %lugibt an, dass eine lange vorzeichenlose Ganzzahl gedruckt werden soll, und \n ist ein Zeilenumbruch.

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

Ich bin auf diesem Stapel, um zu lernen, auch wenn es aus meinen eigenen Fehlern stammt. Warum die Ablehnung?
Normalerweise verwende ich auch sprintf(), aber es ist keineswegs wenig Platzbedarf. Ich erinnere mich nicht an die tatsächlichen Zahlen, aber Sie werden eine deutliche Zunahme der Codegröße bemerken. Aber Silizium ist billig :-)

Ich habe selbst eine Funktion erstellt:

void writeInteger(unsigned long input) {
    unsigned long start = 1;
    unsigned long counter;
    while (start*10 <= input)
        start *= 10;
    for (counter = start; counter >= 1; counter /= 10)
        Write1USART(((input / counter) % 10) + 0x30);
}
Dieser Code wird hier diskutiert .

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) Die Verwendung von „| '0'" anstelle von "+ '0'" verschleiert die auszuführende Operation (obwohl es für ASCII '0' funktioniert). (2) Der rekursive Aufruf ist auf Mikrocontrollern nicht ideal, die oft mit sehr kleinen Stapelgrößen arbeiten. Ein Caller könnte eine böse Überraschung erleben, wenn er am Ende 9 Rekursionsebenen auf seinem Stack hat.
@microtherion ASCII wurde in der Frage angegeben, aber die Verwendung von „0“ ist nicht „süß“, 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 + drückt aus, 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 unsigned int hat 64 Bits, was uns insgesamt aus dem Bereich der Mikroprozessoren herausholt, und die zehnmal verbrauchen hier nicht wesentlich mehr Speicher als andere Lösungen.
@EJP, es ist nicht die '0', gegen die ich Einwände erhebe, es ist die |. Die Operation, die wir ausdrücken, ist „eine Ziffer in einen Bereich zusammenhängender Zeichen abbilden“, also ist + dafür vollkommen klar, und es 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.
Was die Rekursion angeht, die nicht „wesentlich“ mehr Speicher verbraucht, verwendet die 10x-Rekursion für eine 32-Bit-Vorzeichenlose wahrscheinlich 60-80 Byte RAM, abhängig von der Adressgröße des Mikrocontrollers. Eine iterative Lösung verwendet <20 Bytes. Bei einigen MCUs, die nur mit 128 Byte RAM ausgestattet sind, kann die Verschwendung von 40 Byte erheblich sein.
@microtherion Ich nehme an, es sollte wirklich entweder + '0' oder | sein 0x30. Der Rekursionsaufwand wird durch die eigene Winzigkeit der Methode einigermaßen ausgeglichen, aber Grenzen sind Grenzen und müssen unbedingt eingehalten werden.