Ressources sur l'écriture de code C efficace pour les microcontrôleurs? [fermé]

15

Une aide sérieuse était nécessaire ici. J'adore la programmation. J'ai lu récemment un tas de livres (tels que K&R) et des articles / forums en ligne pour le langage C. Même essayé de regarder dans le code Linux (bien que, je ne savais pas par où commencer, mais jeter un œil dans les petites bibliothèques a aidé?).

J'ai commencé en tant que programmeur Java et en Java c'est assez simple et sec; Si les programmes deviennent trop gros, découpez-les en classes puis plus loin en fonctions. Des directives comme, gardez le code lisible et ajoutez des commentaires. Utilisez des techniques de masquage d'informations et de POO. Dont certains s'appliquent toujours pour C.

J'ai codé en C maintenant et jusqu'à présent, je fais fonctionner les programmes dans un sens ou dans l'autre. Beaucoup de gens parlent de performance / efficacité, algorithme / conception, optimisation et maintenabilité. Certaines personnes sont plus stressantes les unes que les autres, mais pour les ingénieurs logiciels non professionnels, vous entendez souvent quelque chose comme par exemple: le développement du noyau Linux ne prendra pas n'importe quel code.

Ma question est la suivante: je prévois d'écrire un code pour microcontrôleur 8 bits sans gaspiller aucune ressource . Sachez que je viens du milieu java donc les choses ne sont plus les mêmes ... les ressources / livres / liens / astuces seront très appréciés. Les performances et la taille comptent désormais. Ressources / Astuces sur le code C efficace (dans les meilleures pratiques) pour les micro-contrôleurs 8 bits?

En outre, inline assemblyjoue un rôle essentiel et coller près de standards micro-contrôleurs. Mais y a-t-il une règle générale pour l'efficacité qui s'applique à tous?

Par exemple: register unsigned int variable_name;est préféré à chartout moment. Ou utilisez uint8_t si vous n'avez pas besoin de grands nombres.

EDIT: Merci beaucoup pour toutes les réponses et suggestions. J'apprécie les efforts de chacun pour partager les connaissances.

As de pique
la source
2
Ce n'est pas souvent le cas sur un processeur x86, mais sur un microcontrôleur, si vous voulez vous assurer d'obtenir la dernière goutte de performances, vous voudrez probablement utiliser l'assembly à la place.
Rei Miyasaka
3
@Rei: l'assemblage artisanal utilise rarement, voire jamais, moins de mémoire et est plus rapide que les compilateurs C modernes. C'est une perte de temps de coder en assembleur quand cela peut (comme il se doit) être fait en C.
mattnz
1
@mattnz By modern, de quoi parlez-vous? En toute honnêteté, je n'ai pas écrit de code pour un microcontrôleur depuis près d'une décennie.
Rei Miyasaka
2
Une astuce simple: en microncontrôlés, il est généralement vrai que "si c'est plus simple, c'est plus rapide". Sur les puces complexes (ARM et plus), le matériel fait tellement d'optimisations que vous ne savez jamais jusqu'à ce que vous testiez.
Javier
1
@ReiMiyasaka à une augmentation considérable des coûts de maintenance. Un bon compilateur C peut produire presque le même code qu'un programmeur hautement expérimenté.

Réponses:

33

J'ai plus de 20 ans de systèmes embarqués, principalement 8 et 16 micros. La réponse courte à votre question est la même que pour tout autre développement logiciel - n'optimisez pas avant de savoir que vous en avez besoin, puis n'optimisez pas tant que vous ne savez pas ce que vous devez optimiser. Écrivez votre code afin qu'il soit fiable, lisible et maintenable en premier. L'optimisation prématurée est autant, sinon plus, un problème dans les systèmes embarqués

