Fabrication industrielle
Internet des objets industriel | Matériaux industriels | Entretien et réparation d'équipement | Programmation industrielle |
home  MfgRobots >> Fabrication industrielle >  >> Manufacturing Technology >> Processus de fabrication

Comment multithread un Arduino (Tutoriel Protothreading)

Composants et fournitures

Arduino UNO
× 1
Écran à cristaux liquides (LCD) Sunfounder Blue 16x2
× 1
Planche à pain (générique)
et les fils bien sûr.
× 1
Potentiomètre rotatif (générique)
Pas sûr de l'indice de résistance, probablement 1Kohm ferait l'affaire.
× 1

À propos de ce projet

Cette vidéo décrit quelque chose que vous avez peut-être voulu faire au cours de votre carrière de prototypage en herbe, en cajolant un arduino monocœur à faire 3 choses à la fois. Dans ce cas, nous sommes :

  • Pulser le rétroéclairage à un rythme constant sans interruption
  • Incrémenter un entier toutes les secondes et l'écrire sur l'écran sans interruption
  • Tourner quelques messages toutes les quelques secondes et les écrire à l'écran sans interruption

Vous avez vu le titre !

Le protothreading est un moyen d'effectuer ce qui serait normalement une opération multitâche (faire deux ou plusieurs choses à la fois ou à des intervalles différents) sur un Arduino . En d'autres termes, c'est "multithread" ! Mais attendez Sparky, l'Arduino est une puce monocœur avec un code procédural, donc un véritable multithreading est impossible. Pourquoi pourtant ? En quoi le protothreading est-il différent ?

Multithreading "réel" vs Protothreading

Pour comprendre le protothreading correctement, nous devons d'abord comprendre pourquoi ce n'est PAS vraiment multithread.

Vous vous souvenez de l'époque où Intel nous vendait ce nouveau truc « Hyperthreading » sur les processeurs Pentium ? Non? Vous n'êtes pas encore né ? Eh bien, c'est l'heure d'une leçon d'histoire, fiston ! L'hyperthreading est une technologie qu'Intel utilise pour faire en sorte qu'un seul cœur sur un processeur « agisse » comme s'il s'agissait de deux cœurs, ou que deux cœurs « agissent » comme s'il s'agissait de 4 cœurs, etc. Mais pourquoi, et en quoi est-ce pertinent pour Arduino ? La réponse est cycles.

Les microcontrôleurs et les processeurs fonctionnent tous deux par "cycles". À quelle vitesse ils les font (combien en une seconde) est la fréquence d'horloge. Vous avez vu la note Ghz d'un processeur et vous savez probablement qu'elle est liée à sa vitesse. Plus il y a de Ghz, mieux c'est, non ? mais pourquoi? Parce que c'est le nombre de cycles par seconde qu'un processeur peut atteindre (sans surchauffer ni prendre feu - vraiment !).

Si vous êtes un nerd de la fiche technique, vous savez peut-être que la puce du microprocesseur de l'Arduino Uno, l'Atmel ATMega328P, fonctionne à 16 MHz dès la sortie de la boîte. Il est capable de 20Mhz, mais il est rappelé pour ne pas gâcher des choses comme l'écriture de données en mémoire (ou, vous savez, prendre feu). 16Mhz signifie que chaque seconde, votre Arduino traite 16 000 000 de cycles, c'est-à-dire qu'il effectue 16 millions de tâches. Maintenant, ce ne sont PAS des lignes de code - ce serait incroyablement rapide et Arduino est relativement lent. Il s'agit d'instructions de processeur telles que le déplacement de données dans et hors des registres. Passer à un niveau inférieur à cet aperçu est assez technique, donc je vais laisser cela comme exercice au lecteur, mais c'est l'essentiel :)

Donc, si nous ne pouvons aller si vite sur un cœur avant que la meilleure puce disponible ne prenne feu, sommes-nous bloqués à cette vitesse pour toujours ? Est-ce le travail le plus rapide que nous puissions faire ? Comme il s'avère, non! Entrez les processeurs multicœurs et le multithreading. Sur un processeur d'ordinateur, les applications multithread sont deux processus distincts qui fonctionnent en parallèle sur différents cœurs d'un processeur. Ces processus interagissent pour que le travail soit fait ensemble, mais ne divisent pas nécessairement le travail de manière égale comme vous pourriez le supposer. Il existe généralement un processus principal / "thread" qui fonctionne comme un gestionnaire des autres threads, puis un ou plusieurs threads de travail qu'il gère, chacun pouvant effectuer des tâches spécifiques. Un bon exemple est Chrome. Chrome est le gestionnaire de tous les onglets de vos pages Web (threads), mais comme Chrome est multithread, chaque onglet est son propre petit programme. Cela signifie non seulement qu'il peut fonctionner plus rapidement si vous disposez de plusieurs cœurs sur lesquels répartir chaque onglet, mais qu'il présente également d'autres avantages, tels que le fait de ne pas faire planter tout le navigateur lorsqu'un onglet se bloque. C'est la première raison pour laquelle Protothreading n'est pas multithread - nous n'avons qu'un seul cœur avec lequel travailler sur un MCU, donc le multithread traditionnel est tout simplement impossible. Nous devons gérer le travail sur un seul cœur, tout en faisant plusieurs choses à la fois. Nous avons besoin de protothreading.

