Wiki » Historique » Révision 103
Révision 102 (Patrice Nadeau, 2023-12-31 12:06) → Révision 103/169 (Patrice Nadeau, 2023-12-31 12:06)
# 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 * Être de style C (/* ... */) * En minuscules et commencer par une majuscule * En français * Précéder l’élément à documenté 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` * Les « décorations » (gras, italique, etc.) sont faites avec la syntaxe *Markdown* * Italique Gras : `*` ou `_` * Gras Italique : `**` 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 : * En minuscule * Un préfixe de 8 caractères maximum * Un des suffixe (extensions) suivants : * `.h` : entête * `.c` : sources * Contient une section Doxygen : * `@file` * `@brief` * `@version` * `@date` * `@author` * `@copyright` * Les fichier d’entête contiennent en plus * Une section Doxygen « mainpage » * Une définition macro pour éviter de ré-inclure le fichier. ### Sources ### Entêtes ### Autres 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 * Comporter au maximum **31** caractères * Être séparées par des traits de soulignement si comporte plusieurs mots * Exceptions : * Fonction et variables DOIVENT * Être en minuscule * Macros, constantes et `#define` DOIVENT * Être en majuscule 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> ### 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*, où *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. 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 est initialisée * @param[in] nb Timer number. @n Valeurs possibles : * − @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 N/A **/ static bool is_timer_set(uint8_t nb); ``` ## Items déconseillés et retirés Les fonctions et variables 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. 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)); ``` ## 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, comme 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) { ... } ... ```