Question:
Entier en ASCII en C18
user17592
2013-04-13 11:50:48 UTC
view on stackexchange narkive permalink

J'écris du code pour un PIC18F46K22 en utilisant le compilateur C18. Je veux écrire la valeur d'un entier \ $ n \ $ en ASCII sur l'USART sur mon PC.

Pour \ $ n<10 \ $, c'est simple:

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

Cela fonctionnerait pour \ $ 10 \ le {} n \ le100 \ $:

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

Mais ce n'est probablement pas le moyen le plus rapide possible.

Donc y a-t-il une fonction intégrée ou une fonction quelque part que je pourrais simplement utiliser au lieu de lancer la mienne ?

Voulez-vous dire quelque chose qui pourrait atteindre la taille complète d'un int?
@Kortuk oui, ou mieux encore, un "unsigned long".
Juste une suggestion: au lieu de `n + 0x30` avec un commentaire que` 0x30 = '0'`, utilisez `n +' 0'`. C et C ++ exigent que les chiffres «0» - «9» aient des valeurs croissantes adjacentes, donc «n +« 0 »» fonctionne toujours et est plus clair.
Cinq réponses:
Anindo Ghosh
2013-04-13 14:19:00 UTC
view on stackexchange narkive permalink

Le compilateur C18 prend en charge la famille number-to-ascii des fonctions C standard dans stdlib.h : itoa () , ltoa () , ultoa () et cetera.

Selon le compilateur / stdlib.h que vous avez, le prototype de fonction pertinent serait:

  extern char * itoa (char * buf, int val, int base); // entier signé char * utoa (char * buf, unsigned val, int base); // unsigned int  

ou

  extern char * itoa (char * buf, int val); // entier signé entier char * utoa (char * buf, unsigned val); // unsigned int  

Si vous recherchiez une méthode C " standard " intégrée relativement robuste pour convertir vos nombres en chaînes ASCII, ces Les fonctions xtoa () seraient celles à utiliser.

Si, d'un autre côté, vous êtes obligé de faire sortir quelques cycles ou octets de mémoire supplémentaires du code final, alors plusieurs des autres réponses à votre question sont la voie à suivre.

Ces fonctions ne sont pas standard C. «itoa» pourrait avoir son origine dans l'ancien Borland C, mais il n'a jamais été intégré à ISO C. Peu importe cependant; s'ils sont là, utilisez-les.
user17592
2013-04-13 11:50:48 UTC
view on stackexchange narkive permalink

J'ai moi-même créé une fonction:

  void writeInteger (unsigned long input) {unsigned long start = 1; compteur long non signé; while (start * 10 < = entrée) start * = 10; pour (compteur = début; compteur > = 1; compteur / = 10) Write1USART (((entrée / compteur)% 10) + 0x30);}  
