Vous êtes sur la page 1sur 61

Initiation au développement C sur

microcontrôleur
Philippe Latu
philippe.latu(at)linux-france.org
http://www.linux-france.org/prj/embedded/

Historique des versions

$Revision: 1153 $ $Date: 2007-06-14 14:25:15 +0200 (jeu, 14 jun 2007) $ $Author: latu $

Année universitaire 2006-2007.

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.

Table des matières


1. Copyright et Licence .............................................................................................................................. 3
1.1. Méta-information ........................................................................................................................ 3
2. Avant Propos ........................................................................................................................................ 4
2.1. C/C++ & Unix ........................................................................................................................... 4
2.2. Programmation procédurale ou orientée objet ................................................................................. 4
2.3. Logiciels libres ........................................................................................................................... 4
3. Développement et Système ..................................................................................................................... 5
3.1. Ordinateur cible avec système d'exploitation .................................................................................. 5
3.2. Ordinateur cible sans système d'exploitation ................................................................................... 7
4. Cycle de développement ......................................................................................................................... 8
5. Un premier programme : «Hello, World!» ................................................................................................ 9
5.1. Codes source .............................................................................................................................. 9
5.2. Compilation & exécution sur Windows XP .................................................................................. 11
5.3. Compilation & exécution sur GNU/Linux .................................................................................... 12
5.4. Compilation sur Windows & exécution sur cible MSC1210 ............................................................ 12
5.5. Compilation sur GNU/Linux & exécution sur cible MSC1210 ........................................................ 15
6. Caractéristiques des microcontrôleurs de la famille MSC12xx ................................................................... 18
7. Éléments de Langage C ........................................................................................................................ 19
7.1. Délimiteurs ............................................................................................................................... 21
7.2. Représentation des données ........................................................................................................ 21
7.3. Types de représentation des données ........................................................................................... 22
7.4. Constantes ................................................................................................................................ 23
7.5. Opérateurs de calcul .................................................................................................................. 25
7.6. Opérateurs relationnels ............................................................................................................... 25
7.7. Opérateurs logiques de bits ........................................................................................................ 25
7.8. Instruction de test if-else ............................................................................................................ 26
7.9. Instructions de choix multiples : switch & if-else-if ....................................................................... 26
7.10. Instructions de boucles : for, while, do-while .............................................................................. 27
7.10.1. Syntaxe de la boucle pour (for) ...................................................................................... 27
7.10.2. Syntaxe de la boucle répéter jusqu'à (do-while) ................................................................ 28
7.10.3. Syntaxe de la boucle tant que (while) .............................................................................. 28
7.11. Sous-programmes .................................................................................................................... 28
8. Entrées et sorties standard : fonctions getchar & putchar ........................................................................... 31

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 1


Initiation au développement C sur microcontrôleur

8.1. Avec Système d'exploitation ....................................................................................................... 31


8.2. Sans système d'exploitation & cible MSC1210 .............................................................................. 33
9. Entrées et sorties formatées : fonctions scanf & printf .............................................................................. 35
9.1. Définitions générales ................................................................................................................. 35
9.2. Exemple avec GCC ................................................................................................................... 36
9.3. Exemple avec SDCC sur cible MSC1210 ..................................................................................... 37
9.4. Utilisation des caractères spéciaux ............................................................................................... 38
10. Gestion d'un afficheur à cristaux liquides (LCD) .................................................................................... 39
10.1. Caractéristiques d'un afficheur du type HD44780U ...................................................................... 39
10.2. Utilisation d'un afficheur LCD avec SDCC sur cible MSC1210 ..................................................... 39
11. Gestion des entrées/sorties logiques ...................................................................................................... 43
11.1. Exemple d'entrées/sorties sur un bit ........................................................................................... 43
11.2. Exemple d'entrées/sorties sur plusieurs bits ................................................................................. 45
12. Contraintes de temps sur les sorties numériques ..................................................................................... 48
12.1. Temps d'exécution d'une instruction nop .................................................................................... 48
12.2. Temps d'exécution d'une temporisation codée en assembleur en ligne ............................................. 48
12.3. Temps d'exécution d'une temporisation basée sur une boucle tant-que ............................................ 50
12.4. Temps d'exécution d'une temporisation basée sur une boucle pour ................................................. 51
12.5. Récapitulatif des temps d'exécution ........................................................................................... 52
13. Outils de développement ..................................................................................................................... 53
13.1. C/C++ sur Windows : Dev-Cpp ................................................................................................. 53
13.2. C/C++ sur GNU/linux .............................................................................................................. 53
13.3. C sur MSC1210 : SDCC .......................................................................................................... 53
13.4. Logiciel de programmation de la mémoire Flash du MSC1210 via le port série ................................ 56
14. Codes source ..................................................................................................................................... 57
14.1. Sources GNU/GCC .................................................................................................................. 57
14.2. Sources MSC1210 ................................................................................................................... 57
15. Documentations de référence ............................................................................................................... 60
15.1. Système d'exploitation GNU/Linux ............................................................................................ 60
15.2. SDCC - Small Device C Compiler ............................................................................................ 60
15.3. Microcontrôleurs MSC12xx ...................................................................................................... 60
15.4. Bus I²C .................................................................................................................................. 61
A. En-tête ser_msc1210.h ......................................................................................................................... 61

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 2


Initiation au développement C sur microcontrôleur

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

Copyright (c) 2000,2007 Philippe Latu.


Permission est accordée de copier, distribuer et/ou modifier ce
document selon les termes de la Licence de Documentation Libre GNU
(GNU Free Documentation License), version 1.2 ou toute version
ultérieure publiée par la Free Software Foundation ; sans
Sections Invariables ; sans Texte de Première de Couverture, et
sans Texte de Quatrième de Couverture. Une copie de
la présente Licence est incluse dans la section intitulée
« Licence de Documentation Libre GNU ».

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 3


Initiation au développement C sur microcontrôleur

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.

2.2. Programmation procédurale ou orientée objet


Jusqu'à la fin des années 80, la seule méthode de programmation utilisable était procédurale ou structurée. Cette
approche consiste à décomposer un problème complexe en un ensemble de tâches ou «procédures» faciles à réaliser.
Les données sont alors choisies en fonction des procédures qui vont les manipuler.

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.

2.3. Logiciels libres


Les évolutions des systèmes d'exploitation basés sur les principes d'Unix et des langages de programmation C/C++ sont
associés au développement de l'Internet. Tous ces éléments utilisant un modèle de développement ouvert ont favorisé
l'émergence des logiciels libres.

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 4


Initiation au développement C sur microcontrôleur

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.

3.1. Ordinateur cible avec système d'exploitation


Ce contexte correspond aux enseignements «traditionnels» de programmation. L'utilisateur dispose d'une interface
graphique (voir Section 13.1, « C/C++ sur Windows : Dev-Cpp ») pour éditer, compiler et exécuter les programmes
(voir Section 4, « Cycle de développement »).

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/

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 5


Initiation au développement C sur microcontrôleur

Les couches logicielles d'un système d'exploitation7

BIOS, Basic Input Output System


