Wiki » Historique » Révision 150
Révision 149 (Patrice Nadeau, 2024-01-06 10:30) → Révision 150/170 (Patrice Nadeau, 2024-01-06 10:54)
# Règles de codage C Le langage C, version [C99] (https://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf) utilisé avec le compilateur [GCC](https://gcc.gnu.org/). > `gcc` n'est pas entièrement compatible avec le standard C99 (<https://gcc.gnu.org/c99status.html>). --- {{>toc}} --- ## Style Le code DOIT : * Être dans le style [K&R](https://fr.wikipedia.org/wiki/Style_d%27indentation#Style_K&R) avec la variante *one true brace style* (1TBS): * L’indentation est de 4 espaces * Le « backslash » est utilisé pour les lignes de plus de 80 caractères * Une instruction par ligne * Une espace avant et après un opérateur sauf pour les opérateurs « [unaires](https://fr.wikipedia.org/wiki/Op%C3%A9ration_unaire) » * Les commentaires DOIVENT * Précéder l’élément à documenté * Être de style C (/* ... */), sur une ou plusieurs lignes * En minuscules et commencer par une majuscule * En français Justifications : * [K&R](https://fr.wikipedia.org/wiki/Style_d%27indentation#Style_K&R) * Prévient les erreurs lors d'ajout dans les boucles n'ayant qu'une instruction comme bloc * Support ASCII 7-bits * Correspondance avec la fiche technique (datasheet) * [Loi sur la langue officielle et commune du Québec, le français](https://www.publicationsduquebec.gouv.qc.ca/fileadmin/Fichiers_client/lois_et_reglements/LoisAnnuelles/fr/2022/2022C14F.PDF) Exemple : ``` c int fonction(void) { int x; if (var != 1) { x = x + 1; y++; /* Longue ligne */ printf("This is a long\ line that should be splitted"); } else { x--; }; return 0; } ``` ## Commentaires Doxygen La documentation est faite a l'aide de commentaires [Doxygen](https://www.doxygen.nl/) dans la déclaration de tous les objets ayant une visibilité publique ainsi que pour les macros. * Dans le format *Javadoc* (`/** */`) * Au minimum, les items suivants doivent être présents : * `@brief` * Facultatifs * `@sa` : *See also* * `@todo` : * `@bug` : * Les « décorations » (gras, italique, etc.) sont faites avec la syntaxe *Markdown* * Italique : `*` ou `_` * Gras : `**` ou `__` * La gradations des notes et remarques se fait selon : * `@remark` : Non importante * `@note` : Général * `@attention` : Important * `@warning` : Conséquence négative Exemple : ``` c /** * @brief Compteur global * @warning Note conséquence négative */ int ctr; ``` ## Fichiers Le nom des fichiers DOIT être composé de la manière suivante : * Un préfixe de 8 caractères maximum * avec seulement des lettres minuscule, chiffres et soulignement * Un des suffixe suivants : * `.h` : entête * `.c` : sources * Contient une section Doxygen : * `@file` * `@brief` * `@version` * `@date` * `@author` * `@copyright` * Les fichiers d’entête contiennent en plus * Une définition macro pour éviter de ré-inclure le fichier. * Le fichier d’entête du projet contient en plus * Une section Doxygen « mainpage » Exemple : ```c #ifndef _test_h #define _test_h /** * @file : test.h * @brief Description * @version 0.00.01 * @date 2023-02-26 * @author Patrice Nadeau <pnadeau@patricenadeau.com> * @copyright 2023 Patrice Nadeau */ /** * @mainpage lcd * @brief ATMEL AVR 8-bit C librairie * @author Patrice Nadeau <pnadeau@patricenadeau.com> * @version 0.0.02 * @date 2023-03-27 * @pre AVR supportés (testés en gras) : * - ATmega88 * - ATmega168 * - **ATmega328P** * @copyright * @include{doc} LICENSE.txt */ ... #endif /*_usart.h*/ ``` --- ## Objets et macros Variables, fonctions et macros * Comportent au maximum **31** caractères * Si plusieurs mots sont utilisés, ils sont séparées par des traits de soulignement * Exceptions : * Fonction et variables DOIVENT * Être en minuscule > Si le mot est un acronyme, il doit être en majuscule * Macros et constantes DOIVENT * Être en majuscule * Les objets ne devant plus être utilisés, DOIVENT générer un message lors de la compilation (`-Wall`) si un appel est effectué. * Les attributs`deprecated` ou `unavailable` DOIVENT être ajoutés à la déclaration. * La documentation DOIT indiquer les substituts à utiliser. Justification : * Linux kernel coding style : <https://www.kernel.org/doc/html/v4.10/process/coding-style.html#naming> * GNU Coding Standards <https://www.gnu.org/prep/standards/html_node/Writing-C.html#Writing-C> * Embedded C Coding Standard : <https://barrgroup.com/embedded-systems/books/embedded-c-coding-standard> Exemple : ``` c /** * @brief OldFunction * @deprecated Utiliser NewFunction à la place * @since Version x.x.xx */ int OldFunction(void) __attribute__((deprecated)); /** * @brief OldFunction * @deprecated Utiliser NewFunction à la place * @since Version x.x.xx */ int OldFunction(void) __attribute__((unavailable)); ``` ### Déclarations locales Une déclaration n’ayant qu’une visibilité locale DOIT : * Être de classe `static` Exemple: ``` c /** * @brief Fonction locale * @return Une valeur */ static int local_func(void) { ... return 0; } ``` ### Constantes Utilisé au lieu d’une macro quand le type ou la visibilité de la variable doit être définis. Exemple : ``` c /** * @name Liste des constantes * @brief */ /** @{ */ /** @brief La chaîne d'initialisation du projet */ static const char INIT_STR[6] = "POWER"; /** @brief Constante globale de la librairie `random` */ extern int RANDOM_MAX = 25; /** @} */ /** @brief Constante */ const int ANSWER 42; ``` ### Énumérations DOIT être utilisée pour définir une série de valeurs. Exemple : ```c /** * @name Liste des valeurs STATUS * @brief * */ enum STATUS { /** @brief Le processus est OK */ STATUS_OK = 0, /** @brief Le processus est en cours d'initialisation */ STATUS_INIT, /** @brief Le processus est arrêté */ STATUS_HALTED }; ``` ### Typedef Format : * En minuscule, suivie de **_t** Exemple : ``` c /** Type de la structure dans la librairie `ds1305` */ typedef struct { /** @brief Dernier deux chiffres : ≥ 00, ≤ 99 */ uint8_t year; /** @brief 01 - 12 */ uint8_t month; /** @brief 01 - 31 */ uint8_t date; /** @brief 1 - 7 */ uint8_t day; /** @brief 00 - 23 */ uint8_t hours; /** @brief 00 - 59 */ uint8_t minutes; /** @brief 00 - 59 */ uint8_t seconds; } ds1305_time_t; ``` ### Variables Exemple : ``` c /** @brief Variable locale */ static int ctr; /** @brief Variable globale */ int RANDOM_CTR; ``` ### Structures Format * En minuscule, séparé par des «underscores» si nécessaire. Exemple : ``` c /** * @brief Structure d'un menu local * @see MenuSelect */ struct menu { /** @brief Caractère utilisé pour l'item */ char choice; /** @brief Description de l'item */ char *item; }; ``` ### Fonctions * Le nom DOIT être dans le format suivant : *Action***_***Item***_***Attribut* * *Action* signifie : * **set**, **get**, **clear** : Règle, obtient ou vide un registre * **read**, **write** : Lis ou écris dans un fichier * **init** : Fonction d’initialisation * **is** : Vérifie un état * **setup** : Fonction de configuration des ports (AVR) * Exceptions * Les fonctions définies dans une librairie de bas niveau pour du matériel (« driver ») devraient utiliser le nom définis dans la fiche technique. * Contient les champs Doxygen * `@brief` : Brève description de la fonction * `@param[in,out]` *paramètre* *Description* : Si nécessaire, sinon ne pas inclure le champ * `@arg` : Valeur prédéfinie d'un paramètre (`#`, `* *`), sinon ne pas inclure le champs * `@return` : Description de la valeur retournée, sinon le terme **Sans objet** * `@retval` : Si une valeur de retour est prédéfinie, une ligne pour chaque valeur, sinon ne pas inclure le champs * `@pre` : Chaque précondition, sur une ligne séparée, sinon le terme **Sans objet** * `@post` : Chaque postcondition, sur une ligne séparée, sinon le terme **Sans objet** * `@sa` : Si une référence a un autre objet doit être faite (#), sinon le terme **Sans objet** * Le bloc d'exemple, si nécessaire * `@par Example` * `@code` * ... * `@endcode` Une fonction DEVRAIT retourner une valeur. * Type entier (oui/non) : * Succès : **0** * Erreur : **1** * Type booléen (Librairie `<stdbool.h>`) * **true** * **false** * Pointeur : * **NULL** : Erreur * Autre valeur : adresse du pointeur Justification : * [AVR1000b](https://ww1.microchip.com/downloads/en/Appnotes/AVR1000b-Getting-Started-Writing-C-Code-for-AVR-DS90003262B.pdf) Exemple : ``` c /** * @brief Vérifie si une horloge est initialisée * @param[in] nb Le numéro du timer parmi * @arg #TIMER_1 * @arg #TIMER_2 * @return * @retval true Horloge *nb* est initialisée * @retval false Horloge *nb* n'est PAS initialisée * @pre init_timer * @post Sans objet **/ static bool is_timer_set(uint8_t nb); ``` ## Préprocesseur Directives du préprocesseur gcc. ### #include Pour inclure d’autres fichier comme les fichiers entête. ### #ifdef / ifndef Surtout utilisé pour des options de compilation sur différentes plateforme. Utiliser une forme évitant les répétitions. > N’est pas documenté dans Doxygen. Exemple : ```c const char BLUE = #if ENABLED(FEATURE_ONE) '1' #else '0' #endif ; ``` ### Diagnostiques Les macros `#warning` et `#error` sont utilisées pour afficher des avertissements ou des erreurs lors de la compilation. > Ne sont pas documentées dans Doxygen. Exemple : ``` c #ifndef usart_AVR #error "__FILE_NAME__ is not supported on this AVR !" #endif #ifndef __test__ #warning "test is not defined !" #endif ``` ### Définitions Un `#define` est utilisé pour remplacer une valeur au moment de la compilation > Pour la définition d'une valeur « integer », un `enum` DOIT être utilisé. Exemple : ``` c /** * @name Nom des registres */ /** @{ */ /** @brief USART1 */ #define USART1 REG1 /** @brief USART2 */ #define USART2 REG2 /** @} */ USART1 = 0x0F; ``` ## Atmel AVR Particularités pour les microcontrôleurs 8 bits AVR d’Atmel. [Atmel AVR4027: Tips and Tricks to Optimize Your C Code for 8-bit AVR Microcontrollers](https://ww1.microchip.com/downloads/en/AppNotes/doc8453.pdf) ### Fichier d’en-têtes Vérification du modèle de microcontrôleur > Via l'option `-m` de [gcc](https://github.com/embecosm/avr-gcc/blob/avr-gcc-mainline/gcc/config/avr/avr-mcus.def) ```c #ifndef defined (__AVR_ATmega48__) || (__AVR_ATmega48P__) || \ (__AVR_ATmega88P__) || defined (__AVR_ATmega88__) || \ (__AVR_ATmega168__) || defined (__AVR_ATmega168P__) || \ (__AVR_ATmega328__) || defined (__AVR_ATmega328P__) #warning "Cette librairie n'as pas été testée sur cette famille de microcontrôleur." #endif ``` ### Macros Définis dans le fichier `config.h` Liste : * `F_CPU` : La fréquence utilisée par l'horloge (interne ou externe) du microcontrôleur > Les « fuses » doivent correspondent à la bonne source de l'horloge. ### Types De nouveau type d'entier sont fournis avec la librairie `<stdint.h>`. L'utilisation de ces types DOIT être utilisé afin d'exprimer le nombre de bit d'un objet. ### Progmem <https://www.avrfreaks.net/s/topic/a5C3l000000U5SFEA0/t034767> Pour mettre des variables en lecture seule dans la section FLASH au lieu de SRAM avec `<avr/pgmspace.h>`. > L’accès à ces variables est faite via les macros de la librairie. Le nom de la variable DOIT être suivie de **_P** Exemple : ```c #include <avr/pgmspace.h> ... /** @brief Variable en FLASH */ const int Variable1_P PROGMEM = 42; ``` ### Fonction main Un microcontrôleur AVR ne termine jamais la fonction `main`. * Déclarer la fonction main avec l’attribut `noreturn` * La boucle sans fin la plus optimisé est le `for (;;)` Justification : [AVR035](https://ww1.microchip.com/downloads/en/AppNotes/doc1497.pdf) Exemple : ```c #include <avr/io.h> /** * @brief Never ending loop */ void main(void) __attribute__((noreturn)); /* main function definition */ void main(void) { ... /* never return */ for (;;) { }; }; ``` ### Opérations « atomiques » Opérations ne devant pas être interrompus (Ex. : charger un registre de 16 bits avec un registre de 8 bits). La librairie `avr-libc` (util/atomic.h) fournit des macros permettant la gestion entre autre des interruptions. Les instructions critiques sont insérées dans un `ATOMIC_BLOCK`. Exemple : ```c #include <util/atomic.h> ... ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { ... } ... ```