D'accord, en quoi le Protothreading est-il différent alors ?

Le protothreading est très similaire à l'hyperthreading que j'ai mentionné, dans une certaine mesure. L'hyperthreading émulerait un deuxième cœur et diviserait littéralement le travail effectué par un cœur en prétendant être deux cœurs virtuels. Cela a fonctionné car ils existaient vraiment sur le même noyau et partageaient donc le même espace de ressources. Étant donné que le MCU arduino ne prend pas en charge l'hyperthreading, nous ne pouvons pas le faire ici. Le protothreading est similaire, sauf qu'à la place des cycles et des instructions du processeur, nous pouvons décomposer le travail en « boucles » ou « lignes » de code exécutées par notre esquisse. Comme vous pouvez l'imaginer, si nous faisons plus de choses, les boucles prendraient plus de temps, donc chaque projet aura des « boucles par seconde » très différentes. Il existe différentes implémentations de protothreading, et celle que j'utilise ici est certes probablement de mauvaise qualité, mais elle fonctionne. Fondamentalement, à chaque boucle nous n'avons pas d'autre travail à faire, nous faisons un travail moins exigeant ou moins fréquent dans la boucle principale (ou rien du tout). Lorsque nous ne sommes pas occupés, nous vérifions s'il est encore temps de faire l'un de ces autres travaux. Si c'est le cas, nous bifurquons et allons le faire. Il est important de noter que les actions qui "bloquent", ce qui signifie qu'elles doivent se terminer en une seule fois sans interruption et ainsi bloquer le MCU pendant un certain temps (comme la lecture de données sur une carte SD et quelques autres tâches) bloquera toujours d'autres protothreads ne se produisent pas "à temps", mais pour des choses simples comme deux boucles effectuant des actions rapides comme des changements de variables ou des valeurs de sortie, cela fonctionnera à merveille. C'est plus ou moins ce que nous allons faire ici. Certains MCU prennent en charge un système d'exploitation en temps réel (RTOS) qui peut fournir davantage de capacités multitâches de type hyperthreading, ce qui peut aider à atténuer les problèmes causés par les tâches de « blocage ».

Commençons.

Nous déterminons d'abord les tâches que nous devons effectuer. Dans mon cas, j'ai choisi (a) atténuer le rétroéclairage de mon panneau LCD pour un effet "pulsé" net, tout en (b) compter un nombre sur un intervalle beaucoup plus lent (et peut-être non divisible), et (c) la rotation de certains messages de chaîne à un intervalle encore beaucoup plus lent. Certaines directives à suivre pour garantir le bon fonctionnement de ce processus consistent à évaluer vos fonctions de la moins bloquante à la plus bloquante. Les actions (appelons-les « fonctions » à partir de maintenant) qui prennent plus de temps, comme la lecture de données ou ont d'autres délais longs, et les fonctions avec des intervalles plus grands entre le moment où elles se déclenchent sont les fonctions les plus bloquantes. Les fonctions qui se déclenchent très fréquemment, voire toutes les boucles, et ne prennent pas longtemps à se terminer sont les fonctions les moins bloquantes. La fonction la moins bloquante est ce que vous devez utiliser comme "fil" principal. Pouvez-vous deviner lequel est au-dessus ?

C'est vrai, c'est "a", faisant entrer et sortir le rétroéclairage. Ce sera à un intervalle régulier et très rapide, perpétuel sans aucun retard entre les incendies autre que l'exécution du travail, et le travail lui-même est très rapide. Le fil conducteur parfait.

