Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

Multithreading eines Arduino (Protothreading-Tutorial)

Komponenten und Verbrauchsmaterialien

Arduino UNO
× 1
Sunfounder Blue 16x2 Flüssigkristallanzeige (LCD)
× 1
Breadboard (generisch)
und natürlich Kabel.
× 1
Drehpotentiometer (allgemein)
Ich bin mir der Widerstandsbewertung nicht sicher, wahrscheinlich würde 1Kohm ausreichen.
× 1

Über dieses Projekt

Dieses Video zeigt etwas, was Sie vielleicht während Ihrer angehenden Prototyping-Karriere tun wollten, indem Sie einen Single-Core-Arduino dazu bringen, 3 Dinge gleichzeitig zu tun. In diesem Fall sind wir:

  • Impuls der Hintergrundbeleuchtung mit einer konstanten Rate ohne Unterbrechung
  • Jede Sekunde eine ganze Zahl inkrementieren und ohne Unterbrechung auf das Display schreiben
  • Alle paar Sekunden ein paar Nachrichten drehen und ohne Unterbrechung auf das Display schreiben

Du hast den Titel gesehen!

Protothreading ist eine Möglichkeit, auf einem Arduino einen Vorgang auszuführen, der normalerweise eine Multitasking-Operation wäre (zwei oder mehr Dinge gleichzeitig oder in unterschiedlichen Intervallen ausführen). . Mit anderen Worten, es ist "multithreaded"! Aber halten Sie sich an Sparky, der Arduino ist ein Single-Core-Chip mit prozeduralem Code, also ist echtes Multithreading unmöglich. Warum allerdings? Wie unterscheidet sich das Protothreading?

"Echtes" Multithreading vs. Protothreading

Protothreading verstehen richtig, müssen wir zuerst verstehen, warum es NICHT wirklich Multithreading ist.

Erinnern Sie sich an den Tag, als Intel uns dieses neue "Hyperthreading"-Ding für Pentium-Prozessoren verkaufte? Nein? Sie sind noch nicht geboren? Dann ist es Zeit für eine Geschichtsstunde, mein Sohn! Hyperthreading ist eine Technologie, die Intel anwendet, um einen einzelnen Kern auf einem Prozessor so zu "verhalten", als ob es zwei Kerne wären, oder zwei Kerne "verhalten" zu lassen, als wären sie 4 Kerne usw. Aber warum und wie ist das für Arduino relevant? Die Antwort sind Zyklen.

Sowohl Mikrocontroller als auch CPUs arbeiten in "Zyklen". Wie schnell sie sie machen (wie viele in einer Sekunde) ist die Taktrate. Sie haben die Ghz-Bewertung einer CPU gesehen und wissen wahrscheinlich, dass sie sich auf ihre Geschwindigkeit bezieht. Je mehr Ghz, desto besser, oder? aber wieso? Denn das ist die Anzahl von Zyklen pro Sekunde, die ein Prozessor erreichen kann (ohne Überhitzung und Feuer zu fangen - wirklich!).

Wenn Sie ein Datenblatt-Nerd sind, wissen Sie vielleicht, dass der Mikroprozessorchip des Arduino Uno, der Atmel ATMega328P, standardmäßig mit 16 MHz läuft. Es ist zu 20 MHz fähig, wird aber zurückgerufen, damit es nicht Dinge wie das Schreiben von Daten in den Speicher durcheinander bringt (oder, wie Sie wissen, Feuer fängt). 16Mhz bedeutet jede Sekunde, dass Ihr Arduino 16.000.000 Zyklen verarbeitet, also 16 Millionen Arbeitsstücke. Nun, dies sind KEINE Codezeilen - das wäre unglaublich schnell und Arduino ist relativ langsam. Dies sind Prozessorbefehle wie das Verschieben von Daten in und aus Registern. Ein niedrigeres Niveau als diese Übersicht wird ziemlich technisch, also überlasse ich das dem Leser als Übung, aber das ist das Wesentliche :)

