PrécédentPourquoi s’intéresser à l’électronique embarquée ?2 / 24SuivantLa carte CREPP.io
3. Les microcontrôleurs

La programmation du microcontrôleur

La programmation du microcontrôleur

Le langage

Pour programmer notre microcontrôleur, nous allons utiliser le langage C++ qui est un langage de programmation très répandu.

Les variables

Pour manipuler les données dans notre programme, qui n’est plus ni moins une suite d’instructions, nous allons avoir besoins de créer des variables, c’est à dire de déclarer que nous allons manipuler une donnée.
Une variable possède donc fondamentalement :

💡

Un octet est une unité fondamentale de stockage de données en informatique. Il est composé de 8 bits, et chaque bit peut avoir deux valeurs possibles : 0 ou 1. Cela signifie qu’un octet peut représenter $2^8$  (soit 256) valeurs différentes, allant de 0 à 255.
Dans les microcontrôleurs et les systèmes embarqués, la mémoire est généralement organisée en octets, et chaque octet est adressable, ce qui signifie qu’il a une adresse mémoire unique qui permet d’y accéder.

La section suivante présente les types de variables possible en fonction de leur utilisation.

Les types de variables

Dans un souci de cohérence, nous choisissons le type de variable en fonction des données :

Les fonctions

Généralités

Afin de rendre plus lisible notre programme, il convient de le séparer dans des fonctions, c’est à dire des portions de code qui effectuent une tâche spécifique ou un ensemble d’opérations bien définies.
Les fonctions sont utilisées pour organiser et structurer un programme en morceaux plus petits et plus gérables, ce qui facilite la compréhension, la maintenance et la réutilisation du code.
Comme pour les variables, les fonctions reposent sur une architecture. Une fonction est composée de :

L’architecture d’une fonction
L’architecture d’une fonction

Prenons un exemple avec une fonction qui va élever un nombre au carré :

L’architecture de notre fonction ”Square”
L’architecture de notre fonction ”Square”

Cette function prend un entier en paramètre, l’élève au carré et retourne cette valeur.

Prototype d’une fonction

Une fonction est donc défini par son prototype qui spécifie les détails essentiels de la fonction, tels que son nom, ses paramètres d’entrée (arguments), et son type de valeur de retour.
Une fonction, en C++, est de la forme suivante :

C++

Prototype d’une fonction

TypeReturn name(typeOfInput inputName)

    //Code
    return outputVariable;

La figure suivante implémente notre fonction ”Square”.

C++

Une fonction ”Square”

int square(int a)

    return a*a;

Il est également possible de créer des variables intermédiaires :

C++

Une fonction ”Square” avec une variable intermédiaire

int square(int a)

int inputValue = a; //Same type
return inputValue*inputValue;

L’architecture d’un programme

Nos futurs programmes vont se baser sur deux fonctions obligatoires :

💡

Avec une utilisation de Plateform.io, on n'oubliera pas d'inclure systématiquement au début du programme la ligne \#include < Arduino.h >

C++

Le code minimal

#include  //For Platform.io
void setup()

    //Code in setup

void loop()

    //Code in loop
💡

void est un type particulier qui veut dire que la fonction ne retourne rien. Dans ce cas, la fonction est simplement vue comme une procédure, c’est à dire un enchaînement d’actions sans attendre de retour particulier.

Le compilateur

Une fois que notre code est rédigé, nous allons invoquer le compilateur qui est un programme qui va transformer notre code en instructions machine.

La compilation du code
La compilation du code

Il s'agit de code bas niveau que le microprocesseur de la machine peut comprendre.

Le code suivant affiche le code nécessaire pour modifier l'état d'une broche :

C++

Un code en assembleur pour modifier l'état d'une broche

"sbi 
    "call OneSecondDelay " //delay
    "cbi 
    "call OneSecondDelay " //delay
    "rjmp 4f "             //exit

"OneSecondDelay: " "ldi r18, 0 " //delay 1 second "ldi r20, 0 " "ldi r21, 0 "

"1: ldi r24, lo8(400) " "ldi r25, hi8(400) " "2: sbiw r24, 1 " //10x around this loop = 1ms "brne 2b " "inc r18 " "cpi r18, 10 " "brne 1b "

"subi r20, 0xff " //1000 x 1ms = 1 second "sbci r21, 0xff " "ldi r24, hi8(1000) " "cpi r20, lo8(1000) " "cpc r21, r24 " "breq 3f "

"ldi r18, 0 " "rjmp 1b "

"3: " "ret "

Ce code ne sera pas expliqué mais vous pouvez voir que les instructions sont moins lisible pour un être humain.