Nous utiliserons ce thread (et toutes les boucles qu'il contient) pour vérifier si les autres threads ont besoin de travailler. Il est probablement préférable de lire le code à ce stade - il est abondamment documenté. Voir la boucle principale vers le bas. Vous pouvez me voir vérifier si les threads ont besoin de travail où j'appelle numberThread.check() et textThread.check() .

Je dois également le faire dans toutes les boucles du thread principal, car elles se bloqueront jusqu'à la fin si je ne le fais pas. J'ai défini l'intervalle auquel les threads doivent se déclencher lorsque je les initialise pendant l'initialisation ou la partie configuration du code. S'il est temps que ces threads se déclenchent, .check() verront cela et effectueront leur travail avant de continuer le fil principal.

C'est vraiment tout en un mot, le reste, vous pouvez probablement le découvrir vous-même en parcourant le code. Permettez-moi de terminer en disant que même si j'en ai l'air, je ne suis en aucun cas un pro du protothreading, ce n'est qu'un exemple simple que j'ai piraté. Si vous avez des conseils ou si je me suis trompé sur quelque chose, j'encourage les commentaires et les corrections ! Merci :)

Code

  • Code LCD multithread - multithread.ino (mis à jour, v1.1)
Code LCD multithread - multithread.ino (mis à jour, v1.1)Arduino
Ce morceau de code utilise la bibliothèque pour effectuer 3 actions répétées avec des intervalles séparés en même temps sur un processeur Arduino Uno. Il va (a) allumer et éteindre le rétroéclairage, tout en (b) incrémenter un nombre et (c) tourner entre quelques chaînes de texte. Voir la vidéo ci-dessus pour une démo :)
/*Arduino Protothreading Example v1.1 de Drew Alden (@ReanimationXP) 1/12/2016- Mise à jour :v1.1 - 18/08/17 Arduino 1.6.6+ prototypage modifié , petites corrections. (créer des fonctions avant utilisation, supprimer foreach et la bibliothèque associée). Notez que TimedAction est maintenant obsolète. Assurez-vous de lire les notes sur les erreurs TimedAction et WProgram.h / Arduino.h.*///COMPONENTS/* Ce code a été créé à l'aide de l'écran LCD bleu du kit de démarrage Sunfounder Arduino. Il peut être trouvé sur Amazon.com dans une variété de kits .*///BIBLIOTHÈQUES TIERCES//celles-ci doivent être ajoutées manuellement à votre installation Arduino IDE//TimedAction//nous permettent de définir des actions à effectuer à des intervalles de temps séparés//http://playground.arduino.cc/Code /TimedAction//http://wiring.uniandes.edu.co/source/trunk/wiring/firmware/libraries/TimedAction#include //REMARQUE :cette bibliothèque a un problème sur les nouvelles versions d'Arduino. Après // avoir téléchargé la bibliothèque, vous DEVEZ aller dans le répertoire de la bibliothèque et // modifier TimedAction.h. À l'intérieur, écrasez WProgram.h avec Arduino.h//NATIVE LIBRARIES#include  /* LiquidCrystal Library - Hello World Démontre l'utilisation d'un écran LCD 16x2. La bibliothèque LiquidCrystal fonctionne avec tous les écrans LCD compatibles avec le pilote Hitachi HD44780. Il y en a beaucoup, et vous pouvez généralement les identifier grâce à l'interface à 16 broches. Un exemple de circuit :* Broche LCD RS vers la broche numérique 12. * Broche LCD Enable/E/EN vers la broche numérique 11 * Broche LCD D4 vers la broche numérique 5 * Broche LCD D5 vers la broche numérique 4 * Broche LCD D6 vers la broche numérique 3 * Broche LCD D7 à broche numérique 2 * broche LCD R/W à la terre * broche LCD VSS à la terre * broche LCD VCC/VDD à 5V * résistance 10K :* se termine à +5V et à la terre * essuie-glace (milieu) à broche LCD VO ( broche 3) *Affichages rétroéclairés :* Broche LCD K à la masse (le cas échéant) * Broche LCD A à une résistance de 220 ohms (rouge rouge noir noir (marron)), puis résistance à la broche 9 Cet exemple de code est dans le domaine public. http://www.arduino.cc/en/Tutorial/LiquidCrystal *///GLOBALSint backlightPin =9 ; // utilisé pour le rétroéclairage fadingint timerCounter =0; // compteur d'incrémentation. finira par planter.int stringNo =0; //quelle chaîne de texte afficher// "16 CHARACTER MAX"char* stringArray[]={"Check it out... ", "J'ai 3 threads", "allant à la fois...", "Cool, hein ?! :RÉ "}; //INIT// Cela devrait probablement être fait dans setup(), mais peu importe.// initialiser la bibliothèque LCD avec les numéros des broches de l'interfaceLiquidCrystal lcd(12, 11, 5, 4, 3, 2);//FUNCTIONS/ /c'est notre première tâche, imprimer un nombre incrémentiel sur le LCDvoid incrementNumber(){ // placer le curseur sur la colonne 0, ligne 1 // (remarque :la ligne 1 est la deuxième ligne, car le comptage commence par 0) :lcd. setCursor(0, 1); // en ajoute un au compteur, puis l'affiche. timerCounter =timerCounter + 1; lcd.print(timerCounter);}//notre deuxième tâche, se déclenche toutes les quelques secondes et fait pivoter les chaînes de textevoid changeText(){ // Imprime un message sur l'écran LCD. lcd.setCursor(0, 0); lcd.print(stringArray[stringNo]); // hack méchant pour obtenir le nombre d'éléments du tableau if (stringNo>=sizeof(stringArray)/sizeof(char *)){ stringNo =0; changeText(); } else{ stringNo =stringNo + 1; }}//Créez quelques temporisateurs qui se déclencheront à plusieurs reprises toutes les x ms//edit :ces lignes étaient auparavant devant les fonctions incrementNumber et changeText//. cela n'a pas fonctionné car les fonctions n'étaient pas encore définies !TimedAction numberThread =TimedAction(700,incrementNumber);TimedAction textThread =TimedAction(3000,changeText);// où est notre troisième tâche ? eh bien, c'est la boucle principale elle-même :) la tâche // qui se répète le plus souvent doit être utilisée comme boucle. les autres// les tâches sont capables d'"interrompre" la tâche qui se répète le plus rapidement.void setup() { //définir le nombre de colonnes et de lignes de l'écran LCD :lcd.begin(16, 2); //tirez changeText une fois pour peindre la chaîne initiale [0] changeText();}void loop() { //vérifiez nos threads. en fonction de la durée de //exécution du système, ont-ils besoin de se déclencher et de fonctionner ? si oui, fais-le ! nombreThread.check(); textThread.check(); //troisième tâche, fondu en rétro-éclairage de la luminosité minimale à la luminosité maximale //par incréments de 5 points :digitalWrite(13, HIGH); for (int fadeValue =0; fadeValue <=255; fadeValue +=10) { // attendez une seconde, pourquoi suis-je en train de vérifier les fils ici ? parce que // c'est une boucle for. vous devez vérifier vos threads pendant N'IMPORTE QUELLE //boucle qui se produit, y compris la principale ! nombreThread.check(); textThread.check(); //définit la valeur (plage de 0 à 255) :analogWrite(backlightPin, fadeValue); // attendez 20 millisecondes pour voir l'effet de gradation // gardez les délais sur la boucle principale COURT. ceux-ci empêcheront // d'autres threads de se déclencher à temps. retard(20); } // fondu de max à min par incréments de 5 points :digitalWrite(13, LOW); for (int fadeValue =255; fadeValue>=0; fadeValue -=10) { //vérifier à nouveau nos threads numberThread.check(); textThread.check(); //définit la valeur (plage de 0 à 255) :analogWrite(backlightPin, fadeValue); //attendez 20 millisecondes pour voir le retard de l'effet de gradation (20) ; } /* Pour s'amuser à faire défiler les messages à l'avenir... lcd.setCursor(15,0); // place le curseur sur la colonne 15, ligne 0 pour (int positionCounter1 =0; positionCounter1 <26; positionCounter1++) { lcd.scrollDisplayLeft(); // Fait défiler le contenu de l'affichage d'un espace vers la gauche. lcd.print(array1[positionCounter1]); // Imprime un message sur l'écran LCD. retard(tim); //attendre 250 microsecondes } lcd.clear(); //Efface l'écran LCD et positionne le curseur dans le coin supérieur gauche. lcd.setCursor(15,1) ; // place le curseur sur la colonne 15, ligne 1 pour (int positionCounter =0; positionCounter <26; positionCounter++){ lcd.scrollDisplayLeft(); // Fait défiler le contenu de l'affichage d'un espace vers la gauche. lcd.print(array2[positionCounter]); // Imprime un message sur l'écran LCD. retard(tim); //attendre 250 microsecondes } lcd.clear(); //Efface l'écran LCD et positionne le curseur dans le coin supérieur gauche. */ }

Processus de fabrication

  1. Comment mesurer la qualité de l'air sur OpenSensors
  2. Tutoriel sur le verrouillage RFID Arduino
  3. Comment pirater des télécommandes infrarouges
  4. Comment mesurez-vous ?
  5. Est-il facile d'utiliser une thermistance ? !
  6. Comment faire de la musique avec un Arduino
  7. Comment utiliser NMEA-0183 avec Arduino
  8. Tutoriel sur le capteur d'empreintes digitales Arduino
  9. Tutoriel Arduino 01 :Prise en main