Je suppose que c'est une mauvaise chose d'essayer de déboguer un projet basé sur un microcontrôleur en utilisant printf()
.
Je peux comprendre que vous n'avez pas d'endroit prédéfini pour la sortie et qu'il pourrait consommer des broches précieuses. En même temps, j'ai vu des gens consommer une broche UART TX pour la sortie vers le terminal IDE avec une DEBUG_PRINT()
macro personnalisée .
printf
, bien sûr, tout le code nécessaire à l'implémentationprintf
est lié à l'exécutable. Mais c'est parce que le code l'a utilisé, pas à cause de l'en-tête.Réponses:
Je peux trouver quelques inconvénients à utiliser printf (). Gardez à l'esprit que le "système embarqué" peut aller de quelque chose avec quelques centaines d'octets de mémoire de programme à un système QNX RTOS complet monté en rack avec des gigaoctets de RAM et des téraoctets de mémoire non volatile.
Cela nécessite un endroit pour envoyer les données. Peut-être que vous avez déjà un port de débogage ou de programmation sur le système, peut-être pas. Si vous ne le faites pas (ou celui que vous avez ne fonctionne pas), ce n'est pas très pratique.
Ce n'est pas une fonction légère dans tous les contextes. Cela pourrait être un gros problème si vous avez un microcontrôleur avec seulement quelques K de mémoire, car la liaison dans printf peut consommer 4K à elle seule. Si vous avez un microcontrôleur 32K ou 256K, ce n'est probablement pas un problème, et encore moins si vous avez un gros système embarqué.
Il est peu ou pas utile pour trouver certains types de problèmes liés à l'allocation de mémoire ou aux interruptions, et peut changer le comportement du programme lorsque des instructions sont incluses ou non.
C'est assez inutile pour traiter des choses sensibles au timing. Vous feriez mieux avec un analyseur logique et un oscilloscope ou un analyseur de protocole, ou même un simulateur.
Si vous avez un gros programme et que vous devez recompiler plusieurs fois en changeant les instructions printf et en les changeant, vous pourriez perdre beaucoup de temps.
Ce qu'il est bon - c'est un moyen rapide de sortir des données d'une manière préformatée que chaque programmeur C sait utiliser - courbe d'apprentissage zéro. Si vous avez besoin de cracher une matrice pour le filtre Kalman que vous déboguez, il pourrait être agréable de le cracher dans un format que MATLAB pourrait lire. Certainement mieux que de regarder les emplacements de la RAM un par un dans un débogueur ou un émulateur .
Je ne pense pas que ce soit une flèche inutile dans le carquois, mais elle devrait être utilisée avec parcimonie, avec gdb ou d'autres débogueurs, émulateurs, analyseurs logiques, oscilloscopes, outils d'analyse de code statique, outils de couverture de code, etc.
la source
printf()
implémentations ne sont pas thread-safe (c'est-à-dire non-rentrantes), ce qui n'est pas un tueur de transactions, mais quelque chose à garder à l'esprit lors de son utilisation dans un environnement multi-thread.En plus de quelques autres bonnes réponses, le fait d'envoyer des données à un port à des débits en bauds série peut être carrément lent en ce qui concerne le temps de votre boucle, et avoir un impact sur la façon dont le reste de votre programme fonctionne (tout comme n'importe quel débogage processus).
Comme d'autres personnes vous l'ont dit, il n'y a rien de "mauvais" à utiliser cette technique, mais elle a, comme beaucoup d'autres techniques de débogage, ses limites. Tant que vous connaissez et pouvez gérer ces limitations, cela peut être une option extrêmement pratique pour vous aider à obtenir votre code correctement.
Les systèmes embarqués ont une certaine opacité qui, en général, rend le débogage un peu problématique.
la source
Il y a deux problèmes principaux que vous rencontrerez en essayant d'utiliser
printf
sur un microcontrôleur.Tout d'abord, il peut être difficile de diriger la sortie vers le bon port. Pas toujours. Mais certaines plateformes sont plus difficiles que d'autres. Certains fichiers de configuration peuvent être mal documentés et beaucoup d'expérimentation peuvent être nécessaires.
Le second est la mémoire. Une
printf
bibliothèque complète peut être GRANDE. Parfois, vous n'avez pas besoin de tous les spécificateurs de format et des versions spécialisées peuvent être disponibles. Par exemple, lestdio.h
fourni par AVR contient trois différentsprintf
de tailles et fonctionnalités différentes.J'avais une instance où aucune bibliothèque n'était disponible et j'avais peu de mémoire. Je n'ai donc pas eu d'autre choix que d'utiliser une macro personnalisée. Mais l'utilisation de
printf
ou non fait vraiment partie de vos besoins.la source
Pour ajouter à ce que Spehro Pefhany disait à propos des "choses sensibles au timing": prenons un exemple. Disons que vous avez un gyroscope à partir duquel votre système embarqué prend 1 000 mesures par seconde. Vous souhaitez déboguer ces mesures, vous devez donc les imprimer. Problème: les imprimer entraîne une surcharge du système pour lire 1 000 mesures par seconde, ce qui provoque un débordement de la mémoire tampon du gyroscope, ce qui provoque la lecture (et l'impression) de données corrompues. Et donc, en imprimant les données, vous avez corrompu les données, ce qui vous fait penser qu'il y a un bug dans la lecture des données alors qu'il n'y en a peut-être pas. Un soi-disant heisenbug.
la source
La raison principale pour ne pas déboguer avec printf () est qu'il est généralement inefficace, inadéquat et inutile.
Inefficace: printf () et kin utilisent beaucoup de mémoire flash et de RAM par rapport à ce qui est disponible sur un petit microcontrôleur, mais l'inefficacité la plus importante réside dans le débogage réel. Changer ce qui est enregistré nécessite de recompiler et de reprogrammer la cible, ce qui ralentit le processus. Il utilise également un UART que vous pourriez autrement utiliser pour effectuer un travail utile.
Insuffisant: il n'y a que peu de détails que vous pouvez produire sur une liaison série. Si le programme se bloque, vous ne savez pas exactement où, juste la dernière sortie terminée.
Inutile: de nombreux microcontrôleurs peuvent être débogués à distance. JTAG ou des protocoles propriétaires peuvent être utilisés pour mettre le processeur en pause, consulter les registres et la RAM, et même modifier l'état du processeur en cours d'exécution sans avoir à recompiler. C'est pourquoi les débogueurs sont généralement un meilleur moyen de débogage que les instructions d'impression, même sur un PC avec beaucoup d'espace et de puissance.
Il est regrettable que la plate-forme de microcontrôleur la plus courante pour les débutants, Arduino, ne dispose pas d'un débogueur. L'AVR prend en charge le débogage à distance, mais le protocole debugWIRE d'Atmel est propriétaire et non documenté. Vous pouvez utiliser un tableau de développement officiel pour déboguer avec GDB, mais si vous l'avez, vous n'êtes probablement plus trop inquiet pour Arduino.
la source
printf () ne fonctionne pas seul. Il appelle de nombreuses autres fonctions, et si vous avez peu d'espace de pile, vous ne pourrez peut-être pas du tout l'utiliser pour déboguer des problèmes proches de votre limite de pile. Selon le compilateur et le microcontrôleur, la chaîne de formatage peut également être placée en mémoire, plutôt que référencée à partir de la mémoire flash. Cela peut s'additionner considérablement si vous poivrez votre code avec des instructions printf. C'est un gros problème dans l'environnement Arduino - les débutants utilisant des dizaines ou des centaines d'instructions printf rencontrent soudainement des problèmes apparemment aléatoires car ils écrasent leur tas avec leur pile.
la source
Même si l'on veut cracher des données vers une certaine forme de console de journalisation, la
printf
fonction n'est généralement pas un très bon moyen de le faire, car elle doit examiner la chaîne de format passée et l'analyser au moment de l'exécution; même si le code n'utilise jamais d'autre spécificateur de format que%04X
, le contrôleur devra généralement inclure tout le code qui serait nécessaire pour analyser les chaînes de format arbitraires. Selon le contrôleur exact que l'on utilise, il peut être beaucoup plus efficace d'utiliser du code comme:Sur certains microcontrôleurs PIC, cela
log_hexi32(l)
prendrait probablement 9 instructions et pourrait en prendre 17 (s'il sel
trouve dans la deuxième banque), tandis quelog_hexi32p(&l)
cela prendrait 2. Lalog_hexi32p
fonction elle-même pourrait être écrite pour avoir environ 14 instructions, donc elle se paierait si elle était appelée deux fois .la source
Un point qu'aucune des autres réponses n'a mentionné: dans un micro de base (IE, il n'y a que la boucle principale () et peut-être quelques ISR en cours d'exécution à tout moment, pas un système d'exploitation multithread) s'il se bloque / s'arrête / obtient coincé dans une boucle, votre fonction d'impression ne se produira tout simplement pas .
De plus, les gens ont dit "n'utilisez pas printf" ou "stdio.h prend beaucoup d'espace" mais pas beaucoup d'alternative - embedded.kyle fait mention d'alternatives simplifiées, et c'est exactement le genre de chose que vous devriez probablement être faire comme une évidence sur un système embarqué de base. Une routine de base pour injecter quelques caractères hors de l'UART pourrait être de quelques octets de code.
la source