Wenn wir also auf einem Kern nur so schnell fahren können, bevor der beste verfügbare Chip Feuer fängt, bleiben wir dann für immer bei dieser Geschwindigkeit? Können wir so schnell arbeiten? Wie sich herausstellt, nein! Geben Sie Multicore-CPUs und Multithreading ein. Auf einer Computer-CPU sind Multithread-Anwendungen zwei separate Prozesse, die auf verschiedenen Kernen einer CPU parallel zueinander arbeiten. Diese Prozesse interagieren miteinander, um die Arbeit gemeinsam zu erledigen, teilen Sie die Arbeit jedoch nicht unbedingt gleichmäßig auf, wie Sie vielleicht annehmen. Normalerweise gibt es einen Hauptprozess / "Thread", der als Manager für die anderen Threads fungiert, und dann einen oder mehrere von ihm verwaltete Worker-Threads, die jeweils bestimmte Aufgaben ausführen können. Ein gutes Beispiel ist Chrome. Chrome ist der Manager aller Ihrer Webseiten-Tabs (Threads), aber da Chrome Multithreading ist, ist jeder Tab sein eigenes kleines Programm. Das bedeutet, dass es nicht nur schneller ausgeführt werden kann, wenn Sie über mehrere Kerne verfügen, um jede Registerkarte zu verteilen, sondern auch andere Vorteile hat, z. B. nicht den gesamten Browser zum Absturz zu bringen, wenn eine Registerkarte abstürzt. Dies ist der erste Grund, warum Protothreading kein Multithreading ist – wir haben nur einen Kern, mit dem wir auf einer MCU arbeiten können, daher ist traditionelles Multithreading schlichtweg unmöglich. Wir müssen die Arbeit nur auf einem einzigen Kern verwalten, aber dennoch mehrere Dinge gleichzeitig tun. Wir brauchen Protothreading.

Ok, wie unterscheidet sich Protothreading dann?

Protothreading ist bis zu einem gewissen Grad dem von mir erwähnten Hyperthreading sehr ähnlich. Hyperthreading würde einen zweiten Kern emulieren und die Arbeit eines Kerns buchstäblich aufteilen, indem er vorgibt, zwei virtuelle Kerne zu sein. Dies funktionierte, weil sie wirklich auf demselben Kern existierten und somit denselben Ressourcenraum teilten. Da die Arduino-MCU kein Hyperthreading unterstützt, können wir dies hier nicht tun. Das Protothreading ist ähnlich, außer dass wir anstelle von CPU-Zyklen und Anweisungen die Arbeit durch die "Schleifen" oder "Zeilen" des Codes aufschlüsseln können, die von unserem Sketch ausgeführt werden. Wie Sie sich vorstellen können, würden Schleifen länger dauern, wenn wir mehr Dinge tun, sodass jedes Projekt sehr unterschiedliche "Schleifen pro Sekunde" hat. Es gibt verschiedene Implementierungen von Protothreading, und die, die ich hier verwende, ist zugegebenermaßen wahrscheinlich schäbig, aber sie funktioniert. Grundsätzlich haben wir in jeder Schleife keine andere Arbeit zu tun, wir erledigen weniger anspruchsvolle oder weniger häufige Arbeit in der Hauptschleife (oder gar nichts). Wenn wir nicht beschäftigt sind, prüfen wir, ob es schon Zeit ist, eine dieser anderen Arbeiten zu erledigen. Wenn ja, zweigen wir ab und machen es. Es ist wichtig zu beachten, dass Aktionen, die "blockieren", das heißt, sie müssen alle auf einmal ohne Unterbrechung abgeschlossen werden und damit die MCU für eine gewisse Zeit binden (wie das Auslesen von Daten von einer SD-Karte und einige andere Aufgaben) wird immer noch blockieren andere Protothreads nicht "pünktlich" auftreten, aber für einfache Dinge wie zwei gleichzeitig ablaufende Schleifen, die schnelle Aktionen wie Variablenänderungen oder das Ändern von Ausgabewerten ausführen, wird es hervorragend funktionieren. Das werden wir hier mehr oder weniger tun. Einige MCUs unterstützen ein Echtzeit-Betriebssystem (RTOS), das mehr Hyperthreading-ähnliche Multitasking-Fähigkeiten bieten kann, die dazu beitragen können, Probleme zu mildern, die durch "Blockieren" von Aufgaben verursacht werden.

Fangen wir an.

