[Partage] Mini scheduler

Bonjour

Quand on débute sur Arduino, on en vient incontournablement à vouloir programmer plusieurs fonctions en parallèle.

C'est alors l’apprentissage de la fonction millis(), et du fameux « blink without delay ».

A mon sens, le fait que tout le monde soit obligé d’en passer par là est le signe d’une légère lacune dans la trousse à outils logicielle proposée en standard avec Arduino.

Aussi je vous propose une toute petite librairie, extrêmement basique mais très facile d’utilisation.
Elle devrait permettre de couvrir un large éventail de besoins simples.

Cette librairie permet de créer des tâches qui s'exécutent à intervalles réguliers.
La fonction() associée à chaque tâche peut être modifiée, tout comme la période de rappel.

Librairie complète + exemples en pièce jointe.

Voici le header :

//Librairie pour un ordonnanceur très simple sur arduino
//Bricoleau 2015

#ifndef miniOrdonnanceur_h
#define miniOrdonnanceur_h

#include <Arduino.h>

typedef void (*fonctionVoid)();

class tache
{
  public :
    tache(fonctionVoid fonction, uint32_t periode_ms);

    void changerFonction(fonctionVoid nouvelle_fonction);
    void changerPeriode(uint32_t nouvelle_periode_ms);

    void recaler();
};

class ordonnanceur_c
{
  public :
    void lancer();
};

extern ordonnanceur_c ordonnanceur;

#endif

/****************************************************************************************
//Notes :
//
//1) Ceci est un ordonnanceur coopératif basique : les tâches ne s'interrompent pas.
//
//2) Chaque tâche doit s'exécuter rapidement : ne jamais utiliser l'instruction delay().
//   Utiliser des variables statiques pour conserver l'état des données entre les appels.
//
//3) Les tâches ne doivent pas s'appeler pas entre elles.
//   Utiliser des variables globales pour passer des données depuis une tâche vers les autres.
//
//4) Au démarrage, le premier appel à chaque tâche est effectué dès que possible.
//   Une fonction de type setup() peut être associée à la tâche pour sa première exécution,
//   puis être remplacée par une fonction de type loop().
//
//5) L'ordonnanceur essaie de rattraper tout retard, afin de maintenir une période régulière.
//   Le délai entre deux exécutions peut donc être inférieur à la période fixée.
//   Par exemple avec une période de 1000 ms : si la première exécution est à millis()=15,
//   la suivante est planifiée à millis() = 1000.
//   Si besoin, recaler() permet de forcer la prochaine execution à maintenant+période.
//   Nb : recaler() est automatique si l'ordonnanceur est saturé.
//
//6) Une période nulle désactive l'exécution de la tâche associée.
//
/****************************************************************************************/

Exemple blink :

#include <Arduino.h>
#include "miniOrdonnanceur.h"

#define pinLed1 13

tache t1(setup1, 1000);

void setup1()
{
  pinMode(pinLed1, OUTPUT);
  digitalWrite(pinLed1, HIGH);
  t1.changerFonction(loop1);
}

void loop1()
{
  static uint8_t etat = HIGH;

  if (etat == HIGH)
  {
    etat = LOW;
  }
  else
  {
    etat = HIGH;
  }
  digitalWrite(pinLed1, etat);
}

void setup()
{
  ordonnanceur.lancer();
}

void loop()
{
}

miniOrdonnanceur.zip (5.8 KB)

1 Like

10 jours et sommes toutes assez peu de réactions... snif chuis triste :smiley:

N'hésitez pas à poster ici vos remarques ou demandes de précisions.
Je me ferai un plaisir de vous aider à utiliser cette petite lib dans vos projets, car je suis convaincu qu'elle peut vraiment vous simplifier la vie par endroits.

on en est au multi taches là non? avec un temps alloué à chaque tache.
quid d'une tache qui dure 1500ms alors que la suivante est programmée pour se lancer 1000ms après le début de la 1ere?
ou alors il appartient au programmeur de veiller à ne pas tomber dans ce cas de figure?
tu dis qu'il y a un recalage, mais dans quelle mesure

en fait mes questions sont redondantes.

en tous cas merci de ce travail et de ton sens du partage.
je garde au chaud dans mon DD :slight_smile:

nota, il ne manque que la notion de taches prioritaires selon un ordre hiérarchique.
par exemple, 1 tache traite des données, une autre gere l'affichage.
l'affichage n'est pas prioritaire et pourra se voir amputé d'un cycle si le traitement des données est plus long que prévu.

