GPIO
Le STM32F412ZG possède jusqu’à 114 entrées et sorties de type GPIO (General Purpose Input/Output) regroupées
sur 7 ports (A
, B
, C
, D
, E
, F
, G
et H
). Les ports A
à G
possèdent 16 entrées/sorties et
le port H
n’en possède que 2.
Le STM32F412ZG fonctionne en 3.3V et les broches des GPIOs configurées en sortie donnent soit 0V, soit 3.3V. Les broches configurées en entrées sont capables de supporter des tensions jusqu’à 5V. On dit que ces broches sont “5V tolerant” et c’est très pratique pour lire des signaux provenant de périphériques qui fonctionnent avec 5V.
La figure ci-dessous illustre les composants et le fonctionnement d’une broche GPIO tolérante à 5V :
Les broches du GPIO peuvent également être configurées pour lire des tensions analogiques, mais nous n’utilisons pas cette fonctionnalité dans ce cours. Pour plus d’information à ce sujet, référez-vous à la documentation du microcontrôleur STM32F412ZG.
Utilisation avec Mbed OS
Pour configurer et programmer les broches du GPIO avec Mbed OS, nous utilisons principalement les classes DigitalIn
et DigitalOut
. Notez que Mbed OS est programmé en C++ et offre une interface orientée objet.
Note
Dans Mbed OS, il y a aussi la classe DigitalInOut
qui permet de lire et d’écrire sur une broche et
la classe PwmOut
qui permet de contrôler une broche en PWM, mais nous ne les utiliserons pas ici.
DigitalOut
Pour utiliser une broche en sortie, il suffit de créer une instance de la classe DigitalOut
. Par exemple :
DigitalOut led(PE_0);
Contrairement à LibOpenCM3, vous n’avez pas besoin de spécifier le port de la broche séparément. L’argument PE_0
signifie que la broche est située sur le port E
et que son numéro est 0. Vous n’avez pas non plus besoin de vous soucier de la configuration du “clock” (rcc) du GPIO; le constructeur de la classe DigitalOut
le fait automatiquement.
Le code ci-dessous est celui du constructeur de DigitalOut
et on voit qu’il initialise l’attribut gpio
à la ligne 9 et qu’ensuite, il
appelle la fonction gpio_init_out
à la ligne 11:
https://github.com/ARMmbed/mbed-os/blob/mbed-os-6.9.0/drivers/include/drivers/DigitalOut.h | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
Le type gpio_t
est spécifiques à une cible donnée. Pour les microcontrôleurs de STM, il est défini comme suit :
https://github.com/ARMmbed/mbed-os/blob/mbed-os-6.9.0/targets/TARGET_STM/gpio_object.h | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
La fonction gpio_init_out
est dans la partie “HAL” (Hardware Abstraction Layer) de Mbed OS :
https://github.com/ARMmbed/mbed-os/blob/mbed-os-6.9.0/hal/source/mbed_gpio.c | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
- A la ligne 3,
gpio_init_out
appellegpio_init_out_ex
avec la valeur0
. - A la ligne 8,
gpio_init_out_ex
appelle_gpio_init_out
- A la ligne 16,
_gpio_init_out
appellegpio_init
- Ensuite
_gpio_init_out
configure le port en sortie.
La fonction gpio_init
est, elle aussi, dépendante de la cible. Pour notre microcontrôleur, elle est définie comme suit :
https://github.com/ARMmbed/mbed-os/blob/mbed-os-6.9.0/targets/TARGET_STM/gpio_api.c | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
À la ligne 11, on voit la commande pour activer le clock du GPIO.
On observe donc une différence importante entre la programmation en C avec une librairie de bas niveau de type LibOpenCM3 où on gère tout nous-même et un système comme Mbed OS en C++ où le travail est fait pour nous dans le code de la classe. On s’assure ainsi que l’état d’un objet est en tout temps cohérent.
Si vous souhaitez configurer la valeur initiale de la broche de sortie (et c’est très recommandé de le faire), vous pouvez passer un deuxième argument au constructeur. Par exemple :
DigitalOut led(PE_0, 1);
Vous pouvez ensuite manipuler la broche en sortie avec la méthode write()
. Par exemple :
led.write(0);
ou
led.write(1);
Note
Écrire un 1
sur une broche à laquelle est connecté une LED ne signifie pas forcément que la LED va s’allumer
car ca dépend de la manière dont la LED est connectée au micro-contrôleur.
La bonne pratique consiste à utiliser des constantes à la place de des valeurs numériques. Le premier
travail pratique vous donne plus de détails sur cette pratique.
Mais comme Mbed OS profite des avantages de C++ pour simplifier l’interface, vous pouvez également manipuler les sorties avec une simple assignation :
led = 0; // same as led.write(0);
ou
led = 1; // same as led.write(1);
Cette fonctionnalité est possible grâce à la surcharge d’opérateurs en C++.
Les opérateurs surchargés sont les suivants :
DigitalOut &operator= (int value);
DigitalOut &operator= (DigitalOut &rhs);
operator int();
Les deux premiers opérateurs sont des surcharges de l’opérateur d’affectation et permettent d’assigner un entier
ou la valeur d’un autre objet de type DigitalOut
à l’objet courant. Le troisième opérateur (operator int()
)
permet de consulter la valeur du DigitalOut
en tant qu’entier.
Ces opérateurs permettent de facilement inverser la valeur de la broche en utilisant l’opérateur logique “NOT” (!
) :
led = !led;
Notez que dans l’exemple ci-dessus, l’opérateur !
agit sur des entiers et non sur des objets de type DigitalOut
.
Note
En Java, l’opérateur logique !
ne peut être utilisé qu’avec un opérande de type boolean
. En C/C++, cet opérateur est également défini pour
un opérande de type entier.
Pour plus d’information concernant la classe DigitalOut
, consultez la documentation de Mbed OS.
DigitalIn
Pour utiliser une broche en entrée, nous créons une instance de la classe DigitalIn
. Par exemple :
DigitalIn button(PA_0);
Normalement, nous configurons une broche d’entrée en spécifiant une résistance de pull-up ou de pull-down :
DigitalIn button(PA_0, PullDown);
Pour lire l’état de la broche, on peut utiliser la méthode read()
:
int state = button.read();
ou alors, utiliser la surcharge de operator int()
:
int state = button;
DigitalIn
, consultez la documentation de Mbed OS.
Exercice
Exercice GPIO/1
Écrivez un programme en C++ qui inverse l’état d’une LED connectée à la broche PE_0
lorsqu’un bouton
connecté sur la broche PA_0
est appuyé. Notez que la LED est allumée lorsqu’on écrit un 0
dans la broche
et éteinte lorsqu’on écrit un 1
. On aimerait qu’elle soit éteinte au démarrage. Notez aussi que le
bouton a besoin d’une résistance de pull-down pour
fonctionner. Faites attention à ne pas utiliser de “Magic numbers” dans votre code (respect des
10 commandements)
Solution
#include "mbed.h"
constexpr int kLedOn = 0;
constexpr int kLedOff = 1;
constexpr int buttonReleased = 0;
constexpr int buttonPressed = 1;
int main()
{
DigitalOut led(PE_0, kLedOff);
DigitalIn button(PA_0, PullDown);
int prevState = button;
while (true) {
int state = button;
if (prevState == buttonReleased && state == buttonPressed) {
led = !led;
}
prevState = state;
}
}