jm_Scheduler

> Home > Français > Arduino > Librairies > jm_Scheduler

Révisions

Librairie « Multitâche coopératif » pour Arduino

Cette librairie est le fruit d'un questionnement lors d'un concours de robotique: Comment gérer et contrôler des processus parallèles et indépendants avec Arduino?

Sans librairie additionnelle, la réponse est assez complexe et nécessite une programmation délicate et coûteuse en effort de programmation: Chaque processus doit effectuer sa propre gestion cyclique dans la fonction loop().

La réponse professionnelle serait sans doute l'usage de RTOS avec le modèle multitâche préemptif. Cela induit une complexité de programmation avec les librairies non-réentrantes d'Arduino ou avec la taille de stack très petite des Arduino Uno, Leonardo, etc....

Au final, le mode multitâche coopératif est bien plus approprié, à la manière de nombreux logiciels de programmation d'applications modernes comme NodeJS, Python, Lua... En plus d'offrir une gestion multitâche coopératif presque native, ces logiciels offrent également une gestion d'événements. Générer des événements, on peut le faire également avec la librairie jm_Scheduler.

Concepts

jm_Scheduler gère des coroutines activées et réactivées à la manière de la fonction JavaScript setInterval(), avec des fonctionnalités supplémentaires:

La librairie officielle Scheduler ne fonctionne que pour les processeurs SAM et SAMD (Arduino Due et Zero) et planifie les tâches avec le modèle préemptif. jm_Scheduler planifie les tâches avec le modèle coopératif POUR TOUS LES MODÈLES D'ARDUINO, avec plus de fonctionnalités et avec une gestion centralisée du temps des processus.

La fonction yield() qui suspend une tâche est implémentée.

La fonction startLoop() de la librairie officielle Arduino n'est pas implémentée car elle n'est pas appropriée au principe du multitâche coopératif.

jm_Scheduler exécute les coroutines séquentiellement sur le stack du processeur.

Les règles de yield et resume sont les suivantes:

Exemple basique


// This example schedules a coroutine every second

#include <jm_Scheduler.h>

jm_Scheduler scheduler;

void coroutine()
{
	Serial.print('.');
}

void setup(void)
{
	Serial.begin(9600);

	scheduler.start(coroutine, TIMESTAMP_1SEC); // Start coroutine() immediately
	// and repeat it every second
}

void loop(void)
{
	yield();
}

Plan d'étude

Timestamp

La déclaration Arduino de la fonction micros() est la suivante:


unsigned long micros();

Regardez dans les références Arduino micros() pour les détails de la fonction.

timestamp est un compteur 32-bit en [us] et redémarre à 0 environ toutes les 70 minutes (précisément 1h+11m+34s+967ms+296us).

Déclaration et constantes Timestamp


typedef uint32_t timestamp_t;

#define timestamp_read() ((timestamp_t)micros())

#define TIMESTAMP_DEAD (0x01CA0000) // coroutine dead time [30s + 15ms + 488us]
#define TIMESTAMP_TMAX (0xFE35FFFF) // [1h + 11m + 4s + 951ms + 808us - 1]

#define TIMESTAMP_1US	(1UL)					// [1us]
#define TIMESTAMP_1MS	(1000*TIMESTAMP_1US)	// [1ms]
#define TIMESTAMP_1SEC	(1000*TIMESTAMP_1MS)	// [1s]
#define TIMESTAMP_1MIN	(60*TIMESTAMP_1SEC)		// [1 minute]
#define TIMESTAMP_1HOUR	(60*TIMESTAMP_1MIN)		// [1 hour]

Méthodes jm_Scheduler


// start coroutine immediately
void start(voidfuncptr_t func);

// start coroutine immediately and repeat it at fixed interval
void start(voidfuncptr_t func, timestamp_t ival);

// start coroutine on time and repeat it at fixed interval
void start(voidfuncptr_t func, timestamp_t time, timestamp_t ival);

// stop coroutine, current or scheduled, remove it from chain
void stop();

// rearm current coroutine and set or reset interval
void rearm(timestamp_t ival);

// rearm current coroutine, change coroutine function and set or reset interval
void rearm(voidfuncptr_t func, timestamp_t ival);

// rearm coroutine asynchronously and set interval
void rearm_async(timestamp_t ival);

// rearm coroutine asynchronously, change coroutine function and set interval
void rearm_async(voidfuncptr_t func, timestamp_t ival);

// wakeup a scheduled coroutine (maybe repeated)
void wakeup();

// wakeup a scheduled coroutine (maybe repeated but only 1st wakeup_time is recorded)
void wakeup(uint32_t wakeup_time);

// read wakeup count, reset it and remove coroutine from wakeup chain
int wakeup_read();

Arduino loop() et yield()

Le contrôle du scheduler implique au minimum la mise en place de la fonction yield() dans la fonction Arduino loop().

Déclaration de la fonction yield():


static void yield();

yield() est la pierre angulaire du scheduler. Elle doit être invoquée aussi souvent que possible. Prenez note que yield() est une méthode statique de la librairie jm_Scheduler. Elle remplace la fonction weak native d'Arduino.

Le bon endroit pour invoquer yield() est dans la fonction Arduino loop().

Exemple:


void setup()
{
	// ...
}

void loop()
{
	yield();

	// ...
}

yield() peut aussi être invoqué dans la fonction Arduino setup().

Exemple:


void setup(void)
{
	// here, some jm_Scheduler variables initialized...

	Serial.begin(9600);
	while (!Serial)
	{
		// wait for USB Serial ready...

		yield(); // split loop while()...
	}


	yield(); // split long setup()...

	// continue setup()...
}

Remarques:

Bonnes pratiques de scheduling

Pour garantir la bonne exécution de toutes les tâches coopératives, le temps d'exécution des coroutines doit être le plus court possible. Cette pratique est identique à celle d'une routine d'interruption.

Utilisez la fonction Arduino delay() pour mettre en pause une coroutine ou la fonction yield() pour fractionner l'exécution d'une coroutine.

Une meilleure pratique est de fractionner le code d'une coroutine en plusieurs coroutines. Les méthodes jm_Scheduler rearm() et rearm_async() permettent de sérialiser l'exécution de plusieurs coroutines.

Changer le Timestamp

Ci-dessous sont listés quelques hacks qui peuvent être implémentés en modifiant le fichier jm_Scheduler.h:

Document mis à jour le 09.03.2021 18:10:56 UTC+01:00