Action disabled: source

C

Les micro-contrôleurs peuvent être programmés pour exécuter le code de l'utilisateur, et il faut souvent utiliser un langage très proche du C, voire le C lui-même. Cette section n'a pas le but de vous faire devenir des pros de ce langage (il y a des sites Web dédiés pour cela, dont l'ancien Site du Zéro), mais à vous donner les bases pour vous débrouiller avec et réussir à coder vos programmes. N'hésitez pas à demander de l'aide aux membres les plus expérimentés si vous avez des problèmes.

Types de fichiers

Le programmeur C a la possibilité d'utiliser deux types de fichiers: les .c et les .h. Les premiers contiennent le “vrai” code avec le corps des différentes fonctions, la déclaration des variables statiques, … Les deuxièmes contiennent les prototypes des fonctions présentes dans le .c (sauf le main).

Structure du programme

Voilà un exemple de programme en C. Il permet de montrer les fonctions de base du C: initialisation de variables, boucles, affichages sur la console, opérations, …

main.c

// Ces lignes permettent d'inclure des .h pour utiliser les fonctions à leur intérieur, comme printf
#include <stdio.h>
#include <stdlib.h>
#include "main.h"
 
//Ceci est une directive de préprocesseur. Lors de la compilation, tous les VARIABLE_PRE verront changés par des 10.
#define VARIABLE_PRE 10
 
int main()
{
    int var=maFonction(VARIABLE_PRE);
    int i;
    for(i=0;i<var;i++)
    {
        printf("Hello world!\n"); //cette fonction affiche "Hello world!" dans la console.
    }
    boucle();
    salut();
    return 0;
}
/* Cette fonction renvoie le nombre successif à celui passé en paramètre.
 * Ceci dit, c'est bien de commenter votre code, pour qu'il soit plus lisible
 * pour vous et les autres.*/
int maFonction(MON_NOMBRE(test))
{
    return (test++);  //équivaut à return test+1;
} 
 
int maFonctionAvecPointeur(MON_NOMBRE(*test))
{
    *test ++;
}
 
void boucle()
{
    short coucou=0;
    while(coucou<0)
    {
        coucou++;
        check(coucou);
        short reste=coucou%2; //l'opération modulo (%) donne le reste de la division entre deux nombres
        if(reste!=0)
        {
            printf("coucou est impair!");
        }
        else
        {
            printf("coucou est pair...");
            reste *= 2; //ceci équivaut à reste=reste*2;
        }
    }
}
 
void salut()
{
    printf("Salut!");
}
 
void check(long time)
{
    switch(time%10)
    {
        case 0: printf("Aujourd'hui est lundi.");
        break;
        case 1: printf("Aujourd'hui est mardi.");
        break;
        case 2: printf("Aujourd'hui est mercredi.");
        break;
        case 3: printf("Aujourd'hui est jeudi.");
        break;
        case 4: printf("Aujourd'hui est vendredi.");
        break;
        case 5: printf("Aujourd'hui est samedi.");
        break;
        case 6: printf("Aujourd'hui est dimanche.");
        break;
        default: printf("Le jour inséré n'est pas valide!");
        break;
    }
}

main.h

/* Ces directives de préprocesseur empêchent une inclusion infinie des fichiers.
 * Plus d'informations sur le Site du Zéro, section "Le préprocesseur". */
#ifndef DEF_MAIN
#define DEF_MAIN
 
#define MON_NOMBRE(gamma) unsigned int gamma
 
int maFonction(MON_NOMBRE(test)); //équivalent à int maFonction(unsigned int test);
void maFonctionAvecPointeur(MON_NOMBRE(*test)); //équivalent à void maFonctionAvecPointeur(unsigned int *test);
void boucle();
void salut();
void check(long time);
#endif

Types de variables

Il y a différents types de variables en C, et donc dans Code Composer Studio aussi. En voici une liste.

:!: Certains types pourraient être différents en CCS par rapport à ceux ayant le même nom en C. :!:

Type de variable Codé sur… Minimum Maximum
char 8 bits -127 128
short 16 bits -32 768 32 767
int 16 bits -32 768 32 767
long 32 bits - 2 147 483 648 2 147 483 647
double ? environ -1.8*1019 environ 1.8*1019
float ? ? ?

On peut aussi rajouter unsigned devant ces types pour que leur minimum soit 0, tout en ayant la même étendue. De plus, on peut les représenter en hexadécimal ou en binaire.

double et float peuvent stocker des nombres avec la virgule, contrairement aux autres types de variables, mais ils ne sont pas très utilisés dans les MSP430 car les registres des micro-contrôleurs sont de type entier et car les opérations avec ce genre de nombres demandent plus de temps.

Les bases binaire et hexadécimale

Les ordinateurs travaillent avec des bits (qui peuvent valoir soit 0 soit 1), regroupés en octets (8 bits). On peut les représenter tels qu'ils sont, ou bien les regrouper par paquets de 4 bits et utiliser la notation hexadécimale. Cela permet d'avoir des écritures plus compactes et “lisibles”. Exemple:

11111110 11011100 10111010 10011000 01110110 01010100 00110010 00010000 //représentation binaire d'un ensemble d'octets
1111 1110 1101 1100 1011 1010 1001 1000 0111 0110 0101 0100 0011 0010 0001 0000 //on les regroupe en paquets de 4 bits
F E D C B A 9 8 7 6 5 4 3 2 1 0 //on convertit en hexadécimale
FE DC BA 98 76 54 32 10 //et on regroupe à nouveau les octets initiaux

Les opérations sur les registres

Les registres des micro-contrôleurs étant en binaire, on peut les modifier grâce aux opérateurs logiques: OR (|), AND (&) et XOR (^). Voici leurs tables de vérité:

A B A OR B A AND B A XOR B
0 0 0 0 0
0 1 1 0 1
1 0 1 0 1
1 1 1 1 0

Voici un exemple:

REGISTRE_TEST  = 0xF0; // 1111 0000
REGISTRE_TEST |= 0x44; // 1111 0000 || 0100 0100 =
                       // 1111 0100 ---> REGISTRE_TEST vaut 0xF4
REGISTRE_TEST &= 0xA2; // 1111 0100 && 1010 0010 =
                       // 1010 0000 ---> REGISTRE_TEST vaut 0xA0
REGISTRE_TEST ^= 0x2C; // 1010 0000 ^ 0010 1100 =
                       // 1000 1100 ---> REGISTRE_TEST vaut 0x8C

D'habitude, chaque micro-contrôleur a une liste de “alias” pour rendre plus compréhensible l'opération. Ils peuvent être trouvés sur la datasheet ou bien sur un fichier .h dans Code Composer Studio. Un exemple pour les MSP430F5172:

UCB0IE |= UCRXIE + UCSTTIE + UCSTPIE; //UsCi B0 Interrupt Enable = UsCi Receive Interrupt Enable + UsCi STarT IE + UsCi SToP IE
UCB0IE |= 0x01 + 0x04 + 0x08; //les deux écritures sont équivalentes, mais la première est plus lisible.

Les pointeurs

Tout ordinateur stocke les données dans des cases mémoire, et chacune a une adresse différente. Si une variable “normale” stocke une donnée, un pointeur stocke l'adresse d'une case mémoire. C'est un point assez délicat de la programmation en C, mais important, puisque les pointeurs sont utilisés beaucoup (tableaux, chaînes de caractères, …) Voici ce qu'on peut faire avec:

#include <stdio.h>
#include <stdlib.h>
 
//doit etre dans un main.h
void fonctionSansPointeur(int x);
void fonctionAvecPointeur(int* x);
//
 
int main(void)
{
    int var1 = 15; //variable "normale"
    int *pointeur = NULL; //pointeur initialisé à NULL: il ne contient pas d'adresse
    pointeur = &var1; //pointeur contient maintenant l'adresse de var1
    printf("Adresse de var1 (sans pointeur) = %d \n", &var1);
    //Adresse de var1 (sans pointeur) = -144826404 // l'addresse va varier celon le PC/ucontroller
    printf("Adresse de var1 (avec pointeur) = %p \n", pointeur);
    //Adresse de var1 (avec pointeur) = 0x7ffff75e1fdc // l'addresse va varier celon le PC/ucontroller
    printf("Valeur de var1 = %d \n",var1);
    //Valeur de var1 = 15
    printf("Valeur de la case pointée par le pointeur = %d \n", *pointeur); //cela renvoie donc la valeur de var1
    //Valeur de la case pointée par le pointeur = 15
    printf("Adresse de pointeur = %d \n", &pointeur);
    //Adresse de pointeur = -144826400 // l'addresse va varier celon le PC/ucontroller
    printf("-------------------------\n");
    printf("Valeur de var1 avant la fonction: %d \n",var1); //var1 vaut 15
    //Valeur de var1 avant la fonction: 15
    fonctionSansPointeur(var1);
    printf("Valeur de var1 après la fonction: %d \n",var1); //var1 vaut encore 15
    //Valeur de var1 après la fonction: 15 
    fonctionAvecPointeur(pointeur);
    printf("Valeur de var1 après la fonction: %d \n",var1); //var1 vaut 75
    //Valeur de var1 après la fonction: 75 
    fonctionAvecPointeur(&var1);
    printf("Valeur de var1  après la fonction: %d \n",var1); //var1 vaut 375
    //Valeur de var1  après la fonction: 375
    return 0;
}
 
void fonctionSansPointeur(int x)
{
    x *= 5;
}
 
void fonctionAvecPointeur(int* x)
{
    *x *= 5;  //mettez des espaces entre les opérations et les variables, pour éviter des problèmes avec les pointeurs
}

Les structures

Le C permet de créer des types de variables “personnalisés”, semblables à des objets dans les autres langages. Cela peut être utile pour modéliser des choses (des personnes, des moteurs CC, …). Leur définition est souvent insérée dans les .h. Voilà un exemple:

typedef struct Moteur Moteur; //cette ligne sert dans l'initialisation d'une variable. On peut se limiter à "Moteur m" à la place de "struct Moteur m".
struct Moteur
{
	int erreur;  //variation entre la vitesse obtenue et celle desirée
	int newValeur;	//nouvelle valeur à donner au PWM pour se réaligner à la consigne
	int vitesse;	//vitesse mesurée avec la codeuse
	signed int consigne;	//valeur théorique de PWM à atteindre
	unsigned short coefficientProportionnel;	//coefficient P pour le correcteur PID
	unsigned int sensRotation;	//sens de rotation du moteur (0: sens antihoraire      1: sens horaire)
	unsigned short numero; //numéro du moteur
}; //le point-virgule ici est obligatoire

Pour accéder aux sous-variables contenues, on peut faire ainsi:

Moteur mot=initialisation();
Moteur* pointeurMot=&mot;
mot.consigne = 100;
(*pointeurMot).vitesse = 0;
pointeurMot->vitesse = 0;  //ces deux instructions sont équivalentes.
printf("Erreur = %d",mot.erreur);