Programmation C pour systèmes embarqués

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

Présentation des cours : Sommaire
z

Cours : 10.5 h en 7 séances
z z z

1ère partie : Rappel sur le langage C (exercices de base) 2ème partie : La programmation en langage C avancée 3ème partie : Implémentation de code sur cible embarquée (DSP TMS 320C5416) 4ème partie : Présentation du TP : Réalisation d’un algorithme de compression de données.

z

Université de Savoie

2

Présentation TP
z TD

: 20 h en 5 séances

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 décompresseur qui devra restaurer le fichier original. L’algorithme proposé ici est l'algorithme de Huffman.

Université de Savoie

3

Examen

z

Une note :
z

z

14 points : Le déroulement du TP/Projet jusqu’à la soutenance. 6 points sur la soutenance (présentation de votre projet et de l’intégration sur le DSP)

Université de Savoie

4

1ère partie : Rappel sur le langage C (exercices de base)
z

Donner l’exécution 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° elt est %d\n",i+1,tab[i]); } return EXIT_SUCCESS; }

Université de Savoie

5

j++){ printf("++"). } return EXIT_SUCCESS.i<5.i++){ for(j=5-i. } Université de Savoie 6 . for(i=0.1ère partie : Rappel sur le langage C (exercices de base) z Donner l’exécution du code suivant : #include <stdio.j<5.j. } printf("\n").h> int main() { int i.

1ère partie : Rappel sur le langage C (exercices de base) z Écrire une fonction C calculant la longueur d'une chaîne de caractères. Le prototype de la fonction est le suivant : int longueur(char *s) Université de Savoie 7 . donnée en argument.

} return n. donnée en argument.h> int longueur(char *s) { int n = 0. } Université de Savoie 8 .1ère partie : Rappel sur le langage C (exercices de base) z Écrire une fonction C calculant la longueur d'une chaîne de caractères. while (s[n] != '\0') { n++. #include <stdlib.

1ère partie : Rappel sur le langage C (exercices de base) Soit un texte donné par une chaîne de caractères. Université de Savoie 9 . Un tableau d'entiers statique nommé « occ ». Un pointeur nommé « p » pour parcourir le texte. Vous afficherez la chaîne de caractère à l’écran. Question 1 : Réaliser les déclarations suivantes : z z z z Le texte (chaîne de caractère constante) sera déclaré dans un tableau nommé « ch ». pour compter les occurrences de chaque lettre de l’alphabet dont la taille est fixée par une constante (correspondant au nombre de lettre de l’alphabet). Le but est de compter le nombre d'occurrences de chaque lettre minuscule.

1ère partie : Rappel sur le langage C (exercices de base) Université de Savoie 10 .

1ère partie : Rappel sur le langage C (exercices de base) #include <stdio. } Université de Savoie 11 . */ char *p = ch.h> #define NB_LETTRE 26. /* déclaration d'un pointeur sur une chaîne de caracteres. printf("Chaîne en mémoire : %s\n". void main(void) { /* déclaration d'une chaîne <=> tableau de caractères.ch). */ char ch[]="ceci est une chaîne de test". /* déclaration d'un tableau de 26 cases */ int occ[NB_LETTRE].

1ère partie : Rappel sur le langage C (exercices de base) z Question 2 : Initialiser le tableau d’occurrence à zéro : Université de Savoie 12 .

Université de Savoie 13 . */ int i=0.i++) occ[i]=0. for (i=0.1ère partie : Rappel sur le langage C (exercices de base) z Question 2 : Initialiser le tableau d’occurrence à zéro : /* initialisation du tableau des occurrences à 0. i<NB_LETTRE.

1ère partie : Rappel sur le langage C (exercices de base) z Question 3 : Compter les occurrences jusqu’à la fin de la chaîne de caractère : Université de Savoie 14 .

} p++. } Université de Savoie 15 .1ère partie : Rappel sur le langage C (exercices de base) z Question 3 : Compter les occurrences jusqu’à la fin de la chaîne de caractère : /* parcours de la chaîne jusqu’au ‘\0’ */ while (*p != '\0') { if ( (*p >= 'a‘) && (*p <= 'z‘) ) { occ[*p-'a'] = occ[*p-'a'] + 1.

1ère partie : Rappel sur le langage C (exercices de base) z Question 4 : Afficher le contenu du tableau occ Université de Savoie 16 .

'a'+i. i<nb_lettres. } Université de Savoie 17 .occ[i]).1ère partie : Rappel sur le langage C (exercices de base) z Question 4 : Afficher le contenu du tableau occ for (i=0. i++) { printf("Nombre de %c : %d\n".

les structures. occupation mémoire. les pointeurs. les types de variables. porté des variables. allocation dynamique. les typedef. les opérateurs. les options d’optimisations à la compilation. manipulation de registre. le main(). la pile. pointeurs. type de fonction. les erreurs classiques du C… 18 .2ème partie : La programmation en langage C avancée z z z z z z z z z z z z z z z z Lisibilité du code.

Lisibilité du code C z Exercice : Réaliser un code qui imprime les N premiers éléments d'un tableau d’entier A[] en insérant un espace entre les éléments et en commençant une nouvelle ligne après chaque dixième chiffre. Université de Savoie 19 .

i.&N). scanf(’’%d’’.Lisibilité du code C void main(void){ int A[80].N. A[i]). i<N. else printf(" "). i=i+1){ printf("%d". for (i=0. } } Université de Savoie 20 . if ((i%10) == 9) printf("\n").

très pratique mais beaucoup moins lisible. i<n. } Université de Savoie 21 . for (i=0.Lisibilité du code C z Voici une deuxième façon de coder.N. (i%10==9)?'\n':' '). void main(void){ int A[80]. scanf(’’%d’’.i. i++) printf("%d%c".&N). a[i].

nombre %= 4. nombre++.Lisibilité du code C z Quelques Op. y = x++ y = ++x Université de Savoie .Décrémentation nombre--.1. nombre -= 6. nombre *= 3. nombre = nombre . nombre = nombre % 4 nombre = nombre + 1. nombre /= 2. Fonction += Addition et affectation -= Soustraction et affectation *= Multiplication et affection /= Division et affectation %= Modulo et affectation ++ Incrémentation équivalences… parfois à éviter Exemple Equivalence nombre=nombre+5 nombre += 5. y x x y = = = = x x + 1 x + 1 x 22 -.

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

Organiser la mise en page de votre code. ou respecter celle déjà existante. Université de Savoie 24 .Lisibilité du code C z Règle z z z à respecter : z Mettre des commentaires explicites Ne pas trop compresser le code Respecter une homogénéité dans votre façon de coder.

on utilise la fonction sizeof() : z printf(’’ int=%d octets ’’.Les types de variables char = 1 octet (signed ou unsigned) z Int. double. z Afin de connaître la taille (en octet) d’une variable. sizeof(int)). long. Note : Il n’existe pas de type Booléen en C Université de Savoie 25 . float dépendent de la cible processeur utilisée.

Les types de variables z Cas d’une compilation pour processeur 32 bits z z Voir le cas d’un compilateur C pour PIC16F ou PIC18F (microchip) Voir le cas d’un compilateur C pour DSP TMS320 (Texas Instruments) (page suivante) Université de Savoie 26 .

Les types de variables Université de Savoie 27 .

Les types de variables z Donner la représentation binaire des nombres suivants : z z z z char a=64. unsigned char b=64. c). unsigned char d=128. a). a). char c=128. ». Note : %d -> entier signed / %u -> entier unsigned Université de Savoie 28 . Note : Une déclaration est signed par défaut. ». z Quel est l’affichage des fonctions suivantes? z z z z printf(’’%d printf(’’%d printf(’’%u printf(’’%u ». ». c+a).

le compilateur prévoit une conversion implicite suivant l'ordre : { char -> int -> long -> float -> double } et { signed -> unsigned } Université de Savoie 29 . en respectant la syntaxe suivante (<type>) <expression> z Conversion implicite : Lorsque les deux opérandes sont de type différent.Les types de variables z Conversion explicite : La conversion de type consiste à transformer un type de valeur en un autre.

l = (int) (m / d). d = 5.4. d = 'A' * 6. d = n / ((double) m). } Université de Savoie 30 . l. n = (int) 3.0 – m + 0xA2CL. m = 5. ainsi que les affectations du code suivant : int main(void) { int n. double d. d = 2. return 0.Les types de variables Décrivez le conversion du type. n = 1. d = n / m. m.

f(). f(). printf("i vaut %d et j vaut %d.\n". return 0. j++. /* j sera initialisé à chaque fois */. i++.Portée des variables z static : permet à une variable locale d’être persistante et donc de conserver sa valeur pendant les appels successifs de la fonction. #include <stdio. j). } Université de Savoie 31 .h> void f(void) { static int i = 0. } int main(void) { f(). i. /* i ne sera initialisé qu’une fois*/ int j = 0.

Portée des variables z static : Cas des variables globales. C’est-à-dire qu’elle ne pourra pas être utilisée depuis un autre fichier. Université de Savoie 32 . Une variable globale est déjà persistante. L’objectif de la nommer en « static » est simplement de la « privatiser » au fichier où elle est déclarée.

next().a1). printf("a1=%d\n". une nouvelle variable est créer avec une nouvelle allocation mémoire.c */ #include <stdio.a1). next1(). int a1=1. Si on omet ce terme. void next1(void).h> void next(void). } // a1=2 33 .Portée des variables z extern : permet de spécifier que la variable a été déclarer dans un autre fichier. /* File : ext. printf("a1=%d\n. /* definition of external (non static)*/ void main(void){ a1=2.

b1=19. } /* File file2. } 34 . void next(void) { char a1. void next1(void) { float b1. a1='a'.c */ extern int a1.c */ int b1=0.2.Portée des variables /* File file1. b1=77. a1=13.

volatile : désigne une variable pouvant être modifiée notamment par une source externe indépendante du programme.Qualificateur de variables z Le C définit des qualificateurs pouvant influer sur une variable : z z const : pour définir une variable dont la valeur ne devrait jamais changer . Une variable peut avoir plusieurs qualificateurs Université de Savoie 35 .

Qualificateur de variables Qualificateur 'const' La classe const indique au compilateur que la valeur de la variable ne doit pas changer. Université de Savoie 36 . i = 1. Il est donc impératif d'assigner une valeur à la déclaration de la variable. sans quoi toute tentative de modification ultérieure entraînera une erreur de la part du compilateur : Étudier les codes suivants : const int i = 0.

Université de Savoie 37 . pointeur = "Nouvelle chaîne de caractères". pointeur[0] = 0. pointeur = "Hello world !". pointeur = "Hello world !".Qualificateur de variables void fonction( const char * pointeur ) { pointeur[0] = 0. } char * const pointeur = "Salut tout le monde !". const char * const pointeur = "Salut tout le monde !".

Qualificateur de variables Qualificateur 'volatile' Ce mot-clé sert à spécifier au compilateur que la variable peut être modifiée à son insu. et l'oblige à procéder à chaque lecture ou écriture dans une telle variable tel que le programmeur l'a écrit dans le code. mais dont la valeur peut changer quand même. Par exemple : extern const volatile int horloge_temps_reel. >>déclare une variable entière. qu'on ne peut modifier à partir du programme. Elle pourrait désigner une valeur incrémentée régulièrement par une horloge interne. Cela annule toute optimisation que le compilateur pourrait faire. z On peut combiner const et volatile dans certaines situations. Université de Savoie 38 .

.). tableaux. zéro étant considéré comme faux.. */ } z Attention : int a = 0. Cela peut conduire à des expressions pas toujours très claires. a = une_fonction().Évaluation des expressions booléennes (1) z Le C ne possède pas de type booléen dédié.. réels. a = une_fonction().. Ce qui veut dire que n'importe quelle expression peut être utilisée à l'intérieur des tests (entier.. comme : int a. if (a != 0) { /* . */ } z On préférera : int a. etc. Dans ce langage. if (a = b) { /* Le code qui suit sera toujours exécuté . */ } Université de Savoie 39 .. pointeurs. n'importe quelle valeur différente de zéro est considérée vraie. b = 2. if (a) { /* .

le résultat sera aussi vrai (valeur !=0) et donc l'évaluation de l'opérande droite est inutile.Évaluation des expressions booléennes (2) Les opérateurs logiques de comparaisons (&& et ||. } Université de Savoie 40 . si l'opérande gauche s'évalue à faux (valeur zéro). on sait déjà que le résultat du ET sera faux et donc ce n'est pas la peine d'évaluer l'opérande droite. z Dans le cas du ET logique (&&). similaires sémantiquement à leur équivalent binaire & et |) ont une exécution totalement différente. De la même manière si l'opérande gauche d'un OU logique (||) est évalué à vrai. z if (z != 0 && a / z < 10) { printf("Tout va bien\n").

/* 0000 0000 0000 1111 */ z Mettre à zéro le bit 3 de a : unsigned a = 0x000F.Les manipulations de bits (1) z z Les manipulations de bits sont beaucoup utilisées dans l’embarqué. on retrouve des registres de 8. Pour contrôler un périphérique matériel. Mettre à 1 le bit 4 de a : unsigned a = 0x000F. /* 0000 0000 0000 1111 */ Université de Savoie 41 . 16 ou 32 bits qu’il faut modifier.

/* 0000 0000 0001 0000 */ unsigned c = a | b. /* 0000 0000 0000 1111 */ unsigned b = 0x0010. 16 ou 32 bits qu’il faut modifier. /* 0000 0000 0000 1111 */ unsigned b = 0xFFF7. /* 1111 1111 1111 0111 */ unsigned c = a & b.Les manipulations de bits (1) z z Les manipulations de bits sont beaucoup utilisées dans l’embarqué. /* 0000 0000 0001 1111 soit 0x001F */ z Mettre à zéro le bit 3 de a : unsigned a = 0x000F. /* 0000 0000 0000 0111 soit 0x0007 */ Université de Savoie 42 . on retrouve des registres de 8. Pour contrôler un périphérique matériel. Mettre à 1 le bit 4 de a : unsigned a = 0x000F.

/* 0000 0000 0000 1111 */ 43 . /* 0000 0000 0000 1111 */ z Tester si le bit 3 de a est à 1 et si le bit 15 est à 0 : unsigned a = 0x000F.Les manipulations de bits (2) z Tester si le bit 2 de a est à 1 : unsigned a = 0x000F.

} else { printf("bit 2 = 0"). /* 0000 0000 0000 1111 */ if ( (a & (1u << 3)) && (a&(1u<<15))==0 ) { printf("bit 2 = 1 et bit 15=0"). } 44 . /* 0000 0000 0000 1111 */ if (a & (1u << 2)) { printf("bit 2 = 1").Les manipulations de bits (2) z Tester si le bit 2 de a est à 1 : unsigned a = 0x000F. } else { printf("bit 2 = 1 et bit 15=0 n’est pas vérifié"). } z Tester si le bit 3 de a est à 1 et si le bit 15 est à 0 : unsigned a = 0x000F.

Le main() z Le main() est le point d’entrée d’une application. Université de Savoie 45 . le point d’entrée du programme sera précisé dans la phase d’édition de liens par l’initialisation du vecteur d’interruption nommé RESET. Dans un système embarquée sans système d’exploitation.

A quoi sert un pointeur ? Université de Savoie 46 .

} } Université de Savoie 47 . int i.j<3. } } } // Tableau à 1 dimension void main(void){ int tableau[4]. // Tableau à 2 dimensions void main(void){ int tableau[4][3].j++){ tableau[i][j]=0.j.i++){ tableau[i]=0.Les tableaux (1) z Un tableau est un regroupement consécutif de donnée de même type et de taille fixe. jnt i. for(i=0i<4. for(i=0i<4.i++){ for(j=0.

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 48 .