non là c'est un simple ordonnanceur = le multitâche du pauvre :smiley:

L'ordonnanceur appelle une fonction, attend qu'elle se termine, puis décide quelle fonction suivante doit être appelée, et si un delay est nécessaire avant l'appel de cette fonction suivante.

J'aime bien ce nom ordonnanceur, car il est bien à l'image du fonctionnement : on appelle les diverses fonctions dans un certain ordre.

La fluidité de l'ensemble dépend du temps d'exécution de chaque fonction appelée. Plus il est court, plus c'est fluide. Ce type de solution est largement suffisant dans bon nombre de cas sur arduino.

Un vrai système multitâche exécute plusieurs fonctions "à la fois".
Evidemment, "à la fois" est sans consistance quand on regarde plus en détail, car au bout du bout on a souvent un processeur mono-core qui exécute des instructions élémentaires les unes à la suite des autres.

Grossièrement, un système multitâche est basé sur des interruptions : on exécute quelques instructions de la fonction1, puis quelques instructions de la fonction2, puis on continue là où en était dans la fonction1, etc.
Le résultat global est beaucoup plus fluide.
C'est possible sur arduino (chercher RTOS)

Pour le recalage, normalement il n'y a pas besoin de s'en préoccuper, sauf besoin particulier.

Si je prends une tâche :
t0 est l'horaire théorique d'exécution
t1 est l'horaire réel de début d'exécution (appel depuis l'ordonnanceur)
t2 est l'horaire réel de fin d'exécution (retour vers l'ordonnanceur)
t3 est l'horaire théorique d'exécution suivante

Dans le meilleur des mondes t1=t0, mais il peut y avoir un retard au déclenchement de la tâche, si à t0 une autre tâche est en cours d'exécution.

Une tâche a une certaine durée d'exécution = t2 - t1

Dans cet ordonnanceur, le délai associé à une tâche correspond aux horaires théoriques. t3 = t0 + délai.

Si une tâche a une période de 1000 ms et dure 900 ms, elle débutera 100 ms après la fin d'exécution précédente.

Ce type de fonctionnement convient bien dans la plupart des cas, comme par exemple pour effectuer un accès chaque seconde à un DS1307 pour récupérer l'heure et la stocker dans des variables globales.

Mais prenons une sonde de température DS18B20
Là il faut lui envoyer une demande de prise de mesure, puis attendre au moins 750 ms avant d'aller lire la température relevée.
Si on crée une tâche dédiée à ce DS18B20, dont le but est d'alimenter une variable globale contenant la température, il faut s'assurer du respect de ces 750 ms entre deux appels.

Le recalage permet de recalculer l'horaire théorique de prochaine exécution à partir de l'instant courant, au lieu de l'horaire théorique de dernière exécution.

ok, c'est plus clair

le système regarde en fonction de la durée d’exécution d'une tache , quand il lance son exécution pour qu'elle soit terminée juste à temps. en fait, il la lance en retard si sa durée d'exécution est plus courte que le temps qui lui est alloué.ce qui laisse le temps à la tache précédente de déborder un peu si elle en a besoin .

Le mieux est de l'experimenter, tu verras c'est facile

Le gros avantage, c'est que cela facilite les tests et la mise au point de programmes.
En terme de developpement, les taches sont bien isolées les unes des autres.

Chacune se contente de dialoguer avec un seul hardware, mettre à jour ses variables globales et consulter celles des autres.
Une fois testée et validée unitairement, chaque tâche est à l'abri de regressions liées à des modifs ailleurs dans le programme.

Une manière d'urbaniser son code de manière saine

ok, je vais tester

merci :slight_smile:

Salut Bricoleau,

je suis nouveau sur Arduino, mais pas en électronique. Et je peux te dire que ton petit code est exactement celui dont j'avais besoin et que je m’apprêtais à écrire. Tu me fais donc gagner un temps précieux car je ne travaille sur mon projet que de temps en temps.

Merci beaucoup de partager ce petit trésor. Je teste cela dés que j'ai un moment.

Merci encore :slight_smile:

Pas de problème, c'est fait pour ça.
N'hésite pas à partager ton retour d'expérience sur l'utilisation de cette librairie.

Salut Bricoleau,

je déterre ton post pour te remercier de ce partage, tu me tires une épine du pied ;D

C'est fait pour ça

Perso j'utilise plutôt la V2 disponible ici