C_LANGAGE - Kasimashi/Systemes-embarques GitHub Wiki
On se demande pourquoi le language est particulièrement adapté à l'embarqué? La réponse est que le besoin de l'embarqué est de se retrouver très proche de la machine. Le language le plus proche de la machine est le langage assembleur, cependant écrire un programme en assembleur est pénible et long à faire. Le langage C permet de rajouter une couche, afin de programmer plus rapidement mais en restant proche du hardware. Ce dernier est réutilisable facilement et très bien strusturé pour nos applications. Mais parfois le language C ne suffit pas il faut revenir au language assembleur. Il peut être intéressant de mixer des fichiers écrits en langage C avec des fichiers écrits en langage d’assemblage. Un tel exemple est disponible dans la section STM32, Assembleur et C.
En langage de programmation C, la directive #define permet les définitions de macros dans le code. Ces définitions de macros permettent de déclarer des valeurs constantes pour utiliser tout au long du code Les définitions de macros ne sont pas des variables et ne peuvent pas être modifiées par le programme comme variables. Il est utilisé lors de la création de constantes qui représentent des nombres, des chaînes ou expressions et sont remplacés lors de la phases de compilation par le préprocesseur.
#define CNAME (expression) (called Macros)
Par exemple on peut utiliser une macro HWREG
pour lire en mémoire sur 32 bits (unsigned int) :
#define HWREG(x) (*((volatile unsigned int *)(x))) //Accès à la valeur de l'addresse 'x';
#pragma optimization_level 0 //Permet sur IAR d'enlever l'optimisation pour un fichier
Elles sont utilisés pour modifier le comportement du compilateur (par exemple, en termes d'optimisation du code généré, ou de génération des infos de débogage) Ensuite, l'utilisation des pragmas est propre à chaque compilo, alors c'est la doc de ton compilo qu'il faut aller voir pour avoir la liste des pragmas supportés et leur signification.
Il faut mettre ces pragma sous define de compilation sinon ces lignes remonte des warnings sur d'autres toolchain : voici la liste des defines à utiliser suivant le compilateur : https://sourceforge.net/p/predef/wiki/Compilers/
-
volatile
Mot-clé qui est un qualificatif indiquant au compilateur que la valeur du variable peut changer à tout moment sans qu'aucune action ne soit entreprise par le code. Ceci peut être le cas pour une varibale globale modifiée par une ISR, une variable globale qui est accessible par du multi-tâche., ou encore les périphériques qui sont mappé directement sur la mémoire. -
mutable
Désigne une variable membre qui peut être modifiée par une fonction const.
Pour un programmateur C, la mémoire de l'ordinateur est une succession de cellules mémoires. Avec chacunes son addresse unique. Quand une variable est déclarée , la mémoire a besoin de stocker la valeur dans une zone spécifique mémoire.
Ainsi prenons l'exemple :
uint32_t myvar = 0xDEADBEEF;
uint32_t *pointer_myvar;
pointer_myvar = &myvar;
*pointer_myvar = 0x12345678;
- myvar est déclarée en tant qu'entier unsigned 32-bit
- pointer_myvar est déclarée en tant que pointeur sur entier 32 bits.
- point_myvar est remplis avec la valeur de l'addresse de myvar
- La valeur à l'addresse mémoire peut être ensuite modifiée avec *.
Elles sont particulièrement utile pour mapper des données en mémoires sur des choses répétitives. Par exemple ci dessous la structure matches avec le hardware du STM32L152. Je prend l'exemple des structures pour GPIO dont la structure se répète : Ainsi ceci s'écrirait :
typedef struct
{
uint32_t MODER; /*!< GPIO port mode register, Address offset : 0x00 */
uint32_t OTYPER; /*!< GPIO port output type register, Address offset : 0x04 */
uint32_t OSPEEDR; /*!< GPIO port output speed register, Address offset : 0x08 */
uint32_t PUPDR; /*!< GPIO port pull-up/pull-down register, Address offset : 0x0C */
uint32_t IDR; /*!< GPIO port input data register, Address offset : 0x10 */
uint32_t ODR; /*!< GPIO port output data register, Address offset : 0x14 */
uint32_t BSRR; /*!< GPIO port bit set/reset register, Address offset : 0x18 */
uint32_t LCKR; /*!< GPIO port configuration lock register, Address offset : 0x1C */
uint32_t AFR[2]; /*!< GPIO alternate function register, Address offset : 0x20-0x24 */
uint32_t BRR; /*!< GPIO bit reset register, Address offset : 0x28 */
} GPIO_struct;
Afin de meppaer la structure au bon port (par exemple le port B) on écrit alors :
GPIO_struct *GPIO_portB;
GPIO_portB = (GPIO_struct *) 0x40020400;
(* GPIO_portB).MODER = 0xFFFFFFFF;
Le tout peut être factorisé en :
GPIO_struct * GPIO_portB = (GPIO_struct *) 0x40020400;
GPIO_portB->MODER = 0xFFFFFFFF;
Décallage à gauche de 1 de la variable a : (multiplication par 2)
a = a << 1;
Décallage à droite de 1 de la variable a : (division par 2)
a = a >> 1;
a &=~(1<<n) // met à 0 le nème bit de a.
a |= (1<<n) // met à 1 le nème bit de a.
var = (a & (1<<12)) >> 12; // Query value of bit 12 of value called a
a ^= (1<<4| 1<<3); // Basculler le bit 4 et 3 de a à 1.
// off the top of my head
#define SET_BIT(val, bitIndex) val |= (1 << bitIndex)
#define CLEAR_BIT(val, bitIndex) val &= ~(1 << bitIndex)
#define TOGGLE_BIT(val, bitIndex) val ^= (1 << bitIndex)
#define BIT_IS_SET(val, bitIndex) (val & (1 << bitIndex))
« Un jeune homme fume une cigarette après l’autre sans s’arrêter. Une femme âgée observe cela et dit : « Jeune homme, tu fumes comme un fou ! Ne savez-vous pas qu'il y a un avertissement sur chaque paquet de cigarettes indiquant que cela peut vous tuer ? Le jeune homme finit sa cigarette, regarde la personne âgée et dit : « Oui, je sais. Mais écoutez, je suis programmeur et ce n'est qu'un avertissement.
Je ne fume pas et je fais attention aux avertissements :-). J'essaie toujours de garder mon code source exempt d'avertissements du compilateur. Et je porte toujours une attention particulière aux points suivants :
Alors, que signifie l'avertissement gcc « déclaration implicite de fonction » (un autre compilateur signale une « déclaration de paramètre implicite ») ? Fondamentalement, cela signifie que le compilateur a trouvé un appel à une fonction pour laquelle il n'a pas de prototype. Dans le cas présent, il manquait une déclaration comme celle-ci :
errorCode_t calcMatrix(matrix_t *m, double weight);
Soit parce que je n'en ai pas fourni de « déclaration préalable », soit parce que j'ai oublié d'inclure le fichier d'en-tête avec cette déclaration, soit parce que cette déclaration est manquante dans le fichier d'en-tête.
Sans connaître l'interface, le compilateur doit supposer (implicitement) l'interface à partir des paramètres réels utilisés et que la fonction renvoie un int. Le compilateur doit donc supposer selon les règles :
int calcMatrix(int*, int);
Ce qui bien sûr est faux à bien des égards : non seulement le paramètre 3n'est pas converti en double, mais cela signifie que la mauvaise taille ( intvs. double) est supposée. Si ce paramètre est transmis sur la pile, cela entraînera une incompatibilité d'espace de pile, donc globalement très mauvaise. Cela pourrait « fonctionner » si les types correspondent par hasard à l'implémentation réelle ou poserait moins de problèmes d'exécution si les paramètres et les valeurs de retour sont transmis dans les registres. Pourtant, c’est clairement faux et mauvais, mauvais, mauvais. Il peut s'agir uniquement d'un avertissement si l'éditeur de liens ne trouve pas ce symbole ( calcMatrixdans ce cas) quelque part, donc si vous ne faites pas attention aux avertissements, votre application pourrait planter ou se comporter de manière incorrecte.
Une telle « déclaration implicite » est en réalité un oubli ou une erreur de la part du programmeur, car le compilateur C doit connaître les types des paramètres et renvoyer une valeur pour les allouer correctement sur la pile. Malheureusement en C, ce n'est pas une erreur mais un avertissement (pour des raisons d'héritage, pour pouvoir compiler du vieux code non conforme). La bonne nouvelle est que compiler une telle chose en C++ produira une erreur, une autre bonne raison de compiler du code C brut avec le mode C++.
L'autre bonne nouvelle est que chaque compilateur C a généralement la possibilité de transformer cet avertissement en erreur (ce que je recommande si vous ne faites pas attention aux avertissements). Depuis la page d'aide de gcc :
-Werror-implicit-function-declaration
Donner un avertissement (ou une erreur) chaque fois qu'une fonction est utilisée avant d'être déclarée. Le formulaire -Wno-error-implicit-function-declaration n'est pas pris en charge. Cet avertissement est activé par -Wall (comme un avertissement, pas une erreur).
Avec cette option ajoutée aux paramètres du compilateur, elle est signalée comme une erreur :