Lorsque vous programmez «sans gaspiller aucune ressource», considérez-vous votre temps comme une ressource? Sinon, qui vous paie pour votre temps, et si personne, avez-vous quelque chose de mieux à faire avec cela. Une fois que le choix d'un concepteur de système embarqué est le coût du matériel par rapport au temps d'ingénierie. Si vous expédiez 100 unités, utilisez un micro plus gros, à 100 000 unités, une économie de 1,00 $ par unité équivaut à 1 année-homme de développement logiciel (en ignorant le temps de mise sur le marché, le coût d'opportunité, etc.), à 1 million d'unités, vous commencez obtenir un retour sur investissement pour être obsédé par l'utilisation des ressources, mais soyez prudent car de nombreux projets intégrés n'ont jamais atteint le million car ils ont conçu pour vendre 1 million (investissement initial élevé avec un faible coût de production) et ont fait faillite avant d'y arriver.

Cela dit, les choses que vous devez prendre en compte et prendre en compte avec les (petits) systèmes embarqués, car ceux-ci arrêteront son fonctionnement, de manière inattendue, et pas seulement le ralentiront.

a) Pile - vous n'avez généralement qu'une petite taille de pile et des tailles de cadre de pile souvent limitées. Vous devez être conscient de l'utilisation de votre pile à tout moment. Soyez averti, les problèmes de pile provoquent certains des défauts les plus insidieux.

b) Tas - encore une fois, de petites tailles de tas, donc faites attention à l'allocation de mémoire injustifiée. La fragmentation devient un problème. Avec ces deux, vous devez savoir ce que vous faites lorsque vous manquez - cela ne se produit pas sur un grand système en raison de la pagination fournie par le système d'exploitation. ie quand malloc retourne NULL, vérifiez-vous cela et que faites-vous. Chaque mauve a besoin d'un chèque et d'un gestionnaire, un bloat de code ?. À titre indicatif - ne l'utilisez pas s'il existe une alternative. La plupart des petits systèmes n'utilisent pas de mémoire dynmaique pour ces raisons.

c) Interruptions matérielles - Vous devez savoir comment les gérer de manière sûre et en temps opportun. Vous devez également savoir comment créer un code de retour sécurisé. Par exemple, les bibliothèques standard C ne sont généralement pas réentrantes, elles ne doivent donc pas être utilisées dans les gestionnaires d'interruption.

d) Assemblage - optimisation presque toujours prématurée. Au plus, une petite quantité (en ligne) est nécessaire pour réaliser quelque chose que C ne peut tout simplement pas faire. Comme exercice, écrivez une petite méthode dans un assemblage artisanal (à partir de zéro). Faites de même en C. Mesurez les performances. Je parie que le C sera plus rapide, je sais qu'il sera plus lisible, maintenable et extensible. Maintenant, pour la partie 2 de l'exercice - écrivez un programme utile dans l'assemblage et C.
Comme autre exercice, regardez combien de Linux kernal est assembleur, puis lisez le paragraphe ci-dessous sur le noyau linux.

Il vaut la peine de savoir comment le faire, il peut même être utile de maîtriser les langues pour un ou deux micros communs.

e) "register unsigned int variable_name", "register" est, et a toujours été, un indice pour le compilateur, pas une instruction, au début des années 70 (il y a 40 ans), cela avait du sens. En 2012, c'est un gaspillage de frappes car les compilateurs sont si intelligents et les instructions micros sont si complexes.

Revenons à votre commentaire Linux - le problème que vous avez ici est que nous ne parlons pas d'un simple million d'unités, nous parlons de centaines de millions, avec une durée de vie de toujours. Le temps et le coût d'ingénierie pour l'obtenir aussi optimal que possible humainement en valent la peine. Bien que ce soit un bon exemple de la meilleure pratique d'ingénierie, ce serait un suicide commercial pour la plupart des développeurs de systèmes embarqués d'être aussi pédants que l'exige le noyau Linux.

