Академический Документы
Профессиональный Документы
Культура Документы
microcontrôleur
Philippe Latu
philippe.latu(at)linux-france.org
http://www.linux-france.org/prj/embedded/
$Revision: 1153 $ $Date: 2007-06-14 14:25:15 +0200 (jeu, 14 jun 2007) $ $Author: latu $
Résumé
Ce support est une initiation au développement en Langage C sur les systèmes embarqués. Cette version s'appuie sur le microcontrôleur
MSC1210 de Texas Instruments. Le support est destiné aux débutants. On présente sommairement l'environnement système, les
chaînes de développement, le Langage C et on illustre les applications classiques de ces systèmes spécialisés.Aujourd'hui, la grande
majorité des étudiants ont l'habitude d'utiliser des systèmes informatiques sans se poser de questions sur la présence ou non d'un
système d'exploitation qui prend en charge la gestion du temps processeur, de la mémoire et des périphériques. Ce document essaie
d'illustrer les différences entre les deux contextes d'utilisation avec et sans noyau de système d'exploitation. Le maximum d'exemples de
programmes sont présentés dans ces deux contextes.
1. Copyright et Licence
Copyright (c) 2000,2007 Philippe Latu.
Permission is granted to copy, distribute and/or modify this
document under the terms of the GNU Free Documentation License,
Version 1.2 or any later version published by the Free Software
Foundation; with no Invariant Sections, no Front-Cover Texts,
and no Back-Cover Texts. A copy of the license is included in
the section entitled "GNU Free Documentation License".
1.1. Méta-information
Cet article est écrit avec DocBook1 XML sur un système Debian GNU/Linux2. Il est disponible en version imprimable
aux formats PDF et PostScript : sdcc_course.pdf3| sdcc_course.ps.gz4.
1
http://www.docbook.org
2
http://www.debian.org
3
http://www.linux-france.org/prj/embedded/telechargement/sdcc_course.pdf
4
http://www.linux-france.org/prj/embedded/telechargement/sdcc_course.ps.gz
2. Avant Propos
2.1. C/C++ & Unix
Le système d'exploitation Unix a été conçu au début des années 70. Le Langage C a été développé parallèlement à la
mise au point de ce système. Sa première standardisation «de fait» a été publiée dans l'ouvrage de B.W. KERNIGHAN
et D.M. RITCHIE en 1978.
Même si l'évolution des Langages de programmation C puis C++ est liée à la progression des systèmes Unix, ces
langages sont universellement connus et utilisés. On peut pratiquement affirmer qu'il n'existe pas de plate-forme
informatique actuelle sur laquelle il n'existe pas une chaîne de développement C/C++.
Le Langage C a été conçu pour de multiples utilisations, il n'offre que des structures de programme permettant
l'ordonnancement séquentiel, des tests, des boucles et des sous-programmes. Cet aspect, limité en apparence, garantit
la «portabilité» des codes sources écrits en C.
Dans le domaine des systèmes embarqués, le C est le langage de programmation le plus répandu. Il convient
parfaitement au développement des pilotes de périphériques spécialisés tels que les convertisseurs analogiques/
numériques, les capteurs, etc. Les outils de développement modernes possèdent des fonctions d'optimisation
performantes et le recours au langage assembleur est devenu inutile.
En fait, cette méthodologie convient parfaitement aux logiciels écrits par des informaticiens à l'usage des
informaticiens.
Curieusement, le développement des outils informatiques à l'usage de non spécialistes a considérablement augmenté
la complexité des programmes. Ces logiciels de plus en plus sophistiqués ont amené les concepteurs à changer de
démarche. Il est devenu indispensable de penser d'abord aux données et ensuite aux actions que l'on doit réaliser avec.
Un autre aspect très important dans la conception est la réutilisation des développements précédents. Ce principe
d'héritage permet de construire des composants logiciels avec des propriétés connues que l'on réutilise suivant ses
besoins. De tels composants peuvent même être complétés lors des évolutions des applications.
Si la programmation orientée objet n'est pas largement utilisés dans la mise au point des systèmes embarqués, certains de
ses concepts ont été repris avec succès. La surdéfinition et l'héritage se retrouvent très souvent dans les développements
d'interfaces spécialisées.
Comme le développement de pilotes de périphériques matériels est une problématique assez éloignée de l'interface
utilisateur, le recours à la programmation orientée objet reste très limité dans ce domaine. Le noyau Linux est un
exemple caractéristique ; il est entièrement développé en Langage C.
De nombreux concepteurs ont produit des bibliothèques d'objets qu'ils ont rendu libres d'utilisation à l'aide de la
General Public License5. Cette disponibilité d'outils de très grande qualité a permis à d'autres concepteurs de fournir
de nouveaux outils et etc.
5
http://www.gnu.org/philosophy/license-list.html
Ainsi, avec un minimum de coordination, on trouve aujourd'hui des systèmes d'exploitation, des environnements
graphiques, des chaînes de développement et des suites d'applications de plus en plus performantes.
Les principaux outils utilisés dans le présent document sont issus de projets de logiciels libres. La chaîne de
développement SDCC - Small Device C Compiler6 pour microcontrôleurs en est le meilleur exemple. Voir
Section 13.3, « C sur MSC1210 : SDCC ».
3. Développement et Système
À l'heure actuelle, la majorité des chaînes de développement suivent le modèle initié par les outils GNU au milieu des
années 1980. Les principes sont les suivants.
• Les outils de base comme le compilateur, l'éditeur de liens et le debugger, etc. sont des briques logicielles
individuelles accessibles au niveau Shell. Ils disposent d'un grand nombre d'options ou de commandes permettant
de répondre à pratiquement tous les besoins.
• Ils partagent avec le système d'exploitation de nombreuses bibliothèques telles que la ou bibliothèque standard du
Langage C courament appelée libc. Les fonctions ou les appels systèmes fournis par cette bibliothèque sont supportés
depuis plus de 25 ans. Elles ont donc été largement adoptées sur un très grand nombre de systèmes différents.
• Les interfaces de développement sont indépendantes des outils. Les environnements graphiques utilisateurs ne sont
que des interfaces facilitant l'utilisation des jeux de commandes des outils GNU. Compte tenu du nombre et de la
qualité de ces environnements, les développeurs se «composent» souvent une interface graphique à la carte.
Suivant le contexte de développement, les modes d'exploitation diffèrent. Généralement, on distingue les ordinateurs
cibles avec système d'exploitation tels que les PCs et les ordinateurs cibles sans système d'exploitation comme les
systèmes embarqués qui utilisent des microcontrôleurs spécialisés.
En règle générale, lorsque le programme est exécuté sur une machine cible avec système d'exploitation, les
périphériques tels que le clavier, l'écran et la souris sont toujours disponibles.
Cette situation «confortable» suppose plusieurs couches intermédiaires de logiciel entre l'utilisateur et le matériel.
L'ensemble de ces couches intermédiaires s'appelle un système d'exploitation. L'image ci-dessous représente les
couches constituant un système d'exploitation.
6
http://sdcc.sf.net/
Kernel, noyau
Ce logiciel constitue le coeur du système d'exploitation. Il assure 3 fonctions principales :
Un noyau moderne doit aussi gérer l'ajout et le retrait de périphériques en cours de fonctionnement. On parle
de branchement «à chaud» ou hotplug.
De plus, si le plan mémoire est partagé entre plusieurs programmes, le noyau doit utiliser un algorithme
spécifique de planification et de distribution des accès entre ces programmes. On parle alors de système multi-
tâches. Linux est un exemple caractéristique de ce type de noyau.
La fonction correspondante du noyau est appelée ordonnanceur ou scheduler. Sur un système moderne
dans lequel les temps d'accès aux périphériques et les transmissions réseau sont suffisamment rapides, c'est
l'ordonnanceur qui est le «maître» du temps. On peut le comparer à une horloge de réglage du rythme cardiaque
d'un système.
Shell
Sur les systèmes multi-tâches et multi-utilisateurs tels que GNU/Linux et *BSD, de nombreux Shells peuvent
être actifs en même temps. À l'inverse, sur les systèmes mono-tâches hérités du DOS (Disk Operating System)
de Micro$oft™, on ne dispose que d'un Shell unique.
Application
À ce niveau on trouve généralement l'interface graphique utilisateur qui permet de réaliser les manipulations
système de façon plus intuitives qu'au niveau Shell. À ce gestionnaire de fenêtres sont associées toutes
7
images/os.png
les applications usuelles d'un système généraliste : navigation Web, traitement de textes, Environnement de
développement intégré (comme Section 13.1, « C/C++ sur Windows : Dev-Cpp »), etc.
C'est aussi à ce niveau que les programmes développés par nos soins sont exécutés.
Contrairement à la situation précédente avec système d'exploitation, les conditions de développement sont moins
«confortables». Ceci-dit, comme le nombre de couches logicielles est nettement réduit, la description est plus simple.
Le moniteur
Le seul point commun entre le moniteur et le BIOS se situe au niveau du lancement de programme. Là où le BIOS
identifie le code de lancement du système sur le premier secteur d'un périphérique, le moniteur identifie l'adresse
de lancement d'une application dans le plan mémoire du microcontrôleur.
Sur un système spécialisé avec microcontrôleur, la configuration matérielle est figée. Il est donc totalement inutile
de procéder à un recensement des ressources du système.
Le premier rôle d'un moniteur est d'assurer le téléchargement des programmes entre le système de développement
(généralement un PC) et le système spécialisé (le microcontrôleur). Ce téléchargement est réalisé à l'aide une
liaison série ou réseau.
Les microcontrôleurs récents intègrent en plus du moniteur un ensemble de sous-programmes (routines) qui offrent
des fonctions simples de pilotage de périphériques et de manipulations de la mémoire intégrée au composant.
Application
À ce niveau le microcontrôleur exécute le ou les programme(s) utilisateur. Comme on n'utilise pas de noyau, les
programmes utilisateur doivent intégrer directement le pilotage des périphériques : liaison série, affichage LCD,
conversion analogique/numérique, capteurs, etc.
8
images/eos.png
4. Cycle de développement
Si tous les programmes que vous avez à écrire fonctionnaient parfaitement du premier coup le cycle de développement
se résumerait aux 3 étapes suivantes :
Malheureusement, quelque soit le programme et quelque soit le niveau d'expérience en programmation, on rencontre
toujours des erreurs à chacune des étapes énoncées ci-dessus. Le cycle de développement devient alors le suivant :
9
images/dev-cycle.png
La «mise au point» de ce programme simpliste sert aussi à prendre en main les outils de la chaîne de développement.
#include <stdio.h> ❶
int main() { ❷
D'une manière générale, pour que l'on puisse utiliser les fonctions d'une bibliothèque, il faut que les prototypes
(ou en-têtes) de ces fonctions soient parcourus par le pré-processeur (voir Section 4, « Cycle de développement »).
Si cette condition est satisfaite, le pré-processeur «connaît» la liste des paramètres possibles d'une fonction et
leurs types. Il peut ensuite contrôler que les appels à ces fonctions sont conformes.
Cet exemple de programme utilise la fonction printf qui appartient à la bibliothèque stdio.
❷ int main() { : début du programme principal.
En C, le programme principal peut être assimilé à une «fonction comme les autres» à qui on peut passer des
paramètres (entre les parenthèses) depuis le Shell et qui renvoie à ce même Shell une indication sur ses conditions
d'exécution (un nombre entier).
Une chaîne de caractères est délimitée par des guillemets et les caractères spécifiques sont précédés par '\'. On
parle de caractères d'échappement. Dans cet exemple, '\n' correspond au saut de ligne.
Cette instruction n'a de sens que lorsque le programme est exécuté via un système d'exploitation avec lequel il
est possible d'échanger des informations.
Voici le code source du même premier programme ser_hello.c sous sa forme «embarquée». Le code n'est pas aussi
simple que dans le cas précédent. Comme indiqué dans la Section 3.2, « Ordinateur cible sans système d'exploitation »
on ne dispose pas de noyau dans ce contexte. Le programme doit donc intégrer le pilotage du périphérique d'affichage.
Ici, il s'agit de la liaison série qui est utilisée pour «l'affichage» de la chaîne «Hello, World!».
Avec le synoptique ci-dessus, on développe (voir Section 4, « Cycle de développement ») le programme sur le PC. Une
fois que l'on a obtenu le fichier contenant le code exécutable du programme, on le transfère via la liaison série à l'aide
du moniteur. Enfin, après avoir réinitialisé le microcontrôleur en mode exécution, le programme utilisateur est lancé.
En mode exécution, le programme utilisateur peut piloter la liaison série de façon complètement autonome si on lui
fournit les sous-programmes nécessaires. C'est le cas de l'exemple de code source ci-dessous.
Exemple 2. Affichage de la chaîne «Hello, World!» avec un microcontrôleur MSC1210 & SDCC
#include <stdio.h> ❶
#include <msc1210.h>
#include "ser_msc1210.h"
void main(void) { ❸
while(1); ❻
}
❶ Comme indiqué dans le premier cas, les directives #include servent à inclure des définitions pour le pré-
processeur. Dans le cas présent, 3 fichiers de définitions sont utilisés.
• stdio.h : fichier d'en-tête contenant les prototypes des fonctions d'entrées-sorties standard. Si ce fichier
porte le même nom que celui donné dans le premier fichier source, le catalogue des sous-programmes mis à
disposition sur un microcontrôleur est nettement réduit relativement au cas général d'une machine avec système
d'exploitation.
Dans le cas présent, ce fichier fournit le prototype de la fonction printf utilisée pour émettre la chaîne de
caractères depuis le microcontrôleur sur la liaison série.
• msc1210.h : fichier d'en-tête contenant les définitions des registres des microcontrôleurs de la famille
MSC12xx.
10
images/no_os_dev.png
• ser_msc1210.h : fichier d'en-tête contenant les prototypes des sous-programmes intégrés dans la mémoire
ROM du composant microcontrôleur. Dans cet exemple, ce sont les fonctions de pilotage de la liaison série
qui sont utiles : autobaud et ser_putc.
Le fait que ce fichier soit noté «entre guillemets» indique qu'il s'agit d'un fichier local qui n'est pas fourni avec
la chaîne de développement. Voir le fichier ser_msc1210.h et la documentation MSC1210 ROM Routines sur
ces fonctions intégrées.
❷ void putchar(char c); : surdéfinition de la fonction d'affichage d'un caractère.
Cette fonction fait «normalement» partie de la bibliothèque standard et son rôle est d'envoyer un caractère à
l'écran. Dans le cas présent, on utilise une nouvelle définition de cette fonction. Son rôle est maintenant d'envoyer
le caractère sur la liaison série. La fonction tx_byte fait partie de la bibliothèque intégrée du microcontrôleur.
Sa définition est donnée dans le fichier d'en-tête rom1210.h. Voir MSC1210 ROM Routines.
Le mot clé void signifie que la fonction ne renvoie aucun paramètre. Le paramètre d'entrée c est défini comme
étant de type caractère (représenté sur un octet) par le mot clé char. Voir Section 7.3, « Types de représentation
des données »
❸ void main(void) { : début du programme principal.
Par différence avec le premier programme, on constate avec l'emploi des mots clé void qu'aucun paramètre
n'est passé en entrée et qu'aucun paramètre n'est renvoyé en sortie. Ce codage est tout à fait normal dans un
contexte sans système d'exploitation. Ce programme sera le seul exécuté sur le microcontrôleur. Il n'aura donc
pas à échanger avec un autre logiciel.
❹ autobaud(); : appel de la fonction de détection automatique du débit sur la liaison série.
Cette fonction est très pratique. Le programme reste en attente d'un appui sur la touche <Entrée> du système de
développement (le PC) pour déterminer le débit de transmission en bauds. Sans cette «attente», le programme se
lancerait directement après le reset du microcontrôleur ce qui est trop rapide si des manipulation sont à effectuer
manuellement sur le système embarqué.
❺ printf("Hello World !"); : appel de la fonction printf.
Cet appel de fonction se présente comme dans le cas du premier programme excepté pour le traitement du saut
de ligne en fin de chaîne de caractères. Avec la liaison série, on est obligé d'envoyer deux caractères spécifiques :
'\r' (retour chariot ou Carriage Return ou CR) pour renvoyer le curseur en début de ligne et '\n' (saut de
ligne ou Line Feed ou LF) pour descendre le curseur d'une ligne. Cette opération est réalisée par l'appel à la
fonction putcr().
❻ while(1); : boucle «d'attente» infinie.
Comme le programme est la seule application en cours d'exécution sur le microcontrôleur, et qu'il n'y a plus
d'opération à effectuer, on place le processeur dans un état d'attente infinie grâce à cette instruction de bouclage.
C'est une technique usuelle qui permet de s'assurer que le processeur ne cherche pas à interpréter le contenu de
la mémoire située après l'application utilisateur comme un programme.
Ce contexte est décrit dans la Section 3.1, « Ordinateur cible avec système d'exploitation ».
Pour que le résultat de l'exécution du programme soit «visible», on a ajouté l'instruction system("pause"); qui
demande à l'utilisateur d'appuyer sur une touche. Sans cette instruction, le Shell serait refermé dès la fin de l'exécution
du programme et le message ne serait pas visible.
int main() {
Dans ce contexte, on commence par ouvrir un nouveau Shell dans lequel on effectue toutes les opérations individuelles :
édition du code source, compilation et exécution.
Pour l'exemple de programme retenu, on choisit d'afficher la chaîne de caractères sur le terminal du système de
développement (ie. le PC) via une liaison série.
Outils de développement
Pour que la chaîne de développement SDCC soit plus facile à utiliser sur un système d'exploitation Microsoft™,
il est vivement conseillé de reconstituer un environnement de travail GNU/Linux-like à l'aide des outils Cygwin12.
11
images/xp.hello.png
12
http://www.cygwin.com/
Une fois les outils Cygwin13 installés, on dispose d'un shell Bash14 et surtout de l'application de contrôle du
compilateur make15 qui permet de gérer les dépendances entre les différents fichiers sources et en-têtes.
Après avoir travaillé avec ces outils pendant «un certain temps», il ne restera plus qu'une dernière étape à franchir :
abandonner le système Microsoft™ GNU/Linux-like au profit d'un véritable système GNU/Linux.
Le fichier ser_hello.ihx contient le code exécutable du programme au format Intel Hex. Il s'agit d'un format
texte ASCII de représentation du code machine du microcontrôleur utilisé pour le transfert du système de
développement vers le système cible.
Le fichier ser_hello.asm contient le code source du programme en langage assembleur. Une lecture rapide de
ce fichier montre que les fonctions d'optimisation du compilateur ont été efficaces.
Pour démarrer un nouveau développement, il suffit de copier ce patron dans un nouveau répertoire et d'affecter à
la variable BASENAME le nom du nouveau fichier source en langage C.
Ce patron de Makefile suppose que l'on utilise systématiquement la liaison série comme interface utilisateur. Il fait
donc référence au fichiers sources et en-têtes qui permettent d'utiliser cette liaison série. Ces fichiers sont aussi
présentés dans ce document à la Section 14.2, « Sources MSC1210 ».
Il faut positionner l'interrupteur «vers le haut» pour accéder au moniteur intégré du microcontrôleur. Après un
appui sur le bouton Reset, les opérations de transfert peuvent débuter.
13
http://www.cygwin.com/
14
http://fr.wikipedia.org/wiki/Bash
15
http://fr.wikipedia.org/wiki/Make
16
images/sdcc.xp.hello.png
17
http://www.ti.com/litv/zip/sbac018c
18
images/tidownload-select.png
19
images/tidownload-setup.png
Exécution du programme21
Ce contexte est identique au précédent. Les programmes sont développés sur une machine avec un système GNU/Linux
et exécutés sur le microcontrôleur du système cible. Ces conditions sont décrites dans la Section 3.2, « Ordinateur cible
sans système d'exploitation ».
Relativement à la section précédente, il est inutile d'installer des outils supplémentaires pour mettre en oeuvre la chaîne
de développement SDCC puisque les applications telles que le shell bash et make sont systématiquement fournies avec
les distributions GNU/Linux.
20
images/tidownload-upload.png
21
images/tidownload-run.png
Pour l'exemple de programme retenu, on choisit d'afficher la chaîne de caractères sur le terminal du système de
développement (ie. le PC) via une liaison série.
$ ll
total 144K
-rw-r--r-- 1 phil phil 8.4K Apr 19 22:19 ser_hello.asm
-rw-r--r-- 1 phil phil 466 Apr 18 21:38 ser_hello.c
-rw-r--r-- 1 phil phil 12K Apr 19 22:19 ser_hello.ihx
-rw-r--r-- 1 phil phil 193 Apr 19 22:19 ser_hello.lnk
-rw-r--r-- 1 phil phil 22K Apr 19 22:19 ser_hello.lst
-rw-r--r-- 1 phil phil 14K Apr 19 22:19 ser_hello.map
-rw-r--r-- 1 phil phil 1.1K Apr 19 22:19 ser_hello.mem
-rw-r--r-- 1 phil phil 3.7K Apr 19 22:19 ser_hello.rel
-rw-r--r-- 1 phil phil 22K Apr 19 22:19 ser_hello.rst
-rw-r--r-- 1 phil phil 37K Apr 19 22:19 ser_hello.sym
Pour démarrer un nouveau développement, il suffit de copier ce patron dans un nouveau répertoire et d'affecter à
la variable BASENAME le nom du nouveau fichier source en langage C.
Ce patron de Makefile suppose que l'on utilise systématiquement la liaison série comme interface utilisateur. Il fait
donc référence au fichiers sources et en-têtes qui permettent d'utiliser cette liaison série. Ces fichiers sont aussi
présentés dans ce document à la Section 14.2, « Sources MSC1210 ».
Il est important de désactiver le contrôle matériel de flux pour que la fonction de détection automatique du débit
puisse s'exécuter normalement.
Il faut positionner l'interrupteur «vers le haut» pour accéder au moniteur intégré du microcontrôleur. Après un
appui sur le bouton Reset, on doit obtenir l'invite de commande suivante :
MSC1210 Ver:000305F10
>
Il faut effacer le contenu de la mémoire du microcontrôleur avant de procéder au transfert. La séquence à saisir
est la suivante :
>M0000 ok
>M8000 ok
>E
>L
On peut ensuite lancer le transfert de fichier en mode ASCII après avoir sélectionné le fichier ser_hello.ihx :
+---------[ascii upload - Press CTRL-C to quit]---------+
|.......................................................|
|7.8 Kbytes transferred at 1991 CPS.....................|
|.......................................................|
|.......................................................|
|11.5 Kbytes transferred at 1962 CPS... Done. |
| |
| READY: press any key to continue...
On obtient alors un affichage de la chaîne de caractères «tant attendue» sur le terminal du PC de développement.
MSC1210 Ver:000305F10
>M0000 ok
>M8000 ok
>E
>T
>
>Hello World !
Le coeur MCS51 «amélioré» comprend deux pointeurs de données et exécute la plupart des instructions trois fois plus
vite qu'un coeur 8051 standard. Avec une vitesse d'exécution plus grande, ce composant est utilisable avec un grand
nombre d'applications qui réclament des performances en fréquence.
La liste des documents de référence sur cette famille de microcontrôleurs est donnée à la Section 15.3,
« Microcontrôleurs MSC12xx ».
7. Éléments de Langage C
Cette section présente succinctement les éléments essentiels du Langage C en donnant un maximum d'exemples adaptés
au développement sur système spécialisé.
On utilise un exemple de programme presque aussi connu que le très fameux «Hello, World!» ; le calcul des nombres
de Fibonacci. On commence par présenter le listing complet, puis on détaille les éléments de la syntaxe du Langage C.
Le même programme est présenté sous plusieurs formes différentes en fonction du contexte de développement.
Exemple 3. Calcul des nombres de Fibonacci sur GNU/Linux avec GCC
01: /* $Id: gcc_fibonacci_single.c 1129 2007-05-07 15:07:52Z latu $
02: * Un programme simple pour calculer les n premiers nombres de Fibonacci */
03:
04: #include <stdio.h>
05: #include <stdlib.h>
06:
07: #define MAX_FIB 49
08:
09: main() {
10:
11: int n, i;
12: unsigned int nombrePrecedent, nombreCourant, fibonacci;
13:
14:
15: printf("Saisir le nombre de calculs de Fibonacci(n) :\n");
16: scanf("%d", &n);
17: if (n >= MAX_FIB) {
18: printf("Fibonacci(%d) depasse la capacite d'un mot de 32 bits\n", n);
19: exit(1);
20: }
21:
22: nombrePrecedent = 0;
23: nombreCourant = 1;
24: for (i = 0; i < n; i++) {
25: if (i > 1) {
26: fibonacci = nombrePrecedent + nombreCourant;
27: nombrePrecedent = nombreCourant;
28: nombreCourant = fibonacci;
29: printf("Fibonacci (%d) = %u\n", i, fibonacci);
30: }
31: else
32: printf("Fibonacci (%d) = %u\n", i, i);
33: }
34: }
Fibonacci (10) = 55
Comme dans le cas du premier programme présenté, le code source du programme destiné au microcontrôleur
MSC1210 inclut le pilotage de l'interface série. Dans ce contexte de développement, on ne dispose pas de système
d'exploitation.
Exemple 4. Calcul des nombres de Fibonacci sur microcontrôleur MSC1210 avec SDCC
01: #include <stdio.h>
02: #include <stdlib.h>
03: #include <msc1210.h>
04: #include "ser_msc1210.h"
05:
06: #define MAX_FIB 49
07:
08: // surdefinition du sous-programme putchar
09: void putchar(char c) {
10: ser_putc(c);
11: }
12:
13: void main(void) {
14:
15: unsigned char s[3] = {'\0','\0','\0'};
16: int n, i;
17: unsigned long nombrePrecedent, nombreCourant, fibonacci;
18:
19: // Appuyer sur <Entree> pour ajuster automatiquement
20: // le debit de la liaison
21: autobaud();
22: EA=1;
23:
24: do {
25: printf("Saisir le nombre de calculs de Fibonacci :\r\n");
26: ser_gets_echo(s, 2);
27: n = atoi(s);
28: if (n >= MAX_FIB)
29: printf("\nFibonacci(%d) depasse la capacite d'un mot de 32 bits\r\n", n);
30: } while (n >= MAX_FIB);
31:
32: printf("\r\n\n");
33: nombrePrecedent = 0;
34: nombreCourant = 1;
35: for (i = 0; i < n; i++) {
36: if (i > 1) {
37: fibonacci = nombrePrecedent + nombreCourant;
38: nombrePrecedent = nombreCourant;
39: nombreCourant = fibonacci;
40: printf("Fibonacci (%d) = %lu\r\n", i, fibonacci);
41: }
42: else
43: printf("Fibonacci (%d) = %d\r\n", i, i);
44: }
45: while(1);
46: }
Fibonacci (0) = 0
Fibonacci (1) = 1
Fibonacci (2) = 1
Fibonacci (3) = 2
Fibonacci (4) = 3
Fibonacci (5) = 5
Fibonacci (6) = 8
Fibonacci (7) = 13
Fibonacci (8) = 21
Fibonacci (9) = 34
Fibonacci (10) = 55
7.1. Délimiteurs
Généralités sur les délimiteurs de syntaxe du Langage C des exemples ci-dessus :
Les commentaires
Ils sont repérés par deux slashes (//) si le texte du commentaire ne comprend qu'une seule ligne. Ils sont délimités
entre les balises /* et */ si le texte du commentaire comprend plusieurs lignes.
• Exemple 3, « Calcul des nombres de Fibonacci sur GNU/Linux avec GCC » : les lignes 1 et 2 correspondent
à un même commentaire.
• Exemple 4, « Calcul des nombres de Fibonacci sur microcontrôleur MSC1210 avec SDCC » : les lignes 8, 19
et 20 sont des commentaires sur une ligne unique.
Ces directives ne sont pas considérées comme faisant partie du code. Elles ne sont donc pas délimitées comme
des instructions «classiques».
• Exemple 3, « Calcul des nombres de Fibonacci sur GNU/Linux avec GCC » : lignes 4, 5 et 7.
• Exemple 4, « Calcul des nombres de Fibonacci sur microcontrôleur MSC1210 avec SDCC » : lignes 1 à 4 puis 6.
Les instructions
Elles sont séparées par le caractère point-virgule :
printf ("Hello,");
printf (" World!");
Dans les deux exemples de référence ci-dessus, on relève que toutes les instructions (déclarations de variables,
opérations, etc.) sont délimitées par des caractères ';' à l'exception des directives du préprocesseur.
Une variable sert au stockage d'une information en vue de sa réutilisation dans le programme principal et|ou un sous-
programme.
Du point de vue gestion de la mémoire (RAM ou Random Access Memory), une variable est une zone mémoire réservée
et «étiquetée» avec le nom de cette variable.
Sur une machine avec système d'exploitation, la phase de déclaration d'une variable correspond à une demande de
réservation mémoire au gestionnaire de mémoire virtuelle du noyau. Ce gestionnaire doit gérer au mieux ces demandes
en fonction des programmes actifs sur le système.
Sur un système spécialisé, c'est au développeur de gérer «manuellement» son espace mémoire. Il n'existe aucune
protection contre le débordement de la capacité mémoire.
Du point de vue syntaxique, il existe certaines restrictions dans l'emploi des noms de variables et de constantes. Les
caractères de ponctuation ne sont pas utilisables. Le caractère «souligné» (underscore) est considéré comme une lettre ;
il est beaucoup utilisé par le préprocesseur.
Avertissement
En Langage C, les majuscules et les minuscules sont traitées différemment. On utilise généralement les
majuscules pour les définitions de constantes et les minuscules pour les variables.
Suivant la génération du compilateur utilisé, le nombre de caractère pris en compte pour les noms de variables est
plus ou moins limité. Historiquement, cette limite était fixée à 8 bien que l'on puisse en utiliser davantage dans le
code source.
Dans les deux exemples ci-dessus, on trouve les déclarations des variables après l'accolade de début de programme
principal (fonction main()).
• Exemple 3, « Calcul des nombres de Fibonacci sur GNU/Linux avec GCC » : lignes 11 et 12.
• Exemple 4, « Calcul des nombres de Fibonacci sur microcontrôleur MSC1210 avec SDCC » : lignes 15 à 17.
caractère char
À la lecture de la table ci-dessus, on constate que le langage C n'offre pas de type booléen. Pour autant, la valeur entière
0 correspond à l'état faux et toutes les autres valeurs entières correspondent à l'état vrai. La technique usuelle pour
représenter les deux états booléens consiste à définir deux constantes. Voir Section 7.4, « Constantes ».
En Langage C, si les noms de types sont toujours les mêmes, leurs formats diffèrent suivant les chaînes de
développement et les processeurs utilisés. Les deux exemples ci-dessous illustrent ces différences.
Dans le cas de la chaîne de développement GNU utilisée sur PC, il est possible de représenter de très grands nombres
réels sur 12 octets soit 96 bits. À l'inverse, il n'est pas possible de représenter une donnée sur moins d'un octet.
Voici le résultat d'un programme utilisant la fonction sizeof pour chacun des types donnés dans la liste ci-dessus. Le
code source de ce programme est disponible à l'adresse : gcc_sizeof.tar.bz222.
Exemple 5. Formats des types supportés par GCC
Dimension de chaque type en octets :
'char' : 1
'unsigned char' : 1
'short' : 2
'int' : 4
'long' : 4
'long long' : 8
'float' : 4
'double' : 8
'long double' : 12
Dans le cas du compilateur SDCC et du microcontrôleur cible MSC1210, il n'est pas possible d'utiliser des nombres
réels au delà de la représentation simple sur 4 octets. La plus petite unité représentée est le bit même si l'appel à la
fonction sizeof pour ce type produit un résultat surprenant 1 octet pour 1 bit.
Comme avec la chaîne de développement GNU/GCC, voici le résultat d'un programme utilisant la fonction sizeof
pour chacun des types donnés dans la liste ci-dessus. Le code source de ce programme est disponible à l'adresse :
sdcc_sizeof.tar.bz223.
Exemple 6. Formats des types supportés par SDCC
>Dimension de chaque type en octets :
'char' : 1
'unsigned char' : 1
'short' : 2
'int' : 2
'long' : 4
'long long' : 4
'float' : 4
Attention ! le type bit existe :
'bit' : 1
• Exemple 3, « Calcul des nombres de Fibonacci sur GNU/Linux avec GCC » : lignes 11 et 12. Ce programme est
limité au calcul des nombres de Fibonacci qui ne dépassent pas la capacité d'un entier représenté sur 32 bits (soit 4
octets). On utilise donc le type int dans le code source du programme.
• Exemple 4, « Calcul des nombres de Fibonacci sur microcontrôleur MSC1210 avec SDCC » : ligne 16 et 17. Ce
programme est aussi limité à une représentation des nombres entiers sur 32 bits. Avec le compilateur SDCC, c'est
le type long qui est utilisé dans le code source du programme pour répondre au critère de calcul.
7.4. Constantes
Une constante est une valeur particulière que l'on utilise tout au long du programme. Si cette valeur doit être modifiée,
il est préférable de n'avoir qu'une seule ligne à éditer. En terme de maintenabilité du code source, l'utilisation de
constantes est très avantageux. De plus, l'utilisation d'une représentation littérale d'une valeur numérique particulière
permet de gagner en lisibilité.
En informatique industrielle, on fait un usage «massif» des constantes. En effet tous les registres et les adresses de
pilotage des périphériques spécialisés sont représentés à l'aide de constantes. Si ce mode de représentation n'existait
22
http://www.linux-france.org/prj/embedded/sources/gcc_sizeof.tar.bz2
23
http://www.linux-france.org/prj/embedded/sources/sdcc_sizeof.tar.bz2
pas, on ne pourrait utiliser que des valeurs hexadécimales. Les difficultés de développement des programmes seraient
beaucoup plus importantes du fait des confusions possibles entre des valeurs voisines.
En Langage C, les constantes sont en fait des directives destinées au préprocesseur. En effet, comme le rôle du
préprocesseur est d'analyser la syntaxe du code source d'un programme, il peut se charger de substituer toutes les
occurrences des constantes par leurs valeurs numériques avant de passer à la compilation proprement dite.
Cette technique est très avantageuse puisque l'utilisation des constante ne nécessite aucune réservation en mémoire.
Les deux exemples de programmes montrent la même constante MAX_FIB déclarée à l'aide de la directive #define.
Cette constante correspond aux rang maximum du nombre de Fibonacci que l'on peut représenter sur 32 bits.
Avertissement
Il faut noter qu'une directive de préprocesseur n'est pas instruction du langage C. C'est la raison pour
laquelle on n'utilise pas le caractère ';' comme délimiteur. Cette absence de délimitation ne pose pas de
difficulté dans la mesure où on ne peut trouver qu'une directive par ligne.
• Exemple 3, « Calcul des nombres de Fibonacci sur GNU/Linux avec GCC » : ligne 7.
• Exemple 4, « Calcul des nombres de Fibonacci sur microcontrôleur MSC1210 avec SDCC » : ligne 6.
Dans les exemples «rituels» de définition de constantes on trouve les deux états booléens :
#define FALSE 0
#define TRUE 1
Le fichier d'en-tête standard limits.h est un exemple caractéristique de liste de définitions de constantes. Ce fichier
contient les définitions des valeurs entières maximales pour chaque type connu. Voici un extrait du fichier fourni avec
la chaîne GNU/GCC :
/* Number of bits in a `char'. */
# define CHAR_BIT 8
Opérateur modulo, %
Cet opérateur, noté %, renvoie le reste de la division entre 2 entiers. Par exemple : 24 % 9 = 6
La position de l'opérateur, à la gauche ou à la droite du nom de variable à une signification particulière. Dans
l'expression 'while (++n < 10)', la variable n sera incrémentée avant d'être comparée à 10. À l'inverse, dans
l'expression 'while (n++ < 10)', la variable n est comparée à 10 avant d'être incrémentée.
• Exemple 3, « Calcul des nombres de Fibonacci sur GNU/Linux avec GCC » : à la ligne 24, on trouve un opérateur
d'incrémentation du compteur des calculs à effectuer.
• Exemple 4, « Calcul des nombres de Fibonacci sur microcontrôleur MSC1210 avec SDCC » : à la ligne 35, on
retrouve le même opérateur d'incrémentation.
== égalité
!= différence
> supérieur à
< inférieur à
• Exemple 3, « Calcul des nombres de Fibonacci sur GNU/Linux avec GCC » : lignes 17, 24 et 25. Ces opérateurs
relationnels sont utilisés pour évaluer une condition sur des valeurs entières dans les calculs.
• Exemple 4, « Calcul des nombres de Fibonacci sur microcontrôleur MSC1210 avec SDCC » : lignes 28, 30, 35 et
36. Comme dans le cas précédent, on évalue une condition sur des valeurs entières.
& ET
| OU (inclusif)
^ OU (exclusif)
Symbole Opérateur
Si l'expression est vraie, c'est l'instruction 1 qui est exécutée ; sinon c'est l'instruction 2. Les instructions 1 et 2 peuvent
correspondre à des groupes délimités par des caractères { et }. La partie else est optionnelle. Si l'instruction 2 n'existe
pas, on n'utilise pas ce mot clé.
Si la condition codée dans l'expression n'est pas vérifiée (est fausse) c'est la valeur numérique 0 qui est
renvoyée. Pour toutes les autres valeurs renvoyées par l'expression, la condition est considérée comme
vérifiée (est vraie).
En langage C, il est très fréquent d'utiliser directement les valeurs numériques dans les instructions if-
else sans coder explicitement la condition dans l'expression. De nombreux exemples de ce mode de
codage ont été volontairement placés dans les exemples de programmes fournis dans le présent document.
• Exemple 3, « Calcul des nombres de Fibonacci sur GNU/Linux avec GCC » : les lignes 17 à 20 permettent de
contrôler que l'utilisateur a bien saisi un nombre de calculs compatible avec la représentation sur 32 bits du résultat.
Si l'utilisateur saisit une valeur supérieure à la constante MAX_FIB, on affiche le message d'erreur à la console et
l'exécution du programme s'arrête. Les deux instructions à traiter si l'expression est vraie sont encadrées par des
accolades.
Les lignes 25 à 30 montrent que l'on effectue le calcul des nombres de Fibonacci pour les valeurs de la variable i
strictement supérieures à 1. Si cette condition n'est pas respectée, on affiche directement le numéro du calcul comme
résultat.
• Exemple 4, « Calcul des nombres de Fibonacci sur microcontrôleur MSC1210 avec SDCC » : les lignes 28 et
30 permettent de contrôler que l'utilisateur a bien saisi un nombre de calculs dont les résultats sont correctement
représentés sur 32 bits. Dans ce cas, on se contente d'afficher le message d'erreur. On n'utilise pas d'accolades de
délimitation de groupe d'instructions.
Comme dans la version GNU/GCC, les lignes 36 à 41 montrent que l'on effectue le calcul des nombres de Fibonacci
pour les valeurs de la variable i strictement supérieures à 1.
case ValeurN:
// Instructions
break;
default:
// Instructions
}
Le cas classique d'utilisation de l'instruction switch consiste à programmer un menu à choix multiples. La variable
à tester est de type simple puisqu'il s'agit d'un caractère. Le programme sdcc_switch.c illustre justement l'utilisation
d'un menu suivi d'un test à choix multiples.
Cette structure est très pratique lorsque le résultat de l'expression est de type simple. Lorsque l'on doit évaluer non pas
une valeur mais une plage de valeurs, il devient alors plus pratique de retenir un codage du type suivant.
if (condition1) {
// Instructions
}
else if (condition2) {
// Instructions
}
else if (condition3) {
// Instructions
}
// autres conditions
else {
// Instructions
}
Indépendemment du langage de programmation utilisé, il existe des règles dans l'emploi des boucles.
1. On emploie la boucle pour (for) lorsque l'on connaît le nombre d'itérations (ie. le nombre de fois où le traitement
doit être répété dans la boucle).
2. On emploie la boucle répéter jusqu'à (do-while) lorsque le corps de la boucle doit être exécuté au moins une fois.
3. On emploie la boucle tant que (while) lorsque le corps de la boucle peut ne pas être exécuté.
La boucle pour (for) en C/C++ comprend 3 champs distincts : l'initialisation, le test de sortie et l'incrémentation.
for (initialisation; test; incrémentation ) {
// Instructions
}
Les 3 champs définis ci-dessus peuvent être composés à l'aide de n'importe quelle expression C/C++ valide. Ce mode
de construction des boucles pour est une originalité importante du C/C++. Il faut cependant être vigilant lors du choix
des expressions placées dans ces champs. Un codage trop compact (avec des appels multiples de fonctions par exemple)
et|ou des opérations sur des variables sans relations sont très pénalisant pour la mise au point des programmes.
• Exemple 3, « Calcul des nombres de Fibonacci sur GNU/Linux avec GCC » : lignes 24 à 30. On trouve ici un
exemple de boucle pour servant à compter le nombre de calculs à effectuer. La variable i sert au comptage. Elle
est initialisée à 0 dans le premier champ. Le comptage se poursuit tant que la variable i est strictement inférieur au
nombre n. À chaque «tour» la variable i est incrémentée.
• Exemple 4, « Calcul des nombres de Fibonacci sur microcontrôleur MSC1210 avec SDCC » : lignes 35 à 41. On
retrouve une syntaxe identique à l'exemple précédent.
Comme dans le cas de la boucle précédente, la condition testée peut être composée à l'aide de n'importe quelle
expression C/C++ valide. Il est donc préférable de limiter la complexité de cette expression pour faciliter la mise au
point des programmes.
• Exemple 4, « Calcul des nombres de Fibonacci sur microcontrôleur MSC1210 avec SDCC » : lignes 24 à 30. Ici, la
saisie du nombre de calculs à effectuer dans la suite du programme est répétée tant que la valeur est supérieure ou
égale à la limite MAX_FIB. La limite MAX_FIB correspond au maximum pour lequel le calcul du nombre de Fibonacci
est représentable sur 32 bits.
• Exemple 9, « Utilisation des fonctions getchar & putchar avec GCC » : lignes 7 et 8. On trouve ici un exemple
de boucle tant que servant à reproduire à la console le caractère saisi au clavier tant que celui-ci est différent du code
ASCII EOF. On remarque que la boucle ne comprend qu'une seule instruction putchar(c). C'est la raison pour
laquelle les accolades ne sont pas nécessaires. Voir Section 7.1, « Délimiteurs ».
• Exemple 10, « Utilisation des fonctions getchar & putchar sur microcontrôleur MSC120 avec SDCC » : lignes
24 à 28. On retrouve le même traitement que dans l'exemple précédent. On a simplement ajouté un test nécessaire
au saut le ligne lors d'un appui sur la touche Entrée.
7.11. Sous-programmes
La démarche classique de construction d'un programme informatique veut que l'on découpe un problème complexe en
sous-ensembles plus faciles à traiter. Du point de vue du codage, cette démarche se traduit par l'utilisation de sous-
programmes correspondant à ces sous-ensembles.
La réutilisation de code est un autre argument pour l'emploi des sous-programmes. Si un même traitement doit être
répété à plusieurs reprises dans un programme, il est préférable de le placer dans un sous-programme qui sera appelé
autant de fois que nécessaire.
Enfin, le recours aux sous-programmes doit faciliter la lecture et la mise au point des programmes. Un jeu de sous-
programmes bien écrit et compréhensible par un grand nombre de programmeurs peut être réutilisé sous forme de
bibliothèque.
La bibliothèque glibc est probablement l'exemple le plus emblématique de bibliothèque partagée. Elle est disponible
avec la totalité des chaînes de développement en Langage C sur tous les systèmes d'exploitation et pour presque tous
les processeurs.
Les fonctions getchar, putchar, printf et atoi sont quelques exemples de sous-programmes partagés à l'aide de
la bibliothèque glibc. On les retrouve dans les programmes d'illustration présentés dans ce document.
Exemple 7. Utilisation d'un sous-programme dans le calcul des nombres de Fibonacci
01: #include <stdio.h>
02: #include <stdlib.h>
03:
On retrouve aussi des résultats identiques aux précédents avec le programme sdcc_fibonacci_iterative.c.
MSC1210 Ver:000305F10
>M0000 ok
>M8000 ok
>E
>T
>
>Saisir le nombre de calculs de Fibonacci :
11
Fibonacci (0) = 0
Fibonacci (1) = 1
Fibonacci (2) = 1
Fibonacci (3) = 2
Fibonacci (4) = 3
Fibonacci (5) = 5
Fibonacci (6) = 8
Fibonacci (7) = 13
Fibonacci (8) = 21
Fibonacci (9) = 34
Fibonacci (10) = 55
• Exemple 7, « Utilisation d'un sous-programme dans le calcul des nombres de Fibonacci » : Dans le programme
principal (main()), à la ligne 35, on trouve l'appel au sous-programme iterative_fibonacci(i). La variable
i est passée comme paramètre d'entrée lors de l'appel au sous-programme.
Sur un ordinateur, les caractères sont généralement lus à partir d'un clavier. Ils peuvent cependant provenir de
différents «canaux» : fichiers, redirections de flux ou opérations de séquencement entre les programmes de la
couche Shell (pipes ou tubes).
Sur un système embarqué les caractères peuvent provenir des même canaux. Sans système d'exploitation, la liste
des canaux est limitée aux interfaces disponibles. Dans les exemples présentés dans ce document, c'est la liaison
série qui sert le plus souvent de flux d'entrée standard.
Par principe, la fonction getchar transmet au programme appelant le caractère suivant de la file d'entrée à chaque
appel.
Sur un ordinateur, les caractères sont émis par défaut vers le terminal (ie. la fenêtre du Shell courant). Comme pour
toute manipulation au niveau Shell, les opérations de séquencement, de redirection et les tubes sont utilisables.
Voir les exemples d'exécution ci-après.
Ces mécanismes d'entrées-sorties standard ont été définis avec les systèmes d'exploitation UNIX au début des années
70. On les retrouve aujourd'hui sur la totalité des systèmes d'exploitation contemporains. Les files d'attentes des
caractères issus de l'écran, du clavier, des fichiers stockés sur tous types de supports, etc. sont appelés flux d'entrées-
sorties (input-output streams). Des flux standards ont été définis ; chacun étant dédié à un «pseudo-périphérique».
Voici un exemple simple de programme utilisant les flux d'entrées-sorties standard via les fonctions getchar et
putchar. Ce programme recopie les caractères reçus sur le flux d'entrée stdin vers le flux de sortie stdout. Cette
copie se poursuit tant que le caractère saisi est différent du code ASCII de délimitation de fin de fichier EOF (code
obtenu en appuyant simultanément sur les touches Ctrl + D).
Exemple 9. Utilisation des fonctions getchar & putchar avec GCC
01: #include <stdio.h>
02:
03: int main() {
04:
05: char c;
06:
07: while ((c = getchar()) != EOF)
08: putchar(c);
09:
10: return 0;
11: }
L'exemple d'exécution du programme ci-dessous montre que l'opération de transfert d'un flux vers l'autre est séquencée
par le caractère de saut de ligne \n. Le flux d'entrée se «remplit» tant qu'aucun appui sur la touche Entrée n'intervient.
Dès que le code ASCII du saut de ligne entre dans le flux d'entrée standard, son contenu est transféré au programme
utilisateur via l'appel à la fonction getchar.
Les appels à la fonction putchar suivent le même fonctionnement vis-à-vis du flux de sortie standard.
Voici deux autres exemples d'exécution du même programme en utilisant les opérations de séquencement entre les
programmes offertes par le Shell.
• On commence par créer un fichier test.txt à l'aide de la commande cat. Tous les caractères saisis au clavier sont
redirigés vers le fichier. L'opération s'arrête avec la saisie du code ASCII EOF (Ctrl + D).
• Le programme est exécuté avec une nouvelle redirection : ./stdio <test.txt. Avec cette redirection, le contenu
du fichier test.txt se substitue au flux d'entrée standard stdin.
• Le programme est à nouveau exécuté en utilisant un tube (pipe). En fait, deux programmes sont exécutés : cat
test.txt et stdio. Le premier à pour but d'afficher le contenu du fichier texte sur le flux de sortie standard stdout
(ie. la console). Le fonctionnement du second programme a déjà été décrit ci-dessus. L'utilisation du tube permet de
transférer la sortie du premier programme vers l'entrée du second.
Pour plus de détails sur les manipulations à la console, voir Linux - Base d'administration pour le superutilisateur.
Voici maintenant le même exemple simple de programme utilisant les flux d'entrées-sorties standard sur un système
spécialisé. Dans ce contexte sans système d'exploitation, on ne dispose pas de définition des flux stdin et stdout
entre le noyau et le Shell. On continue cependant à utiliser les fonctions getchar et putchar.
La technique usuelle sur les systèmes spécialisés consiste à donner une nouvelle définition de ces deux fonctions en
désignant explicitement le canal de réception et d'émission des caractères. Dans l'exemple, c'est la liaison série entre
le PC de développement et le système spécialisé qui sert à faire transiter les caractères dans les deux sens.
Exemple 10. Utilisation des fonctions getchar & putchar sur microcontrôleur MSC120 avec SDCC
01: #include <stdio.h>
02: #include <msc1210.h>
03: #include "ser_msc1210.h"
04:
05: #define EOF 0x04
06: #define CR 0x0d
07:
08: char getchar(void) {
09: return ser_getc();
10: }
11:
12: void putchar(char c) {
13: ser_putc(c);
14: }
15:
16: void main(void) {
17:
18: char c;
19:
20: // Press <Enter> for auto baudrate adjust
21: autobaud();
22: EA=1;
23:
24: while ((c = getchar()) != EOF) {
25: if (c == CR)
26: putcr();
27: putchar(c);
28: }
29: putcr();
30: putchar('!');
31: putcr();
32:
33: while (1);
34: }
25
images/no_os_stdio.png
Comme on l'a déjà indiqué, il n'existe pas de couche Shell dans ce contexte. On ne dispose donc pas d'opération de
séquencement entre programmes puisque cet exemple est le seul programme exécuté par le microcontrôleur.
• Exemple 3, « Calcul des nombres de Fibonacci sur GNU/Linux avec GCC » : ce programme ne fait pas directement
appel aux fonctions getchar et putchar. Il utilise plutôt les fonctions d'entrées-sorties formatées décrites ci-après.
• Exemple 4, « Calcul des nombres de Fibonacci sur microcontrôleur MSC1210 avec SDCC » : lignes 11 à 13. La
liaison série étant utilisée comme canal d'entrée-sortie standard, on définit la fonction putchar en conséquence.
Cette définition est ensuite réutilisée pour les sorties formatées lors des appels à la fonction printf : lignes 27,
31, 34, 42 et 45. Pour la saisie du nombre de calcul, la fonction ser_gets_echo est définie dans le fichier d'en-
tête ser_msc1210.h.
• control est une chaîne de caractère qui sert à mettre en forme les arguments. Cette chaîne peut contenir des
caractères «ordinaires» qui sont recopiés directement depuis ou vers le flux d'entrée-sortie standard et des formats
de conversion positionnés dans la chaîne à l'aide du caractère balise % suivi du format choisi.
• Les arguments arg1, arg2, ..., doivent correspondre à chaque caractère % placé dans la chaîne control.
Le caractère % est une balise de positionnement suivie d'une spécification de traduction dont le prototype est :
%[indicateur][largeur][.precision][modification]type.
type signification
%c caractère
%s chaîne de caractères
[indicateur] signification
[indicateur] signification
force l'affichage du point décimal avec les types e, E et f même si la partie décimale ne
#
contient que des zéros
force l'affichage du point décimal avec les types g et G sans supprimer les zéros inutiles
• Spécifications du champ optionnel [largeur] :
Tableau 6. Largeur de l'affichage
[largeur] signification
nombre nombre minimum de caractères à afficher ; ajout de caractères d'espacement si la valeur est
plus «courte» que l'affichage demandé
0nombre idem ci-dessus ; ajout de caractères 0 si la valeur est plus «courte» que l'affichage demandé
*nombre la largeur n'est pas spécifiée dans la chaîne control mais par un entier précédent
l'argument à afficher
• Spécifications du champ optionnel [precision] :
Tableau 7. Precision de l'affichage
[precision] signification
l argument traité comme un entier long pour les types entiers (long) ou comme un réel double
pour les types réels (double)
Voici deux programmes d'illustration de sorties formatées avec la fonction printf. Le premier bénéficie du jeu de
définitions complet sur GNU/Linux. Le second, exécuté sur un microcontrôleur MSC1210 et compilé avec SDCC, ne
dispose que d'un jeu de définitions de formats plus restreint.
>
>Caracteres : a A 0 0
Entiers : 2007 650000
Affichage avec espaces : | 2007|
Affichage avec zeros : |0000002007|
Differentes bases : 100 64 144 #x #o
Reels : |3.141593| |+6305.1762| |E|
Largeur en argument : |*d|
Debian GNU/Linux
La copie d'écran ci-dessus montre que toutes les options de formatage des sorties ne sont pas supportées par la
bibliothèque standard implantée dans la chaîne de développement SDCC. C'est une situation classique, sachant qu'un
système spécialisé n'est pas fait pour réaliser une interface utilisateur.
Séquence signification
d'échappement
\t tabulation horizontale
\v tabulation verticale
Il existe un standard très populaire qui permet de gérer les communications avec la grande majorité des modèles
d'afficheurs LCD quelque soit le fabricant. Ce standard est connu sous le le nom HD44780U. Ce nom correspond au
contrôleur qui reçoit les données du microcontrôleur et qui les communique directement avec l'afficheur à cristaux
liquides.
EN, enable
Le signal enable est utilisé pour indiquer à l'afficheur à cristaux liquides qu'on va lui envoyer des données. Pour
envoyer des données à l'afficheur, le programme doit s'assurer que le signal est au niveau bas (0) avant de faire
évoluer les deux autres signaux de contrôle et de placer des données sur le bus de données. Quand l'ensemble des
autres lignes sont correctement positionnées, le signal EN doit être placé au niveau haut (1) et il faut attendre un
temps minimum requis par le composant (ce temps varie d'un afficheur à l'autre) avant de le placer au niveau bas
(0) à nouveau.
RW, read/write
Le signal read/write est utilisé pour distinguer si les informations sur le bus de données sont lues ou écrites. Si
on place le signal RW au niveau haut (1), le programme interroge l'afficheur à cristaux liquides sur son état (Get
LCD status). Dans tous les autres cas des commandes sont écrites. Ainsi, le signal RW sera presque toujours au
niveau bas.
10.2. Utilisation d'un afficheur LCD avec SDCC sur cible MSC1210
Le premier exemple reprend le programme «Hello, World!» en utilisant l'afficheur LCD comme écran.
Exemple 13. Programme «Hello, World!» sur afficheur LCD
01: #include <stdio.h>
02: #include <msc1210.h>
03: #include "ser_msc1210.h"
04: #include "lcd_msc1210.h"
05:
06: void putchar(char c) {
07: if (print2lcd)
08: lcd_putc(c);
09: else
10: ser_putc(c);
11: }
12:
13: void main(void) {
14:
15: // Appuyer sur <Entrée> pour ajuster
16: // automatiquement le (débit|baudrate)
17: autobaud();
18:
19: // Initialisations pour SDCC
20: EA=1;
21:
22: print2lcd = 0;
23:
24: // initialisation de l'afficheur LCD
25: lcd_init();
26:
27: // effacement de l'écran
28: lcd_clear();
29:
30: // positionnement du curseur dans
31: // le coin supérieur gauche
32: lcd_home();
33:
34: print2lcd = 1;
35: printf("Hello, World !");
36:
37: // Affichage du même message
38: // sur la console
39: print2lcd = 0;
40: printf("Hello, World!");
41: putcr();
42:
43: while(1);
44: }
Le second exemple illustre le positionnement du curseur d'affichage sur l'écran à cristaux liquides.
Le modèle d'écran à cristaux liquides utilisé dans les exemples suivants dispose de 4 lignes de 20 caractères. Chaque
caractère alphanumérique est accessible via une adresse définie dans le plan ci-dessous. On remarque que les adresses
de chaque ligne ne sont pas consécutives ; ce qui n'est pas des plus pratique pour gérer le positionnement du curseur
d'affichage.
ligne 0 : 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13
ligne 1 : 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53
ligne 2 : 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27
ligne 3 : 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67
Exemple 14. Programme de positionnement du curseur sur un afficheur LCD
01: #include <stdio.h>
02: #include <stdlib.h>
03: #include <msc1210.h>
04: #include "ser_msc1210.h"
26
images/hello_lcd.png
>M0000 ok
>M8000 ok
>E
>T
>
>Programme de positionnement du curseur
L'abscisse X est comprise entre 0 et 19
L'ordonnee Y est comprise entre 0 et 3
(Sortie pour une valeur X > 19)
Saisie de x : 5
Saisie de y : 2
27
images/lcd_set_xy.png
Voici un programme «minimaliste» d'illustration du fonctionnement des entrées/sorties sur un bit : sdcc_ttl_1bit.c. À
chaque appui sur le bouton poussoir, on allume la LED rouge. Tous les quatre appuis sur le même bouton poussoir,
on allume la LED jaune.
01: #include <stdio.h>
02: #include <msc1210.h>
03: #include "ser_msc1210.h"
04:
05: __sbit __at (0xb2) IN_SW2; // p3.2
06: __sbit __at (0xb4) OUT_RED; // p3.4
07: __sbit __at (0xb5) OUT_YELLOW; // p3.5
08:
09: void putchar(char c) {
10: ser_putc(c);
11: }
12:
13: bit antirebond() {
14: unsigned char i = 0;
15: unsigned int j = 0;
16: bit sw_now = 1, sw_previous = 1;
17:
18: while (i < 5) {
19: sw_previous = IN_SW2;
20: // Boucle de temporisation
21: for(j = 0; j < 8000; j++)
22: ;
23: sw_now = IN_SW2;
24: if (sw_now == sw_previous)
25: i++;
26: }
27:
28: return sw_now;
29: }
30:
31: void main(void) {
32:
33: unsigned char c, i = 0;
34: bit sw;
35:
36: /* Appuyer sur Entrée pour ajuster automatiquement
37: * le débit sur la liaison série */
38: autobaud();
39: EA=1;
40:
41: puts("Lecture SW2/Ecriture LED");
42: putcr();
43: while (1) {
44: if (sw = antirebond())
45: OUT_RED = 1;
46: else {
47: OUT_RED = 0;
48: if (++i % 4)
49: OUT_YELLOW = 1;
50: else
51: OUT_YELLOW = 0;
52: }
53: c = sw + 0x30;
54: putchar(c);
55: putchar('\r');
56: }
57: }
Une fois ces affectations en place, on utilise les noms d'entrées/sorties de la même façon que des variables.
Comme avec n'importe quel type de variable, il faut respecter les espaces de représentation mémoire des entrées/
sorties. Dans l'exemple, on a désigné des entrées/sorties de type bit. Il n'est donc pas question de placer le contenu
d'une variable qui occupe plus d'un bit sur une sortie de type bit. À l'inverse, le transtypage automatique offert
par le langage C permet très bien de stocker un bit dans une variable dont l'occupation mémoire est plus grande. À
la ligne ligne 53, on place le bit sw dans c qui est une variable de type character dont l'espace de représentation
est l'octet. Voir la Section 7.3, « Types de représentation des données ».
La fonction présentée ici utilise une boucle de temporisation (lignes 20 à 22). L'emploi de ce type de boucle doit
être évité au maximum mais le bouton poussoir de la carte MSC1210 EValuation Module a été câblé directement
sur la broche P3.2 du microcontrôleur sans cellule RC de filtrage des rebonds.
Le compteur i sert à contrôler que l'on a lu 5 fois le même état entre chaque temporisation.
Sans appui sur le bouton poussoir, la fonction antirebond() renvoie un bit à 1. On éteint alors la LED rouge en
envoyant un niveau 1 sur la broche P3.4 du microcontrôleur : OUT_RED = 1;.
Lors d'un appui sur le bouton poussoir, la fonction antirebond() renvoie un bit à 0. On allume alors la LED
rouge en envoyant un niveau 0 : OUT_RED = 0;.
C'est aussi lors d'un appui sur le bouton poussoir que l'on incrémente le compteur i. On teste ensuite si le reste
de la division entière de i par 4 est nul. Si la condition est vérifiée, on allume la LED jaune. Ce codage permet
d'allumer la LED jaune tous les quatre appuis sur le bouton poussoir.
L'évaluation des conditions des lignes 44 et 48 utilise directement les résultats numériques des expressions codées
avec l'instruction if-else. Si l'expression renvoie la valeur 0, c'est que le test est faux. Voir Une condition fausse
vaut toujours 0.
Comme la scrutation de l'état du bouton poussoir est continue, on envoie le code ASCII de retour en début de ligne
'\r' après chaque affichage de la valeur du bit d'état. De cette façon, on évite de remplir l'écran de la console
et l'affichage est donc plus lisible.
L'utilisation du port P0 a été «étendue» en utilisant deux sorties du port P3 comme signaux de contrôles. Ces bits de
contrôle servent à sélectionner un composant chacun : un pour les entrées et un pour les sorties. De plus, deux bits
supplémentaires des ports P3 et P1 ont été alloués comme entrées et sorties. Le schéma ci-dessous montre que l'on
dispose de 10 entrées et 10 sorties logiques.
Voici un programme d'illustration du fonctionnement des entrées/sorties sur plusieurs bits : sdcc_ttl_io_iut.c. Il propose
un menu qui permet de lire ou écrire 10 bits simultanément ainsi que la lecture ou l'écriture d'un bit individuel parmi 10.
01: #include <stdio.h>
02: #include <stdlib.h>
03: #include <msc1210.h>
04: #include "ser_msc1210.h"
05: #include "iut_msc1210.h"
06:
07: #define EOF 0x04
08: #define MAXCHAR 4
09:
10: char getchar(void) {
11: return ser_getc();
12: }
13:
14: void putchar(char c) {
15: ser_putc(c);
16: }
28
images/p0_ttl_extension.png
17:
18: unsigned char menu(void) {
19:
20: putcr();
21: putcr();
22: puts("------- Menu -------");
23: putcr();
24: puts("(a) : lecture des 10 entrees");
25: putcr();
26: puts("(b) : ecriture des 10 sorties");
27: putcr();
28: puts("(c) : saisie de la valeur a ecrire (< 1024)");
29: putcr();
30: puts("(d) : lecture bit individuel (0 <= b <= 9)");
31: putcr();
32: puts("(e) : ecriture bit individuel (0 <= b <= 9)");
33: putcr();
34: puts("(EOF) : taper CTRL D pour sortir");
35: putcr();
36: puts("--------------------");
37: putcr();
38:
39: return getchar();
40: }
41:
42: void main(void) {
43:
44: unsigned int val = 0;
45: unsigned char c, chaine[MAXCHAR], num;
46:
47: /* Appuyer sur Entrée pour ajuster automatiquement
48: * le débit sur la liaison série */
49: autobaud();
50: EA=1;
51:
52: while ((c = menu()) != EOF) {
53: switch (c) {
54: case 'a':
55: val = lecture_10bits();
56: break;
57: case 'b':
58: ecriture_10bits(val);
59: break;
60: case 'c':
61: puts("Valeur :");
62: putcr();
63: ser_gets_echo(chaine, MAXCHAR);
64: val = atoi(chaine);
65: putcr();
66: break;
67: case 'd':
68: do {
69: puts("Numero du bit :");
70: putcr();
71: ser_gets_echo(chaine, MAXCHAR);
72: num = atoi(chaine);
73: putcr();
74: } while (num > 9);
75: val = lecture_1bit(num);
76: break;
77: case 'e':
78: do {
Cette section traite de l'utilisation des ports «classiques» du microcontrôleur. Les périphériques spécialisés tels que les
timers ou le port SPI font l'objet de sections particulières.
On présente donc un exemple de programme qui permet d'évaluer les différents types de temporisations logicielles ainsi
que le temps d'exécution des différentes boucles du Langage C. Le code source du programme complet est disponible
sous le nom : sdcc_port_timings.c.
Pour chaque exemple de temporisation logicielle, on présente le code en Langage C, le code assembleur correspondant
ainsi que les mesures de temps pour un appel unique et pour une exécution supplémentaire.
Remarque importante : les mesures temps ont été réalisées à l'aide d'un microcontrôleur MSC1210Y5 cadencé à
22.1184MHz.
• Codage en Langage C :
#define nop() {_asm nop _endasm;}
TEST_PIN = 0;
nop();
TEST_PIN = 1;
• Code assembleur produit par le compilateur sdcc :
; sdcc_port_timings.c:79: TEST_PIN = 0;
clr _TEST_PIN
; sdcc_port_timings.c:80: _asm nop _endasm;
nop
; sdcc_port_timings.c:81: TEST_PIN = 1;
setb _TEST_PIN
• Temps mesuré à l'analyseur logique :
• Codage en Langage C :
; sdcc_port_timings.c:98: TEST_PIN = 0;
clr _TEST_PIN
; sdcc_port_timings.c:99: inline_delay(v);
lcall _inline_delay
; sdcc_port_timings.c:100: TEST_PIN = 1;
setb _TEST_PIN
• Temps mesuré à l'analyseur logique :
L'appel du sous-programme inline_delay(n) dure 2.890µs et l'incrémentation d'une unité de la variable n ajoute
720ns au temps d'exécution du sous-programme.
temps d'exécution d'une temporisation écrite en assembleur en ligne avec le paramètre n = 130
temps d'exécution d'une temporisation écrite en assembleur en ligne avec le paramètre n = 231
12.3. Temps d'exécution d'une temporisation basée sur une boucle tant-que
À partir de cette étape, on n'utilise plus d'artifice d'optimisation et on s'appuie entièrement sur le travail du compilateur.
Une boucle while doit avoir un temps d'exécution plus court que les autres types de boucles sachant qu'elle utilise
moins d'opérations.
• Codage en Langage C :
void while_delay (unsigned char n) {
while (n--)
;
}
; sdcc_port_timings.c:114: TEST_PIN = 0;
clr _TEST_PIN
; sdcc_port_timings.c:115: while_delay(v);
mov dpl,r2
lcall _while_delay
; sdcc_port_timings.c:116: TEST_PIN = 1;
setb _TEST_PIN
L'appel du sous-programme while_delay(n) dure 5.040µs et l'incrémentation d'une unité de la variable n ajoute
1.280µs au temps d'exécution du sous-programme.
temps d'exécution d'une temporisation basée sur une boucle tantque avec le paramètre n = 132
temps d'exécution d'une temporisation basée sur une boucle tantque avec le paramètre n = 233
12.4. Temps d'exécution d'une temporisation basée sur une boucle pour
Comme dans le cas précédent, on s'appuie entièrement sur le compilateur. Sachant qu'une boucle for effectue
davantage d'opérations relativement à la boucle while, le temps d'exécution doit être plus important que dans tous
les cas précédents.
• Codage en Langage C :
void for_delay (unsigned char n) {
_for_delay:
mov r2,dpl
; sdcc_port_timings.c:59: for (i = 0; i < n; i++)
mov r3,#0x00
00101$:
clr c
mov a,r3
subb a,r2
jnc 00105$
inc r3
sjmp 00101$
00105$:
ret
; sdcc_port_timings.c:136: TEST_PIN = 0;
clr _TEST_PIN
; sdcc_port_timings.c:137: for_delay(v);
mov dpl,r2
lcall _for_delay
; sdcc_port_timings.c:138: TEST_PIN = 1;
setb _TEST_PIN
• Temps mesuré à l'analyseur logique :
L'appel du sous-programme for_delay(n) dure 5.790µs et l'incrémentation d'une unité de la variable n ajoute
1.790µs au temps d'exécution du sous-programme.
temps d'exécution d'une temporisation basée sur une boucle pour avec le paramètre n = 134
temps d'exécution d'une temporisation basée sur une boucle pour avec le paramètre n = 235
Dev-Cpp39 est téléchargeable à partir du site SourceForge40. Le fichier exécutable de la forme devcpp-
x.x.x_x_setup.exe comprend l'interface graphique et tous les outils de développement. Il n'est pas nécessaire
d'installer d'outils supplémentaires pour commencer à développer en C/C++.
Ce compilateur est une alternative intéressante pour les microcontrôleurs qui ne sont pas directement supportés par
la chaîne d'outils GNU.
Les composants Texas Instruments™ de la famille MSC1210 sont des convertisseurs analogiques/numériques (ADC)
qui intègrent un microcontrôleur 8051 avec 2Ko de ROM contenant des fonctions de pilotage de périphériques et de
contrôle. Ce type de composant entre donc bien dans les cibles du compilateur SDCC.
Sur Windows XP
Le compilateur et ses bibliothèques sont téléchargeables à partir du site SourceForge41. Le fichier exécutable de
la forme sdcc-2.6.0-setup.exe disponible à la rubrique sdcc-win3242 de la page de téléchargement du projet.
Comme pour tout logiciel libre, les sources de la chaîne de développement sont disponibles à partir des pages Web
du projet SDCC - Small Device C Compiler45 sur SourceForge46. Pour bénéficier des toutes dernières avancées de
l'équipe de développement, il est possible de compiler la chaîne de développement.
Toutes les mises à jour suivantes se font avec la commande svn update.
phil@b0x:~/SVN/sdcc$ svn update
À la révision 4788.
Build:
Host:
Source directory: .
C compiler: gcc
CFLAGS: -pipe -ggdb -g -O2
ENABLED Ports:
avr yes
ds390 yes
ds400 yes
gbz80 yes
hc08 yes
mcs51 yes
pic yes
pic16 yes
xa51 yes
z80 yes
Disable packihx: 0
44
http://www.debian.org/
45
http://sdcc.sf.net/
46
http://www.sourceforge.net/
Disable ucsim: 0
Disable device lib: 0
Disable sdcpp: 0
Disable sdcdb: 0
Enable documentation: 0
Enable libgc: 0
Install paths:
binary files: ${prefix}
include files: ${datarootdir}/sdcc/include
library files: ${datarootdir}/sdcc/lib
documentation: ${datarootdir}/doc/${PACKAGE_TARNAME}
prefix: /usr/local
datadir: ${datarootdir}
datarootdir: ${prefix}/share
• La commande make lance les opérations de compilation en fonction des cibles définies lors de l'étape précédente.
phil@b0x:~/SVN/sdcc$ make
for misc in debugger/mcs51 sim/ucsim; do make -C $misc ; done
make[1]: entrant dans le répertoire « /home/phil/SVN/sdcc/debugger/mcs51 »
make[1]: quittant le répertoire « /home/phil/SVN/sdcc/debugger/mcs51 »
make[1]: entrant dans le répertoire « /home/phil/SVN/sdcc/sim/ucsim »
make -f main.mk all
make[2]: entrant dans le répertoire « /home/phil/SVN/sdcc/sim/ucsim »
make[2]: quittant le répertoire « /home/phil/SVN/sdcc/sim/ucsim »
<snipped/>
• La commande make install doit être exécutée avec les droits superutilisateur pour installer les binaires et les
bibliothèques dans l'arborescence /usr/local.
phil@b0x:~/SVN/sdcc$ su
Password:
b0x:/home/phil/SVN/sdcc# make install
<snipped/>
mkdir -p /usr/local/share/sdcc/lib/src
cp -u -r build/* /usr/local/share/sdcc/lib
cp -u -r small medium large ./mcs51 ./ds390 ./ds400 \
./gbz80 ./z80 ./hc08 \
./pic ./pic16 ./*.c /usr/local/share/sdcc/lib/src
<snipped/>
• On peut enfin vérifier que la version active de la chaîne de développement correspond aux outils que l'on vient
de compiler.
$ sdcc -v
SDCC : mcs51/gbz80/z80/avr/ds390/pic16/pic14/TININative/xa51/ds400/hc08
2.7.2 #4851 (Jun 13 2007) (UNIX)
-#if defined(__ds390)
+//#if defined(__ds390)
#define USE_FLOATS 1
-#endif
+//#endif
#include <stdarg.h>
#include <string.h>
Il suffit ensuite de reprendre la compilation des outils de la chaîne de développement et leur installation comme
présentées ci-dessus pour bénéficier du support des nombres réels.
TIDownloader est disponible à l'adresse : Software for Programming the Flash Memory Using the Serial Port v1.3.4
(Rev. C) (sbac018c.zip, 2381 KB )47
47
http://www.ti.com/litv/zip/sbac018c
gcc_sizeof.c, gcc_sizeof.tar.bz248
Fichier source C du programme d'affichage des dimensions de représentation mémoire des types simples par le
compilateur GNU/GCC. Les résultats obtenus sont à comparer avec ceux du même programme exécuté sur un
microcontrôleur. Voir le fichier source sdcc_sizeof.c pour le microcontrôleur MSC1210.
gcc_fibonacci_single.c, gcc_fibonacci_single.tar.bz249
Fichier source C du programme d'introduction aux éléments de la syntaxe du Langage C. Ce programme est
exemple simple de calculs illustrant les limites de représentation des données sur les types entiers du Langage C.
gcc_fibonacci_iterative.c, gcc_fibonacci_iterative.tar.bz250
Fichier source C du programme d'illustration d'appel à un sous-programme en Langage C.
gcc_stdio.c, gcc_stdio.tar.bz251
Fichier source C du programme d'illustration de la gestion des entrées sorties sur les flux standards stdin et
stdout. Ce programme utilise les fonctions fournies par la bibliothèque glibc.
gcc_printf.c, gcc_printf.tar.bz252
Fichier source C du programme d'illustration des formats d'affichages standards disponibles avec la fonction
printf.
rom1210.h, rom1210.h54
Fichier d'en-tête contenant les prototypes en Langage C des fonctions intégrées dans la mémoire ROM du
microcontrôleur. La documentation sur ces fonctions est donnée par le constructeur du composant : MSC1210
ROM Routines.
rom1210.asm, rom1210.asm55
Fichier source en assembleur asx8051 de définition des adresses d'appel des sous-programmes intégrés dans la
mémoire ROM du microcontrôleur. Ce fichier est utilisé avec le fichier d'en-tête contenant les prototypes des sous-
programmes en Langage C (rom1210.h). Lors de l'étape d'édition des liens, le compilateur fait correspondre les
appels de sous-programmes avec leurs adresses en mémoire ROM.
48
http://www.linux-france.org/prj/embedded/sources/gcc_sizeof.tar.bz2
49
http://www.linux-france.org/prj/embedded/sources/gcc_fibonacci_single.tar.bz2
50
http://www.linux-france.org/prj/embedded/sources/gcc_fibonacci_iterative.tar.bz2
51
http://www.linux-france.org/prj/embedded/sources/gcc_stdio.tar.bz2
52
http://www.linux-france.org/prj/embedded/sources/gcc_printf.tar.bz2
53
http://svn.sourceforge.net/viewvc/sdcc/trunk/sdcc/device/include/mcs51/msc1210.h?view=markup
54
http://www.linux-france.org/prj/embedded/sources/rom1210.h
55
http://www.linux-france.org/prj/embedded/sources/rom1210.asm
La table d'adressage des sous-programmes est fournie par le constructeur du composant : MSC1210 ROM Routines.
ser_msc1210.h, ser_msc1210.h56
Fichier d'en-tête contenant les prototypes des sous-programmes de gestion de la liaison série entre le PC de
développement et le système spécialisé.
lcd_msc1210.h, lcd_msc1210.h57
Fichier d'en-tête contenant les prototypes des sous-programmes de gestion d'un afficheur à cristaux liquides (LCD)
standard de type HD44780U.
iut_msc1210.h, iut_msc1210.h58
Fichier d'en-tête contenant les prototypes des sous-programmes de gestion des entrées/sorties spécifiques à la carte
microcontrôleur utilisée au département Génie Électrique & Informatique Industrielle de l'IUT 'A' Paul Sabatier.
On y trouve notamment les sous-programmes utilisés dans l'exemple sdcc_ttl_io_iut.c.
hello_serial.c, hello_serial.c59
Fichier de démonstration de l'utilisation des sous-programmes intégrés dans la mémoire ROM du microcontrôleur.
On peut l'utiliser comme patron pour démarrer un nouveau développement.
Makefile, Makefile.tpl60
Ce fichier est un patron de compilation pour les applications utilisateur. On peut l'utiliser comme modèle pour
démarrer un nouveau développement. Il suffit de renommer ce fichier en Makefile sans extension puis de l'éditer
en désignant le fichier source C utilisateur avec la variable BASENAME.
sdcc_sizeof.c, sdcc_sizeof.tar.bz261
Fichier source C du programme d'affichage des dimensions de représentation mémoire des types simples par le
compilateur SDCC.
sdcc_fibonacci_single.c, sdcc_fibonacci_single.tar.bz262
Fichier source C du programme d'introduction aux éléments de la syntaxe du Langage C. Ce programme est
exemple simple de calculs illustrant les limites de représentation des données sur les types entiers du Langage C
implémentés dans la chaîne de développement SDCC.
sdcc_fibonacci_iterative.c, sdcc_fibonacci_iterative.tar.bz263
Fichier source C du programme d'introduction à l'utilisation de sous-programmes. Ce programme illustre aussi le
passage de paramètres lors de l'appel du sous-programme dans le programme principal.
sdcc_stdio.c, sdcc_stdio.tar.bz264
Fichier source C du programme d'illustration de la gestion des entrées sorties sur la liaison série de la carte
microcontrôleur. Ce programme utilise les fonctions de base fournies par le moniteur du MSC1210.
sdcc_printf.c, sdcc_printf.tar.bz265
Fichier source C du programme d'illustration des formats d'affichages standards disponibles avec la fonction
printf sur le microcontrôleur. Il montre les limites du support des formats standards sur un système embarqué
simple.
hello_lcd.c, hello_lcd.tar.bz266
Fichier source C de démonstration de l'affichage sur écran à cristaux liquides (LCD). Ce programme est l'équivalent
du fichier hello_serial.c qui utilise une console via une liaison série.
56
http://www.linux-france.org/prj/embedded/sources/ser_msc1210.h
57
http://www.linux-france.org/prj/embedded/sources/lcd_msc1210.h
58
http://www.linux-france.org/prj/embedded/sources/iut_msc1210.h
59
http://www.linux-france.org/prj/embedded/sources/hello_serial.tar.bz2
60
http://www.linux-france.org/prj/embedded/sources/Makefile.tpl
61
http://www.linux-france.org/prj/embedded/sources/sdcc_sizeof.tar.bz2
62
http://www.linux-france.org/prj/embedded/sources/sdcc_fibonacci_single.tar.bz2
63
http://www.linux-france.org/prj/embedded/sources/sdcc_fibonacci_iterative.tar.bz2
64
http://www.linux-france.org/prj/embedded/sources/sdcc_stdio.tar.bz2
65
http://www.linux-france.org/prj/embedded/sources/sdcc_printf.tar.bz2
66
http://www.linux-france.org/prj/embedded/sources/hello_lcd.tar.bz2
sdcc_lcd_set_xy.c, sdcc_lcd_set_xy.tar.bz267
Fichier source C de positionnement du curseur sur un écran à cristaux liquides (LCD). Ce programme illustre
l'utilisation de «macro» qui permet de diminuer le volume de code exécutable en effectuant les traitements au
niveau du préprocesseur.
sdcc_switch.c, sdcc_switch.tar.bz268
Fichier source C d'illustration de la syntaxe du test à choix multiple : switch. Ce programme donne un exemple
de menu offrant plusieurs options à la console.
sdcc_ttl_1bit.c, sdcc_ttl_1bit.tar.bz269
Fichier source C d'illustration de la gestion des entrées/sorties sur un bit disponibles sur la carte MSC1210
EValuation Module. Ce programme propose une solution logicielle de traitement anti-rebonds sur l'entrée bouton
poussoir.
sdcc_ttl_io_iut.c, sdcc_ttl_io_iut.tar.bz270
Fichier source C d'illustration de la gestion des entrées/sorties sur plusieurs bits disponibles sur la carte
microcontrôleur du département Génie Électrique & Informatique Industrielle de l'IUT 'A' Paul Sabatier. Ce
programme propose un menu permettant la lecture ou l'écriture de 10bits ou d'un bit parmi 10.
sdcc_port_timings.c, sdcc_port_timings.tar.bz271
Fichier source C permettant la mesure des temps d'exécution de différents types de temporisations logicielles.
67
http://www.linux-france.org/prj/embedded/sources/sdcc_lcd_set_xy.tar.bz2
68
http://www.linux-france.org/prj/embedded/sources/sdcc_switch.tar.bz2
69
http://www.linux-france.org/prj/embedded/sources/sdcc_ttl_1bit.tar.bz2
70
http://www.linux-france.org/prj/embedded/sources/sdcc_ttl_io_iut.tar.bz2
71
http://www.linux-france.org/prj/embedded/sources/sdcc_port_timings.tar.bz2
La version originale du livre est aussi disponible sous forme de paquet Debian : rutebook. Une fois le paquet
installé, les pages HTML du livre sont placées dans le répertoire /usr/share/doc/rutebook/html.
MSC12xx Programming with SDCC: An Open-Source Alternative, Programmation des composants MSC12xx avec
SDCC, sbaa109a
Le document MSC12xx Programming with SDCC75 a été publié en 2003 et révisé en 2006. Plusieurs remarques
sont à faire sur l'utilisation du compilateur SDCC et les exemples proposés.
• Il n'est plus nécessaire de compiler les outils SDCC manuellement à partir d'une distribution cygwin. Voir
Section 13.3, « C sur MSC1210 : SDCC ».
• L'exemple de programme «Hello, World!» s'appuie sur les bibliothèques livrées avec SDCC. Ce sont des
bibliothèques génériques pour les microcontrôleurs de type 8051. En utilisant les sous-programmes fournis
directement en ROM dans le composant, on optimise considérablement l'occupation mémoire et les temps
d'exécution.
• Toujours dans le même programme exemple, l'appel à la fonction autobaud() effectue bien l'ajustement
automatique du débit de la liaison série entre le terminal du PC et l'interface du MSC1210 mais l'appel suivant
à la fonction ser_init() impose un débit de 19200 bauds par programmation du diviseur de fréquence. Il est
donc préférable de s'appuyer sur les fonctions intégrées du composant.
Pour utiliser les fonctions de la bibliothèque intégrée au composant avec SDCC, il faut procéder à quelques
adaptations.
72
http://www.loligrub.be/contrib/tlepoint/BASE/version-internet.html
73
http://sdcc.sourceforge.net/doc/sdccman.pdf
74
http://www.ti.com/litv/pdf/sbau101a
75
http://www.ti.com/litv/pdf/sbaa109a
76
http://www.ti.com/litv/pdf/sbaa085c
• Le fichier source d'adressage des sous-programmes ROM.A51 proposé en annexe du document doit être converti
en un format compatible avec l'assembleur asx8051 ; voir rom1210.asm.
Le code objet correspondant à ce nouveau fichier source, le fichier rom1210.rel, est obtenu à l'aide de la
commande : asx8051 -losffgp rom1210.asm.
Ce code objet est ajouté à celui de l'application utilisateur lors de la phase d'édition des liens (voir Section 4,
« Cycle de développement »). Avec l'exemple du premier programme hello.c, on utilise la commande :
sdcc hello.rel rom1210.rel.
• Le fichier d'en-tête contenant les prototypes des fonctions de la bibliothèque doit aussi être modifié ; voir
rom1210.h.
A. En-tête ser_msc1210.h
Le fichier d'en-tête ser_msc1210.h rassemble les sous-programmes de gestion de la liaison série entre le système de
développement (c'est à dire le PC) et le système embarqué. Voici une description des prototypes de sous-programmes.
77
http://focus.ti.com/docs/toolsw/folders/print/msc1210evm.html
78
http://www.nxp.com/acrobat_download/literature/9398/39340011.pdf