Aller au contenu

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 :

GPIO

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

Avec Mbed OS, 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
class DigitalOut {

public:
    /** Create a DigitalOut connected to the specified pin
     *
     *  @param pin DigitalOut pin to connect to
     */
    DigitalOut(PinName pin) : gpio()
    {
        // No lock needed in the constructor
        gpio_init_out(&gpio, pin);
    }

    ...

protected:
    gpio_t gpio;
};

Le type gpio_t est spécifique à 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
/*
 * Note: reg_clr might actually be same as reg_set.
 * Depends on family whether BRR is available on top of BSRR
 * if BRR does not exist, family shall define GPIO_IP_WITHOUT_BRR
 */
typedef struct {
    uint32_t mask;
    __IO uint32_t *reg_in;
    __IO uint32_t *reg_set;
    __IO uint32_t *reg_clr;
    PinName  pin;
    GPIO_TypeDef *gpio;
    uint32_t ll_pin;
} gpio_t;

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
void gpio_init_out(gpio_t *gpio, PinName pin)
{
    gpio_init_out_ex(gpio, pin, 0);
}

void gpio_init_out_ex(gpio_t *gpio, PinName pin, int value)
{
    _gpio_init_out(gpio, pin, PullNone, value);
}

static inline void _gpio_init_out(gpio_t *gpio,
                                  PinName pin,
                                  PinMode mode,
                                  int value)
{
    gpio_init(gpio, pin);
    if (pin != NC) {
        gpio_write(gpio, value);
        gpio_dir(gpio, PIN_OUTPUT);
        gpio_mode(gpio, mode);
    }
}
  • A la ligne 3, gpio_init_out appelle gpio_init_out_ex avec la valeur 0.
  • A la ligne 8, gpio_init_out_ex appelle _gpio_init_out
  • A la ligne 16, _gpio_init_out appelle gpio_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
void gpio_init(gpio_t *obj, PinName pin)
{
    obj->pin = pin;
    if (pin == (PinName)NC) {
        return;
    }

    uint32_t port_index = STM_PORT(pin);

    // Enable GPIO clock
    GPIO_TypeDef *gpio = Set_GPIO_Clock(port_index);

    ... skip non relevant code ...

    // Fill GPIO object structure for future use
    obj->mask    = gpio_set(pin);
    obj->gpio    = gpio;
    obj->ll_pin  = ll_pin_defines[STM_PIN(obj->pin)];
    obj->reg_in  = &gpio->IDR;
    obj->reg_set = &gpio->BSRR;
#ifdef GPIO_IP_WITHOUT_BRR
    obj->reg_clr = &gpio->BSRR;
#else
    obj->reg_clr = &gpio->BRR;
#endif
}

À 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 STM32CubeF4 où le programmeur doit gérer le hardware avec peu d’abstraction et un système comme Mbed OS en C++ où le travail est fait pour nous dans le code de la classe. De plus, avec une telle approche, on s’assure également 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;
Pour plus d’information concernant la classe DigitalIn, consultez la documentation de Mbed OS.

Exercice: Inverser l’état d’une LED