Projet

Général

Profil

Actions

Wiki » Historique » Révision 26

« Précédent | Révision 26/170 (diff) | Suivant »
Patrice Nadeau, 2023-07-10 12:59


Règles de codage C

Le langage C, version C99 (ISO/IEC 9899:1999) utilisé avec le compilateur GCC.

gcc n'est pas entièrement compatible avec le standard C99 (https://gcc.gnu.org/c99status.html).


Style

Le code DOIT :

  • Être dans le 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 »

Justification :

  • K&R
  • Prévient les erreurs lors d'ajout dans les boucles n'ayant qu'une instruction comme bloc

Exemple :

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)

Copier dans un fichier LICENSE.txt

Doxygen

Logiciel Doxygen 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 (/** */)
  • Avant sa déclaration
  • Les « décorations » sont faites avec la syntaxe Markdown

Exemple :

/**
 * *italique*, **gras**
 * `code en ligne`
 * @remark Note non importante
 * @note Note générale
 * @attention Note importante
 * @warning Note conséquence négative
 * ```
 * code
 * ```
 */

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 :

#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
 * - **ATmega328P**
 * @copyright 
 * @include{doc} 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 :

/* Une seule ligne... */

/*
* Sur
* plusieurs
* lignes
*/

Convention de noms

En général : librairie_action_subject

  • 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 :

Déclarations locales

Une déclaration n’ayant qu’une visibilité locale DOIT :

  • Être de classe static

Exemple:

/**
* @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 :

/**
 * @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 :

/** 
 * @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 :

/**
 * @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 :

/** Type of structure in the ds1305 library */
typedef struct {
    /** @brief last two digits : &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 :

/** @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 :

/**
* @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 :

Exemple :

/**
* @brief Check if a timer is set
* @param[in] nb Timer number. @n Possible values :
* − @arg **TIMER_1**
* − @arg **TIMER_2**
* @return
* @retval true Timer *nb* is set
* @retval false Timer *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);

Préprocesseur

Directives du préprocesseur gcc.

#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 :

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 :

#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 :

/**
* @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

Fichier d’en-têtes

Vérification du modèle de microcontrôleur
> Via l'option -m de gcc

#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

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 :

#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

Exemple :

#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 :

...
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
    ...
}
...

Mis à jour par Patrice Nadeau il y a plus d'un an · 26 révisions