table des matières
Mbed-OS possède un noyau mutitâches temps réel. C’est aec la qualité de son API C++ la raison qui nous a fait choisir cette solution pour notre enseignement.
Sans revenir sur le fonctionnement des threads, je vais détailler quelques implémentations multitâches des composants principaux de l’API.
Les threads
Instanciation d’un thread
- Constructeur de Thread
rtos::Thread::Thread(osPriority priority = osPriorityNormal,
uint32_t stack_size = OS_STACK_SIZE,
unsigned char* stack_mem = NULL,
const char* name = NULL
)
- Dans un programme
Thread thread;
Communication et synchronisation de thread
Par défaut, un tread principal est créé lors de l’exécution du programme.
Le programme ci-dessous montre la création d’un second thread et la synchronisation des deux threads sur un signal reçu.(STOP_FLAG
)
Thread thread2;
#define STOP_FLAG 1
void blink(DigitalOut *led) { //blink tant que FLAG STOP_FLAG pas reçu
while (!ThisThread::flags_wait_any_for(STOP_FLAG, 1000)) {
*led = !*led;
}
}
int main() { // thread1 start
thread.start(callback(blink, &led1)); // thread2 start
ThisThread::sleep_for(5000); // thread1 attente 5s
thread2.flags_set(STOP_FLAG); //thread2 envoi d’un signal (FLAG)
thread2.join(); //Synchronisation (et mort)
}
EventQueue
Une EventQueue est une file d’attente pour planifier des évènements.
- L’exemple ci-dessous montre comment on peut s’en servir pour appeler des évènements périodiquements.
int main()
{
// creates a queue with the default size
EventQueue queue;
// events are simple callbacks
queue.call(printf, "called immediately\n");
queue.call_in(2000ms, printf, "called in 2 seconds\n");
queue.call_every(1000ms, printf, "called every 1 seconds\n");
// events are executed by the dispatch_forever method
queue.dispatch_forever();
}
On peut associer une EventQueue à un objet lié lui-même à un thread pour un fonctionnement multitâche de l’application.
-
Programme principal
Thread thread_mqtt; //Creation du thread static EventQueue event_queue_mqtt(16 * EVENTS_EVENT_SIZE); //Creation de la l'EventQueue AdafruitMqtt myfruit(&net, event_queue_mqtt); //Instanciation d'un objet et association de l'EventQueue thread_mqtt.start(callback(&myfruit, &AdafruitMqtt::start)); //Demarrage du thread dédié, association avec l'objet et appel de la méthode start de celui-ci
-
Classe AdafruitMqtt
void AdafruitMqtt::start() { //Appel périodique à la méthode publish et dispatch _id_publish = _event_queue.call_every(10s, this, &AdafruitMqtt::publish); _event_queue.dispatch_forever(); }
Synchronisation des données
La synchronisation de données est un mécanisme qui vise à conserver la cohérence entre différentes données dans un environnement multitâche.
Les threads obtiennent le processeur selon le scheduler. Il est impossible de prévoir l’ordre d’exécution de ceux-ci. Il faut donc mettre en place des mécanismes de synchronisation ou de protection des zones de mémoires partagées.
Mutex
Les mutex (mutual exclusion) permettent de protéger une section critique du code.
- Exemple de mise en place d’un mutex
Thread thread1;
Thread thread2;
Mutex battery_mutex;
void update_sensor(const char* name) {
battery_mutex.lock();
//Section Critique
battery_mutex.unlock();
}
void test_thread(void const *args) {
while (true) {
update_sensor((const char*)args); wait(1);
update_sensor((const char*)args); wait(1);
}
}
int main() {
thread1.start(callback(test_thread, "Thread1"));
thread2.start(callback(test_thread, "Thread2"));
while (1) {}
}
Modèle producteur/consommateur
En multitâche, on peut synchroniser les taches en attribuant des rôles aux threads Dans le modèle Producteur consommateur :
- Le thread producteur ajoute un message dans la file et avertit de la présence du message
- Le thread consommateur est averti de la présence du message et le consomme
Avec Mbed-os, on utilise le MemoryPool. Dès qu’un message est ajouté dans le pool par le thread producteur, le thread consommateur est averti et peut consommer la donnée.
- Exemple de production et de consommation de données
typedef struct {
//DATA
} message_t;
MemoryPool<message_t, 16> mpool;
Queue<message_t, 16> queue;
void send_thread (void) {
while (true) {
//PRODUCER
queue.put(message);
wait(1);
}
}
int main (void) {
thread_DATA.start(callback(send_thread));
while (true) {
osEvent evt = queue.get();
if (evt.status == osEventMessage) {
//CONSUMMER
}
}
}