Wir überlegen uns zunächst, welche Aufgaben wir erfüllen müssen. In meinem Fall habe ich (a) die Hintergrundbeleuchtung meines LCD-Panels ein- und ausgeblendet, um einen ordentlichen "pulsierenden" Effekt zu erzielen, während (b) eine Zahl in einem viel langsameren (und möglicherweise nicht teilbaren) Intervall hochgezählt wurde, und (c) Rotieren einiger Zeichenfolgennachrichten in einem noch viel langsameren Intervall. Einige Richtlinien, die Sie befolgen müssen, um sicherzustellen, dass dieser Prozess reibungslos funktioniert, sind, Ihre Funktionen von der am wenigsten blockierenden bis zur am meisten blockierenden zu bewerten. Aktionen (nennen wir sie ab jetzt "Funktionen"), die länger dauern, wie das Lesen von Daten oder andere lange Verzögerungen, und Funktionen mit größeren Intervallen zwischen dem Auslösen sind die am meisten blockierenden Funktionen. Funktionen, die sehr häufig, wenn nicht sogar jede Schleife, ausgelöst werden und nicht lange brauchen, um abzuschließen, sind die am wenigsten blockierenden Funktionen. Die am wenigsten blockierende Funktion ist das, was Sie als Ihren primären "Thread" verwenden sollten. Kannst du erraten, welche es oben ist?

Das ist richtig, es ist "a", das die Hintergrundbeleuchtung ein- und ausschaltet. Dies geschieht in regelmäßigen und sehr schnellen Intervallen, ohne Unterbrechungen zwischen den Feuern außer der Erledigung der Arbeit, und die Arbeit selbst ist sehr schnell. Der perfekte Manager-Thread.

Wir werden diesen Thread (und alle darin enthaltenen Schleifen) verwenden, um zu überprüfen, ob die anderen Threads noch arbeiten müssen. Es ist wahrscheinlich am besten, den Code an dieser Stelle durchzulesen - er ist ausführlich dokumentiert. Siehe die Hauptschlaufe nach unten. Sie können sehen, wie ich überprüfe, ob Threads Arbeit benötigen, bei denen ich numberThread.check() aufrufe und textThread.check() .

Ich muss dies auch in allen Schleifen im Hauptthread tun, da sie bis zum Abschluss blockiert werden, wenn ich dies nicht tue. Ich lege das Intervall fest, in dem die Threads ausgelöst werden müssen, wenn ich sie während der Initialisierung oder des Setup-Teils des Codes initialisiere. Wenn es an der Zeit ist, dass diese Threads ausgelöst werden, .check() werden das sehen und ihre Arbeit ausführen, bevor sie mit dem Hauptthread fortfahren.

Das ist es wirklich auf den Punkt gebracht, den Rest können Sie wahrscheinlich selbst herausfinden, indem Sie den Code schrittweise durchgehen. Lassen Sie mich abschließend sagen, obwohl ich so klingen mag, ich bin auf keinen Fall ein Protothreading-Profi, dies ist nur ein einfaches Beispiel, das ich gehackt habe. Wenn Sie Tipps haben oder ich mich in etwas geirrt habe, ermutige ich Feedback und Korrekturen! Danke :)

Code

  • Multithreaded-LCD-Code - multithread.ino (Aktualisiert, v1.1)