Ce logiciel ne fait pas vraiment partie du système d'exploitation. À la mise sous tension de l'équipement (PC ou
autre), son rôle est de recenser les ressources matérielles disponibles : type de processeur, quantité de mémoire,
nombre et type de périphériques de stockage, interfaces réseau, écran, clavier, etc. Une fois l'étape de recensement
passée, les périphériques de stockage sont scrutés un à un à la recherche d'un code particulier qui permet le
lancement du système d'exploitation.

Kernel, noyau
Ce logiciel constitue le coeur du système d'exploitation. Il assure 3 fonctions principales :

La gestion des entrées-sorties


Le noyau intègre ses propres fonctions de recensement des ressources matérielles. Il peut très bien ne
tenir aucun compte des informations fournies par le BIOS. Le noyau doit posséder un pilote pour chaque
périphérique d'entrée-sortie «présent» dans le système.

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.

La gestion des accès mémoire


Un système peut intégrer différents types de mémoires dynamiques et le noyau doit gérer ces différents
éléments comme un tout cohérent. Les programmes doivent accéder à ce «plan mémoire» de façon transparente
en utilisant des segments alloués en fonction de leurs besoins.

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 gestion du temps processeur


Le temps du processeur doit être partagé entre les programmes comme le plan mémoire. On découpe donc ce
temps processeur en tranches qui sont attribuées aux programmes à la demande.

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

L'interface des commandes utilisateur


Un Shell permet à l'utilisateur du système de lancer des programmes ou commandes. On parle de commande
lorsqu'il s'agit de «manipuler» le système : lister les fichiers d'un répertoire, afficher la quantité de mémoire
disponible, etc.

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.

Le séquencement entre le les programmes


Lorsque plusieurs programmes doivent être exécutés sur un même système, il est fréquent que ces programmes
échangent des paramètres où se lancent en fonction d'actions réalisées par d'autres programmes. Lorsque l'on
utilise une interface graphique et que l'on clique sur une fenêtre pour l'activer, on réalise une opération de
ce type.

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 6


Initiation au développement C sur microcontrôleur

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.

3.2. Ordinateur cible sans système d'exploitation


Ce contexte correspond aux enseignements pratiques d'informatique industrielle. Si l'utilisateur dispose d'une interface
graphique de développement intégrée, le logiciel développé doit être exécuté sur un système spécialisé. On parle de
système embarqué dédié à la réalisation de fonctions spécifiques. Il est rare que ces systèmes disposent d'un écran et
d'un clavier.

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.

Les couches logicielles d'un système embarqué simple8

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 7


Initiation au développement C sur microcontrôleur

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 :

1. Écrire le code source,


2. le compiler,
3. lancer le programme.

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 :

Synoptique du cycle de développement9

9
images/dev-cycle.png

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 8


Initiation au développement C sur microcontrôleur

5. Un premier programme : «Hello, World!»


Pour illustrer les contextes et le cycle de développement présentés ci-avant, on prend un premier exemple de programme
très connu : afficher «Hello, World!» à l'écran.

La «mise au point» de ce programme simpliste sert aussi à prendre en main les outils de la chaîne de développement.

5.1. Codes source

Voici le code source du premier programme hello.c sous sa forme «usuelle».


Exemple 1. Affichage de la chaîne «Hello, World!» sur GNU/Linux avec GCC

#include <stdio.h> ❶

int main() { ❷

printf ("Hello, World!\n"); ❸


return 0; ❹
}

❶ #include <stdio.h> : inclusion des prototypes de sous-programmes de la bibliothèque standard d'entrée-


sortie (stdio ou standard input output).

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

L'accolade '{' est le délimiteur de début des instructions du programme principal.


❸ printf ("Hello, World!\n"); : appel de la fonction printf de la bibliothèque standard d'entrée-sortie.

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.

Les instructions sont séparées par le caractère ';'.


❹ return 0; : valeur renvoyée au système d'exploitation (via le Shell) en fin de programme.

La valeur 0 correspond à une sortie «sans erreur» du programme.

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!».

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 9


Initiation au développement C sur microcontrôleur

Environnement de développement sur système embarqué10

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"

// (sur)définition du sous-programme putchar utilisé par printf


void putchar(char c) { ❷
ser_putc(c);
}

void main(void) { ❸

// Appuyer sur <Entrée> pour ajuster automatiquement


// le débit de la liaison
autobaud(); ❹

// Initialisations pour SDCC


EA=1;

// Affichage sur le terminal


printf("Hello, World!"); ❺
putcr();

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 10


Initiation au développement C sur microcontrôleur

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

5.2. Compilation & exécution sur Windows XP

Ce contexte est décrit dans la Section 3.1, « Ordinateur cible avec système d'exploitation ».

On distingue deux familles de systèmes d'exploitation : Windows et GNU/Linux.

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 11


Initiation au développement C sur microcontrôleur

Copie d'écran Dev-Cpp & exécution Hello, World!11

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.

5.3. Compilation & exécution sur GNU/Linux


Les outils de la chaîne de développement sont installés par défaut avec le système d'exploitation. On peut donc appeler
directement le compilateur gcc (voir Section 13.2, « C/C++ sur GNU/linux ».
phil@b0x:~/src$ cat hello.c
#include <stdio.h>

int main() {

printf ("Hello, World!\n");


return 0;
}

phil@b0x:~/src$ gcc hello.c


phil@b0x:~/src$ ./a.out
Hello, World!
phil@b0x:~/src$

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.

5.4. Compilation sur Windows & exécution sur cible MSC1210


Ici, les programmes sont développés sur un PC et exécutés sur la machine cible. C'est le contexte décrit dans la
Section 3.2, « Ordinateur cible sans système d'exploitation ».

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/

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 12


Initiation au développement C sur microcontrôleur

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.

Compilation avec Windows XP


On appelle le compilateur SDCC à partir d'une console.

Copie d'écran compilation hello.c sur Windows XP avec SDCC16

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.

Utilisation d'un Makefile


Pour faciliter les développements de programmes «plus complets» que cet exemple, un patron de Makefile est
fourni avec les fichiers sources distribués avec ce document : Makefile.

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

Transfert du programme avec Windows XP


Texas Instruments™ fournit un logiciel appelé TIDownloader qui pilote la liaison série entre le PC et le système
embarqué. Ce logiciel sert à la fois au transfert du code du programme et aux tests d'exécution avec son terminal.
Il est téléchargeable à l'adresse : Software for Programming the Flash Memory Using the Serial Port v1.3.4 (Rev.
C) (sbac018c.zip, 2381 KB )17.

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 13


Initiation au développement C sur microcontrôleur

Sélection du fichier ser_hello.ihx18

Paramétrage de la liaison série pour le transfert19

18
images/tidownload-select.png
19
images/tidownload-setup.png

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 14


Initiation au développement C sur microcontrôleur

Transfert du code du programme vers le système cible20

Exécution du programme depuis Windows XP


Une fois le transfert achevé, on positionne l'interrupteur «vers le bas» et on appuie sur le bouton Reset pour lancer
le programme.

Exécution du programme21

5.5. Compilation sur GNU/Linux & exécution sur cible MSC1210

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 15


Initiation au développement C sur microcontrôleur

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.

Compilation avec GNU/Linux


L'appel du compilateur est identique entre les deux systèmes. Les formats de fichiers sont aussi identiques. Le code
exécutable du programme est contenu dans le fichier ser_hello.ihx.
$ sdcc ser_hello.c

$ 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

Utilisation d'un Makefile


Pour faciliter les développements de programmes «plus complets» que cet exemple, un patron de Makefile est
fourni avec les fichiers sources distribués avec ce document : Makefile.

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

Transfert du programme avec GNU/Linux


Pour effectuer le transfert de fichier en mode ASCII entre le PC de développement et le microcontrôleur, on utilise
une application appelée minicom sur les systèmes GNU/Linux.

À partir d'un Shell, on appelle minicom avec les options :


• -o pour désactiver le dialogue Hayes entre les équipements connectés sur la liaison série.
• -8 pour pouvoir utiliser les codes ASCII sur 8 bits.
$ minicom -o -m -8

Le paramétrage de la liaison se fait avec les options suivantes :


A - Serial Device : /dev/ttyS0
B - Lockfile Location : /var/lock
C - Callin Program :
D - Callout Program :
E - Bps/Par/Bits : 19200 8N1
F - Hardware Flow Control : No
G - Software Flow Control : No

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
>

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 16


Initiation au développement C sur microcontrôleur

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

Exécution du programme depuis GNU/Linux


Une fois le transfert achevé, on positionne l'interrupteur «vers le bas» et on appuie sur le bouton Reset pour lancer
le programme.

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 !

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 17


Initiation au développement C sur microcontrôleur

6. Caractéristiques des microcontrôleurs de la famille MSC12xx


Les composants de la famille MSC12xx reprennent les caractéristiques usuelles d'un microcontrôleur :

• des fonctions de conversion analogique numérique,


• un coeur «amélioré» de la famille Intel™ MCS51,
• de la mémoire de type RAM et de type FLASH,
• des périphériques logiques performants.

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 18


Initiation au développement C sur microcontrôleur

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: }

Voici un exemple d'exécution du programme gcc_fibonacci_single.c :


$ ./gcc_fibonacci_single.out
Saisir le nombre de calculs de Fibonacci(n) :
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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 19


Initiation au développement C sur microcontrôleur

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: }