Ce sont ce genre d’instructions que comprend votre microcontrôleur.

💡

Chaque famille de microcontrôleur possède son jeu de langage, cependant on retrouve de nombreux points communs.

L'un des compilateur les plus connu est pour le language C et pour le langage C++.

Les instructions spécifiques

La configuration des broches

Nous souhaitons utiliser des broches de notre microcontrôleur. Il nous faut donc définir le type de broche. Pour cela, il existe l’instruction qui prend en argument le numéro de la broche et son type (entrée ou sortie).
Par exemple, nous souhaitons mettre la broche en sortie :

C++

Mise en sortie de la broche

void setup()

    pinMode(D4, OUTPUT);
💡

Pour la carte CREPP.io, cela veut dire que nous allons utiliser la broche en sortie.
Cette broche est reliée à la LED verte. Sur la carte NodeMCU, une LED est déjà câblé cependant, le niveau logique est inversé :
- Un 1 logique sur cette broche allume la LED verte externe et éteint la LED interne.
- Un '0' logique sur cette broche allume la LED bleue interne et éteint la LED externe.

Pour la mettre en entrée, il convient de faire :

C++

Mise en entrée de la broche

void setup()

    pinMode(D4, INPUT);

Le changement d’état des broches

Pour changer le niveau logique d’une broche  [2], on va utiliser la fonction qui prend en argument le numéro de la broche et son niveau logique (haut ou bas).

⚠️

Ne pas oublier d’écrire la fonction avant la fonction loop.

C++

Mise à l’etat haut de la broche

void loop()

digitalWrite(D4, HIGH);

Et pour mettre à l’état Bas, on utilise le mot clé LOW à la place de HIGH :

C++

Mise à l’etat bas de la broche

void loop()

    digitalWrite(D4, LOW);

Les délais

Parfois, nous souhaitons des délais dans notre programme, afin de le stabiliser pour certaines actions par exemple.

Il existe la fonction qui prend en argument le temps pendant lequel le microcontrôleur sera bloqué à cette instruction pendant la durée passée.

💡

La valeur en argument est exprimée en millisecondes !

C++

Mise en pratique de la fonction

void loop()

    digitalWrite(D4, HIGH);
    delay(1000);
    digitalWrite(D4, LOW);
    delay(1000);

Physiquement, l'allure de la tension sur la broche est la suivante :

La lecture des broches

Pour lire l’état d’une broche, nous utiliserons la fonction qui prend en argument le numéro la broche.

Il faudra au préalable avoir mis la broche en entrée :

C++

lecture de la broche

void setup()

    pinMode(D4, INPUT);

void loop()

    bool value = digitalRead(D4);

La valeur lue sur la broche sera stockée dans la variable value.

La communication série

Intêret de cette communication

Lors de l’exécution d’un programme, nous souhaitons parfois savoir ou en est le programme, quelle instruction il est en train de réaliser. Pour cela, le langage Arduino (surcouche C++) propose une bibliothèque dédiée : la bibliothèque .

Pour les bibliothèques, il faut voir ca comme un regroupement de fonctions.

L’utilisation de la bibliothèque permet de communiquer avec un ordinateur ou d’autres périphériques à l’aide de la communication série. C’est un outil très utile pour le débogage, la surveillance et la transmission de données entre votre microcontrôleur et un ordinateur hôte via un câble USB ou un adaptateur série.

Le protocole série est décrit de manière plus précise dans la section La liaison UART.

Initialisation de la communication

Il faut tout d’abord initialiser la communication série dans la fonction :

C++

Initialisation de la communication série

void setup()

Serial.begin(9600); //9600 bauds
Ici, nous initialisons une communication à 9600 bauds par second. Un baud représente un caractère par seconde.Il s’agit d’une vitesse normalisée, une autre valeur courante est 115200 bauds

Transmission des donneés

Vous pouvez envoyer des données depuis le microcontrôleur vers l’ordinateur hôte en utilisant la fonction ou  [3]

C++

Transmission des donneés

void loop()

    int value = digitalRead(D4);
    Serial.print("Value : ");
    Serial.println(value);
    delay(1000); //wait 1s

Vous pouvez également recevoir des données depuis l’ordinateur hôte vers le microcontrôleur. Cela peut être utile pour contrôler le microcontrôleur à partir de votre ordinateur.
Cependant, cette section sera abordée plus tard, cela ne rentre pas dans le contexte des bases des microcontrôleurs.

Les opérations courantes

Il est possible de faire les opérations courantes :

⚠️

