L’auteur a terminé son projet d’ingénierie de dernière année avec les microcontrôleurs dsPic, acquérant ainsi une connaissance approfondie de ces dispositifs.
Le code en langage C d’un microcontrôleur peut nécessiter une optimisation dans certaines applications avancées. Cette optimisation du code est pratiquée pour réduire deux choses cruciales :
- Taille des codes : Les microcontrôleurs peuvent stocker des données et des instructions limitées en raison de la taille limitée de leur RAM. Par conséquent, le code doit être optimisé, de sorte que la mémoire d’instructions et de données disponible puisse être utilisée de la manière la plus efficace.
- Temps d’exécution du code : Les microcontrôleurs sont des dispositifs séquentiels qui exécutent une instruction à la fois. Chaque instruction assembleur consomme un certain nombre de cycles d’horloge pour s’exécuter. Par conséquent, le code doit être optimisé pour s’assurer qu’il exécute la tâche requise en un minimum de cycles d’horloge ou d’instructions d’assemblage. Moins un code utilise de cycles d’horloge, plus il s’exécute rapidement. Cela signifie que les applications peuvent s’exécuter plus rapidement car les temps de traitement sont réduits au minimum.
Utilisateur de Pixabay – StockSnap
Cet article présente des trucs et astuces qui peuvent être employés pour réduire la taille et le temps d’exécution d’un code de microcontrôleur.
L’IDE de développement MplabX de Microchip sera utilisé pour démontrer des exemples, le cas échéant.
Comment mesurer expérimentalement le temps d’exécution du code
Pour avoir une idée du temps que votre code met réellement à s’exécuter en temps réel, vous devez le mesurer expérimentalement. Un analyseur logique peut être utilisé de manière pratique pour mesurer le temps d’exécution du code et les personnes intéressées peuvent se renseigner sur le processus à suivre auprès de moi par e-mail. À côté de cette:
- Certains compilateurs ont la capacité de compter les cycles d’horloge qu’un code consommera.
- Certains débogueurs, par exemple l’ICD 3 de microchip, peuvent mesurer directement le temps d’exécution via un chronomètre.
1. Connaître la puissance de traitement et la taille de la mémoire de votre microcontrôleur
Ce n’est pas toujours la fréquence d’horloge (Mhz) qui donne la vraie image de la vitesse de traitement d’un microcontrôleur, une mesure plus réaliste est MIPS (méga instructions par seconde) ou le nombre d’instructions que le MCU peut exécuter en une seconde.
Les MCU vont généralement de 60 à 70 MIPS dans la catégorie haut de gamme à 20 MIPS 8 bits AVR. Un microcontrôleur MIPS élevé est susceptible d’être plus cher qu’un appareil bas de gamme, vous avez donc ici un compromis entre le coût et la vitesse de traitement.
Les microcontrôleurs ont une mémoire séparée pour stocker les données et le code de programme. La taille des deux peut être trouvée dans la fiche technique. Vous aurez peut-être besoin d’un MCU avec une plus grande taille de mémoire si votre code est considérablement volumineux.
Utilisateur de Pixabay – David Degliame
2. Choix des variables pour l’optimisation de la taille du code
Les microcontrôleurs ont une mémoire de données limitée, généralement comprise entre 1 et 4 Ko. Dans ce cas, il est judicieux de choisir le type de variable le plus approprié en fonction de la plage attendue de la date à stocker. Le tableau ci-dessous résume ces variables :
Exemple:
- Si deux variables X et Y doivent être ajoutées et que le résultat doit être stocké dans Z mais que la valeur de Z devrait être supérieure à 65 535 après addition, alors Z peut être déclaré long et X et Y peuvent être déclarés non signés int, les valeurs de X et Y ne devraient pas non plus devenir négatives. Cela permettra d’économiser 04 octets dans la mémoire de données qui auraient autrement été utilisés si toutes les variables devaient être déclarées aussi longues.
- Deux variables X et Y, dont les valeurs sont censées être des nombres entiers, doivent être divisées, mais le résultat de la division peut donner une décimale, puis X et Y peuvent être déclarés int et le résultat peut être déclaré float ou double selon la précision recherchée.
Faites défiler pour continuer
Le choix du type de données peut être crucial lors de la déclaration de tableaux contenant un grand nombre d’éléments.
3. Choix des variables pour l’optimisation du temps d’exécution du code
- C’est un fait établi que les calculs en virgule flottante prennent plus de temps que les calculs en virgule fixe. N’utilisez pas de variable à virgule flottante lorsqu’une valeur décimale n’est pas requise. Travaillez avec des entiers non signés dans la mesure du possible.
- Les variables locales sont préférées aux variables globales. Si une variable est utilisée uniquement dans une fonction, elle doit être déclarée dans cette fonction car l’accès aux variables globales est plus lent qu’aux variables locales.
- Un MCU 8 bits trouvera une variable de la taille d’un seul octet plus rapide d’accès et un MCU 16 bits trouvera une variable de 2 octets plus facile d’accès en raison de la longueur de l’adresse générée.
4. Optimisation des opérations arithmétiques
Les opérations arithmétiques peuvent être optimisées des manières suivantes.
- Utilisez des tables de correspondance de valeurs pré-calculées au lieu d’évaluer un sinus ou toute autre fonction trigonométrique ou toute autre opération dont le résultat peut être connu à l’avance dans le code.
- Dans le cas où une table de recherche de sinus est déjà stockée dans la mémoire, un cosinus peut être évalué en avançant le pointeur de tableau équivalent à 90 degrés.
- Parmi les quatre opérations arithmétiques, la division et la multiplication prennent le plus de temps de traitement, en pratique, cela peut être de l’ordre de centaines de microsecondes environ dans le cas de valeurs à virgule flottante.
- Utilisez les instructions de décalage de bits au lieu de la division et de la multiplication. Une instruction de décalage à droite >>3 sert à diviser par 23 alors qu’une instruction de décalage à gauche <<1 servira à multiplier par 21.
Propre travail
5. Utilisez un microcontrôleur compatible DSP pour les calculs intensifs
Certains microcontrôleurs ont une unité de traitement DSP autre que l’ALU conventionnelle intégrée dans leur architecture. Ce moteur DSP est conçu pour effectuer des calculs arithmétiques très rapidement dans le moins de cycles d’horloge (un dans la plupart des cas) plusieurs fois plus rapidement que l’ALU.
Les instructions qu’un processeur DSP peut exécuter plus rapidement qu’un ALU sont :
- Instructions de décalage et de rotation des bits.
- Multiplications, divisions et autres opérations arithmétiques.
- Évaluation des sinus et d’autres fonctions trigonométriques.
- Toutes les opérations DSP telles que FFT, DFT, convolution et filtrage FIR.
L’utilisation du moteur DSP d’un microcontrôleur nécessite que :
- Des bibliothèques DSP séparées sont incorporées dans le projet.
- Les noms des fonctions sont différents de la bibliothèque mathématique standard du langage C. La documentation de ces bibliothèques et fonctions peut être consultée sur le site Web des fabricants respectifs.
- Le moteur DSP utilise un type de variable différent « fractionnel ». Apprenez à utiliser les variables de type fractionnaire avant de continuer avec les fonctions de la bibliothèque dsp.
Notez que les fonctions de la bibliothèque mathématique standard n’invoqueront pas le moteur DSP car elles sont traduites en instructions d’assemblage ALU.
6. Travailler avec des interruptions
Utilisez des interruptions pour exécuter des fonctions spécifiques telles que :
- Lecture des valeurs ADC.
- Envoi et réception depuis UART.
- Mise à jour des registres de rapport cyclique PWM.
- Communication CAN ou I2C.
Les interruptions serviront ces fonctions rapidement par rapport à leur exécution dans le corps principal au moyen d’un appel de fonction ou d’un code en ligne.
Les interruptions ne se déclencheront également que si nécessaire, alors que si elles sont codées dans le corps principal, le code s’exécutera à chaque itération de la boucle while(1).
7. Utilisez les meilleurs compilateurs disponibles
Les compilateurs peuvent implémenter automatiquement certaines des optimisations décrites ci-dessus tout en traduisant le code du langage C en langage assembleur s’il est correctement configuré. Recherchez des options d’optimisation dans votre compilateur et, si possible, mettez à niveau vers des versions professionnelles des compilateurs, car ce sont des optimiseurs de code plus puissants.
8. Utilisez intelligemment les instructions conditionnelles
- Lorsque vous utilisez une série d’instructions if-else, conservez la condition la plus probable en premier. De cette façon, le MCU n’aura pas à parcourir toutes les conditions après avoir trouvé la véritable condition.
- Une instruction switch-case est généralement plus rapide qu’une instruction if-else.
- Utilisez des instructions if-else imbriquées à la place d’une série d’instructions. Un bloc if-else contenant de nombreuses instructions peut être divisé en sous-branches plus petites pour optimiser la condition la plus défavorable (dernière).
9.Utilisez les fonctions en ligne
Les fonctions qui ne doivent être utilisées qu’une seule fois dans le code peuvent être déclarées comme statiques. Cela obligera le compilateur à optimiser cette fonction en une fonction en ligne et, par conséquent, aucun code assembleur ne sera traduit pour l’appel de fonction.
- Une fonction peut être déclarée inline en utilisant le mot clé ‘static’ avec elle.
10. Utilisez des boucles décrémentées
Une boucle décrémentée générera moins de code assembleur qu’une boucle incrémentée.
En effet, dans une boucle d’incrémentation, une instruction de comparaison est nécessaire pour comparer l’indice de boucle avec la valeur maximale dans chaque boucle afin de vérifier si l’indice de boucle atteint la valeur maximale. Au contraire dans une boucle de décrémentation, cette comparaison n’est plus nécessaire car le résultat décrémenté de l’index de boucle mettra le drapeau zéro dans SREG s’il atteint zéro.
Propre travail.
Étant donné que la boucle doit itérer cent fois, la réduction d’une instruction de la boucle évitera qu’elle ne soit exécutée cent fois, de sorte que l’impact est susceptible d’être plus important lorsque la boucle doit itérer plusieurs fois.
Emballer
Ces conseils peuvent être utiles, mais leur véritable application et leur puissance dépendent de la compétence du programmeur et de la commande qu’il a sur son code. N’oubliez pas que la taille du programme ne détermine pas toujours les temps d’exécution, certaines instructions peuvent consommer plus de cycles d’horloge que les autres, donc encore une fois, les compétences du programme doivent jouer leur rôle.
Cet article est exact et fidèle au meilleur de la connaissance de l’auteur. Le contenu est uniquement à des fins d’information ou de divertissement et ne remplace pas un conseil personnel ou un conseil professionnel en matière commerciale, financière, juridique ou technique.
© 2017 StormsHalted
Eman Abdallah Kamel d’Egypte le 26 juillet 2018 :
Un article très instructif, merci.