Voici un exemple de résultat d'exécution du programme sdcc_fibonacci_single.c :


MSC1210 Ver:000305F10
>M0000 ok
>M8000 ok
>E
>Saisir le nombre de calculs de Fibonacci :
11

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 20


Initiation au développement C sur microcontrôleur

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.

Les directives du préprocesseur


Elles sont repérées par le caractère dièse en début de ligne :
#include <stdio.h>
#define TRUE 1

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.

Les groupes d'instructions


Ils sont délimités par les caractères accolades ouvertes et fermées :
while(1) {
printf ("Hello,");
printf (" World!");
}
• Exemple 3, « Calcul des nombres de Fibonacci sur GNU/Linux avec GCC » : les lignes 9 et 34 contiennent les
accolades qui délimitent le début et la fin du programme principal.
• Exemple 4, « Calcul des nombres de Fibonacci sur microcontrôleur MSC1210 avec SDCC » : les lignes 24 et 30
délimitent le groupe d'instructions qui doivent être répétées tant que le test (n >= MAX_FIB) n'est pas satisfait.

7.2. Représentation des données


En programmation, les données manipulées sont appelées variables ou constantes.

Une variable sert au stockage d'une information en vue de sa réutilisation dans le programme principal et|ou un sous-
programme.

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 21


Initiation au développement C sur microcontrôleur

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.

7.3. Types de représentation des données


Pour représenter les données à manipuler on distingue différents types élémentaires définis dans la syntaxe du langage
de programmation. La table ci-dessous donne une liste complète de ces types.
Tableau 1. Types élémentaires du Langage C

Type de donnée syntaxe C

rien ou vide void

caractère char

nombre entier court short

nombre entier int

nombre entier long long

nombre entier long long long


double

nombre réel float

nombre réel double double

nombre réel long long double


double

À 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 ».

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 22


Initiation au développement C sur microcontrôleur

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 23


Initiation au développement C sur microcontrôleur

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

/* Minimum and maximum values a `signed char' can hold. */


# define SCHAR_MIN (-128)
# define SCHAR_MAX 127

/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */


# define UCHAR_MAX 255

/* Minimum and maximum values a `char' can hold. */


# ifdef __CHAR_UNSIGNED__
# define CHAR_MIN 0
# define CHAR_MAX UCHAR_MAX
# else
# define CHAR_MIN SCHAR_MIN
# define CHAR_MAX SCHAR_MAX
# endif

/* Minimum and maximum values a `signed short int' can hold. */


# define SHRT_MIN (-32768)
# define SHRT_MAX 32767

/* Maximum value an `unsigned short int' can hold. (Minimum is 0.) */


# define USHRT_MAX 65535

/* Minimum and maximum values a `signed int' can hold. */


# define INT_MIN (-INT_MAX - 1)
# define INT_MAX 2147483647

/* Maximum value an `unsigned int' can hold. (Minimum is 0.) */


# define UINT_MAX 4294967295U

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 24


Initiation au développement C sur microcontrôleur

7.5. Opérateurs de calcul


On retrouve les opérateurs usuels en Langage C : +, -, * et /. À cette liste, il faut ajouter les éléments propres aux
calculs en Langage C.

Opérateur modulo, %
Cet opérateur, noté %, renvoie le reste de la division entre 2 entiers. Par exemple : 24 % 9 = 6

Opérateurs d'incrémentation et de décrémentation, ++ et --


Pour ajouter une unité à une variable, on utilise l'opérateur d'incrémentation ++. Pour soustraire une unité à une
variable, on utilise l'opérateur de décrémentation --.

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.

Opérateurs et expressions d'affectation


En Langage C, on peut appliquer la règle suivante sur les 2 expressions e1 et e2 :
'(e1) = (e1) (opérateur) (e2)' est équivalent à '(e1) (opérateur)= (e2)'. Tous les opérateurs
usuels de calculs sont utilisables avec cette règle. Ainsi, i = i + 2; est équivalent à i += 2;.

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

7.6. Opérateurs relationnels


Ces opérateurs évaluent une condition sur des nombres entiers ou réels et renvoient une valeur booléenne 0 ou 1.
Tableau 2. Opérateurs relationnels
Symbole Opérateur|Condition

== égalité

!= différence

> supérieur à

< inférieur à

>= supérieur ou égal à

<= inférieur ou égal à

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

7.7. Opérateurs logiques de bits


Tableau 3. Opérateurs logiques de bits
Symbole Opérateur

& ET

| OU (inclusif)

^ OU (exclusif)

<< décalage à gauche avec remplissage par 0

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 25


Initiation au développement C sur microcontrôleur

Symbole Opérateur

>> décalage à gauche avec remplissage par 0

~ négation binaire ou complément à 1

7.8. Instruction de test if-else


On utilise l'instruction if-else pour prendre des décisions. Sa forme générale est :
if ( expression )
instruction1;
else
instruction2;

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

Une condition fausse vaut toujours 0

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.

7.9. Instructions de choix multiples : switch & if-else-if


Le traitement des choix multiples peut devenir compliqué lorsque l'imbrication des blocs d'instructions entre
instructions if-else augmente. C'est pour cette raison qu'une syntaxe particulière à été introduite : l'instruction
switch dont le synoptique est le suivant.
switch (expression) {
case Valeur1:
// Instructions
break;
case Valeur2:
// Instructions
break;
...

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 26


Initiation au développement C sur microcontrôleur

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
}

7.10. Instructions de boucles : for, while, do-while


Dès lors qu'une ou plusieurs instructions doivent être répétées, on utilise les instructions de boucles disponibles avec
le Langage C.

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

7.10.1. Syntaxe de la boucle pour (for)

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.

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 27


Initiation au développement C sur microcontrôleur

7.10.2. Syntaxe de la boucle répéter jusqu'à (do-while)


Conformément à la règle énoncée ci-dessus, le test est effectué après une première exécution du corps de la boucle
répéter jusqu'à (do-while).
do {
// Instructions
} while (test);

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.

7.10.3. Syntaxe de la boucle tant que (while)


Conformément à la règle énoncée ci-dessus, le test est effectué avant que le corps de la boucle tant que (while) ne
soit exécuté.
while (test) {
// Instructions
}

Une fois encore ! attention à l'expression de test.

• 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:

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 28


Initiation au développement C sur microcontrôleur

04: #define MAX_FIB 49


05:
06: unsigned long iterative_fibonacci(unsigned long f) {
07:
08: unsigned long nombrePrecedent = 0, nombreCourant = 1, fibonacci = 0;
09: int i;
10:
11: if (f > 1 )
12: for (i = 1; i < f; i++) {
13: fibonacci = nombrePrecedent + nombreCourant;
14: nombrePrecedent = nombreCourant;
15: nombreCourant = fibonacci;
16: }
17: else
18: fibonacci = f;
19:
20: return fibonacci;
21: }
22:
23: main() {
24:
25: int n, i;
26:
27: printf("Saisir le nombre de calculs de Fibonacci(n) :\n");
28: scanf("%d", &n);
29: if (n >= MAX_FIB) {
30: printf("Fibonacci(%d) depasse la capacite d'un mot de 32 bits\n", n);
31: exit(1);
32: }
33:
34: for (i = 0; i < n; i++)
35: printf("Fibonacci (%d) = %lu\r\n", i, iterative_fibonacci(i));
36: }

On retrouve des résultats identiques aux précédents avec le programme gcc_fibonacci_iterative.c.


Exemple 8. Utilisation d'un sous-programme dans le calcul des nombres de Fibonacci sur la cible MSC1210
01: #include <msc1210.h>
02: #include "ser_msc1210.h"
03: #include <stdio.h>
04: #include <stdlib.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: unsigned long iterative_fibonacci(unsigned long f) {
14:
15: unsigned long nombrePrecedent = 0, nombreCourant = 1, fibonacci = 0;
16: int i;
17:
18: if (f > 1 )
19: for (i = 1; i < f; i++) {
20: fibonacci = nombrePrecedent + nombreCourant;
21: nombrePrecedent = nombreCourant;
22: nombreCourant = fibonacci;
23: }
24: else
25: fibonacci = f;
26:

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 29


Initiation au développement C sur microcontrôleur

27: return fibonacci;


28: }
29:
30: void main(void) {
31:
32: unsigned char s[3] = {'\0','\0','\0'};
33: int n, i;
34:
35: // Appuyer sur <Entree> pour ajuster automatiquement
36: // le debit de la liaison
37: autobaud();
38: EA=1;
39:
40: do {
41: printf("Saisir le nombre de calculs de Fibonacci :\r\n");
42: ser_gets_echo(s, 2);
43: n = atoi(s);
44: if (n >= MAX_FIB)
45: printf("\nFibonacci(%d) depasse la capacite d'un mot de 32 bits\r\n", n);
46: } while (n >= MAX_FIB);
47:
48: printf("\r\n\n");
49: for (i = 0; i < n; i++)
50: printf("Fibonacci (%d) = %lu\r\n", i, iterative_fibonacci(i));
51:
52: while(1);
53: }

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.

Entre les lignes 6 et 21, on trouve le sous-programme iterative_fibonacci(i). La ligne 6 correspond au


prototype du sous-programme : c'est là que l'on désigne le nom du sous-programme ainsi que les types des paramètres
d'entrée et de sortie. Dans l'exemple, les paramètres échangés sont de type unsigned long.
• Exemple 8, « Utilisation d'un sous-programme dans le calcul des nombres de Fibonacci sur la cible MSC1210 » :
On retrouve les mêmes éléments que dans le cas précédent. Le sous-programme iterative_fibonacci(i) est
appelé au niveau de la ligne 50. Ce sous-programme est défini entre les lignes 13 et 28. Les paramètres échangés
sont du même type entre les deux programmes.

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 30


Initiation au développement C sur microcontrôleur

8. Entrées et sorties standard : fonctions getchar & putchar


8.1. Avec Système d'exploitation
Entrée standard, Fonction getchar, Flux stdin
Le mécanisme d'entrée le plus simple consiste à lire un caractère provenant de l'entrée standard. Suivant le
contexte, cette entrée standard peut prendre différente formes.

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.

Sortie standard, Fonction putchar, Flux stdout


Par analogie avec le cas de l'entrée standard, le mécanisme le plus simple consiste à émettre un caractère vers la
sortie standard. Suivant le contexte cette sortie standard peut aussi prendre différentes formes.

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

• stdin : flux d'entrée ; généralement le clavier,


• stdout : flux de sortie ; généralement l'écran,
• stderr : flux d'erreurs vers lequel sont redirigés tous les messages d'erreurs lors de l'exécution d'un programme,
• stdprn : flux d'impression ; généralement le port parallèle,
• stdaux : flux auxiliaire ; généralement le port série.

Flux d'entrées-sorties standard et système d'exploitation24


24
images/os_stdio.png

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 31


Initiation au développement C sur microcontrôleur

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 un exemple d'exécution du programme gcc_stdio.c :


$ ./gcc_stdio.out
test
test
123
123

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.

phil@b0x:~/src$ cat >test.txt


Fichier texte
exemple
phil@b0x:~/src$ ./stdio <test.txt
Fichier texte
exemple
phil@b0x:~/src$ cat test.txt | ./stdio
Fichier texte
exemple

Pour plus de détails sur les manipulations à la console, voir Linux - Base d'administration pour le superutilisateur.

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 32


Initiation au développement C sur microcontrôleur

8.2. Sans système d'exploitation & cible MSC1210

Flux d'entrées-sorties standard sans système d'exploitation25

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 33


Initiation au développement C sur microcontrôleur

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.

Voici un exemple d'exécution du programme sdcc_stdio.c :


MSC1210 Ver:000305F10
>M0000 ok
>M8000 ok
>E
>T
>
>azerty
123

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 34


Initiation au développement C sur microcontrôleur

9. Entrées et sorties formatées : fonctions scanf & printf


9.1. Définitions générales
Les flux d'entrées-sorties standards sont limités aux manipulations sur des caractères codés en ASCII. Cette limitation
est très contraignante lorsque l'on cherche à formater l'affichage de valeurs numériques. C'est pour dépasser ces
contraintes que l'on a introduit deux fonctions dédiées à la «traduction» entre valeurs numériques et chaînes de
caractères. La fonction printf sert à formater les sorties sur le flux stdout et la fonction scanf sert à formater les
entrées reçues sur le flux stdin.

Les prototypes génériques de ces deux fonctions se présentent sous la forme :


scanf(control, arg1, arg2, ...)
printf(control, arg1, arg2, ...)

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

• Tous les champs notés entre crochets sont optionnels.


• Spécifications du champ obligatoire type :
Tableau 4. Types de conversion

type signification

%c caractère

%s chaîne de caractères

%d nombre entier en décimal

%e nombre réel sous la forme mantisse/exposant [-]m.nnnnnne[+|-]xx

%E nombre réel sous la forme mantisse/exposant en majuscule [-]m.nnnnnnE[+|-]xx

%f nombre réel sous la forme [-]mmm.nnnnnn

%g nombre réel sous la forme la plus courte entre les types %e et %f

%G nombre réel sous la forme la plus courte entre les types %E et %f

%o nombre entier en octal

%p pointeur ou adresse de la valeur numérique

%u nombre entier non signé en décimal

%x nombre entier en hexadécimal

%X nombre entier en hexadécimal ; lettres affichées en majuscules


• Spécifications du champ optionnel [indicateur] :
Tableau 5. Indicateur d'affichage

[indicateur] signification

- alignement à gauche pour la largeur donnée

+ affichage forcé du signe de la valeur numérique

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 35


Initiation au développement C sur microcontrôleur

[indicateur] signification

espace insertion d'un caractère d'espacement si la valeur numérique est positive

affichage précédé de 0, 0x ou 0X avec les types respectifs o, x ou X

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

pour les types d, i, o, u, x et X : nombre minimum de chiffres décimaux à afficher ; ajout de


caractères d'espacement si la valeur est plus «courte» que l'affichage demandé

pour les types e, E et f : nombre de chiffres à afficher après le point décimal


.nombre
pour les types g et G : nombre maximum de chiffres significatifs à afficher

pour le type s : nombre maximum de caractères à afficher

pour le type c : pas d'effet


• Spécifications du champ optionnel [modification] :
Tableau 8. Modification de l'affichage
[modification] signification

h argument traité comme un entier court (short)

l argument traité comme un entier long pour les types entiers (long) ou comme un réel double
pour les types réels (double)

L argument traité comme un réel long 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.

9.2. Exemple avec GCC


Exemple 11. Sorties formatées avec printf sur GNU/Linux avec GCC
01: /* $Id: gcc_printf.c 1129 2007-05-07 15:07:52Z latu $ */
02:
03: #include <stdio.h>
04:
05: #define M_PI 3.14159265358979323846 /* pi */
06:

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 36


Initiation au développement C sur microcontrôleur

07: #define YEAR 2007


08:
09: int main() {
10:
11: printf ("Caracteres : %c %c %c %c\n", 'a', 65, 0x30, '0');
12: printf ("Entiers : %d %ld\n", YEAR, 650000);
13: printf ("Affichage avec espaces : |%10d|\n", YEAR);
14: printf ("Affichage avec zeros : |%010d|\n", YEAR);
15: printf ("Differentes bases : %d %x %o %#x %#o \n", 100, 100, 100, 100, 100);
16: printf ("Reels : |%4.2f| |%+.4e| |%E|\n", M_PI, M_PI*YEAR, M_PI);
17: printf ("Largeur en argument : |%*d|\n", 5, 10);
18: printf ("%s\n", "Debian GNU/Linux");
19: return 0;
20: }

Voici un exemple d'exécution du programme gcc_printf.c :


$ ./gcc_printf.out
Caracteres : a A 0 0
Entiers : 2007 650000
Affichage avec espaces : | 2007|
Affichage avec zeros : |0000002007|
Differentes bases : 100 64 144 0x64 0144
Reels : |3.14| |+6.3052e+03| |3.141593E+00|
Largeur en argument : | 10|
Debian GNU/Linux

9.3. Exemple avec SDCC sur cible MSC1210


Par défaut, le support des nombres réels est désactivé dans la chaîne de développement SDCC. Il est nécessaire de
passer par une recompilation manuelle des outils de compilation pour obtenir le résultat ci-dessous. Voir Section 13.3,
« C sur MSC1210 : SDCC ».
Exemple 12. Sorties formatées avec printf sur un microcontrôleur MSC1210 avec SDCC
01: #define YEAR 2007
02:
03: void putchar(char c) {
04: ser_putc(c);
05: }
06:
07: int main() {
08:
09: autobaud();
10: EA = 1;
11:
12: printf ("Caracteres : %c %c %c %c\n\r", 'a', 65, 0x30, '0');
13: printf ("Entiers : %d %ld\n\r", YEAR, 650000);
14: printf ("Affichage avec espaces : |%10d|\n\r", YEAR);
15: printf ("Affichage avec zeros : |%010d|\n\r", YEAR);
16: printf ("Differentes bases : %d %x %o %#x %#o \n\r", 100, 100, 100, 100, 100);
17: printf ("Reels : |%f| |%+.4f| |%e|\n\r", M_PI, M_PI*YEAR, M_PI);
18: printf ("Largeur en argument : |%*d|\n\r", 5, 10);
19: printf ("%s\n\r", "Debian GNU/Linux");
20: return 0;
21: }

Voici un exemple d'exécution du programme sdcc_printf.c :


MSC1210 Ver:000305F10
>M0000 ok
>M8000 ok
>E
>T

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 37


Initiation au développement C sur microcontrôleur

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

9.4. Utilisation des caractères spéciaux


Tableau 9. Caractères spéciaux

Séquence signification
d'échappement

%% affichage du caractère '%'

\0 caractère null ; valeur 0, délimiteur de fin de chaîne de caractères

\a alerte ; beep système

\b backspace ; déplacement du curseur d'un caractère en arrière

\f form feed ; saut de page

\n new line ; saut de ligne

\r carriage return ; retour chariot

\t tabulation horizontale

\v tabulation verticale

\\ affichage du caractère '\'

\' affichage du caractère '''

\" affichage du caractère '"'

\Onn affichage de la valeur nn en octal

\Xnn affichage de la valeur nn en hexadécimal

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 38


Initiation au développement C sur microcontrôleur

10. Gestion d'un afficheur à cristaux liquides (LCD)


L'utilisation d'un afficheur à cristaux liquides ou LCD est assez courante sur les systèmes embarqués. C'est un écran
de faible dimension employé pour informer l'utilisateur sur l'état du système ou pour afficher les mesures effectuées
par différents capteurs.

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.

10.1. Caractéristiques d'un afficheur du type HD44780U


La standard HD44780U utilise 3 signaux de contrôle ainsi que 4 ou 8 lignes d'entrée-sortie pour le bus de données.
L'utilisateur peut choisir si l'afficheur à cristaux liquides doit fonctionner avec un bus de données de 4 bits ou un bus
de données de 8 bits. Si un bus de données de 4 bits est utilisé l'afficheur LCD utilise un total de 7 lignes (3 signaux
de contrôle plus les 4 lignes pour le bus de données). Si un bus de données de 8 bits est utilisé l'afficheur LCD utilise
un total de 11 lignes (3 signaux de contrôle plus les 8 lignes pour le bus de données).

Les trois signaux de contrôle sont appelés : EN, RS et RW.

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.

RS, register select


Le signal register select est utilisé pour distinguer les instructions de commande des données à afficher. Si on
place RS au niveau bas (0), les données sont traitées comme une commande ou instruction spéciale (effacer l'écran,
positionner le curseur de position, etc.). Si on place RS au niveau haut (1), les données envoyées sont des caractères
que l'écran doit afficher.

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) {

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 39


Initiation au développement C sur microcontrôleur

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: }

Voici le résultat de l'exécution du programme hello_lcd.c :

Programme «Hello, World!» sur afficheur LCD26

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 40


Initiation au développement C sur microcontrôleur

05: #include "lcd_msc1210.h"


06:
07: #define MAXCHAR 10
08: #define EOF 4
09:
10: void putchar(char c) {
11: if (print2lcd)
12: lcd_putc(c);
13: else
14: ser_putc(c);
15: }
16:
17: void main(void) {
18:
19: unsigned int x, y;
20: unsigned char s[MAXCHAR];
21:
22: // Appuyer sur <Entrée> pour ajuster automatiquement le (débit|baudrate)
23: autobaud();
24:
25: // Initialisations pour SDCC
26: EA=1;
27:
28: print2lcd = 0;
29:
30: // Initialisation de l'afficheur LCD
31: lcd_init();
32:
33: ser_puts("Programme de positionnement du curseur");
34: putcr();
35: ser_puts("L'abscisse X est comprise entre 0 et 19");
36: putcr();
37: ser_puts("L'ordonnee Y est comprise entre 0 et 3");
38: putcr();
39: ser_puts("(Sortie pour une valeur X > 19)");
40: putcr();
41:
42: do {
43: ser_puts("Saisie de x : ");
44: ser_gets_echo(s, MAXCHAR);
45: putcr();
46: x = atoi(s);
47: ser_puts("Saisie de y : ");
48: ser_gets_echo(s, MAXCHAR);
49: putcr();
50: y = atoi(s);
51: lcd_clear();
52: lcd_set_xy(x, y);
53: print2lcd = 1;
54: printf("!ici");
55: print2lcd = 0;
56: } while (x < 20);
57:
58: // Affichage sur la console
59: ser_puts("The End!");
60: putcr();
61:
62: while(1);
63: }

Voici le résultat de l'exécution du programme sdcc_lcd_set_xy.c :


MSC1210 Ver:000305F10

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 41


Initiation au développement C sur microcontrôleur

>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

Positionnement du curseur sur un afficheur LCD27

27
images/lcd_set_xy.png

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 42


Initiation au développement C sur microcontrôleur

11. Gestion des entrées/sorties logiques


Le type de gestion d'entrées/sorties le plus simple à gérer pour un microcontrôleur consiste à utiliser directement des
niveaux logiques '0' ou '1'. En effet, l'exploitation des niveaux logiques ne nécessite aucune transformation du signal.

11.1. Exemple d'entrées/sorties sur un bit


Dans cet exemple, on utilise la carte MSC1210 EValuation Module. On utilise un bouton poussoir et deux diodes
électroluminescentes (LED) dont le câblage est fixé comme suit :
Tableau 10. Entrées/Sorties un bit MSC1210EVM
Type Désignation Broche microcontrôleur

Entrée Bouton poussoir SW2 P3.2

Sortie LED rouge P3.4

Sortie LED jaune P3.5


Exemple 15. Programme d'illustration des entrées/sorties logiques sur un bit

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();

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 43


Initiation au développement C sur microcontrôleur

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: }

Adressage des entrées/sorties, lignes 5 à 7


En fonction du tableau donné ci-dessus on affecte les noms d'entrées/sorties à l'aide des directives du compilateur.
Ici, la directive __sbit __at (0xb2) IN_SW2; affecte le nom IN_SW2 au bit accessible à l'adresse 0xb2. Cette
adresse correspondant au troisième bit du port numéro 3 du microcontrôleur : P3.2.

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

Fonction antirebond(), lignes 13 à 29


Comme l'exemple d'illustration propose de compter les appuis sur le bouton poussoir, il est nécessaire de se
préoccuper des «rebonds». Lors d'un appui manuel sur le contact électrique du bouton poussoir, une suite
d'impulsions transitoires est transmise. Ces impulsions parasites sont communément appelées rebonds.

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.

Programme principal, lignes 43 à 56


La condition d'appel au corps de la boucle tant que while (1) est toujours vraie. Il s'agit donc d'une boucle
infinie. Seule la ré-initialisation du microcontrôleur peut interrompre le programme.

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.

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 44


Initiation au développement C sur microcontrôleur

Affichage de la valeur d'un bit, lignes 53 à 55


Pour afficher l'état de l'appui sur le bouton poussoir sur la console du système de développement via la liaison
série, il est nécessaire de transformer le bit lu en un code ASCII. Sachant que le code ASCII du chiffre 0 est 0x30,
il suffit d'ajouter la valeur du bit à ce code pour afficher les caractères '0' ou '1' sur la console.

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.

11.2. Exemple d'entrées/sorties sur plusieurs bits


Dans cet exemple, on utilise la carte du département Génie Électrique de l'IUT 'A' Paul Sabatier. Relativement à la
carte MSC1210 EValuation Module, cette carte offre une extension sur les entrées/sorties logiques.

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.

Schéma de principe extension port P028


Exemple 16. Programme d'illustration des entrées/sorties logiques sur plusieurs bits

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 45


Initiation au développement C sur microcontrôleur

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 {

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 46


Initiation au développement C sur microcontrôleur

79: puts("Numero du bit :");


80: putcr();
81: ser_gets_echo(chaine, MAXCHAR);
82: num = atoi(chaine);
83: putcr();
84: } while (num > 9);
85: do {
86: puts("Valeur du bit :");
87: putcr();
88: ser_gets_echo(chaine, MAXCHAR);
89: val = atoi(chaine);
90: putcr();
91: } while (val > 1);
92: ecriture_1bit(num, val);
93: break;
94: default:
95: puts("Option non reconnue");
96: putcr();
97: }
98: printf ("/%03x/\n\r", val);
99: putcr();
100: }
101: // Fin du programme
102: puts("The End!");
103: putcr();
104:
105: while(1);
106:}

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 47


Initiation au développement C sur microcontrôleur

12. Contraintes de temps sur les sorties numériques


Même si les microcontrôleurs de la famille MSC12xx ne peuvent rivaliser avec les composants logiques programmables
spécialisés (FPGAs, etc.), il est utile d'évaluer précisément les performances en fréquence que l'on peut obtenir sur
les sorties numériques.

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.

12.1. Temps d'exécution d'une instruction nop


L'instruction nop (acronyme de no operation) est une instruction assembleur dont le seul but est de gaspiller un cycle
d'horloge du microcontrôleur. En appelant cette instruction directement à partir du langage C on doit obtenir le plus
petit temps d'exécution possible.

• 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 :

temps d'exécution d'une instruction nop29

12.2. Temps d'exécution d'une temporisation codée en assembleur en ligne


Après avoir mesuré le plus petit temps d'exécution, on utilise une boucle de type do-while codée en assembleur et
insérée dans le code source du programme en langage C. Cette technique, baptisée assembleur en ligne, est sensée
optimiser la compilation relativement au code généré par le compilateur. Si l'optimisation manuelle est efficace, le
temps d'exécution sera plus réduit qu'avec un programme écrit en langage C.

• Codage en Langage C :

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 48


Initiation au développement C sur microcontrôleur

void inline_delay (unsigned char n) __naked {


n;
_asm
mov r0, dpl
00001$: nop
djnz r0, 00001$
ret
_endasm;
}

// dans le programme principal

ser_puts("Temporisation assembleur en ligne: ");


ser_gets_echo(chaine, MAXCHAR);
v = atoi(chaine);
TEST_PIN = 0;
inline_delay(v);
TEST_PIN = 1;
• Code assembleur produit par le compilateur sdcc :
;------------------------------------------------------------
;Allocation info for local variables in function 'inline_delay'
;------------------------------------------------------------
;n Allocated to registers
;------------------------------------------------------------
; sdcc_port_timings.c:37: void inline_delay (unsigned char n) __naked {
; -----------------------------------------
; function inline_delay
; -----------------------------------------
_inline_delay:
; sdcc_port_timings.c:44: _endasm;

mov r0, dpl


00001$:
nop
djnz r0, 00001$
ret
; naked function: no epilogue.

// dans le programme principal

; 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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 49


Initiation au développement C sur microcontrôleur

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--)
;
}

// dans le programme principal

ser_puts("Temporisation boucle tantque: ");


ser_gets_echo(chaine, MAXCHAR);
v = atoi(chaine);
TEST_PIN = 0;
while_delay(v);
TEST_PIN = 1;
• Code assembleur produit par le compilateur sdcc :
;------------------------------------------------------------
;Allocation info for local variables in function 'while_delay'
;------------------------------------------------------------
;n Allocated to registers r2
;------------------------------------------------------------
; sdcc_port_timings.c:47: void while_delay (unsigned char n) {
; -----------------------------------------
; function while_delay
; -----------------------------------------
_while_delay:
mov r2,dpl
; sdcc_port_timings.c:48: while (n--)
00101$:
mov ar3,r2
dec r2
mov a,r3
jnz 00101$
ret

// dans le programme principal

; 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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 50


Initiation au développement C sur microcontrôleur

• Temps mesuré à l'analyseur logique :

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) {

__idata unsigned char i;

for (i = 0; i < n; i++)


;
}

// dans le programme principal

ser_puts("Temporisation boucle tantque: ");


ser_gets_echo(chaine, MAXCHAR);
v = atoi(chaine);
TEST_PIN = 0;
for_delay(v);
TEST_PIN = 1;
• Code assembleur produit par le compilateur sdcc :
;------------------------------------------------------------
;Allocation info for local variables in function 'for_delay'
;------------------------------------------------------------
;i Allocated to registers r3
;n Allocated to registers r2
;------------------------------------------------------------
; sdcc_port_timings.c:55: void for_delay (unsigned char n) {
; -----------------------------------------
; function for_delay
; -----------------------------------------

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 51


Initiation au développement C sur microcontrôleur

_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

// dans le programme principal

; 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

12.5. Récapitulatif des temps d'exécution


Tableau 11. Tableau récapitulatif des temps d'exécution

Instruction 1er appel Appel suivant

nop 540ns idem

Boucle do-while assembleur en ligne 2.890µs 720ns

Boucle while en langage C 5.040µs 1.280µs

Boucle for en langage C 5.790µs 1.790µs

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 52


Initiation au développement C sur microcontrôleur

13. Outils de développement


Cette section rassemble les références sur les outils utilisés dans ce support. Ce «catalogue» ne prétend pas être
exhaustif.

13.1. C/C++ sur Windows : Dev-Cpp


Dev-Cpp est une interface de développement intégrée (IDE ou Integrated Development Environment très connue sur
Windows. Elle permet d'utiliser les outils GNU : le compilateur gcc36, le debugger gdb37, make38, etc.

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

13.2. C/C++ sur GNU/linux


Les outils de la chaîne de développement C/C++ sont installés par défaut avec le système d'exploitation. On peut le
vérifier en interrogeant le gestionnaire de paquets APT.
$ aptitude search gcc-4.1
i gcc-4.1 - The GNU C compiler
i gcc-4.1-base - The GNU Compiler Collection (base package)
p gcc-4.1-doc - documentation for the GNU compilers (gcc, gobjc, g++)
i gcc-4.1-locales - The GNU C compiler (native language support files)
i gcc-4.1-multilib - The GNU C compiler (multilib files)
p gcc-4.1-source - Source of the GNU Compiler Collection

13.3. C sur MSC1210 : SDCC


SDCC est un compilateur C multi-cibles conforme à la norme ANSI. Il supporte les microcontrôleurs (MCUs) des
familles Intel 8051, Maxim 80DS390, Zilog Z80, Motorola 68HC08, Microchip PIC16 et PIC18.

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.

Cette installation ne comprend que les outils de compilation.

Copie d'écran version SDCC43


36
http://gcc.gnu.org/
37
http://www.gnu.org/software/gdb/
38
http://www.gnu.org/software/make/
39
http://www.bloodshed.net/devcpp.html
40
http://www.sourceforge.net/
41
http://www.sourceforge.net/
42
http://sourceforge.net/project/showfiles.php?group_id=599
43
images/sdcc-version.png

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 53


Initiation au développement C sur microcontrôleur

Sur Debian GNU/Linux


Le compilateur et ses bibliothèques sont disponibles sous forme de paquets dans la distribution Debian44. La liste
des paquets utiles est obtenue en interrogeant le gestionnaire de paquets.
$ aptitude search sdcc
p sdcc - Small Device C Compiler
p sdcc-doc - Small Device C Compiler (documentation)
p sdcc-libraries - Small Device C Compiler (libraries)
p sdcc-ucsim - Micro-controller simulator for SDCC

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.

Téléchargement des sources avec Subversion


On commence par créer une arborescence de stockage local des sources.
phil@b0x:~$ mkdir -p SVN/sdcc
phil@b0x:~$ cd SVN/sdcc
phil@b0x:~/SVN/sdcc$

Le premier téléchargement se fait avec la commande svn checkout.


phil@b0x:~/SVN/sdcc$ svn co https://svn.sourceforge.net/svnroot/sdcc/trunk/sdcc sdcc

Toutes les mises à jour suivantes se font avec la commande svn update.
phil@b0x:~/SVN/sdcc$ svn update
À la révision 4788.

Compilation de la chaîne de développement


La compilation sur un système GNU/Linux utilise les outils classiques de la chaîne GNU. On retrouve les trois
étapes rituelles : ./configure, make et make install
• La commande ./configure recense les outils installés sur le poste de travail et les bibliothèques nécessaires à la
compilation. Si cette étape se passe correctement, on obtient un résultat du type :
phil@b0x:~/SVN/sdcc$ ./configure
<snipped/>

sdcc 2.7.2 is now configured for

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/

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 54


Initiation au développement C sur microcontrôleur

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

Search paths (incomplete, see manual for all search paths):


binary files: $SDCC_HOME/bin
include files: /share/sdcc/include
path(argv[0])/../share/sdcc/include
/usr/local/share/sdcc/include
library files: $SDCC_HOME/share/sdcc/lib/<model>
path(argv[0])/../share/sdcc/lib/<model>
/usr/local/share/sdcc/lib/<model>

• 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)

Activation du support des nombres réels


Par défaut, le support des nombres réels (type float) n'est activé que pour les microcontrôleurs de la famille
DS390. Pour activer l'utilisation des nombres réels sur les microcontrôleurs de la famille MSC121x, il faut éditer
manuellement le fichier device/lib/printf_large.c et procéder aux modifications suivantes :
phil@b0x:~/SVN/sdcc$ diff -uBb device/lib/printf_large.c.dist device/lib/printf_large.c

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 55


Initiation au développement C sur microcontrôleur

--- device/lib/printf_large.c.dist 2006-05-22 23:27:38.868496000 +0200


+++ device/lib/printf_large.c 2006-05-22 22:29:34.298724000 +0200
@@ -24,9 +24,9 @@
what you give them. Help stamp out software-hoarding!
-------------------------------------------------------------------------*/

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

13.4. Logiciel de programmation de la mémoire Flash du MSC1210 via le port


série
Texas Instruments™ fournit un logiciel pour Windows de pilotage de la liaison série entre le PC et le système embarqué.
Ce logiciel sert aussi bien à la programmation du composant qu'aux communications lorsque le programme est en
cours d'exécution.

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 56


Initiation au développement C sur microcontrôleur

14. Codes source


Cette section liste les fichiers sources utilisés dans ce document. Ces fichiers sont téléchargeables à partir des liens
donnés ci-dessous.

14.1. Sources GNU/GCC


Ces exemples de programmes sont utilisables dans un environnement de développement avec système d'exploitation.
Ils sont utilisés pour illustrer les différences avec le contexte de développement sur les systèmes embarqués industriels.

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.

14.2. Sources MSC1210


msc1210.h, Lien vers le dépôt Subversion SDCC : msc1210.h53
Fichier d'en-tête contenant les définitions des registres de fonctions spécifiques (Special Functions Registers ou
sfr) et les définitions des bits spécifiques (Special Bits ou sbit) du microcontrôleur MSC1210.

Ce fichier est directement distribué avec la chaîne de développement SDCC.

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 57


Initiation au développement C sur microcontrôleur

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 58


Initiation au développement C sur microcontrôleur

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 59


Initiation au développement C sur microcontrôleur

15. Documentations de référence


Cette section rassemble les références documentaires qui ont été utilisées pour construire ce support.

15.1. Système d'exploitation GNU/Linux


Linux - Base d'administration pour le superutilisateur, RUTE
Le livre Linux - Base d'administration pour le superutilisateur72 est une ressource essentielle pour l'apprentissage
des manipulations de base sur un système d'exploitation GNU/Linux. Il présente de façon détaillée les commandes
utilisables sur la console du Shell ainsi que les opérations de séquencement entre les programmes (pipes,
redirections, etc.).

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.

15.2. SDCC - Small Device C Compiler


SDCC Compiler User Guide
Le manuel SDCC Compiler User Guide73 est une ressource essentielle pour l'utilisation des outils de la chaîne
de développement SDCC. Toutes les directives et les éléments de syntaxe y sont décrits en détails et mis à jour
régulièrement.

15.3. Microcontrôleurs MSC12xx


MSC121x User's Guide, Guide de l'utilisateur MSC121x, sbau101a
Le document MSC121x User's Guide (Rev. A)74 regroupe les définitions des registres de fonctions spécifiques et
des exemples de programmation des différents types de périphériques intégrés dans le microcontrôleur.

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.

MSC1210 ROM Routines, Bibliothèque intégrée du MSC1210, sbaa085c


Les composants de la famille MSC1210 intègrent 2Ko de ROM dans laquelle sont placés des sous-programmes de
pilotage du microcontrôleur. Le premier sous-programme utilisé dans ce support : autobaud(); permet d'ajuster
automatiquement le débit de la liaison série entre la console du PC de développement et le microcontrôleur. La
documentation sur ces sous-programmes est fournie par le constructeur Texas Instruments™ : MSC1210 ROM
Routines (Rev. C)76.

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

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 60


Initiation au développement C sur microcontrôleur

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

MSC1210 EValuation Module


Le module d'évaluation MSC1210EVM est une carte complète fournie par le constructeur dont les caractéristiques
sont décrites à la page : Full EVM for use with the MSC121077.

15.4. Bus I²C


THE I²C-BUS SPECIFICATION
La documentation THE I²C-BUS SPECIFICATION VERSION 2.1 JANUARY 200078 contient l'ensemble des
définitions et des contraintes de communication sur un bus I²C. Le code I²C proposé dans ce document permet au
microcontrôleur MSC1210 d'être le maître sur un bus I²C. Cette réalisation logicielle doit respecter les contraintes
de temps sur les signaux de données (SDA) et d'horloge (SCL) définies dans ces spécifications. Le code I²C proposé
est à vocation pédagogique. Dans le cas d'une exploitation industrielle, il est préférable d'utiliser des composants
spécialisés dans le contrôle du bus I²C dont le matériel garantit le respect des spécifications I²C.

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.

• Émission d'un caractère c sur la ligne série.


void ser_putc(unsigned char c)
• Émission d'une chaîne de caractères s sur la ligne série.
void ser_puts(char code * data s)
• Transmission au programme appelant d'un caractère reçu sur la ligne série.
unsigned char ser_getc(void)
• Transmission au programme appelant d'un caractère reçu sur la ligne série avec echo.
unsigned char ser_getc_echo(void)
• Réception d'une chaîne de caractères s sur la ligne série. La longueur maximum de la chaîne est limitée à len
caractères.
void ser_gets(unsigned char *s, unsigned char len)
• Réception d'une chaîne de caractères s sur la ligne série avec echo. La longueur maximum de la chaîne est limitée
à len caractères.
void ser_gets_echo(unsigned char *s, unsigned char len)

77
http://focus.ti.com/docs/toolsw/folders/print/msc1210evm.html
78
http://www.nxp.com/acrobat_download/literature/9398/39340011.pdf

Initiation au développement C sur microcontrôleur $Revision: 1153 $ 61