Vous êtes sur la page 1sur 49

Programmation C pour

systmes embarqus
Sylvain MONTAGNY
sylvain.montagny@univ-savoie.fr
Btiment chablais, bureau 13
04 79 75 86 86
Retrouver tous les documents de Cours/TP sur le site
www.master-electronique.com

Prsentation des cours : Sommaire

Cours : 10.5 h en 7 sances

1re partie : Rappel sur le langage C (Exercices de base)

2me partie : La programmation en langage C avance

3me partie : Prsentation du TP : Ralisation dun


algorithme de compression de donnes.

Universit de Savoie

Prsentation TP et examen

TP : 20 h en 5 sances
Le but de ce projet est d'crire un programme de compression de
fichiers textes, sans perte d'information. On demande galement
d'crire un dcompresseur qui devra restaurer le fichier original.
Lalgorithme propos ici est l'algorithme de Huffman.

Une note :

18 points : Examen de TP avec une partie thorique.


2 points proviennent du travail fait en TP

Universit de Savoie

1re partie : Rappel sur le langage C


(exercices de base)

Donner lexcution du code suivant :


#include <stdio.h>
#include <stdlib.h>
int main(void){
unsigned char i;
unsigned char tab[5]={1,2,4,8,16};
for (i=0;i<5;i++) {
printf("Le %d element est %d\n",i+1,tab[i]);
} return EXIT_SUCCESS;
}

Universit de Savoie

1re partie : Rappel sur le langage C


(exercices de base)

Donner lexcution du code suivant :


#include <stdio.h>
int main() {
int i,j;
for(i=0;i<5;i++){
for(j=5-i;j<5;j++){
printf("++");
}
printf("\n");
}
return EXIT_SUCCESS;
}

Universit de Savoie

1re partie : Rappel sur le langage C


(exercices de base)

crire une fonction C calculant la longueur d'une


chane de caractres, donne en argument. Le
prototype de la fonction est le suivant :

int longueur(char *s)

Universit de Savoie

1re partie : Rappel sur le langage C


(exercices de base)

crire une fonction C calculant la longueur d'une


chane de caractres, donne en argument.

int longueur(char *s) {


int n = 0;
while (s[n] != '\0') {
n++;
}
return n;
}

Universit de Savoie

1re partie : Rappel sur le langage C


(exercices de base)

Soit un texte donn par une chane de caractres. Le


but est de compter le nombre d'occurrences de
chaque lettre minuscule.
Question 1 : Raliser les dclarations suivantes :

Le texte (chane de caractre constante en lettre minuscule)


sera dclar dans un tableau nomm ch . Vous afficherez
la chane de caractre lcran.
Un tableau d'entiers nomm occ permet de compter les
occurrences de chaque lettre de lalphabet. La taille du
tableau est fixe par une constante (nombre de lettre de
lalphabet).
Un pointeur nomm p pour parcourir le texte.

Universit de Savoie

1re partie : Rappel sur le langage C


(exercices de base)
#include <stdio.h>
#define NB_LETTRE 26;
void main(void) {
/* dclaration d'une chane <=> tableau de caractres. */
char ch[]="ceci est une chane de test";

/* dclaration d'un pointeur sur une chane de caracteres. */


char *p = ch;
/* dclaration d'un tableau de 26 cases */
int occ[NB_LETTRE];
printf("Chane en mmoire : %s\n",ch);
}

Universit de Savoie

1re partie : Rappel sur le langage C


(exercices de base)

Question 2 : Initialiser le tableau doccurrence zro :


int i=0;
for (i=0; i<NB_LETTRE;i++)
occ[i]=0;

Question 3 : Compter les occurrences jusqu la fin de la chane


de caractre :
while (*p != '\0') {
if ( (*p >= 'a) && (*p <= 'z) ) {
occ[*p-'a'] = occ[*p-'a'] + 1;
}
p++;
}

Question 4 : Afficher le contenu du tableau occ en prcisant la


lettre : le nombre de a est le nombre de b est .
for (i=0; i<nb_lettres; i++)
printf("Nombre de %c : %d\n", 'a'+i,occ[i]);
Universit de Savoie

10

2me partie : La programmation en


langage C avance

Lisibilit du code,
les types de variables,
les typedef,
occupation mmoire,
port des variables,
les oprateurs,
manipulation de registre,
les structures,
les pointeurs,
le main(),
pointeurs,
la pile,
type de fonction,
allocation dynamique,
les options doptimisations la compilation,
les erreurs classiques du C

11

Lisibilit du code C (1)