mattnz
la source
4
mattnz: c'est l'une des plus belles réponses sur les sites .stackexchange.
Ahmed Masud
1
Je ne peux pas améliorer cette réponse. Je pourrais seulement ajouter que l'insertion de code d'assembly n'a que rarement de sens pour les performances, mais cela pourrait avoir du sens pour des choses comme piquer des puces d'E / S ou d'autres astuces matérielles qui pourraient ne pas être faciles à faire en C.
Mike Dunlavey
@mattnz Merci pour la bonne réponse. +1
AceofSpades
1
L'assembleur @MikeDunlavey est parfois nécessaire pour un timing exact. Je viens de terminer une superposition vidéo qui utilise le bit banging pour générer de la vidéo NTSC sur une broche d'E / S, le timing est en termes de - tension élevée pour 3 cycles d'horloge, puis faible pour 6 puis ....
Martin Beckett
@Martin: C'est parfaitement logique. Cela fait longtemps que je n'ai pas codé à ce niveau.
Mike Dunlavey
3

Votre question ("sans gaspillage de ressources") est trop générale, il est donc difficile de donner beaucoup de conseils. Pris littéralement, si vous ne voulez pas gaspiller des ressources, vous devriez peut-être prendre du recul et évaluer si vous avez besoin de faire quoi que ce soit, c'est-à-dire si vous pouvez résoudre le problème par d'autres moyens.

De plus, les conseils utiles dépendent beaucoup de vos contraintes - quel type de système construisez-vous et quel type de CPU utilisez-vous? Est-ce un système difficile en temps réel? De combien de mémoire disposez-vous pour le code et les données? Prend-il en charge nativement toutes les opérations C (notamment la multiplication et la division) et pour quels types? Plus généralement: lisez l'intégralité de la fiche technique et comprenez -la.

Le conseil le plus important: restez simple.

Par exemple: oubliez les structures de données complexes (hachages, arbres, éventuellement même listes liées) et utilisez des tableaux de taille fixe. L'utilisation de structures de données plus complexes n'est garantie qu'après avoir prouvé par mesure que les tableaux sont trop lents.

