Domanda:
Integer to ASCII in C18
user17592
2013-04-13 11:50:48 UTC
view on stackexchange narkive permalink

Sto scrivendo il codice per un PIC18F46K22 utilizzando il compilatore C18. Voglio scrivere il valore di un intero \ $ n \ $ in ASCII tramite USART sul mio PC.

Per \ $ n<10 \ $, è facile:

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

Questo funzionerebbe per \ $ 10 \ le {} n \ le100 \ $:

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

Ma questo non è il modo più veloce possibile, probabilmente.

Quindi c'è una funzione incorporata o una funzione da qualche parte là fuori che potrei usare invece di eseguire il mio ?

Intendi qualcosa che potrebbe raggiungere la dimensione intera di un int?
@Kortuk sì, o meglio ancora, un "unsigned long".
Solo un suggerimento: invece di "n + 0x30" con un commento che "0x30 =" 0 ", usa" n + "0". Sia C che C ++ richiedono che le cifre "0" - "9" abbiano valori crescenti adiacenti, quindi "n +" 0 "" funziona sempre ed è più chiaro.
Cinque risposte:
Anindo Ghosh
2013-04-13 14:19:00 UTC
view on stackexchange narkive permalink

Il compilatore C18 supporta la famiglia da numero ad ascii di funzioni C standard in stdlib.h : itoa () , ltoa () , ultoa () e così via.

A seconda del compilatore / stdlib.h, il prototipo della funzione pertinente sarebbe:

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

o

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

Se stavi cercando un metodo C " standard " integrato relativamente robusto per convertire i tuoi numeri in stringhe ASCII, questi Le funzioni xtoa () sarebbero quelle da usare.

Se d'altra parte sei costretto a spremere alcuni cicli o byte di memoria extra dal codice finale, allora molte delle altre risposte alla tua domanda sono la strada da percorrere.

Quelle funzioni non sono standard C. "itoa" potrebbe aver avuto origine nell'antico Borland C, ma non è mai arrivato in ISO C. Non importa, però; se ci sono, usali.
user17592
2013-04-13 11:50:48 UTC
view on stackexchange narkive permalink

Ho creato una funzione da solo:

  void writeInteger (unsigned long input) {unsigned long start = 1; contatore lungo non firmato; while (inizio * 10 < = input) inizio * = 10; for (counter = start; counter > = 1; counter / = 10) Write1USART (((input / counter)% 10) + 0x30);}  