Exercice :
Raliser un code qui imprime les N premiers lments d'un
tableau dentier A[ ] en insrant un espace entre les lments et
en commenant une nouvelle ligne aprs chaque dixime chiffre.
void main(void){
int A[80],N,i;
scanf("%d",&N);
for (i=0; i<N; i=i+1){
printf("%d", A[i]);
if ((i%10) == 9)
printf("\n");
else
printf(" ");
}
}

//Saisie de N
//Boucle N fois
//Affichage contenu tableau
//Test du 10me chiffre

12

Lisibilit du code C (2)

Rgles respecter :

Mettre des commentaires explicites


Ne pas trop compresser le code
Respecter une homognit dans votre faon de coder.
Dclarer des variables explicites
Organiser la mise en page de votre code, ou respecter
celle dj existante.

Universit de Savoie

13

Lisibilit du code C (3)

Voici une deuxime faon de coder, trs pratique


mais beaucoup moins lisible.
void main(void){
int A[80],N,i;
scanf("%d",&N);
for (i=0; i<n; i++)
printf("%d%c", a[i],(i%10==9)? \n : ' ');
}

Universit de Savoie

14

Lisibilit du code C (4)

Quelques quivalences parfois viter


Op. Fonction
+= Addition et
affectation
-= Soustraction et
affectation
*= Multiplication
et affection
/= Division et
affectation
%= Modulo et
affectation
++ Incrmentation

Exemple

nombre += 5;

Equivalence
nombre=nombre+5

nombre -= 6;
nombre *= 3;
nombre /= 2;
nombre %= 4;

nombre = nombre % 4

nombre++;

nombre = nombre + 1;

-- Dcrmentation nombre--;

nombre = nombre - 1;

y = x++
y = ++x

y
x
x
y

=
=
=
=

Universit de Savoie

x
x + 1
x + 1
x
15

Lisibilit du code C (5)