Multithreaded-LCD-Code - multithread.ino (Aktualisiert, v1.1)Arduino
Dieser Code verwendet die Bibliothek , um 3 sich wiederholende Aktionen mit separaten Intervallen gleichzeitig auf einem Arduino Uno-Prozessor auszuführen. Es wird (a) die Hintergrundbeleuchtung ein- und ausblenden, während (b) eine Zahl inkrementiert wird und (c) zwischen einigen Textfolgen rotiert wird. Sehen Sie sich das Video oben für eine Demo an :)
/*Arduino Protothreading Beispiel v1.1von Drew Alden (@ReanimationXP) 12.01.2016 - Update:v1.1 - 18.08.17 Arduino 1.6.6+ Prototyping geändert , kleine Korrekturen. (Funktionen vor der Verwendung erstellen, foreach und zugehörige Bibliothek entfernt). Beachten Sie, dass TimedAction jetzt veraltet ist. Lesen Sie unbedingt die Hinweise zu TimedAction und WProgram.h / Arduino.h-Fehlern.*///COMPONENTS/*Dieser Code wurde mit dem blauen LCD des Sunfounder Arduino-Starterkits erstellt. Er ist bei Amazon.com in einer Vielzahl von Kits erhältlich .*///BIBLIOTHEKEN VON DRITTANBIETERN//diese müssen manuell zu Ihrer Arduino IDE-Installation hinzugefügt werden//TimedAction//ermöglicht es uns, Aktionen festzulegen, die in getrennten Zeitintervallen ausgeführt werden sollen//http://playground.arduino.cc/Code /TimedAction//http://wiring.uniandes.edu.co/source/trunk/wiring/firmware/libraries/TimedAction#include //HINWEIS:Diese Bibliothek hat ein Problem mit neueren Versionen von Arduino. Nach// dem Herunterladen der Bibliothek MÜSSEN Sie in das Bibliotheksverzeichnis gehen und// TimedAction.h bearbeiten. Überschreiben Sie WProgram.h mit Arduino.h//NATIVE LIBRARIES#include  /* LiquidCrystal Library - Hello World Demonstriert die Verwendung eines 16x2 LCD-Displays. Die LiquidCrystal-Bibliothek funktioniert mit allen LCD-Displays, die mit dem Hitachi HD44780-Treiber kompatibel sind. Es gibt viele davon, und Sie können sie normalerweise an der 16-Pin-Schnittstelle erkennen. Eine Beispielschaltung:* LCD RS-Pin auf Digital-Pin 12. * LCD Enable/E/EN-Pin auf Digital-Pin 11 * LCD D4-Pin auf Digital-Pin 5 * LCD D5-Pin auf Digital-Pin 4 * LCD D6-Pin auf Digital-Pin 3 * LCD D7 Pin an Digital Pin 2 * LCD R/W Pin an Masse * LCD VSS Pin an Masse * LCD VCC/VDD Pin an 5V * 10K Widerstand:* Enden an +5V und Masse * Wischer (Mitte) an LCD VO Pin ( Pin 3) * Displays mit Hintergrundbeleuchtung:* LCD K-Pin zu Masse (falls vorhanden) * LCD A-Pin zu 220 Ohm (rot rot schwarz schwarz (braun)) Widerstand, dann Widerstand zu Pin 9 Dieser Beispielcode ist gemeinfrei. http://www.arduino.cc/en/Tutorial/LiquidCrystal *///GLOBALSint backlightPin =9; // verwendet für Hintergrundbeleuchtung Fadingint TimerCounter =0; // Zähler inkrementieren. wird irgendwann abstürzen.int stringNo =0; //welche Textzeichenfolge soll angezeigt werden// "16 CHARACTER MAX"char* stringArray[]={"Schau es dir an...", "Ich habe 3 Threads", "geht gleichzeitig...", "Cool, huh ?! :D "}; //INIT// Dies sollte wahrscheinlich innerhalb von setup() erfolgen, aber was auch immer.// Initialisieren Sie die LCD-Bibliothek mit den Nummern der SchnittstellenpinsLiquidCrystal lcd(12, 11, 5, 4, 3, 2); // FUNCTIONS/ /Dies ist unsere erste Aufgabe, drucke eine inkrementelle Zahl an die LCDvoid IncrementNumber(){ // setze den Cursor auf Spalte 0, Zeile 1 // (Hinweis:Zeile 1 ist die zweite Zeile, da das Zählen mit 0 beginnt):lcd. setCursor(0, 1); // füge eins zum Zähler hinzu und zeige es dann an. TimerCounter =TimerCounter + 1; lcd.print(timerCounter);} // Unsere zweite Aufgabe wird alle paar Sekunden ausgelöst und rotiert den Text stringsvoid changeText () { // Eine Nachricht auf dem LCD ausgeben. lcd.setCursor(0, 0); lcd.print (stringArray[stringNr]); // böser Hack, um die Anzahl der Array-Elemente zu erhalten if (stringNo>=sizeof(stringArray)/sizeof(char *)){ stringNo =0; changeText(); aufrechtzuerhalten. Sonst { StringNr =StringNr + 1; }}//Erstelle ein paar Timer, die alle x ms wiederholt ausgelöst werden //edit:Diese Zeilen standen früher vor den Funktionen inkrementNumber und changeText//. das hat nicht funktioniert, weil die Funktionen noch nicht definiert waren!TimedAction numberThread =TimedAction(700,incrementNumber);TimedAction textThread =TimedAction(3000,changeText);// wo ist unsere dritte Aufgabe? Nun, es ist die Hauptschleife selbst :) Die Aufgabe// die sich am häufigsten wiederholt, sollte als Schleife verwendet werden. andere // Tasks können die sich am schnellsten wiederholende Task "unterbrechen".void setup () { // die Anzahl der Spalten und Zeilen des LCD definieren:lcd.begin (16, 2); //ChangeText einmal auslösen, um den Anfangsstring zu malen [0] changeText();}void loop() { //Überprüfe unsere Threads. basierend darauf, wie lange das System // läuft, müssen sie feuern und funktionieren? wenn ja, mach es! numberThread.check(); textThread.check(); // dritte Aufgabe, Hintergrundbeleuchtung von minimaler auf maximale Helligkeit einblenden // in Schritten von 5 Punkten:digitalWrite (13, HIGH); for (int FadeValue =0; FadeValue <=255; FadeValue +=10) {// warten Sie eine Sekunde, warum überprüfe ich hier die Threads? weil // dies ist eine for-Schleife. Sie müssen Ihre Threads während JEDER auftretenden // Schleifen überprüfen, einschließlich der Hauptschleife! numberThread.check(); textThread.check(); // setzt den Wert (Bereich von 0 bis 255):analogWrite (backlightPin, fadeValue); // 20 Millisekunden warten, um den Dimmeffekt zu sehen // Verzögerungen in der Hauptschleife KURZ halten. diese WERDEN verhindern, dass // andere Threads rechtzeitig ausgelöst werden. Verzögerung (20); } // von max auf min in Schritten von 5 Punkten ausblenden:digitalWrite (13, LOW); for (int fadeValue =255; fadeValue>=0; fadeValue -=10) {//überprüfe unsere Threads noch einmal numberThread.check(); textThread.check(); // setzt den Wert (Bereich von 0 bis 255):analogWrite (backlightPin, fadeValue); // 20 Millisekunden warten, um die Verzögerung des Dimmeffekts zu sehen (20); } /* Für ein bisschen Spaß beim Scrollen von Nachrichten... lcd.setCursor(15,0); // setze den Cursor auf Spalte 15, Zeile 0 für (int positionCounter1 =0; positionCounter1 <26; positionCounter1++) {lcd.scrollDisplayLeft(); // Scrollt den Inhalt der Anzeige um eine Stelle nach links. lcd.print (array1 [positionszähler1]); // Eine Nachricht auf dem LCD ausgeben. Verzögerung(tim); // 250 Mikrosekunden warten} lcd.clear (); // Löscht den LCD-Bildschirm und positioniert den Cursor in der oberen linken Ecke. lcd.setCursor(15,1); // setze den Cursor auf Spalte 15, Zeile 1 für (int positionCounter =0; positionCounter <26; positionCounter++) { lcd.scrollDisplayLeft(); // Scrollt den Inhalt der Anzeige um eine Stelle nach links. lcd.print (array2 [positionszähler]); // Eine Nachricht auf dem LCD ausgeben. Verzögerung(tim); // 250 Mikrosekunden warten} lcd.clear (); // Löscht den LCD-Bildschirm und positioniert den Cursor in der oberen linken Ecke. */ }

Herstellungsprozess

  1. Messen der Luftqualität auf OpenSensors
  2. Anleitung zum Arduino-RFID-Schloss
  3. Wie man IR-Fernbedienungen hackt
  4. Wie groß bist du?
  5. Wie einfach ist es, einen Thermistor zu verwenden?!
  6. Wie man mit einem Arduino Musik macht
  7. So verwenden Sie NMEA-0183 mit Arduino
  8. Anleitung zum Arduino-Fingerabdrucksensor
  9. So verwenden Sie Modbus mit Arduino
  10. Arduino-Tutorial 01:Erste Schritte