Questo codice è in discussione [qui] (http://electronics.stackexchange.com/q/65475/17592).
abdullah kahraman
2013-04-13 12:47:00 UTC
view on stackexchange narkive permalink

Potresti provare una funzione che utilizza il metodo della forza bruta per convertire in stringa. La funzione sotto non utilizza l'operatore modulo né la moltiplicazione. Restituisce una stringa.

  / * * Crea una funzione che restituirà una stringa. * Accetta 'inputValue' fino a 255, ma puoi renderlo int o longint ... * ... dopo aver apportato alcune modifiche alla funzione. * inputValue: 5 7 6 * Cifre: 1 ° 2 ° 3 ° * / unsigned char * returnString (unsigned char inputValue) {static unsigned char processingString [4]; // Restituisce una stringa di 3 cifre. carattere non firmato firstDigitCounter = 0; // Contatore forza bruta per la prima cifra. carattere non firmato secondDigitCounter = 0; // Contatore della forza bruta per la seconda cifra. if (inputValue > 99) // Se abbiamo un numero di 3 cifre, {while (inputValue > 99) // Fino a quando il nostro numero è di 3 cifre, cioè maggiore di 99, {inputValue - = 100; // Sottrai 100 e .. firstDigitCounter ++; // .. incrementa la prima cifra. } while (inputValue > 9) // Fino a quando il nostro numero è di 3 cifre, cioè più grande di 9, {inputValue - = 10; // Sottrai 10 e .. secondDigitCounter ++; // .. incrementa la seconda cifra. } // Ora, abbiamo lasciato "inputValue" come una singola cifra. ProcessingString [0] = firstDigitCounter + 0x30; // Prima cifra ProcessingString [1] = secondDigitCounter + 0x30; // Seconda cifra processingString [2] = inputValue + 0x30; // Terza cifra elaborataString [3] = '\ 0'; // Terminatore di stringa. } else // Se abbiamo un numero di 2 cifre, {while (inputValue > 9) // Fino a quando il nostro numero è di 3 cifre, cioè maggiore di 99, {inputValue - = 10; // Sottrai 10 e .. secondDigitCounter ++; // .. incrementa la seconda cifra. } processingString [0] = secondDigitCounter + 0x30; // Seconda cifra processingString [1] = inputValue + 0x30; // Terza cifra
processingString [2] = '\ 0'; // Terminatore di stringa. } return processingString; // Restituisce la stringa elaborata.}  

Pastebin del codice precedente.

Sono bloccato con un dinosauro che ha solo assemblatore e ho rinunciato al problema, inviando i dati in esadecimale. Quindi +1 per una procedura che non utilizza moltiplicazioni o, cosa più importante, divisioni. Ma non scala facilmente per numeri a 4 o 5 cifre. Qualche idea su come ridimensionarlo a numeri più grandi?
@BobbiBennett Generalmente uso hex sui miei dispositivi, se voglio che l'output sia carino, lo lascio fare al mio computer. Ho anche usato microcomputer che non possono supportare la moltiplicazione direttamente, una divisione richiederebbe più di un millisecondo, in quel caso questo sarà l'unico modo.
jippie
2013-04-13 12:48:18 UTC
view on stackexchange narkive permalink

Ho già utilizzato sprintf (); . Oltre ad essere conveniente con la formattazione, non sono del tutto sicuro che sia veloce e abbia un ingombro ridotto. Viene fornito con.

  #include <stdio.h>const uint8_t stringLength = 16; char string [stringLength] = {0}; volatile uint32_t measure = 12345; sprintf (string, "Misurato:% lu millisecondi \ n ", misura); uint8_t charCounter = 0; while ((charCounter < stringLength) e (string [charCounter]! = 0x00)) {serialByteOut (string [charCounter]); // Invia un singolo carattere charCounter ++;}  

Dove misurazione è un numero intero a 32 bit aggiornato in un ISR, che voglio stampare e stringa è il buffer di output. % lu indica che deve essere stampato un intero lungo senza segno e \ n è una nuova riga.

L'uso è in gran parte lo stesso di printf (); La documentazione è ampia e può essere facilmente trovata su Internet su vari siti Web: http://linux.die.net/man/3/sprintf

Sono su questa pila per imparare, anche se è dai miei errori. Perché il voto negativo?
Normalmente uso anche sprintf (), tuttavia non ha un ingombro ridotto. Non ricordo i numeri reali, ma noterai un aumento significativo della dimensione del codice. Ma il silicio costa poco :-)
Marquis of Lorne
2013-04-13 13:06:12 UTC
view on stackexchange narkive permalink

Puoi provare questo:

  void writeInteger (unsigned i) {if (i > 9) writeInteger (i / 10); write1USART (i% 10 + '0');}  
Questo codice funziona (in ASCII), ma è inutilmente carino sotto due aspetti: (1) usando “| "0" invece di "+" 0 "offusca l'operazione che deve essere eseguita (anche se funziona per ASCII" 0 "). (2) la chiamata ricorsiva non è l'ideale sui microcontrollori, che spesso funzionano con stack di dimensioni molto ridotte. Un chiamante potrebbe avere una brutta sorpresa se si ritrova con 9 livelli di ricorsione ammucchiati nel proprio stack.
@microtherion ASCII è stato specificato nella domanda, ma l'uso di "0" non è "carino", è un modo accettato per isolare le differenze di set di caratteri. Se il compilatore usasse BCD il codice funzionerebbe anche in BCD. È 0x30 che funziona solo in ASCII. Utilizzando | invece di + esprime il fatto che stiamo impostando il bit di zona, non eseguendo un calcolo aritmetico magico. La ricorsione non può ripetersi più di dieci volte a meno che un int senza segno non abbia 64 bit, il che ci porta fuori dai regni dei microprocessori del tutto, e le dieci volte non utilizzano molta più memoria rispetto ad altre soluzioni qui.
@EJP, non è lo "0" a cui mi oppongo, è il |. L'operazione che stiamo esprimendo è "mappare una cifra in un intervallo di caratteri contigui", quindi + è perfettamente chiaro per questo, e funziona nei casi in cui gli LSB dello "zero" di quell'intervallo non sono 0 (ad esempio per alcuni delle rappresentazioni numeriche in Unicode). | è meno chiaro e meno generale.
Per quanto riguarda la ricorsione che non utilizza "significativamente" più memoria, per una ricorsione a 32 bit senza segno, la ricorsione 10x probabilmente utilizza 60-80 byte di RAM, a seconda della dimensione dell'indirizzo del microcontrollore. Una soluzione iterativa utilizza <20 byte. Con alcuni MCU dotati di soli 128 byte di RAM, sprecare 40 byte ** può ** essere significativo.
@microtherion Suppongo che dovrebbe essere davvero + '0' o | 0x30. Il costo di ricorsione è bilanciato in una certa misura dalle piccole dimensioni del metodo, ma i limiti sono limiti e devono essere rispettati con certezza.


Questa domanda e risposta è stata tradotta automaticamente dalla lingua inglese. Il contenuto originale è disponibile su stackexchange, che ringraziamo per la licenza cc by-sa 3.0 con cui è distribuito.
Loading...