#include <stdio.h>
main(t,_,a)char*a;{return!0<t?t<3?main(-79,-13,a+main(-87,1_,main(-86,0,a+1)+a)):1,t<_?main( t+1, _, a ):3,main( -94, 27+t, a )&&t == 2 ?_
<13 ?main ( 2, _+1, "%s %d %d\n" ):9:16:t<0?t<-72?main( _,
t,"@n'+,#'/*{}w+/w#cdnr/+,{}r/*de}+,/*{*+,/w{%+,/w#q#n+
,/#{l,+,/n{n+,/+#n+,/#;#q#n+,/+k#;*+,/'r :'d*'3,}{w+K
w'K:'+}e#';dq#'l
q#'+d'K#!/+k#;q#'r}eKK#}w'r}eKK{nl]'/#;#q#n'){)#}w'){){nl]'/
+#n';d}rw' i;# ){nl]!/n{n#'; r{#w'r nc{nl]'/#{l,+'K {rw'
iK{;[{nl]'/w#q#n'wk nw' iwk{KK{nl]!/w{%'l##w#' i;
:{nl]'/*{q#'ld;r'}{nlwb!/*de}'c ;;{nl'{}rw]'/+,}##'*}#nc,',#nw]'/+kd'+e}+;#'rdq#w! nr'/ ')
}+}{rl#'{n' ')# }'+}##(!!/"):t<-50?_==*a
?putchar(31[a]):main(-65,_,a+1):main((*a == '/') + t, _, a + 1
):0<t?main ( 2, 2 , "%s"):*a=='/'||main(0,main(-61,*a, "!ek;dc
i@bK'(q)-[w]*%n+r3#l,{}:\nuwloca-O;m
.vpbks,fxntdCeghiry"),a+1);}
The International Obfuscated C Code Contest >> http://www.ioccc.org/

Les types de variables (1)


char = 1 octet (signed ou unsigned)
Int, long, double, float dpendent de la cible processeur utilise.

Afin de connatre la taille (en octet) dune variable, on


utilise la fonction sizeof() :
printf("int=%d octets", sizeof(int));
Note : Il nexiste pas de type Boolen en C

Universit de Savoie

17

Les types de variables (2)

Cas dune compilation pour processeur 32 bits

Voir le cas dun compilateur C pour PIC16F ou PIC18F (microchip)

Voir le cas dun compilateur C pour DSP TMS320 (Texas


Instruments) (page suivante)

Universit de Savoie

18

Les types de variables (3)

Universit de Savoie

19

Les types de variables (4)

Donner la reprsentation binaire des nombres suivants :

char a=64;
unsigned char b=64;
char c=128;
unsigned char d=128;

Quel est laffichage des fonctions suivantes?

printf("%d",a);
printf("%d",c);
printf("%u",a);
printf("%u",c+a);

Note : Une dclaration est signed par dfaut gnralement.


Note : %d -> entier signed
/
%u -> entier unsigned
Universit de Savoie

20

Les types de variables (5)

Conversion explicite :
La conversion de type consiste transformer un type de valeur en un
autre, en respectant la syntaxe suivante
(<type>) <expression>

Conversion implicite :
Lorsque les deux oprandes sont de type diffrent, le compilateur
prvoit une conversion implicite suivant l'ordre :
{ char -> int -> long -> float -> double } et { signed ->
unsigned }

Universit de Savoie

21

Les types de variables (6)


Dcrivez le conversion du type, ainsi que les
affectations du code suivant :
int main(void){
int n, m, l;
double d;
d = 5;
n = (int) 3.4;
n = 1;
m = 5;
d = 2;
l = (int) (m / d);
d = n / m;
d = n / ((double) m);
d = 'A' * 6.0 m + 0xA2CL;
return 0;
}
22

Porte des variables


Globale / Locale

Les variables locales sont dclares dans la fonction les utilisant.


les variables globales sont dclares en dbut de programme. Le
linker attribue une adresse fixe et dfinitive ces dernires
(variable globale) pendant toute la dure du programme.

L'utilisation de variables locales amliore considrablement la


lisibilit et la scurit des donnes dans un programme en C.
Une variable locale existe exclusivement pendant lexcution de
la fonction o est dclarer la variable.

Universit de Savoie

23

Classes de stockage et qualificateurs

Classes de stockage

auto : Dfinition automatique pas utilise car implicite.


register : Demande au compilateur dutiliser un registre (plus utile)
static : La variable locale conserve sa valeur
extern : La variable est dclare dans un autre fichier

Qualificateurs

const : Spcifie une variable non modifiable


volatile : Limite les effets de loptimisation sur la variable

Universit de Savoie

24

Classe de stockage et qualificateur


classe auto
Cette classe nest pas utilis car elle est implicite pour les
variables locales. En effet, Les variables locales sont par
dfaut "automatiques" , cres l'entre de la fonction
qui les dclare et dtruites la sortie. Pour cela elles
sont ranges dans la pile.
void f(void) {
auto int j = 0;
printf("j vaut %d",j);
}

void f(void) {
int j = 0;
printf("j vaut %d",j);
}
void main(void) {
f();
}

Mais jamais utilise

. . .

25

Classe de stockage et qualificateur


classe static

La classe static permet une variable locale dtre persistante et donc


de conserver sa valeur pendant les appels successifs de la fonction.
void f(void) {
static int i = 0; /* i ne sera initialis quune fois*/
int j = 0; /* j sera initialis chaque fois */;
i++;
j++;
printf("i vaut %d et j vaut %d.\n", i, j);
}
void main(void) {
f();
f();
}

Note : La classe static sur une variable globale ou une fonction aura
pour objectif de privatiser lobjet au fichier o elle est dclare. Cest-dire quelle ne pourra pas tre utilise depuis un autre fichier.

Classe de stockage et qualificateur


classe extern (1)

extern : permet de spcifier que la variable a t dclar dans un


autre fichier.

/* File : ext.c */
void next(void);
void next1(void);
int a1=1; /* definition of external (non static)*/
void main(void){
a1=2;
printf("a1=%d\n",a1);
next();
next1();
printf("a1=%d\n,a1);
}
27

Classe de stockage et qualificateur


classe extern (2)
/* File file1.c */

/* File file2.c */

int b1=0;

extern int a1;

void next(void){
char a1;
a1='a';
b1=77;
}

void next1(void){
float b1;
b1=19.2;
a1=13;
}

Si on omet le terme extern, une nouvelle variable est


crer avec une nouvelle allocation mmoire.

28

Classe de stockage et qualificateur


classe register
On peut demander au compilateur de ranger une variable trs
utilise dans un registre, laide de la classe register. Le nombre
de registres tant limit, cette requte ne sera satisfaite que sil
reste des registres disponibles. Cette technique permettant
dacclrer les programmes a aujourdhui perdu tout son intrt
grce aux performances des optimiseurs de code intgrs au
compilateur.
Documentation du compilateur MikroC :

Universit de Savoie

29

Classe de stockage et qualificateur


classe register

Le C dfinit des qualificateurs pouvant influer sur une


variable :

const : pour dfinir une variable dont la valeur ne doit jamais


changer ;
volatile : dsigne une variable dont les accs ne doivent pas
tre optimiser par le compilateur. Cette variable sera relue
depuis son emplacement dorigine chaque accs. En effet,
cela est important lorsque dautre sources (priphrique
matriel, processus, etc) accde la variable en mme temps
que notre programme.

Une variable peut avoir plusieurs qualificateurs


Universit de Savoie

30

Classe de stockage et qualificateur


Qualificateur const
Le qualificateur const indique au compilateur que la valeur de la variable
ne doit pas changer. Il est donc impratif d'assigner une valeur la
dclaration de la variable, sans quoi toute tentative de modification
ultrieure entranera une erreur de la part du compilateur :
tudier les codes suivants :
const int i = 0;
i = 1;
void fonction( const char * pointeur ) {
pointeur[0] = 0;
pointeur = "Nouvelle chane de caractres";
}
char * const pointeur = "Salut tout le monde !";
pointeur = "Hello world !";
const char * const pointeur = "Salut tout le monde !";
pointeur = "Hello world !";
pointeur[0] = 0;
Universit de Savoie

31

Classe de stockage et qualificateur


Qualificateur volatile
int * pReg = (int *) 0x 1234;
while (* pReg == 0) {

Le compilateur va optimiser la boucle while en considrant


que la lecture de *pReg nest jamais modifie. En ralit,
elle peut tre modifie par :

Un priphrique dentre/sortie mapp en mmoire


Une interruption
Une autre tche

Universit de Savoie

32

valuation des expressions boolennes (1)

Le C ne possde pas de type boolen ddi. Dans ce langage,


n'importe quelle valeur diffrente de zro est considre vraie,
zro tant considr comme faux. Ce qui veut dire que n'importe
quelle expression peut tre utilise l'intrieur des tests (entier,
rels, pointeurs, tableaux, etc.). Cela peut conduire des
expressions pas toujours trs claires, comme :
int a;
a = une_fonction();
if (a) { /* ... */ }

On prfrera :
int a;
a = une_fonction();
if (a != 0) { /* ... */ }

Attention :
int a = 0; b = 2;
if (a = b) { /* Le code qui suit sera toujours excut ... */ }
Universit de Savoie

33

valuation des expressions boolennes (2)


Les oprateurs logiques de comparaisons (&& et ||, similaires
smantiquement leur quivalent binaire & et |) ont une
excution totalement diffrente.
Dans le cas du ET logique (&&), si l'oprande gauche s'value
faux (valeur zro), on sait dj que le rsultat du ET sera faux et
donc ce n'est pas la peine d'valuer l'oprande droite. De la
mme manire si l'oprande gauche d'un OU logique (||) est
valu vrai, le rsultat sera aussi vrai (valeur !=0) et donc
l'valuation de l'oprande droite est inutile.

if (z != 0 && a / z < 10)


{
printf("Tout va bien\n");
}

Universit de Savoie

34

Les manipulations de bits (1)

Les manipulations de bits sont beaucoup utilises dans


lembarqu. Pour contrler un priphrique matriel,
on retrouve des registres de 8, 16 ou 32 bits quil faut
modifier.
Mettre 1 le bit 4 de a :
unsigned int a = 0x000F; /* 0000 0000 0000 1111 */

Mettre zro le bit 3 de a :


unsigned int a = 0x000F; /* 0000 0000 0000 1111 */

Faire une fonction int set_bit(int mot, int nbr) qui


retourne le mot modifi lemplacement nbr.

35

Les manipulations de bits (1)

Les manipulations de bits sont beaucoup utilises dans


lembarqu. Pour contrler un priphrique matriel,
on retrouve des registres de 8, 16 ou 32 bits quil faut
modifier.
Mettre 1 le bit 4 de a :
unsigned a = 0x000F; /* 0000 0000 0000 1111 */
unsigned b = 0x0010; /* 0000 0000 0001 0000 */
unsigned c = a | b; /* 0000 0000 0001 1111 soit 0x001F */

Mettre zro le bit 3 de a :


unsigned a = 0x000F; /* 0000 0000 0000 1111 */
unsigned b = 0xFFF7; /* 1111 1111 1111 0111 */
unsigned c = a & b; /* 0000 0000 0000 0111 soit 0x0007 */

Universit de Savoie

36

Les manipulations de bits (2)

Tester si le bit 2 de a est 1 :


unsigned a = 0x000F; /* 0000 0000 0000 1111 */

Tester si le bit 3 de a est 1 et si le bit 15 est 0 :


unsigned a = 0x000F; /* 0000 0000 0000 1111 */

37

Les manipulations de bits (2)

Tester si le bit 2 de a est 1 :


unsigned a = 0x000F; /* 0000 0000 0000 1111 */
if (a & (1 << 2)) {
printf("bit 2 = 1");
}
else {
printf("bit 2 = 0");
}

Tester si le bit 3 de a est 1 et si le bit 15 est 0 :


unsigned a = 0x000F; /* 0000 0000 0000 1111 */
if ( a & (1 << 3)!=0
&&
a&(1<<15)==0 ) {
printf("bit 2 = 1 et bit 15=0");
}
else {
printf("bit 2 = 1 et bit 15=0 nest pas vrifi");
}

38

Le main()

Le main() est le point dentre dune application. Dans


un systme embarque sans systme dexploitation, le
point dentre du programme sera prcis dans la
phase ddition de liens par linitialisation du vecteur
dinterruption nomm RESET.

Universit de Savoie

39

A quoi sert un pointeur ?

Universit de Savoie

40

Les tableaux (1)

Un tableau est un regroupement conscutif de


donne de mme type et de taille fixe.

// Tableau 1 dimension

// Tableau 2 dimensions

void main(void){

void main(void){

int tableau[4];
int i;
for(i=0;i<4;i++){
tableau[i]=0;

int tableau[4][3];
int i,j;
for(i=0;i<4;i++){
for(j=0;j<3;j++){
tableau[i][j]=0;

}
}

}
}

Universit de Savoie

41

Les tableaux (2)


tableau[0]
tableau[1]
tableau[2]
tableau[3]

tableau[0][0]
tableau[0][1]
tableau[0][2]
tableau[1][0]
tableau[1][1]
tableau[1][2]
tableau[2][0]
tableau[2][1]
tableau[2][2]
tableau[3][0]
tableau[3][1]
tableau[3][2]
Universit de Savoie

42

Les tableaux (3)

Passage de tableaux en paramtre des fonctions.


Un tableau nest jamais pass en paramtre, cest son
adresse qui est fournie.

Universit de Savoie

43

Organisation logicielle (1)


0xFFFFFFFF

stack
SP (Stack Pointer)

address space

heap
(dynamically allocated)
data segment
Data : (static and global)

0x00000000

code segment
(=program)

Universit de Savoie

PC (Program Counter)

44

Organisation logicielle (2)

Code segment : Emplacement ou se trouve le code compil (programme)

Data segment :
Data : contient toutes les variables static et globales
(initialises ou non)
Heap : Le tas est gr dynamiquement. Cest une zone de donne qui
grossi la rservation de zone mmoire (malloc) et qui se rduit lors
de la libration (free).

Stack : Cest une zone de stockage de type LIFO. Elle contient les
adresses de retour des fonctions et les variables locales.
Universit de Savoie

45

Organisation logicielle (3)


Imposer une adresse physique pour une variable ou une
constante
Les registres des microcontrleurs sont des adresses imposes
par le concepteur du C, or le linker d'un compilateur C a le
contrle total des adresses, il choisit o sont ranges variables et
constantes. On peut aussi imposer une adresse une variable.
Attention, il faut tre certain que vous soyez le seul utiliser
cette adresse pour ne pas crer derreur de segmentation.

char c;
void main (void){
*(unsigned char *)0x80 = 0xAA;
c= *(unsigned char *)0x80;

46

Organisation logicielle (4)


Directive du linker
Il est possible de prciser lditeur de lien lendroit exact ou nous
souhaitons que le code, les variables ou les constantes seront
positionnes. Exemple : Pour le linker MikroC, on utilise les directives
suivantes :
Directive absolute : Directive absolute specifies the starting address in RAM for a
variable or a starting address in ROM for a constant..
Directive org : Directive org specifies a starting address of a routine in ROM.
Directive org is appended to the function definition.
Directive orgall : If the user wants to place his routines, constants, etc, above a
specified address in ROM, #pragma orgall directive should be used.
Directive funcorg : You can use the #pragma funcorg directive to specify the
starting address of a routine in ROM using routine name only.
47

Optimisation du code (1)

Dans le contexte de lembarqu, il est important de connatre les


options de compilation doptimisation. Il y a en effet un compromis
trouver entre la taille de lexcutable produit et le temps dexcution.

Universit de Savoie

48

Optimisation du code (2)

Option -O0 (niveau 0)

Option -O1 (niveau 1)

Allocates variables to registers


Performs loop rotation
Eliminates unused code
Simplifies expressions and statements

Performs all -O0 optimizations, and:


Removes unused assignments
Eliminates local common expressions

Option -O2 (niveau 2) (default optimization level)

Performs all -O1 optimizations, and:


Performs loop optimizations
Universit de Savoie

49