Il faut faire attention au type des variables qui entrent en compte dans les calculs.
Si nous faisons une addition de nombres relatifs [1], il ne faut pas stocker le résultat dans une variable avec un type non-signé.
Ou alors, une addition de 2 nombres codés sur 8 bits, cela peut donner un nombre plus grand que la représentation maximale d'un des 2 nombres.
Par exemple, $150+150=300$, or pour représenter $300$, il faut plus de 8 bits

Les fonctions mathématiques

En ce qui concerne les fonctions mathématiques, les fonctions trigonométriques sont incluses. Par exemple, pour utiliser la fonction cosinus, on utilisera la fonction qui attend un angle en radian.

Les structures de contrôle

Une fois que nous avons nos variables qui contiennent les éléments que nous souhaitons, il va falloir agir en conséquent, c’est à dire faire des instructions spécifiques en fonction de la valeur.

La structure if

la structure if est une instruction de contrôle de flux qui permet d’exécuter un bloc de code conditionnellement, en fonction de l’évaluation d’une expression booléenne.
L’expression booléenne est une expression qui peut être soit vraie (true) soit fausse (false). Voici la syntaxe de base de la structure if :

C++

Structure du if

if (condition) 
    // if true, do ...

condition est une expression booléenne que vous souhaitez évaluer. Si la condition est vraie, le bloc de code à l’intérieur des accolades est exécuté ; sinon, il est ignoré.
Voici un exemple simple en utilisant la structure if en C++ :

C++

Application du if

int value = digitalRead(D4);
if (value == true)

    digitalWrite(D3, HIGH);

else

    digitalWrite(D3, LOW);

Dans cet exemple, nous lisons l’état logique de la broche puis nous utilisons une structure if pour vérifier si l’état de cette broche est HAUT. Si c’est le cas, nous allumons une LED sur en utilisant , sinon, nous l’éteignons.

💡

Il est également possible d’ajouter des clauses else if et else pour gérer plusieurs conditions.

C++

Structure if avec else if

if (condition1)

    // Code à exécuter si condition1 est vraie

else if (condition2)

    // Code à exécuter si condition2 est vraie (si condition1 est fausse)

else

// Code à exécuter si aucune des conditions précédentes n'est vraie

La structure while

La structure ‘while‘ en C++ est une instruction de boucle qui permet d’exécuter un bloc de code tant qu’une condition spécifiée est vraie (true). La condition est évaluée avant chaque itération de la boucle, et si elle est vraie, le code à l’intérieur du bloc ‘while‘ est exécuté. Voici la syntaxe de base de la structure ‘while‘ en C++ :

C++

Structure while

while (condition) 

    //while true, do ...

Voici un exemple simple d’utilisation de la structure ‘while‘ :

C++

Utilisation de la structure While

int count = 0;
while (count < 5)

    Serial.println(count);
    count++; //Counter increment

Dans cet exemple, la boucle while s’exécute tant que la valeur de ‘count‘ est inférieure à 5. À chaque itération de la boucle, la valeur de ‘count‘ est affichée sur le moniteur série à l’aide de et ensuite incrémentée de 1 avec ‘count++‘.
La boucle continuera à s’exécuter jusqu’à ce que ‘count‘ atteigne la valeur 5, moment où la condition devient fausse et la boucle s’arrête.

💡

En langage C++, le fait de mettre un ++ derrière une variable permet d’incrémenter de 1 la valeur de la variable.

Il s’agit d’une version plus compacte que :
C++

Incrément C

int myVariable = 0;

//C++ classic myVariable = myVariable + 1;

Il existe la version avec un retrait de 1 :

C++

Incrément C

int myVariable = 10;

//C++ compact myVariable–;

//C++ classic myVariable = myVariable -1;

⚠️

Il est important de faire attention à ne pas créer de boucles infinies en utilisant la structure ‘while‘. Assurez-vous toujours que la condition finira par devenir fausse pour éviter que le programme ne reste bloqué dans la boucle.

Note importante

⚠️

La plupart des instructions requièrent un point-virgule à la fin de celle-ci. En cas d'oubli, le compilateur va soulever une erreur.

Guide de référence

La documentation officielle Arduino, pour programmer différents types de microcontrôleurs est disponible à l’adresse https ://www.arduino.cc/reference/fr/

En résumé

Pour programmer notre carte nous avons besoin :


Le simulateur TinkerCAD

L'objectif de ce chapitre est de présenter un outil de simulation Arduino : TinkerCAD.

Le simulateur est disponible à l'adresse suivante : https://www.tinkercad.com

⚠️

