[RÉSOLUT] multitâche coopératif avec fonctions bloquantes

Bonjour,
Mon projet consiste en la fabrication et la programmation d'un téléphone portable.
Mon code fonctionne très bien mais j'ai un problème dans l'architecture de mon code:
L'actualisation arrière plan et répartitions des taches.
actuellement je suis obligé de placer des fonctions d'actualisations dans toutes les boucles de mon code pour que par exemple l'heure s'actualise ou la réception d'un appel.
J’aimerais installer une bibliothèque qui me permette d’exécuter les actualisations d'un coté et les applications (contiennent pas d'exemple des parties qui ne peuvent pas être en non bloquant) d'un autre sans que les deux ensembles interagissent.
Je me suis renseigné mais à vrai dire je n'ai pas tout compris. Pouriez vous m'aiguiller dans ce "multitâche coopératif" ?

Je ne met pas mon code mais il est sur github

Il suffit d'avoir les applications principales écrites normalement, bloquantes ou pas et de mettre ce qui est mis à jour dans une interruption. C'est déjà ce qui est fait dans "blink with delay", l'application principale tourne et est bloquante et la remise à l'heure (qui permet de faire fonctionner delay d'ailleurs) et faite sous interruption.

Autre solution 1, vous gardez vos fonctions blocantes, et vous prenez un processeur costaud et FreeRTOS, qui vous permet de faire de l'ordonnancement préemptif. Attention à la taille de mémoire programme, 1MO est un minimum.

Autre solution 2, vous réécrivez toutes vos fonctions pour qu'elles soient non blocantes, ce qui revient souvent à transformer chaque tache en une machine à état en veillant au temps de passage dans chaque état de chaque tache, et pour l'ordonnancement vous prenez une bibliothèque avec ordonnancement non préemptif, comme celle de Bricoleau.

Bonjour

Je t'invite à installer cette bibliothèque et à téléverser les exemples fournis pour comprendre comment cela fonctionne.

Voir ici un exemple de transformation de fonction en non bloquant.

Mais avec easyRun, il faut réécrire tout le code de la première partie en non bloquant. Si le code fonctionne, pourquoi tout refaire?

J’ai besoin impérativement que mes apps soient bloquantes (peuvent contenir des délay) et que EN MÊME TEMPS (les une apres les autres) les actualisations soient exécutés. je suis sur mega2560 pro donc la mémoire n’est pas un problème. J’aimerais une bibliothèque qui puisse exécuter deux endroits différents de la mémoire programme en séquence

@vileroi il marche mais c’est pour la facilité d’évolution du code... la c’est un peu “débrouillard”

J'ai besoin impérativement que mes apps soient bloquantes

Ce n'est pas tout à fait exact, on peut toujours faire la même chose en bloquant et en non bloquant. Pour un code simple, c'est déjà pas trivial, dans un code un peu complexe, c'est de la haute voltige.
Je ne pense pas qu'il y ait de bibliothèque pour l'instant qui permette de gérer ce dont tu as besoin. Ce qu'il faut faire aujourd'hui, c'est mettre en place une fonction d'interruption (on peut t'aider), puis ce sera à toi de définir le code que tu veux exécuter dedans.
Pour pouvoir faire cela, il faut que tu dises quand il faut déclencher la fonction d'interruption. Ce peut être:

  • sur l'appui d'un bouton
  • toutes les X ms
  • régulièrement mais avec un temps variable....
    Si il y plusieurs réponses, il faut peut être mettre plusieurs fonctions d'interruptions.

gabrielrochet:
je suis sur mega2560 pro donc la mémoire n’est pas un problème. J’aimerais une bibliothèque qui puisse exécuter deux endroits différents de la mémoire programme en séquence

FreeRTOS. C'est plus qu'une "bibliothèque", c'est un système d'exploitation temps réel. Vu l'ambition du projet, cela ne me parait pas démesuré. Il fait du time-slicing et on peut prioriser les tâches. Avec ça, tout est possible.

gabrielrochet:
J’ai besoin impérativement que mes apps soient bloquantes (peuvent contenir des délay) et que EN MÊME TEMPS (les une apres les autres) les actualisations soient exécutés.

C'est ton choix de codage ce n'est pas une fatalité. On peut très bien avoir des délais dans un code et rendre celui-ci non bloquant malgré tout.

Bonjour

Effectivement, je demande à voir le "besoin impérativement", car je pense que dans la plupart des cas on obtient un résultat plus fiable en passant tout en non bloquant, plutôt que d'essayer de maîtriser les interruptions qui sont semées d'embûches lorsqu'on veut leur faire faire trop de choses.

Là nous sommes en train de phosphorer et cramer du gaz pour rien car il nous manque des informations sur la nature du besoin exact.

J'ai regardé le lien github :

  • bon point : le code a l'air bien structuré dans des fichiers bien identifiés
  • points "perfectibles" : il n'y a aucun fichier ino, je ne vois pas la logique d'ensemble du programme, je ne retrouve pas le "je suis obligé de placer des fonctions d'actualisations dans toutes les boucles de mon code". Et accessoirement les fonctions sont entièrement définies dans des fichiers header .h, alors que l'usage normal est d'y définir seulement le prototype d'appel, le code étant déporté dans des fichiers .cpp mais ça on pourra le restructurer correctement plus tard.

STP publie intégralement un code qui compile, explique tes contraintes, et nous pourrons te conseiller au mieux.

Peut-être que vous pourriez consulter ce post jm_Scheduler dont je suis l'auteur. Il s'agit d'un multi-tâche coopératif avec lequel j'ai réalisé plusieurs applications professionnelles.

@vileroi:
J'aimerais en savoir plus sur les interruptions dans un code mais j'ai besoin de verrifier l'etat du bouton home, la liaison serie avec le sim800l pour verrier les messages les appels, et d'autres: je suis obligé de faire:

bool actu()
{

	if(chrono_actu_hour + delay_actu_hour < millis())
	{
		tft.setTextSize(3);
		tft.setFont(&FreeSans12pt7b);
		Time t = rtc.getTime();
		tft.setColor(112, 148, 255);
  		tft.fillRoundRect(0, 0, 320, 25);
  		tft.setColor(0,255,157);
  		tft.fillRoundRect(125, 3, 185, 22);
  		tft.setColor(0,0,0);
  		tft.setCursor(125, 20);
  		tft.print(t.hour);
  		tft.print(":");
  		tft.print(t.min);
  		tft.setCursor(190, 20);
  		tft.print(short(rtc.getTemp()));
  		tft.write("^C");
  		chrono_actu_hour = millis();
  	}

  	if(chrono_off + delay_off_auto < millis())
	{
		light(false);
		while(!but_home()){}
		light(true);
		chrono_off = millis();
		return 1;
  	}
  	if(x < 1000) chrono_off = millis();
  	return 0;
}

void application(){
        while(1){ ......... actu(); ......}
}

tu vois ce que je veux dire?

@JiPe38:
Je pense que ton idée est un peu démesurée, mes capacités en code ne vont pas jusque là.

@fdufnews: Le delay est une image, c'etait pour dire que les applications sont codes dans des while(1){if(....)return; }
ils sont juste a éviter

@bricoleau: J'ai mis le main.ino excuse moi pour cette erreur de ma part. Regardes le apps/pong.h , il y a des fonctions de verrification que l'ecran set touché et ces fonctions appellent les actu();

jmparatte:
Peut-être que vous pourriez consulter ce post jm_Scheduler dont je suis l'auteur. Il s'agit d'un multi-tâche coopératif avec lequel j'ai réalisé plusieurs applications professionnelles.

Cela me semble être un ordonnanceur non préemptif, comme la librairie Bricoleau. Ce qui me fait dire ça est que la doc affirme que les coroutines sont utilisées sur le stack principal. Je me trompe ? Si c'est le cas, ne convient pas à une programmation avec des segments (taches, coroutines...) blocantes.

JiPe38:
Cela me semble être un ordonnanceur non préemptif, comme la librairie Bricoleau. Ce qui me fait dire ça est que la doc affirme que les coroutines sont utilisées sur le stack principal. Je me trompe ? Si c'est le cas, ne convient pas à une programmation avec des segments (taches, coroutines...) blocantes.

Exact, cela ne convient pas à des processus bloquants. En revanche, on peut découper le processus en petites taches, sauter de petites taches en petites taches, avec des temps différents (méthodes rearm()). Egalement réveiller une tache à l'aide d'une interruption (pas nécessairement une interruption, n'importe quel processus peut réveiller un autre processus) et ainsi implémenter des timeouts (méthode wakeup()).
Mais j'en convient, cela demande un temps d'étude conséquent si on n'a pas l'habitude de la programmation événementielle car il faut se retourner l'esprit... Cela m'a pris beaucoup de temps.
jm_Scheduler autorise l'usage de delay() mais uniquement dans la boucle principale. Et avec l'avantage de gérer les événements pendant delay(), par le fait que delay() appelle la fonction "weak" yield(), récupérée par jm_Scheduler pour faire son job, c'est-à-dire "dispatcher" les événements "agendés" ou "réveillés".

C'est intéressant mais ça mériterait une doc plus synthétique, genre chronogramme de taches, etc. Quand vous parlez de yield ou autres fonctions de votre bibliothèque, ou d'autres notions avec un vocabulaire qui, semble t-il vous est propre, on a du mal à suivre. Et pourtant j'utilise l'ordonnancement en round-robin, avec interruptions et taches périodiques depuis longtemps.

"coopératif" et "bloquante" ce n'est pas possible, y'a pas de coopération dans ce cas là....

Le multitâche coopératif est une forme simple de multitâche où chaque tâche doit explicitement permettre aux autres tâches de s’exécuter

Ce que vous voulez c'est du Multitâche préemptif. (mais assez léger car je suppose que le Système quand il prend la main ne fait pas grand chose de long et qu'il n'y a pas 10 apps actives en même temps ?)

J'aimerais en savoir plus sur les interruptions dans un code

Voici un exemple de code:

void setup()
{
  // Initialisation générales
  Serial.begin(115200);
  pinMode(LED_BUILTIN, OUTPUT);

  // Mise en place de la fonction ISR(TIMER1_COMPA_vect)
  // Appel toutes les 200ms
  TCCR1A = 0b00000000; // Pas de comparaisons, mode CTC
  TCCR1B = 0b00001101; // Mode CTC OCR (4), prescaler à /1024 par défaut, 64µs
  OCR1A = 3124; // 3125*64µs = 200ms
  TIMSK1 = 0b00000010; // Interruptions du timer 1 autorisées
}


byte compteur;
void loop()
{
  // Incrémentation du compteur toutes les 1,234s
  Serial.println(++compteur);
  delay(1234);
}


ISR(TIMER1_COMPA_vect) // Appelée toutes les 200ms
{
  // Changer l'état de la led
  digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) == HIGH ? LOW : HIGH);
}

Le programme principal se trouve dans loop(), il affiche un nombre toutes les 1,234s; Ce code est bloquant pendand 1,2s
Indépendamment de ça, est mis en place une fonction d'interruption qui est appelée toutes les 200ms qui fait clignoter la led. Elle clignote donc plusieurs fois pendant le delay()
Pour mettre en place un interruption, cela va dépendre de la source. Pour un timer, il faut les 4 instructions barbares, que l'on peut t'expliquer. Je suppose qu'il existe une bibliothèque qui fait cela, mais pour 4 lignes, je le fais tout seul.
Pour une pin qui change d'état, il y a une instruction attachInterrupt qui s'occupe d'une grande partie.
Il est possible d'avoir une fonction d'interruption régulière qui vient remettre à l'heure, voir si un caractère n'est pas arrivé....
Pour tester un bouton, c'est plus simple de faire une fonction exprès qui sera appelée à chaque changement.

Pour mettre en place un interruption, cela va dépendre de la source. Pour un timer, il faut les 4 instructions barbares, que l'on peut t'expliquer. Je suppose qu'il existe une bibliothèque qui fait cela, mais pour 4 lignes, je le fais tout seul.

en effet - la bibliothèque TimerOne permet de faire une abstraction sur le timer:

#include <TimerOne.h>
byte compteur;

void blinkLED() {
  digitalWrite(LED_BUILTIN, digitalRead(LED_BUILTIN) == HIGH ? LOW : HIGH);
}

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  Serial.begin(115200);
  Timer1.initialize(200000UL);        // Toutes les 200ms (periode en µs)
  Timer1.attachInterrupt(blinkLED);   // on appelle le callback
  Timer1.start();                     // existe aussi stop(), restart(), resume()
}

void loop(void) {
  Serial.println(++compteur);
  delay(1234);
}

vileroi, j'adore ta solution est celle qu'il me faut, mais pourrais tu m'expliquer plus en détails la syntaxe,
si dans le loop j'ai un delay(100000000); et que j'actualise avec ta fonction tout les 200ms, ca va marcher??
si oui c'est possible lorsque que c'est une fonction qui est exécutée juste avant?