Interruptions
Dans le cours “Architecture des ordinateurs”, nous avons vu comment fonctionnent les interruptions sur un Cortex-M. Nous avons vu que nous pouvions réagir à un évènement généré par différentes sources (timer, UART, GPIO, etc…) avec une routine de traitement d’interruptions ou “Interrupt Service Routine (ISR)”. Avec ” LibOpenCM3”, cette routine devait avoir un nom bien précis, défini par la bibliothèque.
Classe InterruptIn
et Callback
Avec Mbed OS, les routines de traitement d’interruptions sont “attachées” à des objets sous la forme de fonctions de rappel (callback). Par exemple, pour réagir à la pression d’un bouton, on attache une routine sur le flanc montant du signal du bouton :
Le code ci-dessous montre comment on peut faire :
int_simple.cpp | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
À la ligne 15, on crée une instance de InterruptIn
pour le bouton et on y attache la routine handler
sur
l’évènement “rise” (flanc montant) à la ligne 16. Dans cet exemple on a choisi d’appeler cette routine handler
,
mais on aurait put choisir n’importe quel nom.
Pour plus de détails concernant la classe InterruptIn
, consultez la documentation de Mbed OS.
Notez que l’utilisation d’interruptions pour traiter la pression d’un bouton peut poser des problèmes si le bouton produit des rebonds. Pour éviter cela, nous pouvons faire du “polling” périodique, ou nous pouvons désactiver les interruptions pendant le traitement :
#include "mbed.h"
constexpr int kLedOn = 0;
constexpr int kLedOff = 1;
DigitalOut led(PE_0, kLedOff);
InterruptIn button(PA_0, PullDown);
void handler()
{
button.disable_irq();
led = !led;
// add delay if needed
button.enable_irq();
}
int main()
{
button.rise(&handler);
while (true) {
asm("nop");
}
}
Dans les exemples ci-dessus, la routine de traitement ne prend pas de paramètre. On souhaite agir sur la LED et
nous avons défini la LED comme un objet global. Ça serait cependant plus propre si on pouvait passer la LED
comme paramètre à la routine de traitement. Ça permettrait aussi d’utiliser la même routine pour gérer d’autres boutons
et d’autres LEDs. On pourrait passer un argument de type void*
à la routine de traitement, et c’est d’ailleurs ce que
nous faisons en C, mais en C++ on peut faire mieux. L’interface de la méthode rise
est :
void rise(Callback<void()> func);
La classe Callback
utilise les templates1
(la manière de faire des classes et des méthodes génériques en C++) pour permettre plusieurs scénarios.
Le code ci-dessous montre comment passer un argument à la routine de traitement :
#include "mbed.h"
constexpr int kLedOn = 0;
constexpr int kLedOff = 1;
void handler(DigitalOut* led)
{
*led = !*led;
}
int main()
{
DigitalOut led(PE_0, kLedOff);
button.rise(callback(handler, &led));
while (true) {
asm("nop");
}
}
Si on souhaite que le bouton connecté sur PG_1
contrôle la LED sur PE_0
et le bouton sur PG_0
contrôle la LED PE_1
, on peut facilement réutiliser la routine de traitement :
#include "mbed.h"
constexpr int kLedOn = 0;
constexpr int kLedOff = 1;
void handler(DigitalOut* led)
{
*led = !*led;
}
int main()
{
DigitalOut led1(PE_0, kLedOff);
InterruptIn button1(PG_1, PullDown);
button1.rise(callback(handler, &led1));
DigitalOut led2(PE_1, kLedOff);
InterruptIn button2(PG_0, PullDown);
button2.rise(callback(handler, &led2));
while (true) {
asm("nop");
}
}
Callback avec un objet
Le Callback
permet aussi d’utiliser la méthode d’un objet comme argument.
Supposons que la classe MyDevice
représente la LED que nous souhaitons contrôler.
Le code ci-dessous crée une instance de MyDevice
et attache la méthode handler
à l’évènement “rise” du bouton :
int_object_callback.cpp | |
---|---|
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 |
|
L’instance est créée à la ligne 21 et à la ligne 22, on utilise un callback
avec le premier argument qui est un pointeur sur l’objet (&device
) et le second argument qui est la méthode à appeler (&MyDevice::handler
).
L’intérêt de cette méthode est de permettre à la routine d’interruption de conserver un “état” en plus du code à exécuter. Il est important de comprendre l’importance de véhiculer un objet avec son état dans le mécanisme des routines d’interruptions. La section “The importance of state{target=blank}” de la documentation de _Mbed OS explique ce concept et l’illustre avec un exemple.
Pour plus de détails concernant les callbacks, consultez les liens ci-dessous:
Exercice
Exercice Interruptions/1
Implémentez la classe MyDevice
pour que la LED soit inversée à chaque deuxième pression du bouton.
Solution
class MyDevice {
public:
MyDevice(PinName led) : led_(led, kLedOff), counter_{0} {};
void handler()
{
counter_++;
if (counter_ >= 2) {
led_ = !led_;
counter_ = 0;
}
}
private:
DigitalOut led_;
int counter_;
};
Remarque importante sur le thread safety
Comme tout RTOS, l’API de Mbed OS permet de développer des applications multi-tâches basées sur plusieurs entités d’exécution (threads). Nous reviendrons en détail sur ces possibilités.
A ce stade, il est important de comprendre que Mbed OS offre des primitives de synchronisation qui permettent aux développeurs de programmer de telles applications en tenant compte des problèmes de concurrence. En utilisant de telles primitives, les développeurs doivent comprendre le niveau de thread safety qui est réalisé par chaque primitive. Ceci est particulièrement important pour le code exécuté dans un contexte ISR (donc dans une routine servant une interruption). Ce code doit être conçu avec soin afin d’éviter les problèmes de concurrence et de thread safety du système. Un développeur doit donc bien comprendre le niveau de thread safety réalisé par chaque composant de l’API utilisé. En particulier, les méthodes appelées dans une routine de service d’interruption doivent être garanties interrupt safe, ce qui signifie que ces méthodes peuvent être utilisées depuis des threads différents et depuis une routine d’interruption. Par exemple, il n’est pas possible d’utiliser un mutex dans une routine ISR (comme documenté dans Mutex).
Le concept de thread safety sera traité plus en détail dans le chapitre Modèles de programmation. A ce stade, vous pouvez trouver plus d’informations sur Thread safety.
-
Vous trouverez aussi un tutoriel sur les templates sur www.cplusplus.com ↩