Wiki » Historique » Révision 2
Révision 1 (Patrice Nadeau, 2023-06-24 16:03) → Révision 2/170 (Patrice Nadeau, 2023-07-01 20:26)
# 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) : * 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) » Justification : * [K&R](https://fr.wikipedia.org/wiki/Style_d%27indentation#Style_K&R) Exemple : ``` c 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; } ``` ### Langue * Fonctions, variables et constantes DOIVENT être en anglais * Commentaires et documentation DOIVENT être en français ### Copyright [BSD 2 clauses (FreeBSD)](https://opensource.org/license/bsd-2-clause/) Copier dans un fichier `LICENSE.txt` ### Doxygen Logiciel [Doxygen](https://www.doxygen.nl/) utilisé pour générer la documentation à partir de commentaires spécialement formatés. Chaque objet (fonctions, variables, etc.) DOIT être commenté/documenté : * Dans le format [Javadoc](https://www.doxygen.nl/manual/docblocks.html) (/** */) * Avant sa déclaration Exemple : ``` c /** * @brief Universal answer */ const int ANSWER 42; ``` ## 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 library * @author Patrice Nadeau <pnadeau@patricenadeau.com> * @version 0.0.02 * @date 2023-03-27 * @pre AVR supported (tested are in bold): * - ATmega88 * - ATmega168 * - @b ATmega328P * @copyright * @include LICENSE.txt */ ... #endif /*_usart.h*/ ``` ## Commentaires Les commentaires DOIVENT : * Être de style « C » * Précéder l’élément à documenté * En minuscules et commencer par une majuscule Exemple : ``` c /* Une seule ligne... */ /* * Sur * plusieurs * lignes */ ``` ## Convention de noms * Les nom de fonction et de variable DOIVENT * Comporter au maximum **31** caractères * Être en minuscule * Être séparées par des traits de soulignement si comporte plusieurs mots * Les nom de macro, constantes et #define DOIVENT * Comporter au maximum **31** caractères * Être en majuscule * Être séparées par des traits de soulignement si comporte plusieurs mots 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 List of constants * @brief */ /** @{ */ /** @brief The initialization string of the project */ static const char INIT_STR[6] = "POWER"; /** @brief Global const in the random library */ extern int RANDOM_MAX = 25; /** @} */ /** @brief Constant */ 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 of structure in the ds1305 library */ typedef struct { /** @brief last two digits : ≥ 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 Local variable */ static int ctr; /** @brief Global variable */ 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**, **clear**, **read** : écris ou lis un registre * **read**, **write** : Lis ou écris dans un fichier * **init** : Fonction d’initialisation * **is** : Vérifie un état Exceptions * Les fonctions définies dans une librairie de bas niveau pour du matériel (« driver ») devraient utiliser le nom définis dans le « datasheet ». Une fonction DEVRAIT retourner une valeur. * Dans le cas d'un « oui/non », la valeur DOIT être de type **int** avec les valeurs suivantes : * 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 Check if a timer is set * @param[in] nb Timer number. @n Possible values : * − @arg @b TIMER_1 * − @arg @b TIMER_2 * @return * @retval true Timer @e nb is set * @retval false Timer @e nb is NOT set * @par Example : * Check if the timer is set * @code * ... * result = is_timer_set(); * ... * @endcode * @pre init_timer **/ static bool is_timer_set(uint8_t nb); ``` ## Macros Ne devrait servir que pour les directives du préprocesseur C. ### #include Pour inclure d’autres fichier comme les fichiers entête. > N’est pas documenté dans Doxygen. ### #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 ```c #include <avr/io.h> ``` ### Macros Liste des macros définies : * `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 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 /** * @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) { ... } ... ```