Ce code est en cours de discussion [ici] (http://electronics.stackexchange.com/q/65475/17592).
abdullah kahraman
2013-04-13 12:47:00 UTC
view on stackexchange narkive permalink

Vous pouvez essayer une fonction qui utilise la méthode de force brute pour convertir en chaîne. La fonction ci-dessous n'utilise pas d'opérateur de module ni de multiplication. Il renvoie une chaîne.

  / * * Créez une fonction qui renverra une chaîne. * Il accepte 'inputValue' qui est jusqu'à 255, mais vous pouvez en faire un int ou longint ... * ... après avoir fait quelques modifications dans la fonction. * inputValue: 5 7 6 * Chiffres: 1er 2ème 3ème * / unsigned char * returnString (unsigned char inputValue) {static unsigned char traitéesString [4]; // Renvoie une chaîne de 3 chiffres. unsigned char firstDigitCounter = 0; // Compteur de force brute pour le premier chiffre. unsigned char secondDigitCounter = 0; // Compteur de force brute pour le deuxième chiffre. if (inputValue > 99) // Si nous avons un nombre à 3 chiffres, {while (inputValue > 99) // Jusqu'à ce que notre nombre soit à 3 chiffres, soit plus grand que 99, {inputValue - = 100; // Soustraire 100 et .. firstDigitCounter ++; // .. incrémenter le premier chiffre. } while (inputValue > 9) // Jusqu'à ce que notre nombre soit à 3 chiffres, c'est-à-dire plus grand que 9, {inputValue - = 10; // Soustraire 10 et .. secondDigitCounter ++; // .. incrémente le deuxième chiffre. } // Maintenant, nous avons laissé 'inputValue' comme un seul chiffre. traitéesString [0] = firstDigitCounter + 0x30; // Premier chiffre traitéString [1] = secondDigitCounter + 0x30; // Deuxième chiffre traitéString [2] = inputValue + 0x30; // Troisième chiffre traitéString [3] = '\ 0'; // Terminateur de chaîne. } else // Si nous avons un nombre à 2 chiffres, {while (inputValue > 9) // Jusqu'à ce que notre nombre soit à 3 chiffres, c'est-à-dire plus grand que 99, {inputValue - = 10; // Soustraire 10 et .. secondDigitCounter ++; // .. incrémente le deuxième chiffre. } traitéesString [0] = secondDigitCounter + 0x30; // Deuxième chiffre traitéString [1] = inputValue + 0x30; // Troisième chiffre
traitéesString [2] = '\ 0'; // Terminateur de chaîne. } return traitéesString; // Renvoie la chaîne traitée.}  

Pastebin du code ci-dessus.

Je suis coincé avec un dinosaure qui n'a que l'assembleur, et j'ai abandonné le problème, envoyant des données en hexadécimal. Donc +1 pour une procédure qui n'utilise pas de multiplications ou surtout de divisions. Mais il ne s'adapte pas facilement aux nombres à 4 ou 5 chiffres. Avez-vous des idées sur la façon de l'adapter à un plus grand nombre?
@BobbiBennett J'utilise généralement hexagone sur mes appareils, si je veux que la sortie soit jolie, je laisse mon ordinateur le faire. J'ai également utilisé des micro-ordinateurs qui ne pouvaient pas prendre en charge la multiplication directe, une division prendrait plus d'une milliseconde, dans ce cas, ce sera le seul moyen.
jippie
2013-04-13 12:48:18 UTC
view on stackexchange narkive permalink

J'ai déjà utilisé sprintf (); . En plus d'être pratique avec le formatage, je ne suis pas tout à fait sûr qu'il soit rapide et qu'il ait un faible encombrement. Il est livré avec.

  #include <stdio.h>const uint8_t stringLength = 16; char string [stringLength] = {0}; volatile uint32_t Measurement = 12345; sprintf (string, "Mesuré:% lu millisecondes \ n ", mesure); uint8_t charCounter = 0; while ((charCounter < stringLength) et (string [charCounter]! = 0x00)) {serialByteOut (string [charCounter]); // Envoie un seul caractère charCounter ++;}  

mesure est un entier 32 bits mis à jour dans un ISR, que je veux imprimer et chaîne est le tampon de sortie. % lu indique qu'un long entier non signé doit être imprimé et \ n est une nouvelle ligne.

L'utilisation est en grande partie la même que pour printf (); La documentation est complète et peut être facilement trouvée sur Internet sur différents sites Web: http://linux.die.net/man/3/sprintf

Je suis sur cette pile pour apprendre, même si c'est de mes propres erreurs. Pourquoi voter contre?
J'utilise normalement aussi sprintf (), mais son encombrement n'est en aucun cas limité. Je ne me souviens pas des chiffres réels, mais vous remarquerez une augmentation significative de la taille du code. Mais le silicium est bon marché :-)
Marquis of Lorne
2013-04-13 13:06:12 UTC
view on stackexchange narkive permalink

Vous pouvez essayer ceci:

  void writeInteger (unsigned i) {if (i > 9) writeInteger (i / 10); write1USART (i% 10 + '0');}  
Ce code fonctionne (en ASCII), mais est inutilement mignon à deux égards: (1) en utilisant “| «0» »au lieu de« + «0» »obscurcit l’opération qui doit être effectuée (même si elle fonctionne pour ASCII« 0 »). (2) l'appel récursif n'est pas idéal sur les microcontrôleurs, qui fonctionnent souvent avec de très petites tailles de pile. Un appelant pourrait avoir une mauvaise surprise s'il se retrouve avec 9 niveaux de récursivité empilés sur sa pile.
@microtherion ASCII a été spécifié dans la question, mais utiliser «0» n'est pas «mignon», c'est une manière acceptée d'isoler les différences de jeu de caractères. Si le compilateur utilisait BCD, le code fonctionnerait également en BCD. C'est 0x30 qui ne fonctionne qu'en ASCII. Utilisation | au lieu de +, cela exprime le fait que nous définissons le bit de zone, sans effectuer de calcul arithmétique magique. La récursivité ne peut pas se reproduire plus de dix fois à moins qu'un int non signé ait 64 bits, ce qui nous fait complètement sortir des domaines des microprocesseurs, et les dix fois n'utilisent pas beaucoup plus de mémoire que les autres solutions ici.
@EJP, ce n’est pas le «0» auquel je fais objection, c’est le |. L'opération que nous exprimons est de «mapper un chiffre dans une plage de caractères contigus», donc + est parfaitement clair pour cela, et cela fonctionne dans les cas où les LSB du «zéro» de cette plage ne sont pas 0 (par exemple pour certains des représentations numériques en Unicode). | est moins clair et moins général.
Quant à la récursivité n'utilisant pas «significativement» plus de mémoire, pour une récursion 32 bits non signée, 10x utilise probablement 60-80 octets de RAM, selon la taille de l'adresse du microcontrôleur. Une solution itérative utilise <20 octets. Avec certains MCU équipés de seulement 128 octets de RAM, le gaspillage de 40 octets ** peut ** être important.
@microtherion Je suppose que cela devrait vraiment être + '0' ou | 0x30. Le coût de récursivité est équilibré dans une certaine mesure par la taille minuscule de la méthode, mais les limites sont des limites et doivent être respectées à coup sûr.


Ce Q&R a été automatiquement traduit de la langue anglaise.Le contenu original est disponible sur stackexchange, que nous remercions pour la licence cc by-sa 3.0 sous laquelle il est distribué.
Loading...