Est-il possible de faire fonctionner plusieurs parties du programme sans faire plusieurs choses dans le même bloc de code?
Un thread attend un périphérique externe tout en faisant clignoter une LED dans un autre thread.
Est-il possible de faire fonctionner plusieurs parties du programme sans faire plusieurs choses dans le même bloc de code?
Un thread attend un périphérique externe tout en faisant clignoter une LED dans un autre thread.
Réponses:
Il n’existe pas de support multi-processus, ni multi-threading sur l’Arduino. Vous pouvez cependant faire quelque chose de proche de plusieurs threads avec certains logiciels.
Vous voulez regarder Protothreads :
Bien sûr, il est un exemple Arduino ici avec un exemple de code . Cette question SO pourrait également être utile.
ArduinoThread est un bon aussi.
la source
Les Arduino basés sur AVR ne supportent pas le threading (matériel), je ne suis pas familier avec les Arduino basés sur ARM. L’utilisation des interruptions, en particulier des interruptions chronométrées, est un moyen de contourner cette limitation. Vous pouvez programmer un minuteur pour interrompre la routine principale toutes les microsecondes, pour exécuter une autre routine spécifique.
http://arduino.cc/en/Reference/Interrupts
la source
Il est possible de faire du multi-thread côté logiciel sur Uno. Le threading de niveau matériel n'est pas pris en charge.
Pour réaliser le multithreading, il faudra implémenter un planificateur de base et gérer un processus ou une liste de tâches afin de suivre les différentes tâches à exécuter.
La structure d'un ordonnanceur non-préemptif très simple serait la suivante:
Ici,
tasklist
peut être un tableau de pointeurs de fonction.Avec chaque fonction du formulaire:
Chaque fonction peut effectuer une tâche distincte, par exemple
function1
effectuer des manipulations de DEL etfunction2
effectuer des calculs flottants. Il sera de la responsabilité de chaque tâche (fonction) de respecter le temps qui lui est imparti.Espérons que cela devrait suffire à vous aider à démarrer.
la source
Selon la description de vos besoins:
Il semble que vous pourriez utiliser une interruption Arduino pour le premier "fil" (je préférerais l'appeler "tâche" en fait).
Les interruptions Arduino peuvent appeler une fonction (votre code) en fonction d'un événement externe (niveau de tension ou changement de niveau sur une broche d'entrée numérique), ce qui déclenchera immédiatement votre fonction.
Cependant, un point important à garder à l'esprit avec les interruptions est que la fonction appelée doit être aussi rapide que possible (en règle générale, il ne devrait exister aucun
delay()
appel ni aucune autre API susceptible de dépendre de cette dernièredelay()
).Si vous avez une longue tâche à activer lors du déclenchement d'un événement externe, vous pouvez éventuellement utiliser un planificateur coopératif et lui ajouter une nouvelle tâche à partir de votre fonction d'interruption.
Un deuxième point important concernant les interruptions est que leur nombre est limité (par exemple, seulement 2 sur l'ONU). Donc, si vous commencez à avoir plus d'événements externes, vous devrez implémenter une sorte de multiplexage de toutes les entrées en une seule et laisser votre fonction d'interruption déterminer quel invas multiplexé était le déclencheur réel.
la source
Une solution simple consiste à utiliser un planificateur . Il y a plusieurs implémentations. Ceci décrit brièvement celui qui est disponible pour les cartes basées sur AVR et SAM. Fondamentalement, un seul appel lancera une tâche. "croquis dans un croquis".
Scheduler.start () ajoutera une nouvelle tâche qui exécutera la tâche taskSetup une fois, puis appellera plusieurs fois taskLoop au moment même où l'esquisse Arduino fonctionne. La tâche a sa propre pile. La taille de la pile est un paramètre facultatif. La taille de pile par défaut est de 128 octets.
Pour permettre le changement de contexte, les tâches doivent appeler rendement () ou délai () . Il existe également une macro de support pour l'attente d'une condition.
La macro est un sucre syntaxique pour:
Await peut également être utilisé pour synchroniser des tâches. Ci-dessous un exemple d'extrait de code:
Pour plus de détails, voir les exemples . Il y a des exemples de plusieurs voyants clignotant sur le bouton anti-rebond et d'un shell simple avec une lecture de ligne de commande non bloquante. Les modèles et les espaces de noms peuvent être utilisés pour aider à structurer et réduire le code source. L' esquisse ci-dessous montre comment utiliser les fonctions de modèle pour le clignotement multiple. C'est suffisant avec 64 octets pour la pile.
Il existe également un repère pour donner une idée de la performance, par exemple le temps nécessaire pour démarrer la tâche, le changement de contexte, etc.
Enfin, il existe quelques classes de support pour la synchronisation et la communication au niveau des tâches; File d'attente et sémaphore .
la source
D'une incantation précédente de ce forum, la question / réponse suivante a été déplacée vers génie électrique. Il contient un exemple de code arduino permettant de faire clignoter une LED à l'aide d'une interruption de minuterie lors de l'utilisation de la boucle principale pour effectuer des entrées / sorties en série.
https://electronics.stackexchange.com/questions/67089/how-can-i-control-things-without-using-delay/67091#67091
Republier:
Les interruptions sont un moyen courant de faire avancer les choses pendant que quelque chose d'autre se passe. Dans l'exemple ci-dessous, le voyant clignote sans être utilisé
delay()
. À chaqueTimer1
déclenchement, la routine de service d'interruption (ISR)isrBlinker()
est appelée. Il allume / éteint la LED.Pour indiquer que d’autres événements peuvent se produire simultanément,
loop()
écrit foo / bar à plusieurs reprises sur le port série, indépendamment du clignotement de la LED.Ceci est une démo très simple. Les ISR peuvent être beaucoup plus complexes et peuvent être déclenchés par des minuteries et des événements externes (broches). La plupart des bibliothèques communes sont implémentées à l’aide d’ISR.
la source
Je suis également venu à ce sujet lors de la mise en œuvre d'un écran LED matriciel.
En un mot, vous pouvez créer un planificateur d’interrogation en utilisant la fonction millis () et une interruption de minuterie dans Arduino.
Je suggère les articles suivants de Bill Earl:
https://learn.adafruit.com/multi-tasking-the-arduino-part-1/overview
https://learn.adafruit.com/multi-tasking-the-arduino-part-2/overview
https://learn.adafruit.com/multi-tasking-the-arduino-part-3/overview
la source
Vous pouvez également essayer ma bibliothèque ThreadHandler
https://bitbucket.org/adamb3_14/threadhandler/src/master/
Il utilise un planificateur d'interruption pour permettre la commutation de contexte sans relayer sur return () ou delay ().
J'ai créé la bibliothèque parce que j'avais besoin de trois threads et que j'avais besoin de deux d'entre eux pour fonctionner à une heure précise, peu importe ce que faisaient les autres. Le premier thread a géré la communication série. La seconde consistait à utiliser un filtre de Kalman utilisant la multiplication à matrice flottante avec la bibliothèque Eigen. Et le troisième était un fil de boucle de contrôle de courant rapide qui devait pouvoir interrompre les calculs de la matrice.
Comment ça fonctionne
Chaque thread cyclique a une priorité et une période. Si un thread ayant une priorité plus élevée que le thread en cours d'exécution atteint sa prochaine heure d'exécution, le planificateur mettra en pause le thread en cours et basculera vers le processus de priorité supérieure. Une fois que le thread hautement prioritaire a terminé son exécution, le planificateur revient au thread précédent.
Règles de planification
Le schéma de planification de la bibliothèque ThreadHandler est le suivant:
Comment utiliser
Les threads peuvent être créés via l'héritage c ++
Ou via createThread et une fonction lambda
Les objets de thread se connectent automatiquement au ThreadHandler lors de leur création.
Pour lancer l'exécution des objets de thread créés, appelez:
la source
Et voici encore une autre bibliothèque multitâche coopérative à microprocesseur - PQRST: file d'attente prioritaire pour l'exécution de tâches simples.
Dans ce modèle, un thread est implémenté en tant que sous-classe d'un
Task
, qui est planifié pour un temps futur (et éventuellement reprogrammé à intervalles réguliers, si, comme cela est courant, il estLoopTask
remplacé par des sous-classes ). Larun()
méthode de l'objet est appelée lorsque la tâche devient due. Larun()
méthode effectue un certain travail, puis retourne (il s’agit du bit coopératif); il va généralement maintenir une sorte de machine à états pour gérer ses actions sur des invocations successives (un exemple trivial est lalight_on_p_
variable dans l'exemple ci-dessous). Cela nécessite de repenser légèrement la façon dont vous organisez votre code, mais il s'est avéré très flexible et robuste pour une utilisation assez intensive.Les unités de temps sont agnostiques, il est donc aussi agréable de fonctionner en unités de
millis()
asmicros()
, ou en tout autre tick qui convient.Voici le programme 'blink' implémenté à l'aide de cette bibliothèque. Cela montre qu'une seule tâche est en cours d'exécution: d'autres tâches seraient généralement créées et démarrées à l'intérieur
setup()
.la source
run()
méthode est appelée, elle n’est pas interrompue et a donc la responsabilité de terminer assez rapidement. Cependant, en règle générale, il fera son travail, puis se planifiera lui-même (éventuellement automatiquement, dans le cas d'une sous-classe deLoopTask
) pour une date ultérieure. Un schéma courant consiste pour la tâche à conserver une machine à états interne (un exemple trivial est l'light_on_p_
état ci-dessus) afin qu'elle se comporte de manière appropriée à la prochaine échéance.run()
. Ceci est en contraste avec les threads coopératifs, qui peuvent donner le processeur par exemple en appelantyield()
oudelay()
. Ou des threads préemptifs, qui peuvent être planifiés à tout moment. Je pense que la distinction est importante, car j'ai vu que beaucoup de gens qui viennent ici à la recherche de threads le font parce qu'ils préfèrent écrire du code bloquant plutôt que des machines à états. Bloquer les vrais threads qui génèrent le processeur est correct. Le blocage des tâches RtC ne l’est pas.