Dans le cadre de l'atelier Electronique Embarquée 2023-2024, les microcontrôleur utilisés sont des ESP12 NodeMCU. Il n'est pas possible de les simuler sous TinkerCAD, cependant, nous utiliserons une carte Arduino à la place.
Seul le numéro de broche changera car la programmation sera identique.

Création du compte

Il vous faudra tout d'abord créer un compte en cliquant en haut à droite sur  [4]} :

Bouton d'inscription
Bouton d'inscription

Veuillez cliquer sur

Choix de création d'un compte
Choix de création d'un compte

Il vous faudra ensuite renseigner vos informations personnelles puis faire .

Création d'un compte personnel
Création d'un compte personnel

Après la saisie de votre nouveau mot de passe, cliquer sur .

Saisie de l'adresse et du mot de passe Tinkercad
Saisie de l'adresse et du mot de passe Tinkercad

Il se peut que vous receviez un mail de confirmation à l'adresse mail fournie. Suivez la procédure du mail puis lors de votre première connexion à TinkerCAD, vous tombez sur une interface avec un menu latéral :

Menu latéral
Menu latéral

Création d'un projet

Sur la page centrale, vous pourrez retrouver vos réalisations :

La liste de vos projets et réalisations
La liste de vos projets et réalisations

💡

TinkerCAD permet également de réaliser des projets de CAO 3D.

Edition d'un projet

Dans notre cas, nous souhaitons éditer une simulation Arduino, nous allons donc dans la section Your Designs puis .

On clique sur New Circuit
On clique sur New Circuit

Vous arrivez ensuite sur la zone d'édition de circuits.

La page d'édition TinkerCAD
La page d'édition TinkerCAD

Nous allons réaliser une simulation avec une carte Arduino et un transistor MOSFET.

⚠️

Bien que nous n’ayons pas encore évoqué les transistors et les moteurs, ce chapitre a surtout pour objectif de montrer les capacité du simulateur et de se familiariser avec. Il s’agit volontairement d’un circuit plus évolué mais il sera amplement évoqué par la suite.

Le menu de droite permet de faire des recherche de composants.

La recherche de composants
La recherche de composants

Il vous suffit de faire un glisser-déposer de la carte Arduino vers la zone centrale pour qu'elle soit affichée dans la zone.

Une carte Arduino
Une carte Arduino

Nous allons ajouter un MOSFET de la même manière :

Un MOSFET
Un MOSFET

Pour ajouter des câbles, il suffit de cliquer sur une des bornes des différents composants.

Ajouter un câble
Ajouter un câble

Et pour modifier la couleur des câbles, il faut cliquer sur le bouton coloré dans la barre supérieure :

Ajouter un câble
Ajouter un câble

A vous de réaliser le montage suivant :

Notre montage d'exemple
Notre montage d'exemple

Voici donc les mot-clés à saisir pour la recherche des composants :

Une fois le schéma édité, nous allons pouvoir programmer la carte Arduino.
Pour cela, il faut cliquer sur en haut à droite dans le menu :

Bouton du code
Bouton du code

Puis mettre le code en mode Texte :

Code en texte
Code en texte
Confirmation du pasage en mode Texte
Confirmation du pasage en mode Texte
Le code par défaut est le code pour faire clignoter la LED interne de la carte.
Code par défaut
Code par défaut

Veuillez le remplacer par le code suivant :

C++

Code MOSFET

#include  //For Platform.io

#define PIN 8 //GATE du transistor void setup() pinMode(PIN, OUTPUT); //Mise en sortie de la broche //Fin setup void loop() digitalWrite(PIN, HIGH); //Mise en route du transistor delay(5000); //Délai de 5s digitalWrite(PIN, LOW); //Arret du transistor delay(5000); //Délai de 5s //Fin loop

Une fois le code copié, nous allons pouvoir lancer la simulation.

Lancement de la simulation
Lancement de la simulation
Le bouton devient vert lorsque la simulation est en cours.
Simulation en cours
Simulation en cours

Pour preuve, pendant 5 secondes, le moteur affiche une vitesse en tour/min :

Simulation du moteur
Simulation du moteur

Puis pendant 5 autres secondes, la vitesse affiché est nulle. La simulation est donc fonctionnelle.


L’éditeur de code

Pour programmer les cartes NodeMCU basées sur la carte CREPP.io, nous utiliserons l'éditeur de code .

La section détaillant son installation est disponible dans cette section{vscode

Notes

  1. Nombre négatifs ou positifs

  2. Il faut évidemment la mettre en sortie pour pouvoir le faire

  3. Avec un retour à la ligne à la fin

  4. Ou bien \shortcut{Inscription

PrécédentPourquoi s’intéresser à l’électronique embarquée ?2 / 24SuivantLa carte CREPP.io