Projet

Général

Profil

Wiki » Historique » Révision 153

Révision 152 (Patrice Nadeau, 2024-01-06 11:22) → Révision 153/170 (Patrice Nadeau, 2024-01-06 11:27)

# 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é, avec la même indentation 
     * Ê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 
     * 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 : &ge; 00, &le; 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 : *Item***_***Action***_***Attribut* *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) { 
     ... 
 } 
 ... 
 ```