Skip to content

Releases: Phildem/Cath

Version 1.5 enhanced by Grok not tested yet

30 Oct 08:22
c03c7d8

Choose a tag to compare

I asked to Grok IA to improve my code, here is the result.

Explication du Scheduling Cath

Le Cath (contraction de "Cathodique" ou simplement un nom inventé par l'auteur Philippe Demerliac, alias Cyrob) est un système de planification de tâches léger conçu pour les microcontrôleurs Arduino. Il permet de gérer plusieurs tâches (ou "jobs") de manière non-bloquante, en s'appuyant sur le timing système (millis()) pour exécuter du code périodiquement, sans utiliser d'interruptions complexes ou de bibliothèques lourdes comme RTOS (Real-Time Operating System). C'est idéal pour des projets embarqués simples, comme clignoter des LEDs, débouncer des boutons ou traiter des signaux logiques, tout en minimisant la consommation mémoire et CPU.

Cath est open-source (licence MIT-like) et optimisé pour des contraintes comme celles d'un Arduino Uno (ATMega328P). Il supporte jusqu'à 9 tâches par défaut (configurable), avec des périodes en millisecondes (ms). Voici une explication détaillée de son fonctionnement, étape par étape.

1. Principes de Base

  • Modèle de Tâche : Chaque tâche est une classe dérivée de la classe abstraite Cath. Vous devez implémenter deux méthodes virtuelles :
    • SetUp() : Appelée une seule fois au démarrage (équivalent d'un setup() par tâche). Utilisée pour initialiser les pins, variables, etc.
    • Loop() : Appelée périodiquement selon la période définie. C'est le cœur de votre logique (ex. : toggle une LED).
  • Timing Non-Bloquant : Cath n'utilise pas de delay() (qui bloque tout le programme). Au lieu de cela, il décompte un compteur par ms pour chaque tâche, en se basant sur millis() (le compteur système Arduino en ms depuis le démarrage).
  • Précision : Le scheduling est précis à ~1 ms, car il vérifie le changement du bit le plus bas de millis() (low byte). Pas de jitter important sur des périodes courtes.
  • Option de Compteur : Par défaut (__CathOpt_SmallCounter__), les compteurs sont en 16 bits (max ~65 secondes). Sans cette option, 32 bits (max ~49 jours).

2. Architecture Interne

La classe Cath gère tout via des membres statiques :

  • S_CathTasks[] : Un tableau fixe (taille kMaxCathTask = 9) de pointeurs vers les objets tâches.
  • S_NbTask : Nombre de tâches enregistrées (commence à 0).
  • S_LastMilli : Dernière valeur du low byte de millis() (pour détecter chaque ms).

Flux de Données :

  1. Enregistrement (Registration) : Dans le constructeur de votre classe dérivée, appelez Cath::S_Register(this, Période, Offset).

    • Période : Intervalle en ms entre deux appels de Loop() (ex. : 100 pour 10 Hz).
    • Offset : Délai initial avant le premier appel (ex. : 1 ms par défaut, pour éviter un appel immédiat).
    • Cela initialise m_CurCounter = Offset et m_LoopDelay = Période pour la tâche, et l'ajoute au tableau.
  2. Initialisation Globale (dans setup()) :

    Cath::S_SetUp();  // Boucle sur toutes les tâches et appelle SetUp() pour chacune.
    • Exécuté une seule fois au démarrage.
  3. Exécution Périodique (dans loop()) :

    Cath::S_Loop();  // Vérifie et exécute les tâches dues.
    • Logique Interne :
      • Récupère CurMilli = millis() & 0xFF (seulement les 8 bits bas, qui changent tous les ms).
      • Si CurMilli != S_LastMilli (i.e., une nouvelle ms est passée) :
        • Met à jour S_LastMilli.
        • Pour chaque tâche dans le tableau :
          • Décremente m_CurCounter--.
          • Si m_CurCounter == 0 :
            • Appelle Loop().
            • Reset m_CurCounter = m_LoopDelay (prochaine exécution dans Période ms).
      • Important : S_Loop() est non-bloquant et rapide (~quelques µs par ms). Vous pouvez y ajouter d'autres codes dans loop(), mais Cath gère le timing.

3. Exemple Visuel du Scheduling

Imaginons 3 tâches avec des périodes différentes (enregistrées à t=0) :

Temps (ms) Tâche 1 (Période=100, Offset=50) Tâche 2 (Période=500) Tâche 3 (Période=20)
0 Compteur=50 (pas d'exécution) Compteur=1 Compteur=1
1 49 0 → Loop() ; reset à 500 0 → Loop() ; reset à 20
20 30 480 0 → Loop() ; reset à 20
50 0 → Loop() ; reset à 100 450 0 → Loop() ; reset à 20
100 0 → Loop() ; reset à 100 400 ...
... ... ... ...
  • Synchronisation : Les tâches sont indépendantes, mais alignées sur le tick ms global. Pas de priorité (FIFO d'enregistrement).
  • Dynamique : Dans Loop(), vous pouvez modifier m_CurCounter pour des timings variables (ex. : pulse court dans ABlinker).

4. Avantages et Limites

  • Avantages :

    • Léger : ~200-300 octets de code, pas de heap allocation.
    • Simple : Pas de yield() ou de mutex ; tout est déterministe.
    • Flexible : Supporte des offsets pour déphaser les tâches (éviter les pics CPU).
    • Débogage : Option CATH_DEBUG pour logs série (ex. : avertissement si trop de tâches).
  • Limites :

    • Pas de Priorités : Toutes les tâches sont traitées dans l'ordre d'enregistrement.
    • Max 9 Tâches : Augmentez kMaxCathTask si besoin (mais surveillez la RAM).
    • Précision Limité : Sur des périodes >65s (avec 16 bits), overflow possible. Utilisez 32 bits pour du long terme.
    • Pas Multitâche Vraie : Sur un seul core, c'est du "cooperative multitasking" (les tâches doivent être courtes pour ne pas bloquer les autres).

5. Exemple d'Utilisation (du Code Amélioré)

Dans le code fourni :

  • Blinker : Clignote une LED toutes les X ms.
  • PushBut : Débouce un bouton à 20 ms.
  • PinAorB, etc. : Appliquent des logiques booléennes (OR, AND, XOR) sur les états des boutons.

Pour ajouter une tâche :

class MaTache : public Cath {
public:
    MaTache() { S_Register(this, 1000, 100); }  // Toutes les 1s, offset 100ms
    void SetUp() override { /* Init */ }
    void Loop() override { /* Logique */ }
};
MaTache instance;  // Instance globale

6. Conseils Pratiques

  • Test : Utilisez Serial.print(millis()) dans Loop() pour vérifier les timings.
  • Optimisations : Pour des boards plus puissants (ESP32), combinez avec FreeRTOS si besoin de vraies priorités.
  • Ressources : Vidéo de présentation (français) : YouTube. Code source sur GitHub (recherchez "Cyrob Cath Arduino").

Si vous voulez un schéma UML, un exemple plus avancé, ou des clarifications sur une partie spécifique (ex. : le bug XOR fixé), dites-le-moi ! 😊