Trucs Et Astuces
Trucs Et Astuces
Trucs Et Astuces
doc 1/7
PROGRAMMATION DES
MICROCONTROLEURS EN C
TRUCS ET ASTUCES
Christian Dupaty
Lyce Fourcade 13120 Gardanne
[email protected]
1. Imposer une adresse physique pour une variable............................................... 2
2. Crer une variable de plus de 256 octets............................................................ 2
3. Grer les bits....................................................................................................... 3
4. Attendre qu'un bit change d'tat .......................................................................... 4
5. Accder un bit par des macros......................................................................... 4
6. Tableaux ou Pointeurs......................................................................................... 4
7. Variables locales ou globales.............................................................................. 5
[email protected] trucs et astuces.doc 2/7
1. Imposer une adresse physique pour une variable
Les registres des microcontrleurs sont des adresses imposes par le concepteur or le linker d'un
compilateur C a le contrle total des adresses il choisit ou sont ranges variables et constantes.
Dfinir une adresse physique permet par exemple de dfinir comme une variable l'adresse d'un
registre (adresse impose par le micro contrleur)
Exemple, on veut associer au mot mamem la mmoire RAM l'adresse 0x80.
Ceci est un nombre : 0x80
Un CAST transforme ce nombre en un pointeur sur un octet (unsigned char *) 0x80
Ceci reprsente le contenu de ce pointeur *(unsigned char *)0x80
Afin d'viter cette criture lourde, on cre une quivalence : #define mamem *(unsigned
char *)0x80
#define mamem *(unsigned char *)0x80
char c;
void main (void)
{
mamem = 0xAA; // on met 0XAA dans la mmoire 0x80
c=mamem; // c= 0xAA
while(1);
}
2. Crer une variable de plus de 256 octets
C18 est configur pour optimiser le temps d'accs aux donnes, il travaille par dfaut sur les bank de
256 octets.
La dclaration d'une variable de plus de 256 octets (un tableau par exemple dclanche une erreur de
link :
MPLINK 3.90.01, Linker Copyright (c) 2005 Microchip Technology Inc.
Error - section '.udata_varlarges.o' can not fit the section.
Section '.udata_varlarges.o' length=0x0000012c Errors : 1
On peut cependant dclarer un linker des espaces mmoires suprieurs. Ils s'en accommode, modifie
automatiquement les noms des banks lors des accs. Le temps d'accs est seulement plus long
// Variables de plus de 256 octets
unsigned char c[300]; // c ne rentre pas dans une bank de 256 octest !!!
void main (void)
{
while(1);
}
Pour cela il faut modifier la config du linker, exemple : p18f452.lkr comme ceci
---------------------------------------------------------------------------
-----------
// Sample linker command file for 18F452i used with MPLAB ICD 2
// $Id: 18f452i.lkr,v 1.2 2002/07/29 19:09:08 sealep Exp $
LIBPATH .
FILES c018i.o
FILES clib.lib
FILES p18f452.lib
CODEPAGE NAME=vectors START=0x0 END=0x29 PROTECTED
CODEPAGE NAME=page START=0x2A END=0x7DBF
CODEPAGE NAME=debug START=0x7DC0 END=0X7FFF PROTECTED
[email protected] trucs et astuces.doc 3/7
CODEPAGE NAME=idlocs START=0x200000 END=0x200007 PROTECTED
CODEPAGE NAME=config START=0x300000 END=0x30000D PROTECTED
CODEPAGE NAME=devid START=0x3FFFFE END=0x3FFFFF PROTECTED
CODEPAGE NAME=eedata START=0xF00000 END=0xF000FF PROTECTED
ACCESSBANK NAME=accessram START=0x0 END=0x7F
DATABANK NAME=gpr0 START=0x80 END=0xFF
DATABANK NAME=grossebank START=0x100 END=0x3FF // GROSSE BANK
DE 768 octets (on peut mettre plus)
DATABANK NAME=gpr4 START=0x400 END=0x4FF
DATABANK NAME=gpr5 START=0x500 END=0x5F3
DATABANK NAME=dbgspr START=0x5F4 END=0x5FF PROTECTED
ACCESSBANK NAME=accesssfr START=0xF80 END=0xFFF PROTECTED
SECTION NAME=CONFIG ROM=config
STACK SIZE=0x100 RAM=gpr4
-------------------------------------------------
grossebank reprsente maintenant un espace de 768 octets, la directive #pragma udata permet de
forcer le linker utiliser grossebank (cela est facultatif, MPLINK recherche automatiquement une
bank adapte et ici il n'y en a qu'une) il n'y a plus d'erreur de link.
// Variables de plus de 256 octets
#pragma udata grossebank // facultatif
unsigned char c[300];
#pragma udata // facultatif
void main (void)
{
while(1);
}
3. Grer les bits
Le C prvoit la gestion indpendantes des bits travers une structure. Il est souvent pratique de
disposer de "drapeaux" indiquant qu'un processus, doit, s'est ou est en train de se drouler. Afin
d'viter d'utiliser la plus petite variable du C, le type char pour dclarer un drapeau mais plutt un bit
(0 ou 1), il faut dclarer une structure "champ de bits"
Dans l'exemple ci dessous on cre le nouveau type "chbits" et deux structures de bits : drap1 et drap2
qui seront des octets puisque le type chbits comporte 8 bits.
On accde ensuite aux lments d'une structure l'aide du point (.). nomstructure.element
struct chbits {
unsigned bit0:1; // on aurait pu appeler ce bit
moteur_on ou led_eteinte etc...
unsigned bit1:1;
unsigned bit2:1;
unsigned bit3:1;
unsigned bit4:1;
unsigned bit5:1;
unsigned bit6:1;
unsigned bit7:1;
} drap1,drap2; // et ici moteur ou affichage ...
char c;
void main (void)
{
drap1.bit2=1; // le bit 2 de drap1 est mis 1
c=drap2.bit5; // c prend la valeur du bit 5 de drap2
( o ou 1)
if (drap1.bit5) drap2.bit4=0; // le bit 4 de drap2 est mis 0 si le
bit 5 de drap1 est 1
[email protected] trucs et astuces.doc 4/7
while(1);
}
4. Attendre qu'un bit change d'tat
Pour cela on utilise une structure champs de bit comme ci dessus ou comme celles dcrites dans
p18f452.h par exemple (accs aux registres du PIC par nom)
Le principe consiste rester dans une boucle tant que le bit concern est dans l'tat oppos celui
que l'on attend.
Exemple : attendre que le bit 0 du port A passe 1
while (PORTAbits.RA0==0); // on reste dans cette boucle tant que RA0=0
et on en sort ds qu'il passe 1
Exemple : attendre que le bit 0 du port A passe 0
while (PORTAbits.RA0==1); // on reste dans cette boucle tant que RA0=1
et on en sort ds qu'il passe 0
Les ==1 et ==0 sont facultatifs puisque la boucle while fonctionne en terme de "vrai" ou "faux", on peut
crire :
while (!PORTAbits.RA0); ou while (PORTAbits.RA0);
5. Accder un bit par des macros
Les #define du C permettent de dclarer des quivalence avec des valeurs numriques mais
galement avec des fonctions
#define mamem (*(volatile unsigned char *) 0x10) // definit mamem
l'adresse 0x10
#define BIT(x) (1<<(x)) // // equivalence de dcalage
#define setbit(p,b) (p)|=BIT(b) // positionne le bit b de
l'octet p 1
#define clrbit(p,b) (p)&=~BIT(b) // positionne le bit b de
l'octet p 0
void main (void)
{
mamem |= BIT(3); // le bit 3 de mamem passe 1
mamem &= ~BIT(3); // le bit 3 de mamem passe 0
mamem ^= BIT(3); // le bit 3 de mamem bascule
if (mamem & BIT(3)) {}; // un test
setbit(mamem,5); // le bit 5 de mamem passe 1
clrbit(mamem,5); // le bit 5 de mamem passe 0
while(1);
6. Tableaux ou Pointeurs
Les faibles espaces mmoires des microcontrleurs peuvent parfois tre rsolus grce une meilleur
gestion de a RAM et de la ROM La dclaration d'un tableau rserve la taille mmoire (RAM ou ROM)
dclare. La dclaration d'un pointeur ne rserve rien.
Exemple :a fonction char *mois(char i); qui retourne un pointeur sur une chane de caractres.
a) criture avec un tableau
char *mois(char i)
{
const char nomdumois[12 ][10] =
{"janvier","fvrier","mars","avril",....};
return nomdumois[i];
}
[email protected] trucs et astuces.doc 5/7
Occupation mmoire 12 mois de 10 caractres max = 120 octets, on rserve de la place inutilement,
exemple pour le mois de mai le tableau contient
m a i \0 \0 \0 \0 \0 \0 \0
donc 6 octets inutiliss
b) criture avec un tableau de pointeurs
char *mois(char i)
{
const char *nomdumois[ ] = { "janvier","fvrier","mars","avril",
.....};
return nomdumois[i];
}
nomdumois janvier\0 8 octets
nomdumois +1 fvrier\0 8 octets
nomdumois +2 mars\0 5 octets
nomdumois +3 avril\0 6 octets
nomdumois +4 mai\0 4 octets
....
Occupation mmoire : 8 + 8 + 5 +6 + 4 +.... L'occupation mmoire correspond la somme des
tailles des chanes, la gestion mmoire est bien meilleure.
7. Variables locales ou globales
L'utilisation de variables locales amliore considrablement la lisibilit et la scurit des donnes
dans un programme en C. Les variables locales sont par dfaut "dynamiques" donc cres l'entre
de la fonction qui les dclare et dtruite la sortie. Pour cela elles sont ranges dans la "pile" , zone
mmoire particulire, destine primairement au stockage des donnes de retour des sous
programme.
Il peut arriver que l'on souhaite conserver une variable entre deux appels une fonction, il faut alors
la dclarer "static", elle sera range dans la RAM une adresse fixe comme une variable globale,
mais ne sera "visible" que dans la fonction qui l'a dclar.
L'inconvnient des variables globales "dynamiques" est leur temps d'accs . Le principal avantage
est l'optimisation de l'utilisation de l'espace mmoire RAM souvent petit sur les microcontrleurs.
Voici trois exemples du mme programme qui met en vidence la ncessit d'un compromis "vitesse
d'excution" "taille du code" "scurit des donnes"
Cadre de gauche , le source en C, cadre de gauche, l'assembleur gnr par le compilateur (C18)
Addition sur des variables globales - Un programmeur en assembleur aurait produit le mme code
char a;//3 globales
char b;
char c;
void main (void)
{
a=0;
b=2;
c=3;
a=b+c;
while(1);
}
void main (void)
{
a=0;
0000E2 0100 MOVLB 0
0000E4 6B8A CLRF 0x8a, BANKED ; a
a t plac en 0x8a par le linker
b=2;
0000E6 0E02 MOVLW 0x2
0000E8 6F8B MOVWF 0x8b, BANKED ; b
a t plac en 0x8b
c=3;
0000EA 0E03 MOVLW 0x3
0000EC 6F8C MOVWF 0x8c, BANKED ; c
a tt plac en 0x8c
a=b+c;
0000EE 518B MOVF 0x8b, W, BANKED ; b
dans w
[email protected] trucs et astuces.doc 6/7
0000F0 258C ADDWF 0x8c, W, BANKED ;
b+c dans w
0000F2 6F8A MOVWF 0x8a, BANKED ; w
dans a
while(1);
0000F4 D7FF BRA 0xf4
}
Addition sur des variables locales dynamiques , cela devient beaucoup plus compliqu en raison
des accs par pointeurs dans la pile
void main (void)
{
char a=0;
char b=2;
char c=3;
a=b+c;
while(1);
}
void main (void)
0000CA CFD9 MOVFF 0xfd9, 0xfe6
0000CC FFE6 NOP
0000CE CFE1 MOVFF 0xfe1, 0xfd9
0000D0 FFD9 NOP
0000D2 0E03 MOVLW 0x3
0000D4 26E1 ADDWF 0xfe1, F, ACCESS
{ char a=0;
0000D6 6ADF CLRF 0xfdf, ACCESS ;
a est dans 0xfdf
char b=2;
0000D8 52DE MOVF 0xfde, F, ACCESS
0000DA 0E02 MOVLW 0x2
0000DC 6EDD MOVWF 0xfdd, ACCESS ;
b dans 0xfdd
char c=3;
0000DE 0E03 MOVLW 0x3
0000E0 6EF3 MOVWF 0xff3, ACCESS
0000E2 0E02 MOVLW 0x2
0000E4 CFF3 MOVFF 0xff3, 0xfdb ;
c dans 0xfdb
0000E6 FFDB NOP
a=b+c;
0000E8 CFDB MOVFF 0xfdb, 0xfe6 ;
c dans fe6
0000EA FFE6 NOP
0000EC 0E01 MOVLW 0x1
0000EE 50DB MOVF 0xfdb, W, ACCESS ;
c dans w
0000F0 52E5 MOVF 0xfe5, F, ACCESS
0000F2 24E7 ADDWF 0xfe7, W, ACCESS ;
w+ ? dans s w
0000F4 6EDF MOVWF 0xfdf, ACCESS ;
w dans a
while(1);
0000F6 D7FF BRA 0xf6
Addition sur des variables locales statiques on retrouve le mme code assembleur que pour les
variables globales
void main (void)
{
static char a;
static char b;
static char c;
a=0;
b=2;
c=3;
a=b+c;
while(1);
}
void main (void)
{
static char a; // trois variables
locales statiques
static char b;
static char c;
a=0;
0000E2 0100 MOVLB 0
0000E4 6B8A CLRF 0x8a, BANKED ;
0 dans a
b=2;
0000E6 0E02 MOVLW 0x2
0000E8 6F8B MOVWF 0x8b, BANKED ;
2 dans b
c=3;
0000EA 0E03 MOVLW 0x3
[email protected] trucs et astuces.doc 7/7
0000EC 6F8C MOVWF 0x8c, BANKED ;
3 dans c
a=b+c;
0000EE 518B MOVF 0x8b, W, BANKED ;
b dans w
0000F0 258C ADDWF 0x8c, W, BANKED ;
w+c dans w
0000F2 6F8A MOVWF 0x8a, BANKED ;
w dans a
while(1);
0000F4 D7FF BRA 0xf4
}
Les variables locales statiques sont grs comme les variables globales, elles restent cependant
invisibles en dehors de la fonction qui les dclare.