Entero a ASCII en C18

Estoy escribiendo código para un PIC18F46K22 usando el compilador C18. quiero escribir el valor de un entero norte en ASCII sobre el USART a mi PC.

Para norte < 10 , es fácil:

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

Esto funcionaría para 10 norte 100 :

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

Pero esta no es la forma más rápida posible, probablemente.

Entonces , ¿hay una función incorporada o una función en algún lugar que pueda usar en lugar de rodar la mía ?

¿Te refieres a algo que podría alcanzar el tamaño completo de un int?
@Kortuk sí, o incluso mejor, un unsigned long.
Solo una sugerencia: en lugar de n + 0x30con un comentario que 0x30 = '0', use n + '0'. Tanto C como C ++ requieren que los dígitos tengan valores crecientes adyacentes, por lo que 0siempre funciona y es más claro. 9n + '0'

Respuestas (5)

El compilador C18 es compatible con la familia de números a ascii de funciones C estándar en stdlib.h : itoa(), ltoa(), ultoa()etcétera.

Dependiendo de qué compilador / stdlib.h tenga, el prototipo de función relevante sería:

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

o

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

Si estuviera buscando una forma C " estándar " incorporada relativamente robusta para convertir sus números a cadenas ASCII, estas xtoa()funciones serían las que debe usar.

Si, por otro lado, está obligado a exprimir algunos ciclos adicionales o bytes de memoria del código final, entonces varias de las otras respuestas a su pregunta son el camino a seguir.

Esas funciones no son C estándar. itoaPodrían haberse originado en el antiguo Borland C, pero nunca llegó a ISO C. Sin embargo, no importa; si están ahí, úsalos.

Podría probar una función que usa el método de fuerza bruta para convertir a cadena. La siguiente función no utiliza el operador de módulo ni la multiplicación. Devuelve una cadena.

/*
 *  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 del código anterior.

Estoy atascado con un dinosaurio que solo tiene ensamblador, y desistí del problema, enviando datos en hexadecimal. Entonces, +1 para un procedimiento que no usa multiplicaciones o, lo que es más importante, divisiones. Pero no escala fácilmente para números de 4 o 5 dígitos. ¿Alguna idea sobre cómo escalarlo a números más grandes?
@BobbiBennett Generalmente uso hexadecimal en mis dispositivos, si quiero que la salida se vea bonita, dejo que mi computadora lo haga. También he usado microcomputadoras que no podían soportar multiplicar directamente, una división tomaría más de un milisegundo, en ese caso esta será la única forma.

he usado sprintf();antes. Además de ser conveniente con el formateo, no estoy completamente seguro de si es rápido y ocupa poco espacio. Viene con

#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++;
}

¿Dónde measurementse actualiza un entero de 32 bits en una ISR, que quiero imprimir y stringes el búfer de salida? %luindica que se va a imprimir un entero largo sin signo y \n es una nueva línea.

El uso es básicamente el mismo que para printf();La documentación es extensa y se puede encontrar fácilmente en Internet en varios sitios web: http://linux.die.net/man/3/sprintf

Estoy en esta pila para aprender, incluso si es de mis propios errores. ¿Por qué el voto negativo?
Normalmente también uso sprintf(), sin embargo, de ninguna manera ocupa poco espacio. No recuerdo los números reales, pero notará un aumento significativo en el tamaño del código. Pero el silicio es barato :-)

Yo mismo he hecho una función:

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);
}
Este código se está discutiendo aquí .

Podrías intentar esto:

void writeInteger(unsigned i)
{
   if (i > 9)
     writeInteger(i/10);
   write1USART(i % 10 + '0');
}
Este código funciona (en ASCII), pero es innecesariamente lindo en dos aspectos: (1) usando “| '0'” en lugar de “+ '0'” ofusca la operación que debe realizarse (aunque funciona para ASCII '0'). (2) la llamada recursiva no es ideal en los microcontroladores, que a menudo funcionan con tamaños de pila muy pequeños. Una persona que llama puede llevarse una desagradable sorpresa si termina con 9 niveles de recursividad apilados en su pila.
@microtherion ASCII se especificó en la pregunta, pero usar '0' no es 'lindo', es una forma aceptada de aislar las diferencias de juego de caracteres. Si el compilador usara BCD, el código también funcionaría en BCD. Es 0x30 que solo funciona en ASCII. Usando | en lugar de + expresa el hecho de que estamos configurando el bit de zona, no realizando un cálculo aritmético mágico. La recursión no puede repetirse más de diez veces a menos que un int sin firmar tenga 64 bits, lo que nos saca del reino de los microprocesadores por completo, y las diez veces no usan mucha más memoria que otras soluciones aquí.
@EJP, no es el '0' lo que me opongo, es el |. La operación que estamos expresando es "asignar un dígito a un rango de caracteres contiguos", por lo que + es perfectamente claro para esto, y funciona en los casos en que los LSB del "cero" de ese rango no son 0 (por ejemplo, para algunos de las representaciones numéricas en Unicode). | es menos claro y menos general.
En cuanto a la recursión que no usa "significativamente" más memoria, para una recursión 10x de 32 bits sin firmar probablemente use 60-80 bytes de RAM, dependiendo del tamaño de la dirección del microcontrolador. Una solución iterativa usa <20 bytes. Con algunas MCU equipadas con solo 128 bytes de RAM, desperdiciar 40 bytes puede ser significativo.
@microtherion Supongo que realmente debería ser + '0' o | 0x30. El costo de recurrencia se equilibra hasta cierto punto con el diminuto tamaño del método, pero los límites son límites y deben observarse con seguridad.