Wiki » Historique » Révision 55
Révision 54 (Patrice Nadeau, 2023-09-03 16:51) → Révision 55/170 (Patrice Nadeau, 2023-09-03 17:12)
# Règles de codage C Le langage C, version C99 (ISO/IEC 9899:1999) 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 fonctions, variables, constantes et `#define` DOIVENT être en [anglais américain](https://fr.wikipedia.org/wiki/Anglais_am%C3%A9ricain) * Les commentaires DOIVENT * Être de style C (/* ... */) * En minuscules et commencer par une majuscule * En français * Précéder l’élément à documenté * La documentation est faite a l'aide de commentaires [Doxygen](https://www.doxygen.nl/) : * Chaque objet (fonctions, variables, etc.) DOIT être commenté/documenté : * Dans le format [Javadoc](https://www.doxygen.nl/manual/docblocks.html) (/** */) * Les « décorations » (gras, italique, etc.) sont faites avec la syntaxe Markdown 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 /** * @brief Fonction principale * @return Une valeur * @remark Note non importante * @note Note générale * @attention Note importante * @warning Note conséquence négative */ int fonction(void) { int x; if (var != 1) { x = x + 1; y++; printf("This is a long\ line that should be splitted"); } else { x--; }; return 0; } ``` ## Fichiers Le nom des fichiers DOIT être composé de la manière suivante : * En minuscule * 8 caractères maximum * L'extension est * `.h` pour les fichiers d’entête * `.c` pour les fichiers sources * Contient une section Doxygen « file » * Les fichier d’entête contiennent en plus * Une section Doxygen « mainpage » * Une définition macro DOIT être faite pour éviter de ré-inclure le fichier. 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*/ ``` ## Noms des objets Convention de noms En général : *librairie*\_*action*\_*sujet* * 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 Local function **/ static int local_func(void) { ... return 0; } ``` ## 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`__attribute__((deprecated))` ou `__attribute__((unavailable))` DOIVENT être ajoutés à la déclaration. * La documentation DOIT indiquer les substituts à utiliser. Exemple : ``` c /** * @brief OldFunction * @deprecated Use NewFunction instead * @since Version x.x.xx */ int OldFunction(void) __attribute__((deprecated)); /** * @brief OldFunction * @deprecated Use NewFunction instead * @since Version x.x.xx */ int OldFunction(void) __attribute__((unavailable)); ``` ## Constantes Utilisé au lieu d’une macro quand le type ou la visibilité de la variable doit être définis. DOIVENT être * De classe `static` ou `extern` selon le besoin 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 List of STATUS values * @brief * */ enum STATUS { /** @brief Everything is fine */ STATUS_OK = 0, /** @brief Initialisation in progress */ STATUS_INIT, /** @brief System halted */ 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 for a local menu * @see MenuSelect */ struct menu { /** @brief Character used for the item */ char choice; /** @brief Description of the 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 Possible values : * − @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 **/ 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 utiliser 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 (continue la compilation) ou des erreurs (arrête 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 Registers name */ /** @{ */ /** @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 (;;) { }; }; ``` ### Atomic 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 ... ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { ... } ... ```