Aussi, ne pas trop concevoir (ce que les développeurs Java / C # ont tendance à faire): écrivez du code procédural simple, sans trop de couches. L'abstraction a un coût!

Soyez à l'aise avec l'idée d'utiliser des variables globales et goto [très utile pour les nettoyages en l'absence d'exceptions];)

Si vous devez faire face à des interruptions, lisez à propos de la réentrance. L'écriture de code réentrant est très simple.

zvrba
la source
3

Je suis d'accord avec la réponse de mattnz - pour la plupart. J'ai commencé à programmer sur le 8085 il y a plus de 30 ans, puis sur le Z80, puis j'ai rapidement migré vers le 8031. Après cela, je suis passé aux microcontrôleurs de la série 68300, puis 80x86, XScale, MXL et (récemment) PICS 8 bits, que j'ai suppose que j'ai bouclé la boucle. Pour mémoire, je peux affirmer que les FAE de plusieurs grands fabricants de microprocesseurs utilisent encore l'assembleur, bien que de manière orientée objet pour une réutilisation de code ciblée.

Ce que je ne vois pas dans la réponse approuvée, c'est une discussion sur le type de processeur cible et / ou l'architecture proposée. Est-ce un amer de 0,50 $ 8 avec une mémoire limitée? Est-ce un noyau ARM9 avec pipelining et 8Meg de flash? Coprocesseur de gestion de la mémoire? Aura-t-il un OS? Une boucle while (1)? Un appareil grand public avec une production initiale de 100 000 unités? Une start-up avec de grandes idées et des rêves?

Bien que je convienne que les compilateurs modernes font un excellent travail d'optimisation, je n'ai jamais travaillé sur un projet en 30 ans où je n'ai pas arrêté le débogueur et vu le code d'assemblage généré pour voir exactement ce qui se passait sous le capot ( certes un cauchemar lorsque le pipelining et l'optimisation entrent en jeu), donc la connaissance de l'assemblage est importante.

Et je n'ai jamais eu de PDG, vice-président de l'ingénierie, client qui ne m'a pas poussé à essayer de fourrer un gallon dans un conteneur de quart, ou d'économiser 0,05 $ en utilisant une solution logicielle pour résoudre un problème matériel (hé c'est juste un logiciel n'est-ce pas?). L'optimisation de la mémoire (code ou données) comptera toujours.

Mon point est que si vous visualisez le projet d'un point de vue de programmation pur, vous obtiendrez une solution de portée plus étroite. Mattnz a raison - faites-le fonctionner, puis faites-le fonctionner plus rapidement, plus petit, mieux, mais vous devez encore passer BEAUCOUP de temps sur les exigences et les livrables avant même de penser au codage.

Gio
la source
Salut Gio, évitez le HTML inutile dans vos messages et utilisez plutôt la syntaxe Markdown . Car <br />vous pouvez simplement appuyer sur Entrée, et pour les paragraphes, laissez simplement une ligne vide entre eux. De plus, lorsque vous faites référence à une autre réponse, veuillez y ajouter un lien. À ce stade, il ne peut y avoir que quelques réponses, mais il pourrait y en avoir plus, réparties sur plusieurs pages, et il ne sera pas très clair de quelle réponse vous parlez. Consultez l' historique des révisions pour voir mes modifications.
yannis
@Gio Merci d'avoir mentionné d'autres facteurs importants. +1 :)
AceofSpades
+1 - Belle extension de ma réponse.
mattnz
1

La réponse de Manttz met très bien les points les plus clés sur la façon de faire une programmation "proche du matériel". C'est après tout ce à quoi C est destiné.

Cependant, je voudrais ajouter que bien que le mot-clé strict de "Classe" n'existe pas en C - il est assez simple de penser en termes de programmation orientée objet en C même lorsque vous êtes proche du matériel.

Vous pouvez considérer cette réponse: OO meilleures pratiques pour les programmes C qui explique ce point.

Voici quelques ressources qui vous aideront à écrire du bon code orienté objet en C.

une. Programmation orientée objet en C
b. c'est un bon endroit où les gens échangent des idées
c. et voici le livre complet

Une autre bonne ressource que je voudrais vous suggérer est:

La série Write Great Code . Il s'agit d'un livre en deux volumes. Le premier livre couvre des aspects très essentiels des machines des travaux de niveau inférieur. Le deuxième livre aborde le thème "Penser à bas niveau - écrire à haut niveau"

Dipan Mehta
la source
1

Vous avez quelques problèmes. voulez-vous d'abord que ce projet / code soit portable? La portabilité vous coûte des performances et de la taille, votre plate-forme de choix et la tâche que vous implémentez peuvent-elles tolérer une taille excessive et des performances inférieures?

Oui, absolument sur une machine 8 bits renvoyant des caractères non signés au lieu de caractères ou de shorts non signés, est un moyen d'améliorer les performances et la taille. De même sur une machine 16 bits, utilisez des shorts non signés et une machine 32 bits non signée. Vous pouvez assez facilement voir que si, par exemple, vous avez utilisé des entiers non signés partout pour la portabilité à travers le système qui prend le relais (ARM par exemple, poussant son chemin vers le bas sur les marchés de plus petite puissance, les plus petits appareils), ce code est un énorme porc de rom. un micro 8 bits. Vous pouvez bien sûr simplement utiliser unsigned sans int ou short ou char et laisser le compilateur choisir la taille optimale.

Pas seulement l'assemblage en ligne, mais le langage d'assemblage en général. L'assemblage en ligne est très non portable et plus difficile à écrire que d'appeler simplement une fonction asm. oui, vous gravez la configuration et le retour d'appel, mais au prix d'un développement plus facile, d'une meilleure maintenance et d'un meilleur contrôle. La règle s'applique toujours, ne l'écrivez en asm que si vous en avez vraiment besoin, avez-vous fait le travail pour conclure que la sortie du compilateur est votre problème dans ce domaine et quel gain de performance vous pouvez obtenir en le faisant à la main. Revenons ensuite à la portabilité et à la maintenance, dès que vous commencez à mélanger C et asm, et chaque fois que vous mélangez C et asm, vous risquez de nuire à votre portabilité et de rendre le projet moins maintenable selon qui d'autre y travaille ou si cela est un produit que vous développez maintenant et que quelqu'un d'autre doit maintenir en cours de route. Une fois que vous avez fait cette analyse, vous savez automatiquement si vous devez aller en ligne ou avec un assemblage droit. J'ai plus de 25 ans dans le domaine, j'écris des mélanges C et asm tous les jours, je vis sur / au niveau matériel / logiciel et, bien, je n'utilise jamais asm en ligne. Cela en vaut rarement la peine, trop spécifique au compilateur, j'écris du code non spécifique au compilateur dans la mesure du possible (presque partout).

La clé de toute votre question est de démonter votre code C. Apprenez ce que le compilateur fait avec votre code, et avec le temps, si vous le souhaitez, vous pouvez apprendre à manipuler le compilateur pour générer le code que vous voulez sans avoir à recourir à asm autant. Avec plus de temps, vous pouvez apprendre à manipuler le compilateur pour produire du code efficace sur plusieurs cibles, ce qui rend le code plus portable sans avoir à recourir à asm. Vous ne devriez pas avoir de problème à comprendre pourquoi le caractère non signé fonctionne mieux comme retour d'état d'une fonction que non signé sur un micro 8 bits, de même le caractère non signé devient plus coûteux sur les systèmes 16 et 32 ​​bits (certaines architectures vous aident dehors, certains ne le font pas).

Certains microcontrôleurs 8 bits, tous?, Sont très peu conviviaux pour le compilateur et aucun compilateur ne produit un bon code. Il n'y a pas assez de demande pour créer un marché de compilateur pour ces appareils afin de créer un excellent compilateur pour ces cibles, de sorte que les compilateurs qui sont là sont là pour attirer plus de programmeurs commerciaux non-asm, et non pas parce que le compilateur est meilleur qu'un asm écrit à la main. . les bras et les mips qui entrent dans ce monde changent ce modèle car vous avez des cibles qui ont des compilateurs qui ont fait beaucoup de travail, des compilateurs qui produisent du bon code, etc. Pour les micros avec des processeurs comme ça, vous avez bien sûr encore dans le cas où vous devez descendre dans asm, mais ce n'est pas aussi fréquent, il est beaucoup plus facile de simplement dire au compilateur ce que vous voulez qu'il fasse que de ne pas l'utiliser. Notez que la manipulation d'un compilateur n'est pas un code laid et illisible, en fait, c'est le contraire, un code simple et propre, peut-être en réorganisant quelques éléments. Contrôler la taille de vos fonctions et le nombre de paramètres, ce genre de chose fait une énorme différence dans la sortie du compilateur. Évitez les fonctionnalités ghee-whiz du compilateur ou du langage, KISS, restez simple et stupide, produit souvent un code bien meilleur et plus rapide.

old_timer
la source
Vous ne précisez pas le type de produits que vous produisez, je suppose que c'est soit un volume très élevé, une marge faible ou un marché de niche spécifique avec des marges folles. Ceci est essentiel pour décider au niveau de l'entreprise si vous devez utiliser un petit assembleur micro et artisanal 8 bits, ou un micro plus grand et C. En réponse à votre commentaire (supprimé?) - Je travaille avec des micros 8 bits, mais nous commencer avec un micro plus grand que nécessaire et réviser à la baisse uniquement lorsque et si le coût de la nomenclature devient un problème.
mattnz