Hvordan kan jeg lage flere løpende tråder?

Er det en måte jeg kan ha flere deler av programmet på, uten å gjøre flere ting i samme kodeblokk?

En tråd som venter på en ekstern enhet mens den også blinker en LED i en annen tråd.

Kommentarer

  • Du bør nok først spørre deg selv om du virkelig trenger tråder. Tidtakere kan allerede være i orden for dine behov, og de støttes av Arduino.
  • Det kan også være lurt å sjekke ut Uzebox. Det ‘ er en hjemmebrygget videospillkonsoll med to brikker. Så selv om det ikke er ‘ t nøyaktig en Arduino, er hele systemet bygget på avbrudd. Så lyd, video, kontroller osv. Er alle avbruddsdrevne mens hovedprogrammet ikke ‘ ikke trenger å bekymre seg for noe av det. Kan være god referanse.

Svar

Det er ingen støtte på flere prosesser, eller multitråding, på Arduino. Du kan gjøre noe i nærheten av flere tråder med noe programvare.

Du vil se på Protothreads :

Protothreads er ekstremt lette stabile tråder designet for sterkt minnebegrensede systemer, for eksempel små innebygde systemer eller trådløse sensornettverksnoder. Protothreads gir lineær kodeutførelse for hendelsesdrevne systemer implementert i C. Protothreads kan brukes med eller uten et underliggende operativsystem for å gi blokkerende hendelsesbehandlere. Protothreads gir sekvensiell strøm av kontroll uten kompliserte tilstandsmaskiner eller full multi-threading.

Selvfølgelig er det et Arduino-eksempel her med eksempelkode . Dette SO-spørsmålet kan også være nyttig.

ArduinoThread er en god også.

Kommentarer

Svar

AVR-baserte Arduino støtter ikke (maskinvare) tråder, jeg er ukjent med ARM-baserte Arduino. En vei rundt denne begrensningen er bruken av avbrudd, spesielt tidsavbrudd. Du kan programmere en tidtaker for å avbryte hovedrutinen hver så mange mikrosekunder, for å kjøre en spesifikk annen rutine.

http://arduino.cc/en/Reference/Interrupts

Svar

Det er mulig å gjøre programvaresiden multitrading på Uno. Tråding på maskinvarenivå støttes ikke.

For å oppnå multitrading vil det kreve implementering av en grunnleggende planlegger og vedlikehold av en prosess eller oppgaveliste for å spore de forskjellige oppgavene som må kjøres.

Strukturen til en veldig enkel ikke-forebyggende planlegger vil være som:

//Pseudocode void loop() { for(i=o; i<n; i++) run(tasklist[i] for timelimit): } 

Her, tasklist kan være en rekke funksjonspekere.

tasklist [] = {function1, function2, function3, ...} 

Med hver funksjon i skjemaet:

int function1(long time_available) { top: //Do short task if (run_time<time_available) goto top; } 

Hver funksjon kan utføre en egen oppgave, for eksempel function1 å utføre LED-manipulasjoner, og function2 gjøre floatberegninger. Det vil være ansvaret for hver oppgave (funksjon) å følge tiden som er tildelt den.

Forhåpentligvis bør dette være nok til å komme i gang.

Kommentarer

  • Jeg er ikke sikker på at jeg vil snakke om » tråder » når du bruker en ikke forebyggende planlegger. Forresten, en slik planlegger eksisterer allerede som et arduino-bibliotek: arduino.cc/en/Reference/Scheduler
  • @jfpoilpret – Cooperative multithreading er en virkelig ting.
  • Ja du ‘ har rett! Min feil; det hadde vært så lenge siden at jeg ikke hadde møtt samarbeidende multitråder at multitrading i mine tanker måtte være forebyggende.

Svar

I henhold til beskrivelsen av dine krav:

  • en tråd som venter på en ekstern enhet
  • en tråd som blinker en LED

Det ser ut til at du kan bruke en Arduino-avbrytelse for den første «tråden» (jeg vil heller kalle den «oppgave» faktisk.)

Arduino-avbrudd kan ringe en funksjon (koden din) basert på en ekstern hendelse (spenningsnivå eller nivåendring på en digital inngangspinne), som vil utløse funksjonen din umiddelbart.

Et viktig poeng å huske på med avbrudd er imidlertid at den kallte funksjonen skal være så rask som mulig (vanligvis skal det ikke være noe delay() -anrop eller noe annet API som vil avhenge av delay()).

Hvis du har en lang oppgave å aktivere ved utløsing av ekstern hendelse, kan du potensielt bruke en samarbeidsplanlegger og legge til en ny oppgave i den fra avbruddsfunksjonen.

Et sekund viktig poenget om avbrudd er at antallet er begrenset (f.eks. bare 2 på UNO). Så hvis du begynner å ha flere eksterne hendelser, må du implementere en slags multiplexing av alle innganger til en, og få avbruddsfunksjonen til å bestemme hvilken multiplexed inut som var den faktiske utløseren.

Svar

En enkel løsning er å bruke en Planlegger . Det er flere implementeringer. Dette beskriver kort en som er tilgjengelig for AVR- og SAM-baserte kort. I utgangspunktet vil en enkelt samtale starte en oppgave; «sketch in a sketch».

#include <Scheduler.h> .... void setup() { ... Scheduler.start(taskSetup, taskLoop); } 

Scheduler.start () vil legge til en ny oppgave som kjører taskSetup en gang og deretter gjentatte ganger kaller taskLoop akkurat som Arduino skisse fungerer. Oppgaven har sin egen stabel. Størrelsen på stakken er en valgfri parameter. Standard stakkstørrelse er 128 byte.

For å tillate kontekstbytte må oppgavene ringe yield () eller forsinkelse ( ) . Det er også en støttemakro for å vente på en tilstand.

await(Serial.available()); 

Makroen er syntaktisk sukker for følgende:

while (!(Serial.available())) yield(); 

Await kan også brukes til å synkronisere oppgaver. Nedenfor er et eksempelutdrag:

volatile int taskEvent = 0; #define signal(evt) do { await(taskEvent == 0); taskEvent = evt; } while (0) ... void taskLoop() { await(taskEvent); switch (taskEvent) { case 1: ... } taskEvent = 0; } ... void loop() { ... signal(1); } 

For ytterligere detaljer, se eksemplene . Det er eksempler fra flere LED-blink for å avvise-knappen og et enkelt skall med ikke-blokkerende kommandolinje lest. Maler og navnerom kan brukes til å strukturere og redusere kildekoden. Under skisse vises hvordan du bruker malfunksjoner for flerblinking. Det er tilstrekkelig med 64 byte for stakken.

#include <Scheduler.h> template<int pin> void setupBlink() { pinMode(pin, OUTPUT); } template<int pin, unsigned int ms> void loopBlink() { digitalWrite(pin, HIGH); delay(ms); digitalWrite(pin, LOW); delay(ms); } void setup() { Scheduler.start(setupBlink<11>, loopBlink<11,500>, 64); Scheduler.start(setupBlink<12>, loopBlink<12,250>, 64); Scheduler.start(setupBlink<13>, loopBlink<13,1000>, 64); } void loop() { yield(); } 

Det er også en referanseindeks for å gi en ide om forestillingen, dvs. tid å starte oppgave, kontekstbytte osv.

Til slutt er det noen få støtteklasser for synkronisering og kommunikasjon på oppgavennivå; og Semaphore .

Svar

Jeg kom også til dette emnet mens jeg implementerte en matrise-LED-skjerm.

I ett ord , kan du lage en avstemningsplanlegger ved å bruke funksjonen millis () og timeravbrudd i Arduino.

Jeg foreslår følgende artikler fra 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

Svar

Fra en tidligere besvergelse av dette forumet ble følgende spørsmål / svar flyttet til Elektroteknikk. Den har eksempler på arduino-kode for å blinke en LED ved hjelp av en timeravbrudd mens du bruker hovedsløyfen til å gjøre seriell IO.

https://electronics.stackexchange.com/questions/67089/how-can-i-control-things-without-using-delay/67091#67091

Repost:

Avbrudd er en vanlig måte å få ting gjort mens noe annet skjer. I eksemplet nedenfor blinker LED uten å bruke delay(). Når Timer1 utløses, kalles avbruddsrutinen (ISR) isrBlinker(). Den slår lysdioden på / av.

For å vise at andre ting kan skje samtidig, skriver loop() flere ganger foo / bar til serieporten uavhengig av at LED-lampen blinker .

#include "TimerOne.h" int led = 13; void isrBlinker() { static bool on = false; digitalWrite( led, on ? HIGH : LOW ); on = !on; } void setup() { Serial.begin(9600); Serial.flush(); Serial.println("Serial initialized"); pinMode(led, OUTPUT); // initialize the ISR blinker Timer1.initialize(1000000); Timer1.attachInterrupt( isrBlinker ); } void loop() { Serial.println("foo"); delay(1000); Serial.println("bar"); delay(1000); } 

Dette er en veldig enkel demo. ISR kan være mye mer komplekse og kan utløses av tidtakere og eksterne hendelser (pins). Mange av de vanlige bibliotekene er implementert ved hjelp av ISR.

Svar

Du kan også prøve ThreadHandler-biblioteket mitt

https://bitbucket.org/adamb3_14/threadhandler/src/master/

Den bruker en avbrytende planlegger for å tillate kontekstbytte uten å videresende yield () eller delay ().

Jeg opprettet biblioteket fordi jeg trengte tre tråder, og jeg trengte to av dem for å løpe på et presist tidspunkt uansett hva de andre gjorde. Den første tråden håndterte seriell kommunikasjon. Den andre kjørte et Kalman-filter ved å bruke flytematriksmultiplikasjon med Eigen-biblioteket. Og den tredje var en hurtigstrømstyringsløyfetråd som måtte være i stand til å avbryte matriksberegningene.

Hvordan det fungerer

Hver sykliske tråd har en prioritet og en periode. Hvis en tråd, med høyere prioritet enn den nåværende utførende tråden, når sin neste utførelsestid, vil planleggeren stoppe den gjeldende tråden og bytte til den med høyere prioritet. Når tråden med høy prioritet er fullført, bytter planleggeren tilbake til forrige tråd.

Planleggingsregler

Planleggingsskjemaet for ThreadHandler-biblioteket er som følger:

  1. Høyeste prioritet først.
  2. Hvis prioriteten er den samme, utføres tråden med den tidligste fristen først.
  3. Hvis to tråder har samme frist, vil den først opprettede tråden først utføres.
  4. En tråd kan bare avbrytes av tråder med høyere prioritet.
  5. Når en tråd er utført, vil den blokkere kjøring for alle tråder med lavere prioritet til kjøringsfunksjonen returnerer.
  6. Sløyfefunksjonen har prioritet -128 sammenlignet med ThreadHandler-tråder.

Hvordan bruke

Tråder kan opprettes via c ++ arv

 class MyThread : public Thread { public: MyThread() : Thread(priority, period, offset){} virtual ~MyThread(){} virtual void run() { //code to run } }; MyThread* threadObj = new MyThread();  

Eller via createThread og en lambda-funksjon

 Thread* myThread = createThread(priority, period, offset, []() { //code to run });  

Trådobjekter kobles automatisk til ThreadHandler når de opprettes.

For å starte utførelse av opprettede trådobjekter, ring:

 ThreadHandler::getInstance()->enableThreadExecution();  

En svare

Og her er enda et mikroprosessor-samarbeidende multitasking-bibliotek – PQRST: en prioritetskø for å kjøre enkle oppgaver.

I dette modell, er en tråd implementert som en underklasse av en Task, som er planlagt for en fremtidig tid (og muligens omlagt med jevne mellomrom, hvis den som vanlig er underklasser LoopTask i stedet). run() -metoden til objektet kalles når oppgaven forfaller. run() -metoden gjør noe arbeid, og returnerer deretter (dette er samarbeidsbiten); det vil vanligvis opprettholde en slags statsmaskin for å administrere sine handlinger på suksessive påkallinger (et trivielt eksempel er light_on_p_ -variabelen i eksemplet nedenfor). Det krever en liten revurdering av hvordan du organisere koden din, men har vist seg å være veldig fleksibel og robust i ganske intensiv bruk.

Det er agnostisk om tidsenhetene, så det er like lykkelig å kjøre i enheter på millis() som micros(), eller et annet kryss som er praktisk.

Her er «blink» -programmet implementert ved hjelp av dette biblioteket. Dette viser bare en enkelt oppgave som kjører: andre oppgaver vil vanligvis opprettes og startes innen setup().

#include "pqrst.h" class BlinkTask : public LoopTask { private: int my_pin_; bool light_on_p_; public: BlinkTask(int pin, ms_t cadence); void run(ms_t) override; }; BlinkTask::BlinkTask(int pin, ms_t cadence) : LoopTask(cadence), my_pin_(pin), light_on_p_(false) { // empty } void BlinkTask::run(ms_t t) { // toggle the LED state every time we are called light_on_p_ = !light_on_p_; digitalWrite(my_pin_, light_on_p_); } // flash the built-in LED at a 500ms cadence BlinkTask flasher(LED_BUILTIN, 500); void setup() { pinMode(LED_BUILTIN, OUTPUT); flasher.start(2000); // start after 2000ms (=2s) } void loop() { Queue.run_ready(millis()); } 

Kommentarer

  • Dette er «run-to-complete» -oppgaver, ikke sant?
  • @EdgarBonet I ‘ Jeg er ikke helt sikker på hva du mener. Etter at run() -metoden er kalt, blir den ikke avbrutt, så den har ansvaret for å fullføre rimelig raskt. Vanligvis vil den imidlertid ‘ gjøre sitt arbeid og deretter omplanere seg selv (muligens automatisk, i tilfelle en underklasse av LoopTask) for noen fremtidig tid. Et vanlig mønster er at oppgaven skal opprettholde en intern tilstandsmaskin (et trivielt eksempel er light_on_p_ -tilstanden ovenfor) slik at den oppfører seg hensiktsmessig når den forfaller.
  • Så ja, disse er run-to-complete (RtC) -oppgaver: ingen oppgaver kan kjøres før den nåværende fullfører kjøringen ved å returnere fra run(). Dette er i motsetning til samarbeidstråder som kan gi CPU ved for eksempel å ringe yield() eller delay(). Eller forebyggende tråder som kan planlegges når som helst. Jeg føler at skillet er viktig, ettersom jeg har sett at mange mennesker som kommer hit og leter etter tråder gjør det fordi de foretrekker å skrive blokkeringskode fremfor statsmaskiner. Å blokkere ekte tråder som gir CPU er greit. Å blokkere RtC-oppgaver er ikke.
  • @EdgarBonet Det ‘ er et nyttig skille, ja. Jeg vil betrakte både denne stilen og tråder med avkastningstilstand som ganske enkelt forskjellige stiler av samarbeidstråd, i motsetning til forebyggende tråder, men det ‘ er sant at de krever en annen tilnærming til koding dem. Det ville være interessant å se en gjennomtenkt og grundig sammenligning av de forskjellige tilnærmingene som er nevnt her; et fint bibliotek som ikke er nevnt ovenfor er prototråder . Jeg finner ting å kritisere i begge deler, men også å rose. Jeg (selvfølgelig) foretrekker tilnærmingen min, fordi den virker mest eksplisitt og trenger ingen ekstra stabler.
  • (rettelse: prototråder ble nevnt, i @sachleen ‘ s svar )

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *