0% ont trouvé ce document utile (0 vote)
72 vues113 pages

Systèmes Temps Réel et Embarqués

Le document présente une introduction aux systèmes temps réel et embarqués, en définissant leurs caractéristiques essentielles et leur architecture. Il aborde également la programmation des microcontrôleurs Arduino et des systèmes temps réels, en détaillant les concepts de modélisation, de gestion des tâches et d'utilisation des RTOS. Enfin, il souligne l'importance des contraintes temporelles dans le fonctionnement de ces systèmes pour garantir leur efficacité et leur fiabilité.

Transféré par

Ephraïm Henri
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
72 vues113 pages

Systèmes Temps Réel et Embarqués

Le document présente une introduction aux systèmes temps réel et embarqués, en définissant leurs caractéristiques essentielles et leur architecture. Il aborde également la programmation des microcontrôleurs Arduino et des systèmes temps réels, en détaillant les concepts de modélisation, de gestion des tâches et d'utilisation des RTOS. Enfin, il souligne l'importance des contraintes temporelles dans le fonctionnement de ces systèmes pour garantir leur efficacité et leur fiabilité.

Transféré par

Ephraïm Henri
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

REPUBLIQUE DEMOCRATIQUE DU CONGO

MINISTERE DE L’ENSEIGNEMENT SUPERIEUR ET UNIVERSITAIRE

ISTA/Kinshasa

Par
Prof. KAPITA Patrick

(Pour les étudiants de L3 informatique LMD)

Année académique 2024-2025


1

TABLE DES MATIÈRES


TABLE DES MATIÈRES .................................................................................... 1
CHAPITRE I : INTRODUCTION AUX SYSTEMES TEMPS REEL ET
SYSTEMES EMBARQUES ................................................................................. 4
I.1. DEFINITION .............................................................................................. 4
I.2. CARACTERISTIQUES D’UN SYSTEME TEMPS REEL ...................... 5
I.2.1. Système informatique en interaction avec son environnement ........... 5
I.2.2. Système qui doit effectuer des traitements simultanés ........................ 7
I.2.3. Activités respectent des contraintes de temps ..................................... 8
I.2.4. Système dont le comportement est à la fois prévisible et déterministe
..................................................................................................................... 11
I.2.5. Système qui possède une grande sûreté de fonctionnement .............. 12
I.3. ARCHITECTURE MATERIELLE D’UNE APPLICATION
TEMPS REEL ................................................................................................. 13
I.3.1. Calculateur ......................................................................................... 14
I.3.2. Transducteurs ..................................................................................... 16
I.4. NOTIONS SUR LE « SYSTEME TEMPS REEL EMBARQUE » ........ 18
I.4.1. Définitions .......................................................................................... 18
I.4.2. Spécificité des systèmes temps réel embarqués................................. 19
I.5. DOMAINES D’APPLICATIONS DES SYSTEMES TEMPS REEL ET
EMBARQUES ................................................................................................ 20
I.6. DECOMPOSITION DES SYSTEMES EMBARQUES .......................... 21
I.7. ARCHITECTURE DES SYSTEMES EMBARQUES ............................ 22
I.7.1. SE de première génération ................................................................. 22
I.7.2. SE de la deuxième génération ............................................................ 22
I.7.3. SE de la troisième génération ........................................................... 23
I.7.4. SE Spécifiques ................................................................................... 25
I.8. MODES DE FONCTIONNEMENT DES SYSTEMES EMBARQUES 25
I.8.1. Fonctionnement général : boucle infinie ........................................... 25
2

I.8.2. Fonctionnement Cyclique : ................................................................ 26


I.8.3. Fonctionnement Evénementiel .......................................................... 26
CHAPITRE II : PROGRAMMATION DES MICROCONTROLEURS
(ARDUINO) ........................................................................................................ 27
II.1. BASE DE LA PROGRAMMATION ARDUINO .................................. 27
II.1.1. Carte Arduino ................................................................................... 27
II.1.2. Logiciel ............................................................................................. 29
II.2. LANGAGE ARDUINO .......................................................................... 30
II.2.1. Structure de base ............................................................................... 32
II.2.2. Bibliothèques (librairies) .................................................................. 36
II.3. COMMUNICATION PAR LA LIAISON SERIE .................................. 36
III.3.1. Envoi de données ............................................................................ 37
II.3.2. Réception des données...................................................................... 38
II.4. GRANDEURS ANALOGIQUES ........................................................... 41
II.4.1. Les entrées analogiques .................................................................... 41
II.4.2. Sorties "analogiques" ........................................................................ 44
II.5. ECRANS LCD......................................................................................... 48
II.5.1. Afficher du texte ............................................................................... 49
II.5.2. Créer un caractère ............................................................................. 51
II.5.3. Défilement de texte ........................................................................... 52
II.6. COMMANDE PAR RESEAU INFORMATIQUE ................................ 54
II.6.1. Carte Arduino en Serveur ................................................................. 54
II.6.2. Carte Arduino en Client .................................................................... 64
CHAPITRE III : PROGRAMMATION DES SYSTEMES TEMPS REELS .... 71
III.1. MODÉLISATION DES SYSTÈMES TEMPS RÉELS ........................ 71
III.1.1. Contraintes temporelles ................................................................... 71
III.1.2. Caractéristiques des tâches .............................................................. 75
III.1.3. Exécutif ou noyau temps réel .......................................................... 78
III.1.4. Gestion des tâches ........................................................................... 80
III.1.5. Classification des algorithmes d'ordonnancement .......................... 81
3

III.2. PROGRAMMATION CONCURRENTE ET PROGRAMMATION


TEMPS REEL ................................................................................................. 83
III.2.1. Définitions ....................................................................................... 84
III.2.2. Intérêts de la programmation concurrente ...................................... 85
III.2.3. Terminologie ................................................................................... 87
III.3. RTOS ...................................................................................................... 88
III.3.1. Définition......................................................................................... 88
III.3.2. Différence entre le système d'exploitation en temps réel et le système
d'exploitation ............................................................................................... 89
III.3.3. Types de RTOS ............................................................................... 90
III.3.4. Avantages de l'utilisation d'un RTOS gratuit .................................. 91
III.3.5. Quelques problèmes majeurs liés aux RTOS .................................. 91
III.3.6. Comment utiliser RTOS .................................................................. 92
III.3.7. RTOS gratuit ................................................................................... 95
III.4. FreeRTOS............................................................................................... 97
III.4.1. Fonctionnement de RTOS ............................................................... 97
III.4.2. Termes fréquemment utilisés dans les RTOS ............................... 100
III.4.3. Installation de la bibliothèque Arduino FreeRTOS ...................... 101
III.4.4. Implementation de la tâche FreeRTOS dans l'IDE Arduino ......... 107
CHAPITRE I : INTRODUCTION AUX SYSTEMES TEMPS REEL ET
SYSTEMES EMBARQUES

I.1. DEFINITION

Dans le processus de choix de moyens conduisant à


l’informatisation, il existe trois types de solutions informatiques :

- Le traitement en temps réel,


- Le traitement en temps différé
- Le traitement en temps partagé.

Un traitement est dit en temps différé, lorsqu’il y a un décalage entre


la saisie des informations et l’obtention des résultats. En d’autres
termes, il y a désynchronisation dans le temps entre les quatre
opérations : saisie des données, transmission, traitement et
restitution des résultats.

Un traitement est dit en temps partagé, lorsque plusieurs utilisateurs


accèdent à un ordinateur central d’une manière simultanée.
Chacun des utilisateurs pouvant travailler sur ses
propres programmes.

Un traitement est dit en temps réel, lorsque l’évènement qui a fourni


l’information n’étant pas encore terminé, il y a un feed back (une
réponse). En d’autres termes, il n’y a pas de décalage de temps
entre les quatre opérations précitées. Nombreux sont ceux qui ont
essayé de donner une définition au terme système temps réel, nous
en avons retenu deux

Définition 1

« On qualifie de temps réel, les systèmes dont la réactivité est


assujettie à l’évolution de l’état d’un environnement, qui peut être
l’homme, un phénomène naturel ou artificiel, un équipement
manufacturé ... Dans ces systèmes, le facteur temps est une
contrainte essentielle à respecter, et ce facteur est prépondérant
pour évaluer la qualité du service. »

Définition 2
5

« Un système temps réel est un système dans lequel l’exactitude de


son fonctionnement ne dépend pas seulement de l’exactitude du
résultat mais aussi du temps auquel ce résultat
est produit. Si les contraintes temporelles de l‟application ne sont p
as respectées, on parle de défaillance du système. Il est donc
essentiel de pouvoir garantir le respect des contraintes temporelles
du système. » (GILLIES, 1995)

En définitive, nous pourrions définir un système temps réel comme,


un système informatique dont les temps de réaction à l’évolution et
aux sollicitations de l’environnement avec lequel il est en relation,
sont compatibles avec la dynamique propre de chacun des objets
de cet environnement.

I.2. CARACTERISTIQUES D’UN SYSTEME TEMPS REEL

Les cinq caractéristiques sui vantes permettent de bien cerner ce


qu’est un système informatique temps réel.

I.2.1. Système informatique en interaction avec son environnement

Tout système informatique temps réel est obligatoirement inséré


dans un environnement dont il doit commander et contrôler
le comportement.
6

Figure I.1 : système informatique en interaction avec son


environnement

Une application temps réel est l’ensemble fermé, formé du système


informatique et des objets en relation avec lui (entités).
L’environnement est l’ensemble des objets de l’application, à
l’exclusion du système informatique. Une entité a un comportement
dynamique et possède sa propre autonomie. Tout comme le
système informatique, elle possède des entrées, des sorties et au
moins un état interne qui caractérise sa situation à chaque instant.
Une entité fortement couplée par des entrées et des sorties avec le
système informatique est considérée en interaction avec lui.

Les objets de diverses natures (certains sont physiques, d’autres sont


humains) qui composent l’environnement d’un système temps réel
sont donc des objets dynamiques. Du fait même de leur activité, ils
génèrent des événements, des informations, des flux de données.
Ils sont d’autre part sensibles à des sollicitations externes selon une
dynamique qui leur est propre. En conséquence,
fonctionnellement, un système temps réel est dit de type réactif, car
il réagit sur l’environnement à partir de sollicitations qui en
proviennent. La fréquence des sollicitations constitue le paramètre
essentiel à prendre en compte lors de la conception du système
temps réel.
7

Pour satisfaire l’objectif qui lui est assigné, le système temps réel est
couplé aux objets de son environnement par des interfaces qui lui
permettent d’observer et de commander ces objets. On peut
distinguer deux grandes catégories d’interfaces :

 Les interfaces homme-machine et ;


 Les interfaces avec les entités physiques.

Les procédures d’échanges entre le système et les différents objets


de son environnement sont tributaires de la nature de ces
interfaces.

Les supports physiques du dialogue homme-machine peuvent être


très variés : il peut s’agir d’un pupitre conventionnel, d’un ensemble
clavier - Écran, de dispositifs mettant en œuvre des technologies
vocales avancées (analyse et synthèse de la parole), etc. Les
entités physiques sont des procédés industriels, des machines et
autres dispositifs physiques.
Ces entités fournissent par l‟intermédiaire de capteurs des signaux
électriques. Ensens inverse, des actionneurs commandés
électriquement permettent d’agir sur ces entités.

I.2.2. Système qui doit effectuer des traitements simultanés

Un système temps réel doit être capable d’assurer simultanément :


8

- L’observation de grandeurs caractérisant l’évolution de


l’activité des entités,
- La prise en compte d’évènements survenant de façon
aléatoire,
- L’évaluation, à partir des événements et des observations, des
décisions à prendre,
- La génération d’actions en direction des entités pour assurer
la cohérence de l’ensemble de l “application.

Le nombre de traitements simultanés que doit assurer un système


temps réel dépend du nombre des entités de son environnement
et de la diversité de leur activité, et donc, du nombre des entrées
et des sorties que le système doit gérer, de la richesse des
fonctionnalités du système. Le degré de simultanéité de ces
traitements est fonction de la technique de réalisation du système
informatique. La simultanéité est complète lorsqu’une unité
centrale (CPU) est attribuée à chaque traitement à effectuer (on
parle de systèmes multiprocesseurs).

Il y a pseudo-simultanéité lorsqu’un processeur unique partage son


activité pour satisfaire plusieurs traitements (on parle de systèmes
monoprocesseurs).

I.2.3. Activités respectent des contraintes de temps

Nous avons déjà indiqué que dans les applications temps réel le
facteur temps constitue une contrainte essentielle à respecter, que
c’est même un facteur prépondérant pour évaluer la qualité de
service du système de commande.

Le système informatique est tributaire de la dynamique, de la


vitesse d’évolution des objets avec lesquels il interagit. Cet
environnement est en effet constitué d’entités qui, chacune à son
propre rythme, génèrent des événements et modifient des informa
tions. Toute fonction dusystème qui est en interaction avec une
entité doit a priori réagir à la même échelle de vitesse.

La commande d’un robot, d’un chariot filoguidé, d’un avion est


intimement liée à la vitesse d’évolution, à la dynamique propre de
9

ces objets. D’autre part, lors de la conception d’un système temps


réel il doit être tenu compte du temps de réaction du programme
informatique à chaque sollicitation externe. Ce temps de réaction
dépend notamment du temps de traitement de l’information, et
donc du volume des calculs et de la puissance du processeur.

Évaluation des contraintes de temps

Un système temps réel est donc un système dont les activités


respectent des contraintes de temps bien précises. L’interaction
importante du système avec son environnement fait apparaître
plusieurs types de contraintes de temps ; celles-ci découlent de :

- La fréquence des sollicitations en provenance des entités :


fréquence d’évènements, débit des données entrantes, …
- La fréquence des actions à déterminer et à appliquer aux
entités, compte tenu notamment, du temps de réponse des
procédés physiques.
- La valeur maximale admissible du temps de réaction, à savoir
le temps entre l’instant d’apparition d’une sollicitation et
l’instant d’achèvement de l’action conséquente (date
d’échéance).

Les performances temporelles qu’un système doit réaliser, peuvent


en définitive être exprimées en considérant d’une part le temps de
réponse du système et d’autre part le taux de transfert des Données
:

- Le temps de réponse du système est le temps maximal qui


s’écoule entre la détection d’un signal externe et la
génération de l’action appropriée.
- Le taux de transfert des données, exprimé sous forme de
débit, est la vitesse avec laquelle des données sont acquises
ou produites par le système.

Importance du respect des contraintes de temps

Le non-respect des contraintes de temps peut conduire l’application


dans un état assimilable à un état de panne, ce qui peut engendrer
des conséquences très graves, jusqu’à la mise en danger de vies
humaines.
10

Dans un système temps réel, on parle de faute temporelle pour


désigner le non-respect d’une échéance, de l’arrivée hors délai
d’un message, de la non régularité d’une période
d’échantillonnage, d’une dispersion temporelle trop importante
dans un lot de mesures censée être simultanées.

Contraintes strictes et contraintes relatives

Parmi toutes les contraintes temporelles d’une application temps


réel on est amené à distinguer contraintes strictes et les contraintes
relatives :

- Le temps réel est contraint strictes lorsque les conséquences


d’une seule faute temporelle peuvent être catastrophiques.
- Le temps réel est à contraintes relatives lorsque des fautes
temporelles engendrent des dégâts dont le coût, rapporté à
leur probabilité d’occurrence, est estimé tolérable pour
l’exploitant de l’application.

A propos du temps chronométrique et du temps chronologique. On


se préoccupe des applications dont la fonction est de piloter (ou
de suivre) en temps réel un environnement par un système
informatique. Il est alors commode de scinder de telles applications
en deux parties : le système informatique et le procédé, c’est-à-
dire l’environnement auquel ce système informatique est connecté
et dont il doit commander le comportement.

Le fait qu’un système informatique intègre une gestion du temps


constitue une spécificité Fondamentale qui le distingue d’un
système informatique classique. En effet, la validité des résultats
produits par une application temps réel ne dépend pas seulement
de la justesse des calculs, mais aussi de leur instant de production :
un résultat juste mais hors délai est un résultat faux, et est considéré
comme une faute temporelle. Or, dans une application temps réel
les deux partenaires que sont le procédé et le système sont régis
par des règles d’évolution différentes.

Le procédé est régi par une dynamique propre


pouvant être exprimée par des mesures précises de durée, c’est-
à-dire par un temps chronométrique. Un système informatique
organise la succession des instructions de la machine, et génère
11

ainsi un temps chronologique. La conduite d’une application


temps réel par un système informatique réside dans le bon contrôle
de la concordance de ces deux temps. Comme le temps
chronométrique est régi par le procédé et en constitue une donnée
intangible, c’est au système informatique de mettre le
cadencement de ses actions en phase avec l’horloge du procédé.

I.2.4. Système dont le comportement est à la fois prévisible et


déterministe

Selon la méthode employée par le système informatique pour


prendre en compte puis répondre aux événements externes, on
obtiendra des temps de réaction plus ou moins longs et surtout plus
ou moins prévisibles.

L’un des aspects importants des systèmes temps réel est la


prévisibilité du comportement du système. Un système temps réel
doit être conçu de telle façon que ses performances soient définies
dans le pire des cas, alors que dans les systèmes classiques on
s’intéresse au traitement du cas « moyen ». La prévisibilité est ce qui
permet de déterminer à l’avance si un système va respecter ses
contraintes temporelles. La prévisibilité est d’autant plus forte que
l’on connaît avec précision les paramètres liés aux calculs des
activités : le temps global de calcul de chaque activité, leur
périodicité, etc. Tous ces éléments conduisent à choisir la politique
d’ordonnancement des activités de telle manière que le
comportement du système soit prévisible.

Le déterminisme est le but que l’on cherche à atteindre afin de


prédire le comportement temporel d’un système : il s’agit d’enlever
toute incertitude sur le comportement des activités individuelles et
sur leurs comportements quand elles sont mises ensemble dans le
contexte d’exécution du système. Dans le cas du temps réel à
contraintes strictes, on cherche à savoir si toutes les échéances de
toutes les activités seront respectées. Dans le cas du temps réel à
contraintes relatives, on cherche à connaître, par exemple,
le retard moyen des activités. Les principaux facteurs qui entraînent
des variations dans le comportement du système, et sont donc
source de non-déterminisme, sont les suivants :
12

- Charge de calcul : variation des durées d’exécution des


activités,
- Entrées/sorties : profil d’arrivée, durées des communications,
temps de réaction, interruptions : temps de réaction
du système,
- Fautes et exceptions matérielles ou logicielles. Le
déterminisme absolu est difficile à atteindre ; cependant, les
systèmes auront un Comportement temporel plus déterministe
s’ils utilisent les services d’un support d’exécution qui masque
les fluctuations mentionnées ci-dessus.

Par ailleurs, certaines méthodes d’ordonnancement des activités


du système permettent une analyse a priori du comportement de
ce dernier. En définitive, un système temps réel est un système
déterministe dans le sens où il doit offrir des temps de réponse
garantis aux diverses sollicitations de son environnement ; et ce,
quel que soit le contexte ou l’état du système au moment où
surviennent ces sollicitations. Le déterminisme est un des concepts
clés des systèmes temps réel.

I.2.5. Système qui possède une grande sûreté de fonctionnement

La sûreté de fonctionnement du système est son aptitude à


minimiser la probabilité d’apparition de défaillances, et à
minimiser leurs effets quand, malgré tout, elles se produisent. Les
principales composantes de la sûreté de fonctionnement sont :

La sécurité : c’est l’aptitude à éviter des événements


catastrophiques pour l’application considérée.

La fiabilité : c’est la probabilité qu’un système fonctionne


correctement durant une période de temps T.

La maintenabilité : c’est la facilité que possède un système à


supporter des changements pour satisfaire les demandes des
utilisateurs et pour corriger les défauts détectés. Qui dit sécurité dit
protection de l’utilisateur et du matériel afin d’éviter des incidents
ou des détériorations. Comme les conséquences d’une défaillance
peuvent être catastrophiques, le système doit donc être en mesure
d’assurer en toutes circonstances la sécurité du dispositif qu’il
commande.
13

Autrement dit, le dépassement d’une valeur critique ou


l’occurrence d’un événement accidentel doit pouvoir être détecté
à tout instant, et le traitement de cet incident doit interrompre les
opérations en cours. Le système doit de plus garantir un service
minimal ; et ceci, même en cas de panne du matériel,
d’évènements accidentels ou d’erreurs humaines. La fonction
sécurité doit donc être prioritaire sur toutes les autres
fonctions assurées par le système.

Autre aspect important des systèmes temps réel : la fiabilité. Il est en


effet difficile de prédire le comportement d’un system dont les
composants matériels et logiciels ne sont pas fiables. Pour cette
raison, les systèmes temps réel sont souvent conçus de façon à être
tolérants aux fautes.

En résumé, dans l’ensemble des systèmes informatiques, les


systèmes temps réel présentent des spécificités telles qu’ils
constituent un domaine à part entière. Un système temps réel, c’est
donc tout à la fois :

- Un système informatique en interaction avec son


environnement,
- Un système qui doit effectuer des traitements simultanés,
- Un système qui effectue ses activités en respectant des
contraintes de temps,
- Un système dont le comportement est à la fois prévisible et
déterministe,
- Un système qui possède une grande sûreté de
fonctionnement.

I.3. ARCHITECTURE MATERIELLE D’UNE APPLICATION TEMPS REEL

Nous appelons architecture matérielle, l’ensemble des composants


physiques qu’il est nécessaire d’ajouter à un processus pour réaliser
l’application temps réel. L’architecturé matérielle est donc
composée d’un calculateur, de capteurs et d’actionneurs.
14

I.3.1. Calculateur

Le calculateur peut être composé d’un ou plusieurs


microprocesseurs, de circuits intégrés spécialisés tels que des ASIC ou
des FPGA ainsi que des médias de communication.

I.3.1.1. ASIC et FPGA

Les ASIC et les FPGA sont des circuits intégrés que l’on peut
configurer pour réaliser à bas cout des fonctions “câblées” simples
dont les temps de réaction sont extrêmement courts.
La programmation de ces circuits n‟est pas évidente car elle néce
ssite l’utilisation de programmateurs ou de procédures spécialisés.
Ces circuits n’intègrent pas de séquenceur d’instructions, ils ne sont
donc capables que d’exécuter une seule fonction.

I.3.1.2. Microprocesseurs, microcontrôleurs

Un microprocesseur est composé d’un CPU2 et d’unités de


communication pour communiquer avec des périphériques
externes ou d’autres microprocesseurs.

Le CPU est une machine séquentielle constituée généralement


d’un séquenceur d’instruction (SI), d’une unité de traitement (UT) et
d’une mémoire. Les dernières générations de microprocesseurs
peuvent aussi intégrer une Unité de calcul flottant (FPU) dont le but
est de considérablement accélérer certains calculs
mathématiques (multiplication, division, sinus, arrondi, etc.).

Un microcontrôleur est un microprocesseur intégrant un certain


nombre d’interfaces supplémentaires (mémoires, timers, PIO3,
décodeurs d’adresse, etc.). Ces nombreuses entrées-sorties
garantissent un interfaçage aisé avec un environnement extérieur
tout en nécessitant un minimum de circuits périphériques ce qui les
rend particulièrement bien adaptés aux applications temps
réel embarquées.

Du fait de leur forte intégration en périphérique (certains


microcontrôleurs vont jusqu’à intégrer des fonctions spécialisées
dans la commande des moteurs), les microcontrôleurs sont souvent
moins puissants que les microprocesseurs ; le CPU qu’ils intègrent est
généralement en retard d’une ou même deux générations.
15

Dans la suite du cours, nous utiliserons le terme de processeur pour


désigner indifféremment un microprocesseur ou un
microcontrôleur. De ce point de vue le processeur est simplement
un circuit qui intègre un CPU.

Figure I.2 : Structure d’un Microcontrôleur

I.3.1.3 Médias de communication

Lorsque le calculateur intègre plusieurs processeurs on dit que


l’architecture matérielle du système est multiprocesseur ou
parallèle. Les médias de communications sont les éléments qui
permettent aux processeurs d’un
calculateur multiprocesseur d’échanger des données.

On peut classer ces médias de communication selon trois


catégories principales :
16

Média point à point SAM (Sequential Access Memory. L’accès à ce


type de liaison est de type FIFO -First In First Out- pour lequel l’ordre
démission des données est aussi l’ordre de réception) aussi appelée
Lien : c’est une liaison bidirectionnelle entre deux mémoires SAM de
deux processeurs différents ;

Média multipoint SAM ou bus SAM : c’est une liaison


multidirectionnelle qui relie les mémoires SAM de plus de deux
processeurs ;

Média multipoint RAM ou bus RAM : c’est aussi une liaison


multidirectionnelle, mais ici contrairement aux médias précédents
la communication est réalisée à travers une mémoire commune de
type RAM5.

I.3.2. Transducteurs

Le calculateur interagit avec le processus par l’intermédiaire de


transducteurs. Ceux
qui permettent d’observer le processus sont appelés capteurs et
ceux qui permettent d’agir sur le processus sont appelés
actionneurs.

I.3.2.1. Capteur

Un capteur est un dispositif conçu pour mesurer une grandeur


physique (température, pression, accélération, etc.) en la
convertissant en une tension ou un courant électrique. Le signal
électrique issu d’un capteur est un signal analogique qui doit être
discrétisé pour pouvoir être traité par le calculateur. Cette
discrétisation ou numérisation est réalisée par un circuit appelé
convertisseur Analogique-Numérique (C.A.N). Il est utilisé pour

Echantillonner le signal électrique issu du capteur, c’est-à-dire


mesurer- le plus souvent à des intervalles réguliers- lavaleur de ce
signal électrique et ainsi produire une suite de valeurs binaires qui
constituent le signal discrétisé ou signal numérique.

L’opération de codage de la valeur échantillonnée en un nombre


binaire codé sur n bits est appelée quantification. La résolution de
la quantification et donc la précision de la mesure dépend du
17

nombre de bits utilisés pour coder la valeur (la valeur peut être
codée sur 2n Niveaux).

Dans le cas particulier où le codage est réalisé à l’aide d’un seul bit,
le capteur est appelé capteur binaire, le signal qu’il délivre est un
signal Booléen qui par définition ne peut prendre que deux valeurs
(0 ou 1). Ce type de capteur est utilisé pour déterminer l’état d’un
phénomène Modélisé par deux états exclusifs (exemple :
présence/non présence d’un objet, seuil de température
atteint/non atteint, récipient vide/non-vide, etc.).

I.3.2.2. Actionneur

L’actionneur est un dispositif qui convertit un signal électrique en un


phénomène physique (moteur, vérin électrique, voyant, haut-
Parleur, etc.) censé modifier l’état courant du processus.

Le signal de commande fourni par le calculateur est un signal


numérique qu’il faut convertir en signal électrique analogique à
l’aide d’un Convertisseur Numérique Analogique (C.N.A).

Pour reconstruire un signal analogique à partir de la suite des


valeurs numériques qui constituent le signal numérique de
commande, le C.N.A maintient grâce à un bloqueur, la valeur du
signal analogique à la même valeur pendant le laps de temps
nécessaire au calculateur pour produire la valeur suivante. Le signal
obtenu est ensuite lissé par un filtre. Il est à noter que ce signal
analogique de commande ne peut être appliqué directement à
l’entrée de l’actionneur. Il est nécessaire d’intercaler un
amplificateur entre le C.N.A et l’actionneur qui fournira à ce dernier
la puissance nécessaire à la création du phénomène physique
pour lequel il est conçu.

Certains actionneurs ne nécessitent pas l’utilisation d’un C.N.A car


ils peuvent directement être commandés par des signaux
numériques, c’est le cas par exemple :

- D’une vanne hydraulique qui peut être commandée par


un signal booléen (ouverte/fermée) ;
- D’un moteur à courant continu piloté par un pont de
transistors (rôle d’amplificateur de
puissance) commandé par un signal booléen dont le rapport
18

cyclique déterminera la tensionmoyenne appliquée au


moteur.

I.4. NOTIONS SUR LE « SYSTEME TEMPS REEL EMBARQUE »

I.4.1. Définitions

La diversité des domaines d’applications rend difficile l’élaboration


de définitions sur lesquelles tout le monde s’accorde.

Voici quelques définitions pour cerner le concept de système


embarqué :

Un système embarqué (SE) est un système informatisé spécialisé qui


constitue une partie intégrante d’un système plus large ou une
machine. Typiquement, c’est un système sur un seul processeur et
ont les programmes sont stockés en ROM. A priori, tous les systèmes
qui ont des interfaces digitales (i.e. montre, caméra, voiture…)
peuvent être considérés comme des SE. Certains SE ont un système
d’exploitation et d’autres non car toute leur logique peut être
implantée en un seul programme.

Un système embarqué est une combinaison de logiciel et matériel,


avec des capacités fixes ou programmables, qui est spécialement
conçu pour un type d’application particulier. Les distributeurs
automatiques de boissons, les automobiles, les équipements
médicaux, les caméras, les avions, les jouets, les téléphones
portables et les PDA sont des exemples de systèmes qui abritent des
SE. Les SE programmables sont dotés d’interfaces de
programmation et leur programmation est une activité spécialisée.

Un système embarqué est une composante primordiale d’un


système (i.e. un avion, une voiture…) dont l’objectif est de
commander, contrôler et superviser ce système.

Un système embarqué est un système enfoui (embedded system)


Par rapport aux autres systèmes informatisés, les systèmes
embarqués sont caractérisés par :

- Encombrement mémoire (mémoire limitée, pas de disque


en général) ;
- Consommation d’énergie (batterie : point faible des SE) ;
19

- Poids et volume ;
- Autonomie ;
- Mobilité ;
- Communication (attention : la communication affecte la
batterie)
- Contraintes de temps réel ;
- Contraintes de sécurité ;
- Coût de produits en relation avec le secteur cible ;

I.4.2. Spécificité des systèmes temps réel embarqués

Lorsque le système temps réel est physique ment intégré à


l’environnement qu’il contrôle et qu‟il est soumis aux mêmes
contraintes physiques (température, pression, etc.) que son
environnement, il est dit embarqué.

Exemples

Un système temps réel intégré dans un robot mobile est un système


embarqué, alors qu’un système de contrôle de bras de robot n’en
est pas un. Dans le premier cas, le système temps réel fait partie
intégrale du robot, il se déplace avec lui, il est soumis aux mêmes
contraintes
physiques externes. Dans le deuxième cas, le système peut être da
ns une armoire électrique placée dans une pièce différente de la
pièce où se situe le robot.

Ce système temps réel est donc indépendant du bras


manipulateur, il n’est pas intégré à son environnement (bras +
objets manipulés). Les systèmes embarqués sont généralement
soumis à des contraintes spécifiques de coûts pris au sens large du
terme. Ces contraintes sont dues, d’une part, au type
d’applications
couvertes par ce type de système, d‟autre part, à l‟intégration du
système à son environnement. Ces contraintes particulières de
coût sont de plusieurs types : encombrement, consommation
d’énergie, prix, etc.

Prenons l’exemple d’un système temps réel embarqué dans un


robot dont la mission est d’explorer des canalisations. Ce système
sera soumis à des contraintes d’autonomie car il devra intégrer et
20

gérer au mieux sa propre source d’énergie et sera soumis à de


fortes contraintes d’encombrement.

D’autres applications telles que les applications automobiles


nécessitent l’utilisation de systèmes embarqués dont les contraintes
sont principalement des contraintes de coût financier (forte
compétitivité du secteur commercial), d’encombrement
(habitabilité du véhicule) et de consommation d’énergie.

Concevoir un système temps réel embarqué nécessite une bonne


maîtrise des coûts matériels mais aussi une bonne maîtrise des coûts
de développement car ces derniers représentent une grande part
du coût financier d’une application.

I.5. DOMAINES D’APPLICATIONS DES SYSTEMES TEMPS REEL ET


EMBARQUES

Jusqu’à une date récente, les systèmes temps réel et embarqués


étaient destinés quasi-exclusivement aux applications de
contrôle/commande de procédés (ou de
phénomènes) physiques (tels que des laminoirs ou des usines de fa
brication de voitures) et applicationsmilitaires.

La notion de temps réel se confondait alors avec ce type


d’applications. Le développement de l’informatique aidant, les
applications temps réel et embarqués sont présentes aujourd’hui
dans des domaines très variés comme le montre la liste suivante,
même si elle n’est pas exhaustive :
- Télécommunications (transport de la parole, systèmes de
commutation, …),
- Domaine médical (assistance et supervision de malades, …),
- Contrôle de différents équipements dans les voitures, bus,
trains, avions, …,
- Contrôle et régulation de trafic en milieu urbain,
- Guidage de véhicules en milieu urbain,
- Industrie (contrôle/commande de procédés manufacturiers
ou continus, …),
- Domaine militaire (suivi de trajectoires de missiles, …)
- Aérospatial (suivi de satellites, simulationde vols, pilotage
automatique, …),
21

- Multimédia (transport d‟images et de voie, téléconférences,


…),
- Finance (suivi du cours des devises et actions, ...),
- Loisirs (consoles de jeu, ...),
- Domotique (sécurité d’habitations, …),
- Contrôle/commande d’appareils électroménagers.

I.6. DECOMPOSITION DES SYSTEMES EMBARQUES

Quelle que soit la nature et la complexité du système, on


décompose un système embarqué en :

- Le système contrôlé
- Le système de contrôlée

Système contrôlé = environnement (procédé) équipé d'une


instrumentation qui réalise l’interface avec le système de contrôle.

Système de contrôle = éléments matériels (microprocesseurs…) et


logiciels dont la mission est d'agir sur le procédé via les actionneurs
en fonction de l’état de ce procédé indiqué par les capteurs de
manière à maintenir ou conduire le procédé dans un état donné.

Figure I.3 : Décomposition d’un système embarqué

Un système électronique embarqué ou enfoui est un élément


constitutif d'un système plus complexe pour lequel il rend des
services bien précis (contrôle, surveillance, communication…). Il est
constitué de parties matérielles et logicielles qui sont conçues
spécifiquement pour réaliser une fonction dédiée.
22

Système embarqué = Système électronique/informatique conçu


pour réaliser une ou plusieurs tâches précises.

I.7. ARCHITECTURE DES SYSTEMES EMBARQUES

Les architectures sont supportées par trois générations d'outils


de conception.

I.7.1. SE de première génération

1Ils sont composés de deux parties :

Partie matérielle des systèmes embarqués de première génération.


Les premiers systèmes embarqués supportés par des outils tels que
COSYMA et Vulcan étaient très simples : ils étaient constitués d'un
processeur qui contrôlait un nombre restreint de CIAS (circuits
intégrés à applications spécifiques) ou ASIC qui étaient appelés
périphériques. Les communications de cette architecture se situent
au niveau du bus du processeur et sont type maitre/esclave : le
processeur est le maître et les périphériques sont les esclaves.

Les périphéries de ces architectures étaient essentiellement des


capteurs et des actionneurs (contrôleurs magnétiques, sortie, etc.).
Le processeur est dédié au calcul et au contrôle de l’ensemble du
système. Les microcontrôleurs assemblent sur une même puce le
processeur et les périphériques.

Partie logicielle des systèmes embarqués de première


génération Ne devant pas exécuter de nombreuses
opérations simultanées (le nombre de périphériques etde fonctions
étant restreint), les parties logicielles étaient constituées d'un seul
programme. La réaction aux événements était effectuée par le
biais de routines de traitement d'interruptions. Cette partie logicielle
était décrite directement en langage d'assemblage ce qui
permettait d'obtenir un code efficace et de petite taille.

I.7.2. SE de la deuxième génération

Les premiers systèmes embarqués ne pouvaient fournir que des


fonctions simples ne requérant que peu de puissance de calcul.
Leur architecture ne peut pas supporter les fonctionnalités requises
pour les systèmes embarqués actuels à qui il est demandé non
23

seulement d'effectuer du contrôle, mais aussi des calculs


complexes tels que ceux requis pour le traitement numérique du
signal. De nouveaux outils tels que N2C (dans ces nouvelles
versions) permettent de traiter des architectures plus complexes. Ils
sont composés de deux parties :

Partie matérielle des systèmes embarqués de deuxième génération


:

L'architecture des systèmes embarqués de deuxième génération


est composée d'un processeur central, de nombreux périphériques,
et souvent de quelques processeurs annexes contrôlés par le
processeur central. Le processeur central est dédié au contrôle de
l'ensemble du système.

Les processeurs annexes sont utilisés pour les calculs ; il s'agit


souvent de processeurs spécialisés comme les DSP. Dans une telle
architecture, plusieurs bus de communication peuvent être
nécessaires : chaque processeur dispose de son bus de
communication.

Partie logicielle des systèmes embarqués de deuxième génération


: La partie logicielle des systèmes embarqués de deuxième
génération est répartie sur plusieurs processeurs (le processeur
principal et les processeurs annexes). Les systèmes actuels sont trop
complexes pour pouvoir être gérés par un unique programme sur le
processeur principal. Il est donc nécessaire d'avoir une gestion
multitâche sur ce processeur, et un système d'exploitation est
couramment employé dans ce but.

Le logiciel du processeur central est souvent décrit dans un langage


de haut niveau tel que le C. Le logiciel des processeurs annexes est
souvent trop spécifique pour être entièrement décrit dans un
langage de haut niveau, et l'utilisation des langages d'assemblage
est nécessaire.

I.7.3. SE de la troisième génération

Les progrès de l'intégration permettent d'envisager des circuits


pouvant contenir plusieurs milliers de portes. Il devient donc
techniquement possible de fabriquer des systèmes
embarqués pouvant remplir toutes les fonctionnalités souhaitées.
24

Parties matérielles des systèmes embarqués de troisième


génération :

Pour pouvoir supporter conjointement les besoins en puissance et


en flexibilité, ces architectures comprennent de plus en plus de
processeurs, qui peuvent chacun se comporter en maitre :
l'architecture couramment utilisée, basée sur un processeur central
contrôlant le reste du système, n'est donc plus suffisante.

Alors que le goulot d'étranglement était les ressources en calcul, de


nos jours il est situé plutôt au niveau des communications. Ce sont
elles qui définissent désormais l'architecture, et non plus les
ressources de calcul. Le premier exemple est basé sur des
communications par bus : ce modèle de communication
consomme peu de surface, mais risque de devenir un goulot
d'étranglement. Le deuxième est basé sur des communications en
barres croisées
très performantes mais aussi très coûteuses en surface. Le troisième
exemple donne une solutionintermédiaire, par réseau commuté.
Enfin le dernier exemple montre qu'il est possible de mixer plusieurs
modèles de communication, et d'apporter de la hiérarchie dans
l'architecture.

Autour de ce modèle d'architecture centré sur les communications,


se greffent les autres modèles d'architecture : architectures des
éléments de calcul et des mémoires. L'architecture des éléments
de calcul consiste à définir quels sont les éléments principaux et
quels sont
leurs périphériques de manière à les grouper dans une architectur
e locale. L'architecture desmémoires sert à définir quelles sont les
mémoires locales à un groupe et quelles sont celles qui seront
partagées.

Parties logicielles des systèmes embarqués de troisième génération


: Les parties logicielles ont beaucoup gagné en importance dans
les systèmes embarqués. Plusieurs systèmes d'exploitation sont
parfois nécessaires pour les divers processeurs de l'architecture. De
plus, la complexité et la diversité des architectures possibles font
qu'il devient de plus en plus nécessaire d'abstraire les tâches
logicielles des détails du matériel. Toute cette complexité est donc
reportée dans les systèmes d'exploitation, qui deviennent de plus
25

en plus complexes. Cette complexité logicielle et matérielle


entraîne de nombreuses alternatives. En particulier, l'aspect
multiprocesseur apporte des alternatives pour les systèmes
d'exploitation : il peut y a voir un seul système pour tous les
processeurs (solution difficilement applicable lorsque
les processeurs sont hétérogènes), ou il peut y avoir un système par
processeur (solution qui peut-être plus coûteuse).

I.7.4. SE Spécifiques

Lorsqu'un système est utilisé pour une tâche bien précise, il est
souvent plus efficace et économe s'il est spécifique à cette
fonctionnalité que s'il est général. Les systèmes embarqués sont très
souvent utilisés dans ces conditions, et il est donc intéressant qu'ils
soient conçus spécifiquement pour les fonctions qu'ils doivent
remplir. Notamment, les contraintes citées dans la section
précédente ne peuvent souvent être respectées que si le système
est conçu dès le départ pour pouvoir les respecter. Il est donc de
par sa conception même spécifique. Le problème qui se pose alors
est que, pour chaque nouvelle fonctionnalité, il faudra concevoir
un système spécifique.

I.8. MODES DE FONCTIONNEMENT DES SYSTEMES EMBARQUES

I.8.1. Fonctionnement général : boucle infinie

Tant que TOUJOURS faire


Acquisition des entrées (données capteurs, mesures…)
Calcul des ordres à envoyer au procédé
Emission des ordres
Fin tant que

Mais, il existe aussi d’autres modes de fonctionnement :

- Fonctionnement cyclique (time driven ou système


"synchrone")
- Fonctionnement événementiel (event driven)
- Fonctionnement mixte : à base de traitements périodiques et
apériodiques (déclenchés sur évènements)
26

I.8.2. Fonctionnement Cyclique :

Le principe est le suivant :

- La scrutation d'une mémoire d'entrée périodiquement


(polling)
- L’échantillonnage des entrées sur l'horloge du système
- L’activation du système à chaque top d'horloge

A chaque top d'horloge faire Lecture de la mémoire des entrées


Calcul des ordres à envoyer au procédé Emission des ordres Fin
Mais ce type de système est peu "réactif" si l'environnement produit
des informations à des fréquences différentes ce qui oblige à
prévoir toutes les réactions du système dans la
même boucle donc il y a un problème de performance et en est
obligé à imbriquer des boucles defréquences multiples ce qui
implique des difficultés de réalisation, de lisibilité du code et
d’évolution

I.8.3. Fonctionnement Evénementiel

Le fonctionnement est basé sur le principe d'activation du système


à chaque événement (notion d’interruption) A chaque interruption
faire Lecture de l'information arrivée activation du
traitement correspondant Émission des ordres issus de ce traitement
Fin Mais dans ce cas le problème réside dans le cas où une
interruption survient alors que le système est en train de traiter
une interruption précédente, ce qui implique des contraintes de
programmation :

- Notion de priorité des interruptions


- Notion de "tâche" associée à une ou plusieurs interruptions
- Mécanisme de préemption et de reprise de tâche
- Gestion de l'exécution concurrente des
tâches (ordonnancement)=>

Un système temps réel est souvent un système multitâche incluant


un gestionnaire de tâches(Ordonnanceur).
CHAPITRE II : PROGRAMMATION DES MICROCONTROLEURS
(ARDUINO)

Dans le dernier chapitre de ce cours, nous allons parler de la


programmation des microcontrôleurs en Arduino. Nous allons
commencer par les notions de base avant de nous arrêter sur la
programmation des processus à commande à distance via le port
Ethernet et le protocole de communication Asynchrone série pour
les liaisons des modules Bluetooth, XBee, M433, etc.

II.1. BASE DE LA PROGRAMMATION ARDUINO

II.1.1. Carte Arduino

Le système Arduino donne la possibilité d'allier les performances de


la programmation à celles de l'électronique. Plus précisément, pour
programmer des systèmes électroniques. Le gros avantage de
l'électronique programmée c'est qu'elle simplifie grandement les
schémas électroniques et par conséquent, le cout de la réalisation,
mais aussi la charge de travail à la conception d'une carte
électronique.

Le système Arduino permet de :

• Contrôler les appareils domestiques


• Fabriquer votre propre robot
• Faire un jeu de lumières
• Communiquer avec l'ordinateur
• Télécommander un appareil mobile (modélisme)
• etc.

Le système Arduino est composé de deux choses principales : le


matériel et le logiciel.
La carte électronique basée autour d'un microcontrôleur Atmega
du fabricant Atmel, dont le prix est relativement bas pour l'étendue
possible des applications.
Il y a trois types de cartes :
28

 Les ≪ officielles ≫ qui sont fabriquées en Italie par le fabricant


officiel : Smart Projects,
 Les ≪ compatibles ≫ qui ne sont pas fabriqués par Smart
Projects, mais qui sont totalement compatibles avec les
Arduino officielles,
 Les ≪ autres ≫ fabriquées par diverse entreprise et
commercialisées sous un nom diffèrent (Freeduino, Seeduino,
Femtoduino, ...). carte Uno et Duemilanove

Figure II.1 : Exemple de quelques cartes

La carte Arduino est équipée d'un microcontrôleur. Le


microcontrôleur est un composant électronique programmable.
On le programme par le biais d’un ordinateur grâce à un langage
informatique, souvent propre au type de microcontrôleur utilise. Un
microcontrôleur est constitué par un ensemble d’éléments qui ont
chacun une fonction bien déterminée. Il est en fait constitué des
mêmes éléments que sur la carte mère d’un ordinateur :

1. La mémoire

Il en possède 5 types :

 La mémoire Flash : C'est celle qui contiendra le programme à


exécuter. Cette mémoire est effaçable et réinscriptible.
29

 RAM : c'est la mémoire dite "vive", elle va contenir les variables


de votre programme. Elle est dite "volatile" car elle s'efface si
on coupe l'alimentation du microcontrôleur.
 EEPROM : C'est le disque dur du microcontrôleur. Vous pourrez
y enregistrer des infos qui ont besoin de survivre dans le temps,
même si la carte doit être arrêtée. Cette mémoire ne s'efface
pas lorsque l'on éteint le microcontrôleur ou lorsqu'on le
reprogramme.
 Les registres : c'est un type de mémoire utilisé par le
processeur.
 La mémoire cache : c'est une mémoire qui fait la liaison entre
les registres et la RAM.

2. Le processeur

C'est le composant principal du microcontrôleur. C'est lui qui va


exécuter le programme qu'on lui donnera à traiter. On le nomme
souvent le CPU.

Pour que le microcontrôleur fonctionne, il lui faut une alimentation


! Cette alimentation se fait en générale par du +5V. D'autres ont
besoin d'une tension plus faible, du +3,3V.

En plus d'une alimentation, il a besoin d'un signal d'horloge. C'est en


fait une succession de 0 et de 1 ou plutôt une succession de tension
0V et 5V. Elle permet en outre de cadencer le fonctionnement du
microcontrôleur a un rythme régulier. Grace à elle, il peut introduire
la notion de temps en programmation.

II.1.2. Logiciel

Au jour d'aujourd'hui, l'électronique est de plus en plus remplacée


par de l'électronique programmée. On parle aussi d'électronique
embarquée ou d'informatique embarquée. Le logiciel du système
Arduino permet de tester le programmer en compilant et de le
transférer au sein de la mémoire du microcontrôleur.
30

II.1.2.1. L'interface
L'interface du logiciel Arduino se présente de la façon suivante :
1. options de configuration du logiciel,
2. boutons pout la programmation des cartes,
3. programme à créer,
4. débogueur (affichage des erreurs de programmation).

Figure II.2 : IDE Arduino

II.2. LANGAGE ARDUINO

Le projet Arduino était destiné à l'origine principalement a la


programmation multimédia interactive en vue de spectacle ou
d'animations artistiques. C'est une partie de l'explication de la
descendance de son interface de programmation de Processing.
31

Processing est une librairie java et un environnement de


développement libre. Le logiciel fonctionne sur Macintosh,
Windows, Linux, BSD et Android.

Cependant, le projet Arduino a développé des fonctions


spécifiques à l'utilisation de la carte qui ont été listées ci-dessous.
Vous obtiendrez la description de chacune d'elles dans le manuel
de reference.

Le programme est lu par le micro-contrôleur de haut vers le bas.


Une variable doit être déclarée avant d'être utilisée par une
fonction.
La structure minimale est constituée :
– en tête : déclaration des variables, des constantes, indication
de l'utilisation de bibliothèques etc...
– un setup (= initialisation) cette partie n'est lue qu'une seule fois,
elle comprend les fonctions devant être réalisées au
démarrage (utilisation des broches en entrées ou en sortie,
mise en marche du midi, du port série de l' I2C etc.....)
– une loop (boucle) : cette partie est lue en boucle ! C'est ici
que les fonctions sont réalisées.

En plus de cette structure minimale, on peut ajouter :


– Des « sous-programmes » ou « routines » qui peuvent être
appelées à tous moments dans la boucle, très pratiquer pour
réaliser des morceaux de codes répétitifs.
– Des « callbacks », ce sont des fonctions qui sont rappelées
automatiquement depuis une bibliothèque.
32

II.2.1. Structure de base

Structure

Fonctions de base Structures de contrôle Syntaxe de base


Ces deux fonctions • if • ; (point-virgule)
sont obligatoires dans • if...else • {} (accolades)
tout programme en • for • / / (commentaire
langage Arduino • switch case sur une ligne)
: • while • /* * /
• void setup() • do... while (commentaire sur
• break plusieurs lignes)
• void loop() • continue • #define
• return • #include
• goto

Opérateurs de Opérateurs booléens


comparaison • && (ET booléen)
Opérateurs
== (égal à) • | | (OU booléen)
arithmétiques •
• != (différent de) • ! (NON booléen)
• = (égalité) • < (inférieur à)
• + (addition) • > (supérieur à)
• - (soustraction) • <= (inférieur ou
• * (multiplication) égal à)
• / (division) • >= (supérieur ou
• % (modulo) égal à)
33

Pointeurs Opérateurs bit à bit Opérateurs composés


• * pointeur • & (ET bit à bit) • ++
• & pointeur • |(OU bit à bit) (incrémentation)
• ^(OU EXCLUSIF • --
Voir également : (décrémentation)
bit à bit)
• Manipulation des
• ˜(NON bit à bit) (à revoir)
Ports
• << (décalage à • += (addition
gauche) composée)
• >> (décalage à • -= (soustraction
droite) composée)
• *= (multiplication
composée)
• /= (division
composée)
• &= (ET bit à bit
composé)
• |= (OU bit à bit
composé)

Variables et constantes
Les variables sont des expressions que vous pouvez utiliser dans les
programmes pour stocker des valeurs, telles que la tension de sortie d'un
capteur présente sur une broche analogique.
34

Constantes prédéfinies Types des données Conversion des


Les constantes prédéfinies Les variables peuvent types de données
du langage Arduino sont être de type variés qui • char()
des valeurs particulières sont décrits cidessous. • byte()
ayant une signification • int()
spécifique. Synthèse des types de
• long()
données Arduino
• float()
• HIGH | LOW • boolean
• word()
• INPUT | OUTPUT • char
• true | false • byte
• in t Portée des variables
A ajouter : constantes • unsigned in t et qualificateurs
décimales prédéfinies • long • Portée des
• unsigned long variables
Expressions numériques
• floa t (nombres à • static
• Expressions virgules) • volatile
numériques entières • double (nombres à • cons t
• Expressions virgules)
numériques à virgule • Les chaînes de
caractères Utilitaires
• objet String NEW • sizeof()
• Les tableaux de (opérateur
variables sizeof )
• le mot-clé void
(fonctions) Référence
• word • Code ASCI I
• PROGMEM
Voir également :

• Déclaration des
variables

Pour info : les types de


données avr-c
35

Fonctions

Entrées/Sorties Temps Trigonométrie


Numériques • unsigned long • sin (rad)
• pinMode (broche, millis() • cos (rad)
mode) • unsigned long • tan (rad)
• digitalWrite (broche, micros()
valeur) • delay (ms)
Bits et Octets
• int • delayMicroseconds
• lowByte ()
digitalRead(broche) (us)
• highByte ()
Entrées analogiques • bitRead ()
Math • bitWrite ()
• int
• min (x, y) • bitSe t()
analogRead(broche)
• max (x, y) • bitClear ()
• analogReference
• abs (x) • bi t()
(type)
• constrain (x, a, b)
Sorties "analogiques" • map (valeur,
Interruptions
toLow, fromHigh,
(génération d'impulsion) Externes
toLow, toHigh)
• analogWrite (broche, • pow (base, • attachInterrup
valeur) - PWM exposant) t(interrupti on,
• sq (x) fonction,
Entrées/Sorties mode)
• sqr t(x)
Avancées • detachInterrup
Pour davantage de t(interrupt ion)
• tone ()
fonctions
• noTone ()
mathématiques, voir
• shiftOu t(broche, Interruptions
aussi la librairie math.h :
BrocheHorloge, log, log10, asin, atan, • interrupts ()

acos, etc... • noInterrupts ()


OrdreBit, valeur)
• unsigned long Voir également la
pulseIn(broche, Nombres randomisés librairie interrupt.h.
valeur)
(hasard)
36

Communication • randomSeed
• [Link], (seed)
• long random(max)
• [Link],
• long random(min,
• [Link] max)
• [Link],
• [Link]

II.2.2. Bibliothèques (librairies)

Les utilisateurs les plus avertis concoctent des bibliothèques pour


interfacer, le plus simplement possible, une vaste diversité de
composants (I2C, SPI...) et de fonctionnalités (MIDI, Ethernet,
OSC...)

Sans ces bibliothèques, la programmation serait vraiment plus


complexe ! À utiliser sans modération.

Les bibliothèques doivent être installées dans le répertoire « libraries


» et doivent être inclues dans le programme (exemple : #include
<MIDI.h>).

II.3. COMMUNICATION PAR LA LIAISON SERIE

Du cote de l'ordinateur l'environnement de développement


Arduino propose de base un outil pour communiquer. Pour cela, il
suffit de cliquer sur le bouton :
37

Figure II.3 : Affichage moniteur série


Dans cette fenêtre, vous allez pouvoir envoyer et recevoir des
messages sur la liaison série de votre ordinateur (qui est emulee par
l'Arduino).
Du côté du programme, pour utiliser la liaison série et communiquer
avec l'ordinateur, on utilise un objet qui est intégré nativement dans
l'ensemble Arduino : l'objet Serial.

Cet objet rassemble des informations (vitesse, bits de données, etc.)


et des fonctions (envoi, lecture de réception, etc.) sur ce qu'est une
voie série pour Arduino.

Pour commencer, il faut initialiser l'objet Serial afin de définir la


vitesse de communication entre l'ordinateur et la carte Arduino
grâce à la fonction begin(). Cette vitesse doit être identique cote
ordinateur et cote programme.

Exemple : [Link](9600); //établissement d'une communication


série a 9600 bauds

III.3.1. Envoi de données

La fonction print() permet d'envoyer des caractères. Le programme


ci-dessous envoi l'ensemble des lettres de l'alphabet.

void setup()
{
38

[Link](9600); //etablissement d'une communication serie a


9600 bauds
}
void loop()
{
char i = 0;
char lettre = 'a'; // ou 'A' pour envoyer en majuscule
[Link]("------ L'alphabet ------"); //petit message d'accueil
//on commence les envois
for (i=0; i<26; i++)
{
[Link](lettre); //on envoie la lettre
lettre = lettre + 1; //on passe a la lettre suivante
delay(250); //on attend 250ms avant de reenvoyer
}
[Link](""); //on fait un retour a la ligne
delay(2000); //on attend 2 secondes avant de renvoyer
l'alphabet
}

II.3.2. Réception des données

Pour vérifier si on a reçu des données, on va régulièrement


interroger la carte pour lui demander si des données sont
disponibles dans son buffer de réception. Un buffer est une zone
mémoire permettant de stocker des données sur un cours instant.
Cette mémoire est dédiée à la réception sur la voie série. Il en existe
un aussi pour l'envoi de donnée, qui met à la queue leu leu les
données à envoyer et les envoie dès que possible. En résumé, un
buffer est une sorte de salle d'attente pour les données.

Pour vérifier si des données sont arrivées, on utilise la fonction


available() (de l'anglais"disponible") de l'objet Serial. Cette fonction
renvoie le nombre de caractères dans le buffer de réception de la
liaison série ou -1 quand il n'y a rien à lire sur le buffer de réception.

Une fois que l'on sait qu'il y a des données, il faut aller les lire. La
lecture se fera tout simplement avec la fonction... read() !
39

Cette fonction renverra le premier caractère arrive non traite. On


accède donc caractère par caractère aux données reçues. Si
jamais rien n'est à lire, la fonction renverra -1 pour le signaler.

Exemple. L'utilisateur saisit un caractère à partir de l'ordinateur et si


ce caractère est minuscule, il est renvoyé en majuscule ; s'il est
majuscule il est renvoyé en minuscule. Enfin, si le caractère n'est pas
une lettre on se contente de le renvoyer normalement, tel qu'il est.

int carlu; //stock le caractere lu sur la voie serie


void setup()
{
[Link](9600); //demarre une nouvelle liaison serie a
9600bauds
}

void loop()
{
//on commence par verifier si un caractère est disponible
dans le buffer
if ( [Link]() > 0 )
{
carlu = [Link](); //lecture du premier caractere
disponible
//Est-ce que c'est un caractere minuscule ?
if ( carlu >= 'a' && carlu <= 'z' )
carlu = carlu – 'a'; //on garde juste le "numero de lettre"
carlu = carlu + 'A'; //on passe en majuscule
} else
//Est-ce que c'est un caractere MAJUSCULE ?
if (carlu >= 'A' && carlu <= 'Z' )
{
carlu = carlu – 'A'; //on garde juste le "numero de lettre"
carlu = carlu + 'a'; //on passe en minuscule
}
//ni l'un ni l'autre on renvoie en tant que BYTE
//ou alors on renvoie le caractere modifie
[Link](carlu); // envoie le caractere en tant que variable
de type byte
}
40

Si vous voulez éviter de mettre le test de présence de données sur


la voie série dans votre code, Arduino a rajouté une fonction qui
s'exécute de manière régulière. Cette dernière se lance
régulièrement avant chaque redémarrage de la loop.

Ainsi, si vous n'avez pas besoin de traiter les données de la voie série
a un moment précis, il vous suffit de rajouter cette fonction. Pour
l'implémenter c'est très simple, il suffit de mettre du code dans une
fonction nommée serialEvent() qui sera à rajouter en dehors du
setup et du loop. Le reste du traitement de texte se fait
normalement, avec [Link] par exemple.

Voici un exemple de squelette possible :

const int Led = 11; //on met une LED sur la broche 11
void setup()
{
pinMode(Led, OUTPUT); //la LED est une sortie
digitalWrite(maLed, HIGH); //on eteint la LED
[Link](9600); //on demarre la voie serie
}
void loop()
{
delay(1000); //fait une petite pause de 1 s
//...on ne fait rien dans la loop
digitalWrite(maLed, HIGH); //on eteint la LED
}
void serialEvent() //declaration de la fonction d'interruption sur la
voie serie
{
while ( [Link]() != -1 );
//lit toutes les donnees (vide le buffer de reception)
digitalWrite(Led, LOW); //on allume la LED
}
41

II.4. GRANDEURS ANALOGIQUES

II.4.1. Les entrées analogiques

La carte Arduino dispose d'un CAN1 10 bits pour une tension


analogique de 0 a +5V. Ce convertisseur, integre dans son micro-
controleur, est un convertisseur "a approximations successives".

La fonction analogRead() permet de lire la valeur lue sur une entree


analogique de l'Arduino. Elle prend un argument et retourne la
valeur lue :

• L'argument est le numéro de l'entrée analogique a lire


• La valeur retournée (un int) sera le résultat de la conversion
analogique->numérique

Sur une carte Arduino Uno, on retrouve 6 CAN. Ils se trouvent tous
du même côté de la carte, là où est écrit "Analog IN" :

Figure II.4 : Entrées Analogiques


Ces 6 entrées analogiques sont numérotées, tout comme les
entrées/sorties logiques. Par exemple, pour aller lire la valeur en
sortie d'un capteur branche sur le convertisseur de la broche
analogique numéro 3, on fera : valeur = analogRead(3);.
Ne confondez pas les entrées analogiques et les entrées
numériques ! Elles ont en effet le même numéro pour certaines, mais
42

selon comment on les utilise, la carte Arduino saura si la broche est


analogique ou non.
La valeur retournée par la fonction est comprise entre 0 et 210 – 1 =
1023 pour une tension de 0 a +5V, soit

Voici une façon de la traduire en code :

int valeurLue; //variable stockant la valeur lue sur le CAN


float tension; //resultat stockant la conversion de valeurLue en Volts
void loop()
{
valeurLue = analogRead(uneBrocheAvecUnCapteur);
// 1ere methode : produit en croix, ATTENTION, donne un resultat en
mV ! tension = valeurLue * 4.88;

// 2eme methose : formule a aspect "physique", donne un resultat


en mV !
tension = valeurLue * (5 / 1024);
}

Il existe également une fonction dans l'environnement Arduino qui


à partir d'une valeur d'entrée, d'un intervalle d'entrée et d'un
intervalle de sortie, retourne la valeur équivalente comprise entre le
deuxième intervalle.

Cette fonction se nomme map(), dont voici le prototype : sortie =


map(valeur_d_entree, valeur_extreme_basse_d_entree,
valeur_extreme_haute_d_entree, valeur_extreme_basse_de_sortie,
valeur_extreme_haute_de_sortie );

D'après l'exemple précédent, on écrira donc :

// map attend des valeurs entières : on utilise des mV pour être plus
précis
43

tension = map(valeurLue, 0, 1023, 0, 5000); //conversion de la valeur


lue en tension en mV

tension = tension / 1000; //conversion des mV en V

Exemple : lecture de la valeur d'un potentiomètre linéaires


(résistance variable dont la tension a ses bornes évolue de manière
proportionnelle au déplacement du curseur).

Pour cela, il suffit de raccorder les alimentations sur les bornes


extrêmes du potentiomètre, puis de relier la broche du milieu sur
une entrée analogique de la carte Arduino :

Figure II.5 : Exemple de la lecture par broche analogique

Une fois le raccordement fait, un petit programme pour va


effectuer une mesure de la tension obtenue sur le potentiomètre,
puis envoyer la valeur lue sur la liaison série :

const int potar = 0; // le potentiomètre, branche sur la broche


analogique 0
44

int valeurLue; //variable pour stocker la valeur lue apres conversion


float tension; //on convertit cette valeur en une tension
void setup()

//on se contente de demarrer la liaison serie


[Link](9600);
}

void loop()

//on convertit en nombre binaire la tension lue en sortie du


potentiomètre
valeurLue = analogRead(potar);
//on traduit la valeur brute en tension (produit en croix)
tension = valeurLue * 5.0 / 1024;
//on affiche la valeur lue sur la liaison serie
[Link]("valeurLue = ");

[Link](valeurLue);
//on affiche la tension calculee
[Link]("Tension = ");
[Link](tension,2);
[Link](" V");
[Link](); //on saute une ligne entre deux affichages
delay(500); //on attend une demi-seconde pour que
l'affichage ne soit pas trop rapide
}

II.4.2. Sorties "analogiques"

La conversion A->N permet de transformer une grandeur


analogique non-utilisable directement par un système à base
45

numérique en une donnée utilisable pour une application


numérique. Quant à la conversion opposée, conversion N->A, les
applications sont différentes : par exemple commander une, ou
plusieurs, LED.

Pour cela, on va utiliser ce que peut fournir la carte Arduino : la


PWM2. La PWM est un signal numérique qui, à une fréquence
donnée, a un rapport cyclique qui varie avec le temps suivant "les
ordres qu'elle recoit" : La carte Arduino dispose de 6 broches qui
soient compatibles avec la génération d'une PWM. Elles sont
repérées par le symbole tilde ~ . Voici les broches générant une
PWM : 3, 5, 6, 9, 10 et 11.

La fonction analogWrite() est une fonction pour utiliser la PWM qui


prend deux arguments :

• Le premier est le numéro de la broche ou l'on veut


générer la PWM
• Le second argument représente la valeur du rapport
cyclique a appliquer. Cette valeur ne s'exprime pas en
pourcentage, mais avec un nombre entier compris entre
0 et 255.

Voilà un petit exemple de code illustrant ceci :

const int sortieAnalogique = 6; //une sortie analogique sur la broche


6
void setup()
{
pinMode(sortieAnalogique, OUTPUT);
}
void loop()
{
analogWrite(sortieAnalogique, 107);
//on met un rapport cyclique de 107/255 = 42 %
}
46

Exemple : mixage de couleurs d'une LED RGB ou RVB. Cette LED est
composée de trois LED de couleurs primaires : Le rouge Le vert Le
bleu. A partir de ces trois couleurs, il est possible de créer n'importe
quelle autre couleur du spectre lumineux visible en mélangeant ces
trois couleurs primaires entre elles.

Pour connaitre les valeurs RGB d'une couleur, il suffit d'utiliser un


logiciel de retouche d'image come GIMP ou Paint. Ci-contre un
exemple de code couleur RGB :

• R = 255
• V= 0
• B = 128

Figure II.6 : Code des couleurs

On utilisera ces valeurs pour faire cette teinte sur la LED RGB :

const int ledRouge = 11;


const int ledVerte = 1;
const int ledBleue = 10;

void setup()
{
//on declare les broches en sorties
47

pinMode(ledRouge, OUTPUT);
pinMode(ledVerte, OUTPUT);
pinMode(ledBleue, OUTPUT);
//on met la valeur de chaque couleur
//-- Les LED sont donc pilotees a l'etat bas.
//-- Ce n'est pas la duree de l'etat haut qui est importante mais
plutot celle de l'etat bas.
//-- Il va donc falloir mettre la valeur "inverse" de chaque
couleur sur chaque broche
analogWrite(ledRouge, 255 - 255);
analogWrite(ledVerte, 255 - 0);
analogWrite(ledBleue, 255 - 128);
}

void loop()
{
//on ne change pas la couleur donc rien a faire dans la
boucle principale
}

Figure II.7 : Commande d’une LEd multi couleurs


48

II.5. ECRANS LCD

Il existe un driver "LCD" pour ce type d'afficheur. Ce composant va


servir à décoder un ensemble "simple" de bits pour afficher un
caractère a une position précise ou exécuter des commandes
comme déplacer le curseur par exemple.

Ce composant est fabriqué principalement par Hitachi et se


nomme le HC44780. Il sert de décodeur de caractères. Ainsi, plutôt
que de devoir multiplier les signaux pour commander les pixels un a
un, il suffira d'envoyer des octets de commandes.

Normalement, pour tous les écrans LCD (non graphiques) ce


brochage est le même. Les broches utiles qu'il faudra relier à
l'Arduino sont les broches 4, 5 (facultatives), 6 et les données (7 à 14
pouvant être réduite à 8 à 14) en oubliant pas l'alimentation et la
broche de réglage du contraste.

Pour envoyer des données sur l'écran, il suffit de :

1. placer la broche RS a 1 ou 0 selon que l'on veut envoyer une


commande
2. place sur les 8 broches de données (D0 a D7) la valeur de la
donnée à afficher
3. faire une impulsion sur E d'au moins 450 ns pour indiquer à l'écran
que les données sont prêtes

Ce composant possède tout le système de traitement pour afficher


les caractères. Il contient dans sa mémoire le schéma d'allumage
des pixels pour afficher chacun d'entre eux.
49

La communication parallele prend beaucoup de broches, il existe


un mode "semi-parallèle". Ce dernier se contente de travailler avec
seulement les broches de données D4 a D7 (en plus de RS et E) et il
faudra mettre les quatre autres (D0 a D3) a la masse. Il libère donc
quatre broches. Dans ce mode, on fera donc deux fois le cycle
"envoi des données puis impulsion sur E" pour envoyer un octet
complet.

On utilisera une libraire nommée LiquidCrystal qui se chargera de


gérer les timings et l'ensemble du protocole.

Figure II.7 : Commande de l’Afficheur LCD

II.5.1. Afficher du texte

Pour l'intégrer la librairie "LiquidCrystal", il suffit de cliquer sur le menu


"Import Library" et d'aller chercher la bonne. Une ligne #include
"LiquidCrystal.h" doit apparaitre en haut de la page de code.
Ensuite, il reste a dire a la carte Arduino ou est branche l'écran (sur
quelles broches) et quelle est la taille de ce dernier (nombre de
lignes et de colonnes).
50

Il faudra commencer par déclarer un objet lcd, de type


LiquidCrystal et qui sera global a notre projet. Voici un exemple
complet de code correspondant aux deux branchements
précédents :

#include <LiquidCrystal.h> //on inclut la librairie


// initialise l'ecran avec les bonnes broches
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
int heures, minutes, secondes;
char message[16];
void setup()
{
[Link](16, 2); // regle la taille du LCD : 16 colonnes et 2
lignes
[Link](4,1); // place le curseur aux coordonnees (4,1)
[Link](); // et le fait clignoter
[Link]("Bonjour !");
delay(2000); // attente 2s
[Link](); // suppression du texte
//changer les valeurs pour demarrer a l'heure souhaitee !
heures = 0;
minutes = 0;
secondes = 0;
}

void loop()
{
//on commence par gerer le temps qui passe...
if ( secondes == 60 ) //une minutes est atteinte ?
{
secondes = 0; //on recompte a partir de 0 minutes++;
}
if (minutes == 60 )
//une heure est atteinte ?
{
minutes = 0;
heures++;
}
if (heures == 24 )
//une journee est atteinte ?
{
51

heures = 0;
}
//met le message dans la chaine a transmettre
sprintf(message,"Il est %2d:%2d:%2d", heures, minutes,
secondes);
[Link](); //met le curseur en position (0;0) sur l'ecran
[Link](message); //envoi le message sur l'ecran
delay(1000); //attend une seconde

//une seconde s'ecoule...secondes++;


}

NB : si jamais rien ne s'affiche, essayez de tourner votre


potentiomètre de contraste.

II.5.2. Créer un caractère

Sur l'écran les pixels sont en réalités divises en grille de 5x8 (5 en


largeur et 8 en hauteur). C'est parce que le contrôleur de l'écran
connait l'alphabet qu'il peut dessiner sur ces petites grilles les
caractères et les chiffres.

Cette grille sera symbolisée en mémoire par un tableau de huit


octets (type byte). Les 5 bits de poids faible de chaque octet
représenteront une ligne du nouveau caractère.

Exemple : création d'un smiley, avec ses deux yeux et sa bouche.


Ce dessin se traduira en mémoire par un tableau d'octets que l'on
pourra coder de la manière suivante :
52

Une fois que le caractère est créé, il faut l'envoyer à l'écran à l'aide
de la fonction createChar(), pour que ce dernier puisse le
connaitre, avant toute communication avec l'écran. Cette
fonction prend deux paramètres : "l'adresse" du caractère dans la
mémoire de l'écran (de 0 a 7) et le tableau de byte représentant le
caractère.

Ensuite, l'étape de départ de communication avec l'écran peut


être faite (le begin). Pour écrire ce nouveau caractère sur l'écran,
on utilise la fonction write() en passant en paramètre l'adresse du
caractère à afficher.

Remarque : cette fonction surcharge la fonction write() qui existe


dans une librairie standard et qui prend un pointeur sur un char. Il
faut donc faire un cast avec le type "uint8_t".

II.5.3. Défilement de texte

Dans un premier temps, on va faire appel aux fonctions


scrollDisplayRight() et scrollDisplayLeft() pour déplacer le texte d'un
carre vers la droite ou vers la gauche. S'il y a du texte sur chacune
désalignes avant de faire appel aux fonctions, c'est le texte de
chaque ligne qui sera déplacé par la fonction.

Exemple : défilement d'un smiley en appuyant sur deux petits


boutons poussoirs.

#include <LiquidCrystal.h> //on inclut la librairie

/les branchements
const int boutonGauche = 11; //le bouton de gauche
const int boutonDroite = 12; //le bouton de droite
// initialise l'ecran avec les bonnes broches
LiquidCrystal lcd(8,9,4,5,6,7);
// le smiley
byte smiley[8] = {
B00000,
B10001,
B00000,
B00000,
53

B10001,
B01110,
B00000,
};

void setup()
{
//on attache des fonctions aux deux interruptions externes (les
boutons)
attachInterrupt(0, aDroite, RISING);
attachInterrupt(1, aGauche, RISING);
//reglage des entrees/sorties
pinMode(boutonGauche, INPUT);
pinMode(boutonDroite, INPUT);
[Link](0, smiley); //apprend le caractere a l'ecran
LCD
[Link](16, 2); // regle la taille du LCD
[Link]((uint8_t) 0); //affiche le caractere de l'adresse 0
}

void loop()
{
//pas besoin de loop pour le moment
}
//fonction appelee par l'interruption du premier bouton

void aGauche()
{
[Link](); //on va a gauche !
}
//fonction appele par l'interruption du deuxieme bouton

void aDroite()
{
[Link](); //on va a droite !
}

Dans un deuxième temps, on va déplacer le texte


automatiquement à l'aide des fonctions leftToRight() pour aller de
la gauche vers la droite et rightToLeft() pour l'autre sens. Ensuite, il
54

suffit d'appeler la fonction autoScroll() pour le faire défiler et


noAutoScroll() pour l’arrêter.

Exemple : défilement des chiffres de 0 à 9.

#include <LiquidCrystal.h> //on inclut la librairie


// initialise l'ecran avec les bonnes broches
LiquidCrystal lcd(8,9,4,5,6,7);

void setup()
{
[Link](16, 2); // regle la taille du LCD
[Link](14, 0); // place le curseur aux coordonnees
(14,0)
[Link](); //indique que le texte doit etre deplacer vers
la gauche
[Link](); //rend automatique ce deplacement
int i=0;
for (i=0; i<10; i++)
{
[Link](i);
delay(1000);
}
}

void loop()
{}

II.6. COMMANDE PAR RESEAU INFORMATIQUE

II.6.1. Carte Arduino en Serveur

Serveur web est un logiciel permettant à des clients d'accéder à


des pages web, c'est-à-dire en réalité des fichiers au format HTML à
partir d'un navigateur installé sur leur ordinateur distant. Un serveur
web est donc un « simple » logiciel capable d'interpréter les
requêtes HTTP arrivant sur le port associé au protocole HTTP (par Le
module Ethernet Arduino permet à une carte Arduino de se
connecter à un réseau. Ce module est basé sur le circuit intégré
Wiznet W5100, il supporte jusqu'à quatre connexions simultanées. Il
55

suffit d'utiliser la librairie Ethernet pour écrire des programmes qui se


connectent à internet en utilisant ce module.

La dernière version de ce module ajoute un connecteur pour carte


SD, qui pourra être utilisé pour stocker des fichiers qui pourront être
envoyés sur le réseau.

La carte Arduino communique avec le W5100 (Ethernet) et la carte


SD (mémoire stockage) en utilisant le bus SPI.

Figure II.8 : Vue du Shield Ethernet

Configuration du shield Ethernet

 Connecter le shield Ethernet sur la maquette Arduino Uno ; à


faire avec délicatesse, attention aux broches.
 Relier la carte Arduino au PC avec le cordon USB Ou soit relier
le shield Ethernet au réseau de la salle par l'intermédiaire d'un
switch ou directement sur une prise réseau.
56

Figure II.9 : Montage et structure de la carte


II.6.1.1. Structure de base d’un programme (voir les commentaires)
#include <SPI.h> //bibliothèqe pour SPI
#include <Ethernet.h> //bibliothèque pour Ethernet
byte mac[] = {0x90, 0xA2, 0xDA, 0x0F, 0xDF, 0xAB}; //adresse mac
de votre carte
byte ip[] = {192, 168, 1, 123}; //adresse IP
EthernetServer serveur(80); // déclare l'objet serveur au port
d'écoute 80

void setup() {
[Link] (9600); //initialisation de communication série
[Link] (mac, ip); //initialisation de la communication
Ethernet
57

[Link]("\nLe serveur est sur l'adresse : ");


[Link]([Link]()); //on affiche l'adresse IP de la
connexion
[Link](); // démarre l'écoute
}

void loop() {
EthernetClient client = [Link](); //on écoute le port
if (client) { //si client connecté
[Link]("Client en ligne\n"); //on le dit...
if ([Link]()) { // si le client est en connecté
while ([Link]()) { // tant qu'il a des infos à transmettre
char c = [Link](); // on lit le caractère
[Link](c);// on l'écrit sur le moniteur série
delay(1); //délai de lecture
}
//réponse au client
entete(client);
[Link]("<a href=?valeur=100>Envoi</a><br>");
[Link]("<a href=?>Refresh</a><br>");
[Link]("<br><hr></body></html>"); //ligne horizontale et
fermeture des balises
[Link](); //on déconnecte le client
[Link]("Fin de communication avec le client");
}
}
}
//fonction d'affichage de l'entête HTML
void entete(EthernetClient cl) {
[Link]("<!DOCTYPE HTML>");
[Link]("<html>");
[Link]("<head><title>Esssai</title></head>");
[Link]("<body><h1>Essai</h1><hr><br>");
}

Voici le programme où le système réagi en fonction des valeurs


envoyées par la méthode GET :
58

#include <SPI.h> //bibliothèqe pour SPI


#include <Ethernet.h> //bibliothèque pour Ethernet
byte mac[] = {0x90, 0xA2, 0xDA, 0x0F, 0xDF, 0xAB}; //adresse mac
de votre carte
byte ip[] = {192, 168, 1, 123}; //adresse IP
EthernetServer serveur(80); // déclare l'objet serveur au port
d'écoute 80

void setup() {
[Link] (9600); //initialisation de communication série
[Link] (mac, ip); //initialisation de la communication
Ethernet
[Link]("\nLe serveur est sur l'adresse : ");
[Link]([Link]()); //on affiche l'adresse IP de la
connexion
[Link](); // démarre l'écoute
}

void loop() {
EthernetClient client = [Link](); //on écoute le port
if (client) { //si client connecté
[Link]("Client en ligne"); //on le dit...
if ([Link]()) { // si le client est en connecté
String affichage = GET(client); //appel de la fonction de
décodage
//réponse au client
entete(client);
[Link](affichage);
[Link]("<a href=?valeur=100&autreValeur=10&anniv=oui
target=_self>Envoi</a><br>");
[Link]("<a href=? target=_self>Refresh</a><br>");
[Link]("<br><hr></body></html>"); //ligne horizontale et
fermeture des balises
[Link](); //on déconnecte le client
[Link]("Fin de communication avec le client\n");
}
}
59

}
//fonction d'affichage de l'entête HTML
void entete(EthernetClient cl) {
[Link]("<!DOCTYPE HTML>");
[Link]("<html>");
[Link]("<head><title>Esssai</title></head>");
[Link]("<body><h1>Essai</h1><hr><br>");
}
//fonctin décodage GET
String GET(EthernetClient cl) {
int lecture = 0; // variable pour les étapes de décodage
String resultat = "<br>"; //initialisation de la chaine de réponse
String donnee = ""; //chaine pour stocker la lecture des données
while ([Link]()) { // tant qu'il a des infos à transmettre
char c = [Link](); // on lit le caractère
if (lecture == 0 && c == '?') { //début de lecture des données donc
d'un nom
lecture = 1;
donnee = "";
}
else if (lecture == 1 && c == '=') { //début de lecture d'une valeur
lecture = 2;
resultat += donnee + " : "; //on construit la chaîne de réponse
donnee = "";
}
else if (lecture == 2 && c == '&') { //nouveau nom
lecture = 1;
resultat += donnee + "<br>"; //on construit la chaîne de réponse
donnee = "";
}
else if ((lecture == 2 || lecture == 1) && c == ' ') { //fin de lecture
lecture = 3;
resultat += donnee + "<br><br>"; // on finit la chaîne réponse.
}
else if (lecture == 1 || lecture == 2) {//récupération des données
de nom ou de valeur
donnee += c;
60

}
delay(1); //delai de lecture
}
return resultat; // retour le la chaîne de réponse
}

II.6.1.2. Allumer des Leds à distance

Apreès acces au navigateur à la carte arduino jouant le role du


serveur, la page suivante doit apparaitre :

Le programme de la carte est le suivant :


#include <SPI.h> //bibliothèqe pour SPI
#include <Ethernet.h> //bibliothèque pour Ethernet
byte mac[] = {0x90, 0xA2, 0xDA, 0x0F, 0xDF, 0xAB}; //adresse mac
de votre carte
byte ip[] = {192, 168, 1, 123}; //adresse IP
unsigned long tpsDep; //temps départ pour la gestion des LED
int pinLed[5] = {2, 3, 5, 6, 7}; //tableau des pins de LED
boolean etatLed[5] = {0, 0, 0, 0, 0}; //tableau des états des LED
int modeLed[5] = {0, 0, 0, 0, 0}; // tableau des modes des LED
EthernetServer serveur(80); // déclare l'objet serveur au port
d'écoute 80

void setup() {
for (int l = 0; l < 5; l++) {
pinMode(pinLed[l], OUTPUT);
61

}
[Link] (9600); //initialisation de communication série
[Link] (mac, ip); //initialisation de la communication
Ethernet
[Link]("*\n-> Le serveur est sur l'adresse : ");
[Link]([Link]()); //on affiche l'adresse IP de la
connexion
[Link](); // démarre l'écoute
}

void loop() {
gestionClient(); // fonction qui gère toute la communication avec
le client
gestionLed(); // fonction qui gère l'allumage des LED
}

//----------------------Fonctions----------------------
//fonction qui gère la communication avec le client
void gestionClient() {
EthernetClient client = [Link](); //on écoute le port
if (client) { //si client existe
[Link]("Client en ligne"); //on le dit...
if ([Link]()) { // si le client est connecté
GET(client); //appel de la fonction de décodage
//réponse au client
entete(client); // fonction pour l'entête de la page HTML
corps(client); // fonction pour le corps
piedPage(client); // fonction pour le pied de page
[Link]("Fin de communication avec le client\n");
[Link](); //on déconnecte le client
}
}
}

//fonction de fabrication de l'entête HTML


void entete(EthernetClient cl) {
//infos pour le navigateur
62

[Link]("HTTP/1.1 200 OK"); // type du HTML


[Link]("Content-Type: text/html; charset=ascii"); //type de
fichier et encodage des caractères
[Link]("Connection: close"); // fermeture de la connexion
quand toute la réponse sera envoyée
[Link]();
//balises d'entête
[Link]("<!DOCTYPE HTML>");
[Link]("<html>");
[Link]("<head><title>Web-Commande de
LED</title></head>");
[Link]("<body><h1>Web-Commande de LED</h1><hr><br>");
}

//fonction de fabrication du corps de page


void corps(EthernetClient cl) {
[Link]("<br>"); // saut de ligne
//boucle pour construire chaque ligne en fonction des LED
for (int l = 0; l < 5; l++) {
[Link]("<br>LED ");
[Link](l);
[Link](" ");
[Link](l);
switch (modeLed[l]) {
case 0:
[Link]("OFF ");
break;
case 1:
[Link]("ON ");
break;
case 2:
[Link]("CLI ");
break;
}
[Link](" <a href=?"); //création du lien inutile de répéter l'adresse
du site
[Link](l);
63

[Link](" target=_self >Change</a><br>");


}
}

//fonction de fabrication du pied de page


void piedPage(EthernetClient cl) {
//balises de pied de page
[Link]("<br><hr>");
[Link]("</body>");
[Link]("</html>");
}

//fonctin décodage GET


void GET(EthernetClient cl) {
boolean lu = 0; //variable pour indiquer l'état de lecture
while ([Link]()) { // tant qu'il a des infos à transmettre
char c = [Link](); // on lit le caractère
delay(1); //delai de lecture
if (c == '?' && lu == 0) { //si "?" repéré
c = [Link](); //on lit le caractère suivant qui contient la donnée
int led = int(c) - 48; //on la transforme en nombre
modeLed[led] = (modeLed[led] + 1) % 3; //on change l'état de
la LED
delay(10);
lu = 1; // on dit qu'on a lu l'info
}

}
}

//fonction d'allumage des LED par rapport au tableau de mode.


void gestionLed() {
unsigned long tpsAct = millis(); // récupération du temps actuel
if (tpsAct - tpsDep > 250) { //si délai dépassé
for (int l = 0; l < 5; l++) { // chaque LED
if (modeLed[l] == 0) { // si mode éteint
etatLed[l] = 0; // on éteind
64

}
else if (modeLed[l] == 1) {// si mode allumé
etatLed[l] = 1; //on allume
}
else if (modeLed[l] == 2) { //si mode clignotant
etatLed[l] = !etatLed[l]; //on inverse l'état
}
digitalWrite(pinLed[l], etatLed[l]); //on met le pin à jour
tpsDep = tpsAct; //on réinitialise le temps
}
}
}

II.6.2. Carte Arduino en Client

Le rôle du client est finalement assez simple. Son but sera d’aller
chercher des informations pour les afficher à un utilisateur ou
effectuer une action particulière. D’une manière générale, le client
sera celui qui traite l’information que le serveur envoie.

II.6.2.1. Préparation minimale du programme

Pour commencer, il va falloir configurer notre module Ethernet pour


qu’il puisse travailler correctement. Pour cela, il va falloir lui donner
une adresse MAC et une adresse IP (qui peut être automatique,
nous y reviendrons). On va utiliser 4 variables différentes :
 Un tableau de byte (ou char) pour les 6 octets de l’adresse
MAC ;
 Un objet IPAddress avec l’adresse IP que l’on assigne au
module ;
 Un objet EthernetClient qui nous servira à faire la
communication ;
 Une chaîne de caractères représentant le nom du serveur à
interroger.

// Ces deux bibliothèques sont indispensables pour le shield


#include <SPI.h>
#include <Ethernet.h>
65

// L'adresse MAC du shield


byte mac[] = { 0x90, 0xA2, 0xDA, 0x0E, 0xA5, 0x7E };
// L'adresse IP que prendra le shield
IPAddress ip(192,168,0,143);
// L'objet qui nous servira à la communication
EthernetClient client;
// Le serveur à interroger
char serveur[] = "[Link]"

Si le serveur DHCP est utilisé dans le réseau, la carte aura son


adresse IP automatique à l’aide des codes suivants :

// Ces deux bibliothèques sont indispensables pour le shield


#include <SPI.h>
#include <Ethernet.h>

// L'adresse MAC du shield


byte mac[] = { 0x90, 0xA2, 0xDA, 0x0E, 0xA5, 0x7E };
// L'adresse IP que prendra le shield
IPAddress ip(192,168,0,143);
// L'objet qui nous servira a la communication
EthernetClient client;
// Le serveur à interroger
char serveur[] = "[Link]";

void setup() {
// On démarre la voie série pour déboguer
[Link](9600);

char erreur = 0;
// On démarre le shield Ethernet SANS adresse IP (donc donnée
via DHCP)
erreur = [Link](mac);

if (erreur == 0) {
[Link]("Parametrage avec ip fixe...");
// si une erreur a eu lieu cela signifie que l'attribution DHCP
// ne fonctionne pas. On initialise donc en forçant une IP
[Link](mac, ip);
66

}
[Link]("Init...");
// Donne une seconde au shield pour s'initialiser
delay(1000);
[Link]("Pret !");
}

II.6.2.2. Envoie d’une requête simple

void setup() {
// ... Initialisation précédente ...

// On connecte notre Arduino sur "[Link]" et le port 80 (defaut


pour l'http)
erreur = [Link](serveur, 80);

if(erreur == 1) {
// Pas d'erreur ? on continue !
[Link]("Connexion OK, envoi en cours...");

// On construit l'en-tête de la requête


[Link]("GET / HTTP/1.1");
[Link]("Host: [Link]");
[Link]("Connection: close");
[Link]();
} else {
// La connexion a échoué :(
[Link]("Echec de la connexion");
switch(erreur) {
case(-1):
[Link]("Time out");
break;
case(-2):
[Link]("Serveur invalide");
break;
case(-3):
[Link]("Tronque");
break;
case(-4):
[Link]("Reponse invalide");
67

break;
}
while(1); // Problème = on bloque
}
}
Remarque : Et si l’on voulait passer des informations
complémentaires à la page ? Eh bien c’est simple, il suffira juste de
modifier la requête GET en lui rajoutant les informations ! Par
exemple, admettons que sur [Link] il y ait une page de
recherche "[Link]" qui prenne un paramètre "m" comme
mot-clé de recherche. On aurait alors :
[Link]("GET /[Link]?m=monmot HTTP/1.1");
Ce serait équivalent alors à aller demander la page
"[Link]/[Link]?m=monmot", soit la page
"[Link]" avec comme argument de recherche "m" le mot
"monmot".

II.6.2.3. Lire une réponse

Lorsque la requête est envoyée (après le saut de ligne), le serveur


va nous répondre en nous renvoyant la ressource demandée. En
l’occurrence se sera la page d’accueil de [Link]. Eh bien vous
savez quoi ? On fera exactement comme avec une voie série. On
commencera par regarder si des données sont arrivées, et si c’est
le cas, on les lira une à une pour en faire ensuite ce que l’on veut
(recomposer des lignes, chercher des choses dedans…)

Dans l’immédiat, contentons-nous de tout afficher sur la voie série !

Première étape, vérifier que nous avons bien reçu quelque chose.
Pour cela, comme avec Serial, on va utiliser la méthode available()
qui nous retourne le nombre de caractères disponibles à la lecture.
Si des choses sont disponibles, on les lit une par une (Avec read() )
et les envoie en même temps à la voie série. Enfin, quand tout a été
lu, on va vérifier l’état de notre connexion et fermer notre client si la
connexion est terminée.
68

En résumé, voici le code complet de la lecture de la page


"[Link]"

// Ces deux bibliothèques sont indispensables pour le shield


#include <SPI.h>
#include <Ethernet.h>

// L'adresse MAC du shield


byte mac[] = { 0x90, 0xA2, 0xDA, 0x0E, 0xA5, 0x7E };
// L'adresse IP que prendra le shield
IPAddress ip(192,168,0,143);
// L'objet qui nous servira à la communication
EthernetClient client;
// Le serveur à interroger
char serveur[] = "[Link]";

void setup() {
// On démarre la voie série pour déboguer
[Link](9600);

char erreur = 0;
// On démarre le shield Ethernet SANS adresse IP (donc donnée
via DHCP)
erreur = [Link](mac);

if (erreur == 0) {
[Link]("Parametrage avec ip fixe...");
// si une erreur a eu lieu cela signifie que l'attribution DHCP
// ne fonctionne pas. On initialise donc en forçant une IP
[Link](mac, ip);
}
[Link]("Init...");
// Donne une seconde au shield pour s'initialiser
delay(1000);
[Link]("Pret !");

// On connecte notre Arduino sur "[Link]" et le port 80 (defaut


pour l'http)
69

erreur = [Link](serveur, 80);

if(erreur == 1) {
// Pas d'erreur ? on continue !
[Link]("Connexion OK, envoi en cours...");

// On construit l'en-tête de la requête


[Link]("GET / HTTP/1.1");
[Link]("Host: [Link]");
[Link]("Connection: close");
[Link]();
} else {
// La connexion a échoué :(
[Link]("Echec de la connexion");
switch(erreur) {
case(-1):
[Link]("Time out");
break;
case(-2):
[Link]("Serveur invalide");
break;
case(-3):
[Link]("Tronque");
break;
case(-4):
[Link]("Reponse invalide");
break;
}
while(1); // On bloque la suite
}
}

void loop()
{
char carlu = 0;
// on lit les caractères s'il y en a de disponibles
if([Link]()) {
70

carlu = [Link]();
[Link](carlu);
}

// Si on est bien déconnecté.


if (![Link]()) {
[Link]();
[Link]("Deconnexion !");
// On ferme le client
[Link]();
while(1); // On ne fait plus rien
}
}
71

CHAPITRE III : PROGRAMMATION DES SYSTEMES TEMPS REELS

III.1. MODÉLISATION DES SYSTÈMES TEMPS RÉELS

III.1.1. Contraintes temporelles

Dans un STR, les contraintes temporelles portent essentiellement sur les


dates de début et de fin d’exécution des tâches. Si nous supposons que
le Système est décomposé en termes de tâches Ti, i=1,,. n alors
pour chaque tâche Ti, les contraintes temporelles sont modélisées par
les paramètres suivants :

-Ai : C’est la date d’arrivée de la tâche au processeur (ou date de la


création de la tâche ou éventuellement la date à laquelle une tâche
transférée par un autre processeur est reçue). Il est désormais possible
d’ordonnancer cette tâche. Ainsi, chaque fois qu’une nouvelle tâche
arrive au processeur ou chaque fois qu’une tâche est
créée, l‟ordonnanceur (mécanismed‟ordonnancement), recherche un
nouvel ordre qui prendra en compte la nouvelle tâche.

-Ri : C’est la date à laquelle une tâche peut commencer son exécution.
Certaines tâches sont dépendantes les unes des autres, et ne peuvent
donc commencer leurs exécutions que si les tâches dont elles
dépendent (les tâches qui les précèdent) ont terminé leurs exécutions.
Dans ce cas, elles sont liées par des relations de précédence
(communication de résultat d’une tâche enfin d’exécution) ou de
causalité (estampillages des tâches). L’estampillage des tâches
consiste à attribuer un numéro d’ordre aux tâches de sorte que durant
l’exécution, cette numérotation soit respectée à travers le réseau. In
convient de souligner que souvent Ai et Ri sont confondues.

-Di : C’est la date au plus tard à laquelle une tâche peut terminer son
exécution. Elle est aussi appelées échéance. Ce paramètre détermine
énormément la performance du système. Ainsi, on distingue les
échéances strictes et les échéances relatives. Les échéances strictes ne
peuvent jamais être dépassées, sinon elles dégradent les performance
s du système. Les échéancesrelatives peuvent être dépassées sans dé
grader les performances du système.

71
72

- Ei : C’est la durée d’exécution de la tâche. Elle est déterminée par des


simulations ou une étude poussée du code source. Cependant, lorsque le
code source est composé de boucles ou de traitements conditionnels, il est
difficile de déterminer d’emblée Ei. Dans ce cas, on détermine un temps
d’exécution dit « au pire des cas » qui reflète une borne supérieure Ei.
Notons que l’on parle de pire des cas lorsqu’on suppose que toutes les
formes possibles du code source de la tâche ont lieu c'est-à-dire le
nombre d’itérations maximum atteint pour toutes les boucles.

Dans les STR dynamiques, Ei ne peut être connue à l’avance puisque la


tâche Ti est créée dynamiquement. Il est mis à jour au fur et à mesure de
l’exécution de la tâche. On peut par exemple procéder à la déduction
du temps restant (E rest i) à partir du temps atteint (E att i), c’est-à-
dire E i= E att i+ E rest i. Parmi les méthodes utilisées pour approximer le
temps d’exécution d’une tâche, nous pouvons citer : Prischner et Koza,
Kligerman et Stoyenko, Leinbaugh et yamini, etc.

-Pi : C’est la période d’une tâche. Une tâche est dite périodique de
période Pi, lorsqu’elle a une occurrence toutes les Pi unités de temps.

D’autres paramètres sont déterminés par le mécanisme


d’ordonnancement grâce aux Paramètres Ri, Ei et Di précédents. Il
s’agit :

-Si : C’est la date à laquelle une tâche Ti accède à la ressource selon


l’algorithme d’ordonnancement déroulé.

-Ci : C’est la date à laquelle une tâche Ti


termine son exécution. Cette date ne correspond pas forcément à Si +
Ei. En effet, selon l’algorithme d’ordonnancement appliqué, une
tâche peut être interrompue ou retardée pour la prise en compte
d’une tâche plus urgente.

-TR i : C’est le temps de réponse de la tâche Ti c’est-à-dire la période


entre la date la plus antérieure à laquelle une tâche peut commencer
son exécution et la date postérieure à laquelle elle termine son
exécution. Il s’exprime par la formule : TR i = Ci - Ri.

72
73

-TLi : C’est le temps de latence d’une tâche Ti c'est-à-dire la période


pendant laquelle une tâche peut être retardée sans que son exécution
ne dépasse son échéance. Elle s’exprime par la

Formule : TLi = Di - R i - Ei.

- Li : C’est la laxité c'est-à-dire la date au plus tard à laquelle la tâche Ti


peut commencer son exécution. Elle s’exprime par la formule : Li = Di -
Ei. Par déduction, on obtient la formule : TLi = Li - R i. Le temps de latence
n’est pas constant. En effet, plus une tâche Ti est retardée plus son
temps de latence diminue.

- Ui : C’est le taux d’utilisation du processeur dédié à la tâche Ti. Il


s’exprime par la formule :Ui=Ei/Pi. Le taux global d’utilisation sera la
somme de tous les Ui.

Pour les tâches périodiques, le taux d’utilisation du processeur ou le


pourcentage de l’activité du processeur dédié à la tâche Ti est donnée
par :Ui=CI/Pi, et la Charge processeur = taux d’utilisation du processeur
pour les tâches périodiques est donnée par
𝑛

∑ 𝐶𝑖/𝑃𝑖
𝑖=1

Lorsque U>m alors l’application est non ordonnançable sur m


processeurs.

N.B. : L’ensemble de ces paramètres doit vérifier les contraintes


suivantes :

Ai < Ri < Si < Ci - Ei < Di - Ei

Schématiquement, nous pouvons illustrés les contraintes temporelles


d’une tâche temps réel de la manière suivante :

73
74

Figure III.1 : Paramètres temporels

Remarque :

Dans certains STR, l’échéance est modélisée par une fonction à valeur
dans le temps (FVT) appelée « Time Value Fonction » (TVF). Chaque
tâche Ti fournit au moment de sa terminaison, une contribution qui est
décrite par une fonction de coût Fi(t). La valeur de cette fonction
renseigne sur l’utilité de la terminaison de la tâche à l’instant t. Fi(t) a la
valeur maximale si Ti termine avant Di, autrement la valeur de la
fonction décroît vers 0, où elle signifie qu’une terminaison à cette date-
là est inacceptable. Quand la valeur de la fonction décroit
brusquement après son échéance, ceci indique une échéance stricte
à ne pas dépasser dans n’importe quelle situation. Par contre, si la valeur
de la fonction décroit progressivement, cela indique une échéance
relative qui peut éventuellement être dépassée de peu.

L’ordonnancement est ramené, dans ce cas, à un problème


d’optimisation des tâches. L’ordonnancement est déterminé par une
série d’évaluations des valeurs des FVT selon la position des tâches dans
la séquence d’ordonnancement.

74
75

III.1.2. Caractéristiques des tâches

Une tâche est une entité élémentaire d’exécution et de structuration de


l’application. Une tâche est localisée dans le temps par une date de
début et/ou une date de fin, dont la réalisation nécessite une durée et
une échéance (la date à laquelle la tâche doit avoir terminé son
exécution).

L’ordonnancement des tâches consiste à déterminer une séquence


d’exécution des tâches sur le
processeur en respectant les contraintes de temps. Cependant certain
es caractéristiques destâches que nous évoquons ci-après peuvent
influencées cet ordonnancement. Il s’agit de la priorité utilisateur et la
périodicité des tâches.

a) Notion de priorité utilisateur

Lorsque toutes les tâches n’ont pas la même importance vis-à-vis de


système, on parle alors de priorité attribuée par l’Ordonnanceur
pour distinguer les tâches entre elles en fonction de leurs échéances.
On distingue ainsi des tâches plus prioritaires par rapport aux tâches
moins prioritaires. C’est l’utilisateur qui définir les priorités.

Selon le système Spring, il existe trois type de tâches selon leurs priorités :

Les tâches temps réel critiques : ce sont des tâches dont les échéances
ou les contraintes temporelles (qui sont connues à l‟avance) doivent
être rigoureusement respectées pour éviter la catastrophe. Elles ont la
priorité la plus haute dans le système.

Les tâches temps réel essentiel : Ce sont des tâches dont les contraintes
sont non
sévères et peuvent éventuellement être relâchées dans la mesure du
possible. Dans certaines applications, leur échéance est composée
d’une échéance stricte et d’une échéance relative. La première partie
de l’exécution d’une telle tâche doit être rigoureuse ementre spectée,
la seconde (qui peut consister en un affinement du résultat délivré par
la
première partie) peut ne pas être terminée. Dans ce cas, le résultat ne

75
76

sera pas très précis. Les tâchesessentielles peuvent être ordonnancées


dynamiquement.

Les tâches non essentielles : Ce sont des tâches qui peuvent ne pas avoir des
contraintes quant à la fin de leur exécution ou la date de leur
déclenchement, elles ont donc la priorité minimale. Elles
seront exécutées une fois que toutes les tâches à contraintes
seront servie et selon leur ordre d’arrivée ou selon un ordre décidé par
l’utilisateur.

b) Notion de périodicité

Lorsque les tâches temps réel n’ont pas de priorité utilisateur, leurs
priorités seront définies par leurs échéances. Toutefois, la notion de
périodicité que nous introduisons dans cette section, révèle une autre
distinction entre les tâches. Outre les priorités des tâches, le mécanisme
doit prendre en compte le fait que ces tâches peuvent être périodiques
apériodiques ou sporadiques.

- Les tâches périodiques : une tâche Ti est dite périodique de période Pi si


elle est exécutée chaque Pi unités de temps. Une telle tâche a ses
paramètres Ri et Di connus. Si Tin, la nème exécution de la tâche Ti, alors
la date au plus tôt Rin est donnée par le taux d’inter-arrivée et la date
au plus tard Din est déterminée par l’occurrence de la
(n+1)ème exécution de T (comme illustré dans la figure suivante).Quand
un système doit ordonnancer une tâche périodique, il doit garantir
toutes les futures occurrences de la tâche.

Figure III.2 : Tâches périodiques

76
77

NB : Sje : représente la date à laquelle une tâche accède à la ressource


selon l’ordonnancement.

Une occurrence de tâche peut avoir son échéance inférieure ou égale


à sa période. Cela dépendu système.

- Les tâches apériodiques : ce sont des tâches dont l’arrivée au


processeur suit une loi aléatoire (la loi de Poisson par exemple).
Leurs paramètres temporels sont donc inconnus à l'avance.
- Les tâches sporadiques : Les tâches apériodiques sont caractérisées par la
moyenne des temps d’arrivée et un standard de déviation (écart
type) par rapport au taux d’arrivée. Cependant, si les occurrences
d’une tâche apériodique sont au moins espacées de q unités de
temps, on caractérise cette tâche de tâche sporadique de
période q.
- Une tâche non préemptive ne peut être interrompues qu’à des
endroits spécifiques et à la demande de la tâche elle-même, par
exemple fin_de_tâche, attente signal…
- Une tâche préemptive peut être interrompu n’importe quoi
instantané et le processus ton affecté à une autre tâche.
- Les tâches indépendantes : ce sont des tâches qui ne sont définies
que par leurs paramètres temporels, sur dit a lorsqu’elles. Freffet,
certain est les tâches doivent communiquer avec les autres
tâches, vous devez être connectés vers l’extérieur pour les
entrées/sorties. Par par conséquent, elles peuvent être lié par un
des types de relations suivantes :
o Synchronisation : se traduit par une relation de précédence
d’exécution entre les tâches,
o Communication : se traduit par une relation de
précédence comme la synchronisation mais avec
l'échange de données entre les tâches,
o Partage de ressources : les ressources sont les moyens
techniques nécessaires à l’exécution des tâches. Au niveau
d'un système, les tâches utilisent des ressources mises en
commun comme des zones de mémoire, des cartes
d’entrées/sorties, etc. Certaines des ressources n’autorise
que des accès en exclusion mutuelle, c’est-à-dire pas plus
d’une tâche peut y accéder à la fois, pourvoir un

77
78

fonctionnement correct. Elles sont appelées ressources


critiques.

Remarques

- Les algorithmes d'ordonnancement sont extrêmement


dépendants du modèle de tâches. Ainsi ils sont plus conçus pour
des tâches périodiques ou apériodiques et les tâches sporadiques
sont convertis en tâches périodiques.
- Les applications temps réel sont en majorité composées de tâches
périodiques.
- Les sources de contraintes de temps sont souvent : le processus physique
(lois de virgulende), la qualité de service (qualité audio/vidéo), le
choix d’architecture, le choix de conception, etc.

III.1.3. Exécutif ou noyau temps réel

Un noyau de système d'exploitation (kernel) est la partie fondamentale


de certains systèmes d’exploitation. Le rôle du noyau est de gérer les
ressources matérielles et permet aux différents composants, matériels et
logiciels, de communiquer entre eux. Un noyau fournit des mécanismes
d'abstraction du matériel (« Gestion du matériel »), notamment de la
mémoire (« Gestionnaire de mémoire »), du (ou des) processeur(s) («
Ordonnanceur »), et des échanges d’informations entre logiciels et
périphériques matériels. Il autorise aussi diverses abstractions logicielles
et facilité la communication entre les processus.

- Ordonnanceur d’un système d’exploitation : n’a de sens qu’en


système multitâche. Il gère l’ordre dans lequel les instructions des
différentes tâches sont exécutées, est assisté par le répartiteur qui
est responsable de la sauvegarde et de la restauration du
contexte des tâches (commutation de contexte).
- Gestionnaire de mémoire : alloue de la mémoire à des processus
lorsqu’ils en ont besoin. Si un processus tentant d'utiliser des zones
de mémoire ne lui appartenant pas, il peut être évincé
automatiquement, c'est-à-dire que le noyau lui-même peut
prendre la décision de suspendre ou détruire immédiatement le
processus fautif.

78
79

- Gestion du matériel : se fait par l’intermédiaire de pilotes de


périphériques, qui sont généralement inclus dans l’espace noyau
et communiqué avec l’espace utile fabricant via les appels
système (des fonctions fournies par le noyau, et utilisées par les
programmes s’exécutant dans l’espace utilisateur).

Exécutif ou noyau temps réel sont souvent assimilés, dans le sens où


l’exécutif est souvent vu comme un noyau possédant des pilotes de
périphériques ou des services supplémentaires (pile réseau,…) par
rapport à un noyau. Un noyau est généralement léger, et on parle
d'empreinte mémoire d’un noyau (occupation mémoire) pour
caractériser son encombrement. Les noyaux

Temps réel fourni au moins deux types de modules, l’un spécifique pour
le temps réel (gestion des tâches, communication, synchronisation,
gestion du temps) et l'autre plus classique (norme bibliothèques). Les
applications temps réel font alors appel à la partie temps réel du noyau.

Contrairement au système d’exploitation classique (politiques


d’ordonnancement des activités je sur le partage équitable du
processeur ; gestion de temporisateurs ou de l'horloge pas assez bien ;
la concurrence de l’application temps réel avec le système
d’exploitation toujours actif…), les principales fonctions d'un noyau
temps réel peuvent être scindées en trois groupes ci-dessous.

Donc, les tâches de même que les processus concourent à l’obtention


du ou des processeurs, et peuvent communiquer ou se synchroniser. La
différence est qu’une tâche est caractérisée par une pile et un pointeur
d'instruction, mais partager la mémoire avec les autres tâches du même
processus ; tandis que la mémoire pour les processus est privée (il en
résulte que le passage par l’exécutif pourra communication entre
processus est plus lourd mais plus sécurisé que pour les tâches) :

- Gestion des entrées/sorties.


- Ordonnancement des tâches.
- Rapports entre les tâches synchronisation, communication, accès
à une ressource critique en exclusion mutuelle, gestion de
temps…).

79
80

Il est important de noter que les tâches sont les unités actives du
système ; le noyau temps réel n’est actif que lors de leur appel : donc,
une tâche activée peut appeler le noyau temps réel sous forme d'une
requête, les différentes requêtes sont servies par des modules du noyau
temps réel appelées primitives (partie temps réel du noyau). Ensuite le
noyau temps réel réactiver une tâche de l’application selon
l’algorithme d’ordonnancement utilisé (partie générique du noyau).

Une autre architecture souvent retenue est un noyau hybride qui


s’appuie sur la combinaison d’un noyau temps réel spécialisé, allouant
du temps d’exécution à un noyau de système d’exploitation non
spécialisée. Cette technique permet d’assurer le fonctionnement temps
réel des applications, tout en maintenant la compatibilité avec des
environnements préexistants. Nous trouvons une telle architecture dans
RTLinux.

III.1.4. Gestion des tâches

Les noyaux temps réel gèrent les tâches suivant le même principe que
les systèmes d’exploitation non spécialisée. Alors, au cours de la vie
d’une application, les tâches peuvent atteindre les états suivants :

- Tâche « inexistante » : la tâche n’est pas créée,


- Tâche « passive » : la tâche est créée, et initialement elle doit être
endormie (passive) pour l'attente de son réveil. De plus, une tâche
peut devenir « passive » à partir de l'état « élue » pendant un
certain temps ou jusqu'à une certaine date, on parle de «
suspension »,
- Tâche « prête » : la tâche attendre d’être élue. Dans cet état elle
requiert un processeur pour s'exécuter,
- Tâche « élue » : lorsque le noyau le décide, une tâche en état «
prête » alloue un processeur afin d’être exécuté. Cependant, de
l’état « élue », une tâche peut être préemptée par une autre
tâche. Dans ce cas, elle passe dans l'état « prête » en attendant le
processeur,
- Tâches- « en attente » : de l’état « exécutée », une tâche peut se
mettre en attente d’un message, d’un événement, ou de l’accès
à une ressource (on parle de « blocage »). Lorsque le message,

80
81

l’évènement (ou la ressource) est arrivé (ou libre), la tâche passe


dans l’état « prête » pour nécessite le processeur.

A tout instant, une tâche peut être supprimée. Dans ce cas, elle passe
dans l'état « inexistante ». Etant donné que les tâches sont générées
finement dans les noyaux temps réel, certains noyaux distinguent la
création et l’initialisation d’une tâche.

III.1.5. Classification des algorithmes d'ordonnancement

L’ordonnancement et l’allocation sont des termes qui concernent des


domaines étroitement liés de sorte que l’allocation des tâches est
souvent confondue avec l’ordonnancement des tâches et ou n parle
alors d’ordonnancement local et d’ordonnancement distribué (ou
global).

Nous estimations que l’appellation « ordonnancement locale » exprimer


parfaitement le partage d’un processeur
entre plusieurs tâches. Concernant l’ordonnancement global ou distrib
oui, nous pensons ns que le terme allocation dynamique con vient
mieux, étant donné qu’il s'agite d’allouer les tâches refusées aux
processeurs. Cela permet d'éviter toute confusion.

II.1.5.1. Terminologie

Ordonnancement des tâches : C’est la détermination d’un ordre


d’exécution des tâches selon les critères spécifiés. Quand cet ordre est
déterminé avant le début de l’exécution, on parle d’un
ordonnancement statique.

Ordonnancement dynamique : Ordonnancement des tâches où la


séquence déterminée au préalable est mise à jour et réordonnée en
fonction de nouvelles tâches.

Allocation statique : placement d’un ensemble de tâches sur un réseau


de processeurs en respectant et en optimisant certains critères ;

Allocation dynamique : placement des tâches créées durant


l’exécution.

81
82

II.1.5.2. Mécanismes et algorithmes d'ordonnancement

Mécanisme d'ordonnancement local de tâches : ensemble de modules


nécessaires pour la coordination de l’ordonnancement des tâches sur
un processeur. On l'appelle aussi ordonnanceur d.

Un bon mécanisme d'ordonnancement doit assurer les fonctions


suivantes :

- Vérification de la possibilité d’exécuter une tâche sur le processeur


sans violer ses contraintes. Cela permet de répondre à la question
suivante : « Etant donné un ensemble de tâches dont on sait que
les contraintes temporelles seront vérifiées, peut-être
- On accepte une nouvelle tâche ? » Par conséquent, une tâche
peut être refusée momentanément lorsque l’Ordonnanceur local
ne peut garantir ses contraintes avec l’ensemble des tâches
préalablement ordonnées.
- Le calcul des priorités des tâches ainsi que leur ordre d’exécution
et leurs dates de début et de fin d’exécution. Plusieurs algorithmes
existent déjà pour réaliser ce calcul.
- La mise à jour et l’adaptation (le re-calcul des priorités, le ré-
ordonnancement) dans le cas de l’arrivée d’une nouvelle tâche.

Ordonnancement optimal : un algorithme d’ordonnancement est dit


optimal lorsque toute configuration T de tâches qui est ordonnanceur
par un algorithme donné, l’est aussi par cet algorithme. En d’autres
termes c’est un algorithme qui permet de déterminer la meilleure
séquence d’ordonnancement qui peut exister (respect des échéances,
le cas éventuel, minimisation es retards, minimisation du temps
d’utilisation des ressources (notamment le processeur).

- Ordonnancement préemptif : C’est ONU algorithme qui permet


d'interrompre l’exécution de la tâche courante pour la prise en compte
d’une autre tâche plus prioritaire. Cela signifie qu’il fournit des
mécanismes nécessaires aires pour gérer la préemption c'est-à-dire la
commutation du contexte : elle consiste à sauvegarder le contexte de
la tâche fr cours l'exécution (registres et zones mémoires) interrompues
pour l’exécution d’une autre tâche. Ce contexte sera restauré quand
l’exécution de la tâche sera reprise. Ainsi, une tâche qui ne peut pas

82
83

être exécuté rentier HNE tout de même accepté si fils exécution peut
être partitionnée. Le temps de commutation doit être négligeable et
doit être pris en compte lors du calcul des dates de début d’exécution
par l’Ordonnanceur.

Il existe deux façons d’autoriser la préemption : la première consiste à


n’autoriser la préemption ou l’interruption de la tâche en cours que si
son exécution peut ultérieurement être repris sur le même processeur et
la seconde envisager le cas échéant de faire migrer la tâche. La
migration se fait lorsque le processeur reçoit une tâche très prioritaire et
qu’il interrompre la tâche en cours car sa priorité est moindre, sans
pouvoir reprendre cette exécution par la suite. Il est déconseillé d'utiliser
à la migration.

On préfère plutôt le transfert de tâches c’est placer des tâches avant le


début de leur exécution. L'ordonnancement préemptif HNE très
recommandé pour toutes les applications temps réel dynamiques.

- Mécanisme d'allocation statique des tâches : c’est un algorithme qui


permet de répartir un ensemble de tâches entre les processeurs répartis
à la machine en tenant compte des contraintes temporelles.

- Mécanisme d'allocation dynamique des tac il est : c’est un algorithme


qui entre en action lorsqu’une tâche est refusée localement. Il se charge
de déterminer un processeur pour exécuter la tâche. Il est doté de
modules de politiques de recherche de processeurs pour la tâche
refusée et de politique vers le maintien d'un État de processeurs afin de
choisir le plus capable d'assumer le respect des contraintes de la tâche.

III.2. PROGRAMMATION CONCURRENTE ET PROGRAMMATION TEMPS REEL

Une application TR est être décrite comme un ensemble de tâches


communicantes qui peuvent partager les critiques de la ressource. La
concurrence est donc un des problèmes fréquemment posés dans les STR et
dans de tels systèmes, plusieurs activités dont chacune est représentée par une
tâche indépendante, peuvent s’exécute en parallèle. D’une manière
générale, le fonctionnement d’une application TR comprend les activités
suivantes :

83
84

- L’acquisition des données depuis l’environnement à l’aide des


capteurs ;
- Le traitement des données et l’élaboration des résultats des
résultats au bout d’un délai limité (caractère tempe réel)
- L’envoi des ordres de commande de l’environnement à l’aide
d’actionneurs ;

Ainsi, des tâches peuvent être chargées de l’acquisition des données


à différentes périodes, d’autres tâches peuvent être dédiées au calcul,
et d’autres à l’application de commandes. Lorsque ces activités
se synchronisent et communiquent, le respect de l’exclusion mutuelle,
de la synchronisation et de la communication est vraiment un point clé auquel
il convient de s'adresser.

L'exclusion mutuelle consiste à garantir un aspect exclusif à une donnée ou une


ressource partagée par plusieurs tâches. La synchronisation permet de bloquer
une tâche tant qu’une autre ne la réveille pas. L une communication
permet à des tâches d’échanger des données.

D’où les méthodes de conception de ces systèmes sont utilisées pour


faciliter l’implémentation de tâches partageant des ressources
communiquant et se synchronisant : DARTS, SA, SD, etc.

III.2.1. Définitions

Sur l'appel programmation concurrente les techniques et notations permettant :

- L’expression et la manipulation (construction, destruction,


lancement, arrêt, ...) d'entité concurrentes que l'on appelle tâches
ou processus ;
- La mise en place de moyens de communication et de
synchronisation entre ces entités ;
- L’abstraction de l'exécution réelle des processus ou tâches sur une
architecture parallèle ou non.

La programmation temps réel est assimilable à la programmation concurrente


avec comme particularités :

84
85

- Des mécanismes ou notations permettant d'accéder au temps


réel (généralement une horloge temps réel) ;
- Des mécanismes permettant de définir ou de modifier la priorité
des processus et/ou de définir et de modifier des échéances
temporelles ;
- La suppression ou l’adaptation de certains traits du langage utilisé
afin d'alléger l'applicatif et/ou de le rendre plus déterministe.

III.2.2. Intérêts de la programmation concurrente

- Optimisation de l'utilisation des ressources :

C'est-à-dire la programmation concurrente optimiser l'utilisation du CPU


sur une architecture monoprocesseur par la prise en compte du
parallélisme entre calcul et entrées / sorties

- Calquer le monde réel :

En effet, le monde réel est fait d'entités concurrentes qu’interagissent. Par


exemple :

- Un système de réservation de billets doit préalablement prendre


en compte le fait que les agences effectuent les réservations en
parallèle.
- Un système de guidage doit utiliser les informations fournies par
plusieurs capteurs simultanément et donner des ordres à plusieurs
actionnés.

La traduction d'un monde à base d'entités concurrentes à l'aide de techniques


de programmation séquentielle conduit à :

- Inclure dans le code la répartition de l'appel des différents


composants du système ;
- Modifier le programme dès que l'on changement d'architecture
(nombre de processus ur, répartition de l'application) ;
- Modifier profondément le programme dès que l'on inclure une
nouvelle activité.

85
86

III.3. Problèmes possibles

Les problèmes inhérents à la programmation concurrente sont :

Problèmes de sûreté : la synchronisation des processus ou tâches peut


entraîner des problèmes tels que l'inter blocage ou la famine. -

Par exemple, le processus A attend le processus B, qui lui-même attend


le processus C qui lui-même attend le processus A : il y a un inter blocage
(ou étreinte fatale ou encore impasse)

Autre exemple, le processus A et B forment une coalition pour


empêcher C d'accéder à une ressource (A et B se la passe) : il y a la
famine du processus C. Ces problèmes sont inhérents au concours

Problème de vivacité : Suite à une mauvaise entente, il est possible que


deux processus n'arrivent jamais à se synchroniseur ; ceci peut conduire
à l'absence de respect d'un objectif fixé sans pour autant qu'il y a
interblocage. Par exemple, pour réparer un chauffe-eau électrique, le
plombier peut exiger que l'électricité soit à la norme : il faut donc
l'intervention préalable de l’électricien ; l'électricien peut lui exiger de
ne faire les travaux que lorsque les problèmes de fuites d'eau seront
résolus : il faut au préalable à l'intervention du plombier.

Les deux processus sont actifs (ils viennent régulièrement) mais ils ne font
pas progresser les choses : on est face à une activité non constructive
et donc un problème de vivacité.

Difficile reproductibilité des erreurs : L'entrelacement des différents


processus (chacun peut avancer à son rythme) va définir un nombre de
comportements possibles très important. Si l'on quatre
processus indépendants, chacun ayant dix états différents, on obtient
un système complet ayant 10^4 = 10 000 états différents ; Chaque
exécution ne va pas nécessairement faire évoluer exactement de la
même façon les processus (leur évolution dépendre généralement du
monde extérieur qui HNE fr perpétuel changement).

Ainsi, si une erreur survient il est très difficile de reproduire cette erreur
afin de comprendre son origine.

86
87

En conclusion

La concurrence introduit des problèmes spécifiques qui peuvent être difficiles à


analyser. Cependant, elle simplifie énormément le travail du concepteur et du
programmeur qui peut à la fois calquer la réalité dans son programme tout en
tirant partie de l'optimisation possible des ressources. Il existe de plus des
méthodes de test formelle adaptées à l'analyse de systèmes concurrents

III.2.3. Terminologie

On dit qu’un système est :

- Multi-tâches lorsque plusieurs entités concurrentes s'exécutent sur une


machine mono processeur.
- Parallèle lorsque plusieurs entités concurrentes s'exécutent sur plusieurs
processeurs avec une mémoire commune.
- Réparti lorsque plusieurs entités concurrentes s'exécutent sur des machines
distinctes reliées en réseau et ne partageant pas de mémoire commune.

On dit que deux processus ou tâches sont

- Indépendants, si l'évolution de l'un n'influence pas et ne dépend


pas de l’autre ;
- En coopération, si chacun mène un travail distinct de l'autre mais
avec un objectif commun et la mise en place de synchronisation
et de communication et qu'il n'y a pas compétition sur l'accès à
certaines ressources ;
- En compétition lorsqu'ils sont en coopération mais lorsque, de plus,
ils sont en compétition sur l'accès de certaines ressources.

Selon le niveau d'observation, deux processus peuvent être vus en


coopération ou en concour.

87
88

III.3. RTOS

III.3.1. Définition

Un système d'exploitation en temps réel, communément appelé RTOS,


permet au contrôleur de répondre aux entrées et d'effectuer des tâches
dans un délai spécifique en fonction de la priorité.

À première vue, un RTOS peut ressembler à n'importe quel autre


programme ou micrologiciel intégré, mais il est construit sur
l'architecture d'un système d'exploitation. Par conséquent, comme tout
système d'exploitation, le RTOS peut permettre à plusieurs programmes
de s'exécuter en même temps, tout en prenant en charge le multiplexage.
Comme nous le savons, le cœur d'un processeur ou d'un contrôleur ne
peut exécuter qu'une seule instruction à la fois, mais le RTOS dispose
d'un planificateur qui décide quelle instruction exécuter en premier et
exécute ainsi les instructions de plusieurs programmes les unes après les
autres. Techniquement, un RTOS ne crée qu'une illusion de multi-prise en
exécutant des instructions parallèles une par une.

Cela rend les RTOS adaptés à diverses applications dans le monde réel.
Dans les RTOS, pour toute entrée, une logique a été évaluée qui donne
la sortie correspondante. Cette logique est mesurée sur la base non
seulement de la créativité logique, mais aussi de la durée pendant
laquelle la tâche spécifique a été effectuée. Si un système ne parvient
pas à exécuter une tâche dans cette durée spécifique, on parle de
défaillance du système.
Pourquoi RTOS ?

 Disponibilité des pilotes : Il existe de nombreux pilotes disponibles


au sein du RTOS, ce qui nous permet de les utiliser directement pour
diverses applications.
 Fichiers planifiés : RTOS s'occupe de la planification. Ainsi, au lieu
de se concentrer sur la planification de n'importe quel système,
nous pouvons simplement nous concentrer sur le développement
d'applications. Par exemple, les fichiers de planification des tâches

88
89

sont utilisés pour définir certaines actions chaque fois qu'un


ensemble de conditions est rempli. RTOS utilise certains algorithmes
avancés pour la planification des états généralement en cours
d'exécution, prêt et bloqué qui, pendant l'exécution, RTOS se
concentre davantage sur le développement de l'application
plutôt que sur la planification.
 Flexibilité d'ajout de fonctionnalités : dans RTOS, même si vous
souhaitez ajouter de nouvelles fonctionnalités, vous pouvez
simplement les ajouter sans perturber les fonctionnalités existantes

III.3.2. Différence entre le système d'exploitation en temps réel et le


système d'exploitation

Il existe plusieurs différences entre un système d'exploitation en temps


réel et des systèmes d'exploitation comme Windows, Linux, etc.
Examinons-les une par une à l'aide du format tableau :


de Système opérateur Système en temps réel
série

Le partage du temps est la base


Les processus sont exécutés en
1 de l'exécution des processus dans
fonction de leur ordre de priorité
le système d'exploitation

Le système d'exploitation agit Le système en temps réel est conçu


2 comme une interface entre le pour être exécuté en fonction
matériel et le logiciel d'un système des problèmes du monde réel

La gestion de la mémoire n’est pas La gestion de la mémoire est


un problème critique lorsqu’il s’agit difficile car elle est basée sur le
3
de l’exécution du système temps réel, ce qui est en soi
d’exploitation critique.

89
90

Applications : Contrôle d'aéronefs


Applications : Bureau, centres de
ou de réacteurs nucléaires,
4 données, système pour la maison,
équipements de recherche
etc.
scientifique

Exemples : Microsoft Windows, Exemples : Vx Works, QNX, Windows


5
Linux, système d'exploitation CE

III.3.3. Types de RTOS

Nous pouvons classer les systèmes d'exploitation en temps réel en trois


grandes parties, à savoir

1. Système d'exploitation en temps réel


2. Système d'exploitation en temps réel souple
3. Système d'exploitation ferme en temps réel

1. Système d'exploitation en temps réel

Commençons par comprendre ce type de système d'exploitation à


l'aide d'un exemple concret, le système de contrôle de vol. Dans ce
système, toutes les tâches données par le pilote sous forme d'entrée
doivent être exécutées à temps. Dans un système d'exploitation en
temps réel, les pannes du système peuvent être tolérées. Les
caractéristiques d'un RTOS dur sont les suivantes :

 Pour effectuer les tâches à temps


 Le non-respect des délais est fatal
 Temps de réponse garanti dans le pire des cas
 Peut conduire à une défaillance du système

2. Système d'exploitation en temps réel souple

L'exemple le plus simple d'utilisation d'un RTOS logiciel est la base de


données en ligne, car dans le cadre d'un RTOS logiciel, le paramètre qui

90
91

nous préoccupe le plus est la vitesse. Par conséquent, les


caractéristiques d'un RTOS logiciel sont les suivantes :

 Les tâches doivent être exécutées le plus rapidement possible


 L’achèvement tardif des tâches est indésirable mais pas fatal
 Il existe une possibilité de dégradation des performances
 Ne peut pas conduire à une défaillance du système

3. Système d'exploitation ferme en temps réel

Le bras robotisé utilisé pour saisir des objets peut être considéré comme
l'un des exemples de RTOS d'entreprise. Ici, dans ce RTOS d'entreprise,
même si le processus est retardé, il est toléré.

III.3.4. Avantages de l'utilisation d'un RTOS gratuit

Voici les avantages de l’utilisation de RTOS dans vos applications.

 Aucun problème de pare-feu


 Faible bande passante pour des performances améliorées
 Sécurité et confidentialité améliorées
 Faible coût, grâce à la réduction des composants matériels et
logiciels utilisés pour le développement

III.3.5. Quelques problèmes majeurs liés aux RTOS

Bien que les RTOS présentent de nombreux avantages dans les


applications concrètes, ils présentent également divers inconvénients.
Certains des problèmes qui y sont liés sont abordés ici.

 Les interruptions sont normalement utilisées dans les programmes


pour arrêter l'exécution du programme afin de détourner le flux
vers une autre partie importante du code. Ici, dans le RTOS, étant
donné qu'un temps de réponse rapide est requis, il est
recommandé de désactiver les interruptions pendant le temps le
plus court possible.
 Étant donné que le noyau doit également répondre à divers
événements, il est nécessaire d'avoir une taille de noyau plus
petite pour qu'il puisse s'adapter correctement à la ROM.

91
92

 Les fonctionnalités sophistiquées des RTOS devraient être


supprimées car il n'existe aucun concept de mémoire virtuelle en
tant que tel.

III.3.6. Comment utiliser RTOS

Maintenant que vous savez ce qu'est un RTOS et où vous pouvez l'utiliser,


pour commencer à utiliser un RTOS, vous devez normalement utiliser
l'environnement de développement Tornado ou FreeRTOS. Jetons un
bref coup d'œil à ces deux environnements de développement.

Tornade – VxWorks

Tornado est un environnement intégré permettant de développer des


applications RTOS embarquées en temps réel sur le système cible.
Tornado se compose de trois éléments de base qui sont répertoriés ci-
dessous.

1) VxWorks

2) Outils de création d'applications (compilateur et programmes


associés)

3) Environnement de développement intégré, capable de gérer,


déboguer et surveiller l'application VxWorks

92
93

VxWorks est un système d'exploitation en réseau en temps réel. Pour


commencer avec VxWorks, nous devons avoir un kit de développement
(cible) ainsi qu'un poste de travail. Ici, le kit de développement n'est rien
d'autre que l'hôte ou le composant cible qui communique avec le
serveur cible sur le poste de travail. La cible ici connecte les outils
Tornado tels que le shell et le débogueur . Par conséquent, en utilisant
VxWorks, nous allons configurer et construire les systèmes tandis que
Tornado nous fournit une interface utilisateur graphique et des outils de
ligne de commande pour la configuration et la construction.

Un point très important qui entre en jeu ici est que lors de l'installation de
Tornado dans votre système, le répertoire d'installation doit utiliser les
noms de chemin suivants :
installDir/target. Par exemple, si vous souhaitez stocker votre Tornado
dans C:\tornado sur un hôte Windows, le nom de chemin complet doit
être identifié dans ce cas comme installDir/target/h/vxworks.h.

Ici, nous n'aborderons pas en détail les fonctionnalités de Vxworks (nous


laisserons cela pour le prochain tutoriel) mais nous verrons comment le

93
94

développement peut être réalisé en utilisant C++ dans Vxworks en


utilisant WindRiver GNU . WindRiver GNU nous aide à fournir une analyse
graphique concernant l'interruption impliquée pendant l'exécution ainsi
que le rapport d'utilisation de la mémoire.

Par exemple, la vue de WindRiver ci-dessus explique le numéro de


processeur associé ainsi que la priorité des tâches (tLowPri et tHighPri).
L'état inactif, c'est-à-dire la ligne de couleur verte, indique la période
pendant laquelle le processeur n'est pas en état de fonctionnement, ce
qui est observé toutes les quelques secondes. t1, t7, t8 et t9 ne sont rien
d'autre que les différents processeurs utilisés. Ici, nous sélectionnons
uniquement le processeur t7.

Par conséquent, ce Windriver est capable d'appeler à la fois VxWorks et


les sous-routines des modules d'application. Vous pouvez lancer
l'application Windriver soit à partir de la barre d'outils de lancement de
Tornado (-> bouton i), soit en cliquant sur le menu, puis sur le shell. Enfin,
à partir de l'invite de commande, tapez « >windsh target server ».

Pour programmer en C++, il est important d'inclure le composant


INCLUDE_CPLUS_DEMANGLER, ce composant de démangleur permet

94
95

aux symboles du shell cible de renvoyer des formes lisibles par l'homme
des noms de symboles C++. Avant de télécharger le module C++ sur la
cible Vxworks, suivez le processus connu sous le nom de munching . Ici,
munching fait référence à une étape de traitement hôte
supplémentaire.

Compilez le programme source de l'application C++ et récupérez par


exemple le fichier [Link]. Exécutez-le ensuite pour grignoter le fichier
.o et compilez le fichier ctdt.c généré. De plus, liez l'application à ctdt.o
pour générer le module téléchargeable, [Link] dans VxWorks. Le
résultat après l'exécution de ce VxWorks sera un fichier make qui sera
utilisé sur une cible.

III.3.7. RTOS gratuit

En général, lorsque nous commençons avec les RTOS, nous préférons


généralement les RTOS Vx Works. Mais, discutons ici brièvement des
RTOS gratuits, qui peuvent également être utilisés par les débutants pour
comprendre le concept de système d'exploitation en temps réel. Free
RTOS est développé par Richard Barry et l'équipe FreeRTOS, il appartient
également à Real time engineers ltd mais il est gratuit et peut être
simplement téléchargé en cliquant sur le lien ci-dessous.

La dernière version du RTOS libre utilisée au moment de la rédaction de


cet article est la version 10, indiquée comme FreeRTOS V10.

Le plus grand avantage des RTOS gratuits, qui les rendent supérieurs aux
autres RTOS, est leur comportement indépendant de la plate-forme en
termes de matériel, c'est-à-dire que le code C que nous utiliserons pour
exécuter un système d'exploitation peut s'exécuter sur différentes plates-
formes ayant une architecture différente. Par conséquent, que vous
utilisiez un microcontrôleur 8051 ou un microcontrôleur ARM de dernière
génération, le code que vous avez écrit ainsi que le processus
d'exécution seront similaires pour les deux.

L'utilisation de RTOS gratuits présente de nombreux autres avantages


par rapport à Vx Works et à d'autres outils d'exploitation RTOS. En voici
quelques exemples :

95
96

i. Permet des tests plus faciles


ii. Favorise le concept de réutilisabilité du code
iii. Moins de temps d'inactivité
iv. Facilité d'entretien
v. Résumé des informations de synchronisation

De plus, le noyau de base, où le noyau fait référence au composant


central d'un système d'exploitation présent dans le RTOS libre, le rend
accessible à l'utilisation pour diverses applications. Comme il est facile
d'attacher des modules étendus aux systèmes d'exploitation pour
obtenir davantage d'applications, le RTOS libre devient plus puissant.

L'un des exemples d'utilisation d'un RTOS libre peut être expliqué en
utilisant le concept de combinaison d'un RTOS libre avec
Nabto. Nabto est un appareil Web libre utilisé pour transférer les
informations de l'appareil vers le navigateur.

Par conséquent, la combinaison de Free RTOS avec Nabto en fait un


petit morceau de code C comme expliqué dans la figure a. De nos
jours, l'Internet des objets (IOT) est à la mode et chaque appareil IOT
auquel nous accédons dispose d'une URL unique sur Internet et la
technologie permet des connexions point à point sécurisées et à bande
passante extrêmement faible. En l'absence de connectivité Internet,
cette combinaison peut être utile. Par conséquent, le RTOS gratuit est
un choix populaire lorsqu'il s'agit de mettre en œuvre l'IOT.

96
97

III.4. FreeRTOS

Le système d'exploitation présent dans les appareils embarqués est


appelé RTOS (Real-Time Operating System) . Dans les appareils
embarqués, les tâches en temps réel sont essentielles et le timing joue
un rôle très important. Les tâches en temps réel sont déterministes dans le
temps, ce qui signifie que le temps de réponse à tout événement est
toujours constant, de sorte qu'il peut être garanti qu'un événement
particulier se produira à une heure fixe. Le RTOS est conçu pour exécuter
des applications avec un timing très précis et un haut degré de fiabilité.
Le RTOS permet également d'effectuer plusieurs tâches à la fois avec
un seul cœur.

Dans cette partie du cours, nous commencerons avec FreeRTOS .


FreeRTOS est une classe de RTOS pour les appareils embarqués qui est
suffisamment petite pour être exécutée sur des microcontrôleurs 8/16
bits, bien que son utilisation ne soit pas limitée à ces microcontrôleurs.

C'est un système entièrement open source et son code est disponible


sur github . Si nous connaissons quelques concepts de base de RTOS, il
est alors très facile d'utiliser FreeRTOS car il dispose d'API bien
documentées qui peuvent être directement utilisées dans le code sans
connaître la partie backend du codage.

Comme FreeRTOS peut fonctionner sur un MCU 8 bits, il peut également


être exécuté sur une carte Arduino Uno. Il suffit de télécharger la
bibliothèque FreeRTOS, puis de commencer à implémenter le code à
l'aide d'API. Ce tutoriel est destiné à un débutant complet.

III.4.1. Fonctionnement de RTOS

Avant de commencer à utiliser le RTOS, voyons ce qu'est une tâche. Une


tâche est un morceau de code qui peut être planifié sur le processeur
pour s'exécuter. Ainsi, si vous souhaitez effectuer une tâche, elle doit
être planifiée à l'aide du délai du noyau ou à l'aide d'interruptions . Ce

97
98

travail est effectué par le planificateur présent dans le noyau. Dans un


processeur monocœur, le planificateur aide les tâches à s'exécuter
dans une tranche de temps particulière, mais il semble que différentes
tâches s'exécutent simultanément. Chaque tâche s'exécute en
fonction de la priorité qui lui est donnée.

Voyons maintenant ce qui se passe dans le noyau RTOS si nous voulons


créer une tâche pour le clignotement des LED avec un intervalle d'une
seconde et placer cette tâche sur la priorité la plus élevée.

Outre la tâche LED, il existe une autre tâche créée par le noyau,
appelée tâche inactive . La tâche inactive est créée lorsqu'aucune tâche
n'est disponible pour l'exécution. Cette tâche s'exécute toujours avec la
priorité la plus basse, c'est-à-dire la priorité 0. Si nous analysons le
graphique de synchronisation donné ci-dessus, nous pouvons voir que
l'exécution commence par une tâche LED et qu'elle s'exécute pendant

98
99

un temps spécifié, puis pendant le temps restant, la tâche inactive


s'exécute jusqu'à ce qu'une interruption de tick se produise. Ensuite, le
noyau décide quelle tâche doit être exécutée en fonction de la priorité
de la tâche et du temps total écoulé de la tâche LED. Lorsqu'une
seconde est écoulée, le noyau choisit à nouveau la tâche LED à
exécuter car elle a une priorité plus élevée que la tâche inactive, nous
pouvons également dire que la tâche LED préempte la tâche inactive.
S'il y a plus de deux tâches avec la même priorité, elles s'exécuteront en
mode tourniquet pendant un temps spécifié.

Ci-dessous le diagramme d'état car il montre le passage de la tâche non


exécutée à l'état d'exécution.

Chaque tâche nouvellement créée passe à l'état Prêt (partie de l'état


non exécuté). Si la tâche créée (Tâche 1) a la priorité la plus élevée par
rapport aux autres tâches, elle passe à l'état En cours d'exécution. Si
cette tâche en cours d'exécution est devancée par l'autre tâche, elle

99
100

revient à l'état Prêt. Sinon, si la tâche 1 est bloquée à l'aide de l'API de


blocage, le processeur ne s'engagera pas dans cette tâche jusqu'à
l'expiration du délai défini par l'utilisateur.

Si Task1 est suspendue en cours d'exécution à l'aide des API de


suspension, Task1 passe alors à l'état Suspendu et n'est plus disponible
pour le planificateur. Si vous reprenez Task1 dans l'état suspendu, elle
reviendra à l'état Prêt, comme vous pouvez le voir dans le diagramme.

Il s'agit de l’idée de base de la manière dont les tâches s'exécutent et


changent d'état . Dans ce cours, nous allons implémenter deux tâches
dans Arduino Uno à l'aide de l'API FreeRTOS.

III.4.2. Termes fréquemment utilisés dans les RTOS

1. Tâche : il s'agit d'un morceau de code dont l'exécution peut être


planifiée sur le processeur.

2. Planificateur : il est chargé de sélectionner une tâche de la liste des


états prêts à l'exécution. Les planificateurs sont souvent mis en œuvre
de manière à ce que toutes les ressources de l'ordinateur restent
occupées (comme dans l'équilibrage de charge).

3. Préemption : Il s’agit de l’acte d’interrompre temporairement une


tâche déjà en cours d’exécution avec l’intention de la retirer de l’état
d’exécution sans sa coopération.

4. Changement de contexte : dans la préemption basée sur la priorité,


le planificateur compare la priorité des tâches en cours d'exécution
avec la priorité de la liste des tâches prêtes à chaque
interruption systick . Si une tâche de la liste a une priorité supérieure à
celle de la tâche en cours d'exécution, un changement de contexte se
produit. En gros, dans ce processus, le contenu des différentes tâches
est enregistré dans leur mémoire de pile respective.

100
101

5. Types de politiques de planification :

a. Planification préemptive : dans ce type de planification, les tâches


s'exécutent avec une tranche de temps égale sans tenir compte
des priorités.
b. Préemptif basé sur la priorité : la tâche à priorité élevée sera
exécutée en premier.
c. Planification coopérative : le changement de contexte ne se
produira qu'avec la coopération des tâches en cours d'exécution.
La tâche s'exécutera en continu jusqu'à ce que la tâche yield soit
appelée.

6. Objets du noyau : pour signaler à la tâche d'effectuer un travail, le


processus de synchronisation est utilisé. Pour effectuer ce processus, des
objets du noyau sont utilisés. Certains objets du noyau sont des
événements, des sémaphores, des files d'attente, des mutex, des boîtes
aux lettres, etc. Nous verrons comment utiliser ces objets dans les
prochains tutoriels.

À partir de la discussion ci-dessus, nous avons obtenu quelques idées de


base sur le concept RTOS et nous pouvons maintenant implémenter le
projet FreeRTOS dans Arduino. Commençons donc par installer les
bibliothèques FreeRTOS dans Arduino IDE.

III.4.3. Installation de la bibliothèque Arduino FreeRTOS

1. Ouvrez Arduino IDE et accédez à Sketch -> Inclure la bibliothèque ->


Gérer les bibliothèques. Recherchez FreeRTOS et installez la
bibliothèque comme indiqué ci-dessous.

101
102

Vous pouvez télécharger la bibliothèque depuis github et ajouter le


fichier .zip dans Sketch-> Inclure la bibliothèque -> Ajouter le fichier .zip.

Maintenant, redémarrez l'IDE Arduino. Cette bibliothèque fournit un


exemple de code, qui peut également être trouvé dans Fichier ->
Exemples -> FreeRTOS comme indiqué ci-dessous.

Ici, nous allons écrire le code à partir de zéro pour comprendre le


fonctionnement, plus tard vous pourrez vérifier les exemples de codes et
les utiliser.

a) Schéma de circuit
Vous trouverez ci-dessous le schéma de circuit permettant de créer une
tâche de LED clignotante à l'aide de FreeRTOS sur Arduino :

102
103

b) Exemple Arduino FreeRTOS - Création de tâches FreeRTOS dans


Arduino IDE
Voyons une structure de base pour écrire un projet FreeRTOS.

1. Tout d'abord, incluez le fichier d'en-tête Arduino FreeRTOS comme

#include <Arduino_FreeRTOS.h>

2. Donnez le prototype de fonction de toutes les fonctions que vous


écrivez pour l'exécution qui est écrit comme

void Tâche1( void *pvParameters );

void Tâche2( void *pvParameters );

..

….

103
104

3. Maintenant, dans la fonction void setup() , créez des tâches et


démarrez le planificateur de tâches.

Pour créer une tâche, l'API xTaskCreate() est appelée dans la


fonction de configuration avec certains paramètres/arguments.

xTaskCreate( TaskFunction_t pvTaskCode, const char * const pcName, uint16_t u


sStackDepth, void *pvParameters, UBaseType_t uxPriority, TaskHandle_t *pxCre
atedTask );

Il y a 6 arguments qui doivent être passés lors de la création d'une tâche.


Voyons quels sont ces arguments

I. pvTaskCode : il s’agit simplement d’un pointeur vers la fonction qui


implémente la tâche (en fait, juste le nom de la fonction).
II. pcName : un nom descriptif pour la tâche. Il n'est pas utilisé par
FreeRTOS. Il est inclus uniquement à des fins de débogage.
III. usStackDepth : Chaque tâche possède sa propre pile unique qui
est allouée par le noyau à la tâche lors de sa création. La valeur
spécifie le nombre de mots que la pile peut contenir, et non le
nombre d'octets. Par exemple, si la pile a une largeur de 32 bits et
que usStackDepth est transmis sous la forme 100, alors 400 octets
d'espace de pile seront alloués (100 * 4 octets) dans la RAM. Utilisez
cela à bon escient car Arduino Uno n'a que 2 Ko de RAM.
IV. pvParameters : Paramètre d'entrée de tâche (peut être NULL).
V. uxPriority : Priorité de la tâche (0 est la priorité la plus basse).
VI. pxCreatedTask : il peut être utilisé pour transmettre un handle à la
tâche en cours de création. Ce handle peut ensuite être utilisé
pour référencer la tâche dans les appels d'API qui, par exemple,
modifient la priorité de la tâche ou suppriment la tâche (peut être
NULL).

Exemple de création de tâche

xTaskCreate(tâche1,"tâche1",128,NULL,1,NULL);

104
105

xTaskCreate(tâche2,"tâche2",128,NULL,2,NULL);

Ici, la tâche 2 a une priorité plus élevée et s'exécute donc en premier.

4. Après avoir créé la tâche, démarrez le planificateur dans une


configuration void à l'aide de l'API vTaskStartScheduler() ;

5. La fonction Void loop() restera vide car nous ne voulons pas exécuter
de tâche manuellement et à l'infini. Car l'exécution des tâches est
désormais gérée par le Scheduler.

6. Maintenant, nous devons implémenter les fonctions de tâches et


écrire la logique que vous souhaitez exécuter à l'intérieur de ces
fonctions. Le nom de la fonction doit être le même que le premier
argument de l'API xTaskCreate() .

void task1(void *pvParameters)

while(1) {

..

..//votre logique

7. La plupart du code nécessite une fonction de délai pour arrêter la


tâche en cours d'exécution, mais dans RTOS, il n'est pas recommandé
d'utiliser la fonction Delay() car elle arrête le processeur et donc RTOS
cesse également de fonctionner. FreeRTOS dispose donc d'une API de
noyau pour bloquer la tâche pendant une durée spécifique.

105
106

vTaskDelay( const TickType_t xTicksToDelay );

Cette API peut être utilisée à des fins de retard. Cette API retarde une
tâche d'un nombre donné de ticks. Le temps réel pendant lequel la
tâche reste bloquée dépend du taux de ticks. La
constante portTICK_PERIOD_MS peut être utilisée pour calculer le temps
réel à partir du taux de ticks.
Cela signifie que si vous voulez un délai de 200 ms, écrivez simplement
cette ligne

vTaskDelay( 200 / portTICK_PERIOD_MS );

Donc, pour ce cours, nous utiliserons ces API FreeRTOS pour implémenter
trois tâches.
API à utiliser :

1. xTaskCreate();
2. vTaskStartScheduler();
3. vTaskDelay();

Tâche à créer pour ce tutoriel :

1. La LED clignote sur la broche numérique 8 avec une fréquence de


200 ms
2. La LED clignote sur la broche numérique 7 avec une fréquence de
300 ms
3. Imprimez les numéros sur un moniteur série avec une fréquence de
500 ms.

106
107

III.4.4. Implementation de la tâche FreeRTOS dans l'IDE Arduino

1. À partir de l'explication de la structure de base ci-dessus, incluez le


fichier d'en-tête Arduino FreeRTOS. Créez ensuite des prototypes de
fonctions. Comme nous avons trois tâches, créez donc trois fonctions et
leurs prototypes.

#include <Arduino_FreeRTOS.h>

void TaskBlink1( void *pvParameters );

void TaskBlink2( void *pvParameters );

void Taskprint( void *pvParameters );

2. Dans la fonction void setup() , initialisez la communication série à 9 600


bits par seconde et créez les trois tâches à l'aide de l'API xTaskCreate() .
Au départ, définissez les priorités de toutes les tâches sur « 1 » et
démarrez le planificateur.

void setup() {

[Link](9600);

xTaskCreate(TaskBlink1,"Tâche1",128,NULL,1,NULL);

xTaskCreate(TaskBlink2,"Tâche2 ",128,NULL,1,NULL);

xTaskCreate(Taskprint,"Tâche3",128,NULL,1,NULL);

vTaskStartScheduler();

3. Maintenant, implémentez les trois fonctions comme indiqué ci-


dessous pour la tâche 1 : clignotement de la LED .

107
108

void TaskBlink1(void *pvParameters)


{
pinMode(8, SORTIE);
while(1)
{
digitalWrite(8, HAUT);
vTaskDelay( 200 / portTICK_PERIOD_MS );
digitalWrite(8, BAS);
vTaskDelay( 200 / portTICK_PERIOD_MS );
}
}

De même, implémentez la fonction TaskBlink2. La fonction Task3 sera écrite


comme

void Taskprint(void *pvParameters)


{
int compteur = 0;
while(1)
{
compteur++;
[Link](compteur);
vTaskDelay( 500 / portTICK_PERIOD_MS );
}
}

Voilà, c'est tout. Nous avons terminé avec succès un projet Arduino
FreeRTOS pour Arduino Uno. Vous trouverez le code complet ainsi
qu'une vidéo à la fin de ce tutoriel.

Enfin, connectez deux LED aux broches numériques 7 et 8 et téléchargez


le code sur votre carte Arduino et ouvrez le moniteur série. Vous verrez

108
109

qu'un compteur s'exécute une fois toutes les 500 ms avec le nom de la
tâche comme indiqué ci-dessous.

Observez également les LED, elles clignotent à des intervalles de temps


différents. Essayez de jouer avec l'argument de priorité dans la
fonction xTaskCreate . Modifiez le numéro et observez le comportement
sur le moniteur série et les LED.

109
110

Vous pouvez maintenant comprendre les deux premiers exemples de


codes dans lesquels des tâches de lecture analogique et de lecture
numérique sont créées. De cette façon, vous pouvez réaliser des projets
plus avancés en utilisant uniquement les API Arduino Uno et FreeRTOS.

#include
void TaskBlink1( void *pvParameters );
void TaskBlink2( void *pvParameters );
void Taskprint( void *pvParameters );
void setup() {
// initialize serial communication at 9600 bits per second:
[Link](9600);
xTaskCreate(
TaskBlink1
, "task1"
, 128
, NULL
, 1
, NULL );
xTaskCreate(
TaskBlink2
, "task2"
, 128
, NULL
, 1
, NULL );
xTaskCreate(
Taskprint
, "task3"

110
111

, 128
, NULL
, 1
, NULL );
vTaskStartScheduler();
}
void loop()
{
}
void TaskBlink1(void *pvParameters) {
pinMode(8, OUTPUT);
while(1)
{
[Link]("Task1");
digitalWrite(8, HIGH);
vTaskDelay( 200 / portTICK_PERIOD_MS );
digitalWrite(8, LOW);
vTaskDelay( 200 / portTICK_PERIOD_MS );
}
}
void TaskBlink2(void *pvParameters)
{
pinMode(7, OUTPUT);
while(1)
{
[Link]("Task2");
digitalWrite(7, HIGH);

111
112

vTaskDelay( 300 / portTICK_PERIOD_MS );


digitalWrite(7, LOW);
vTaskDelay( 300 / portTICK_PERIOD_MS );
}
}
void Taskprint(void *pvParameters) {
int counter = 0;
while(1)
{
counter++;
[Link](counter);
vTaskDelay(500 / portTICK_PERIOD_MS); }
}

112

Vous aimerez peut-être aussi