Université de Savoie 49 .Les tableaux (3) z Passage de paramètre des tableaux à des fonctions : z Un tableau n’est jamais passé en paramètre. c’est son adresse qui est fournie.

Organisation logicielle 0xFFFFFFFF stack SP (Stack Pointer) address space heap (dynamically allocated) data segment Data : (static and global) code segment (=program) PC (Program Counter) 0x00000000 Université de Savoie 50 .

z Stack : C’est une zone de stockage de type LIFO. Data segment : z z Data : contient toutes les variables « static » et « globales » (initialisées ou non) Heap : Le tas est géré dynamiquement. Université de Savoie 51 . C’est une zone de donnée qui grossi à la réservation de zone mémoire (malloc) et qui se réduit lors de la libération (free).Organisation logicielle z z Code segment : Emplacement ou se trouve le code compilé. Cette zone mémoire est beaucoup plus rapide que l’utilisation du Heap. Elle contient les adresses de retour des fonctions et les variables locales.

Université de Savoie 52 . Il y a en effet un compromis à trouver entre la taille de l’exécutable produit et le temps d’exécution.Optimisation du code z Dans le contexte de l’embarqué. il est important de connaître les options de compilation d’optimisation.

Optimisation du code z Option -O0 (niveau 0) z z z z Allocates variables to registers Performs loop rotation Eliminates unused code Simplifies expressions and statements z Option -O1 (niveau 1) z z z Performs all -O0 optimizations. and: Performs loop optimizations Université de Savoie 53 . and: Removes unused assignments Eliminates local common expressions z Option -O2 (niveau 2) (default optimization level) z z Performs all -O1 optimizations.

Sign up to vote on this title
UsefulNot useful