Kuinka voin luoda useita käynnissä olevia ketjuja?

Onko mahdollista, että ohjelman useita osia voidaan suorittaa yhdessä tekemättä useita asioita samassa koodilohkossa?

Yksi säie odottaa ulkoista laitetta samalla kun vilkkuu myös LED-merkkivalo toisessa säikeessä.

Kommentit

  • Sinun pitäisi todennäköisesti ensin kysyä itseltäsi, tarvitsetko todella ketjuja. Ajastimet saattavat olla jo tarpeidesi mukaisia, ja Arduino tukee niitä luonnollisesti.
  • Voit myös tarkistaa Uzeboxin. Se ’ on kahden sirun homebrew-videopelikonsoli. Joten vaikka se ei ’ ole tarkalleen Arduino, koko järjestelmä on rakennettu keskeytyksille. Joten ääni, video, ohjaimet jne. Ovat kaikki keskeytyneitä, kun taas pääohjelman ei tarvitse ’ olla huolissaan mistään niistä. Saattaa olla hyvä viite.

vastaus

Ei moniprosessista eikä monisäikeistä tukea Arduino. Voit kuitenkin tehdä jotain lähellä useita ketjuja joillakin ohjelmistoilla.

Haluat tarkastella -protoketjuja :

Protokierteet ovat erittäin kevyitä pinottomia säikeitä, jotka on suunniteltu vakavasti muistirajoitteisille järjestelmille, kuten pienille sulautetuille järjestelmille tai langattomille anturiverkkosolmuille. Protokierteet tarjoavat lineaarisen koodin suorituksen tapahtumavetoisille järjestelmille, jotka on toteutettu C. Protokierteet tarjoavat peräkkäisen ohjauksen ilman monimutkaisia tilakoneita tai täydellistä monisäikeistä langoittamista.

On tietysti Arduinon esimerkki >

täällä -esimerkillä . Tämä SO-kysymys voi olla hyödyllinen.

ArduinoThread on myös hyvä.

Kommentit

vastaus

AVR-pohjaiset Arduinot eivät tue (laitteisto) ketjutusta, en tunne ARM-pohjaisia Arduinoja. Yksi tapa kiertää tätä rajoitusta on keskeytysten, erityisesti ajastettujen keskeytysten, käyttö. Voit ohjelmoida ajastimen keskeyttämään päärutiinin niin monen mikrosekunnin välein ja suorittamaan tietyn muun rutiinin.

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

vastaus

Unossa on mahdollista tehdä ohjelmistopuolinen monisäikeinen langoitus. Laitetason ketjutusta ei tueta.

Monisäikeisyyden saavuttamiseksi se edellyttää perusajastimen käyttöönottoa ja prosessin tai tehtäväluettelon ylläpitämistä suoritettavien eri tehtävien seuraamiseksi.

Hyvin yksinkertaisen ennalta ehkäisemättömän ajastimen rakenne olisi seuraava:

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

Tässä, tasklist voi olla joukko funktion osoittimia.

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

Lomakkeen jokaisen funktion kanssa:

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

Kukin toiminto voi suorittaa erillisen tehtävän, kuten function1 suorittaa LED-käsittelyjä ja function2 tehdä float-laskelmia. Jokaisen tehtävän (toiminnon) vastuulla on noudattaa sille osoitettua aikaa.

Toivottavasti tämän pitäisi riittää aloittamiseen.

kommentit

  • En ole varma, puhuisinko ” ketjuista ” kun käytetään ennalta ehkäisevää ajoitusta. Sellainen ajastin on jo olemassa arduino-kirjastona: arduino.cc/en/Reference/Scheduler
  • @jfpoilpret – osuuskunta monisäikeisyys on todellinen asia.
  • Kyllä, olet ’ oikeassa! Minun erehdykseni; se oli ollut niin kauan sitten, etten ollut kohdannut yhteistyöhön perustuvaa monisäikeisyyttä, että mielestäni monisäikeisyyden oli oltava ennakoivaa.

Vastaus

Vaatimusten kuvauksen mukaisesti:

  • yksi ketju odottaa ulkoista laitetta
  • yksi säie vilkkuu LEDiä

Näyttää siltä, että voisit käyttää yhtä Arduino-keskeytystä ensimmäiseen ”säikeeseen” (itse asiassa kutsun sitä pikemminkin ”tehtäväksi”).

Arduino-keskeytykset voivat kutsua yhtä toimintoa (koodiasi) ulkoisen perusteella tapahtuma (jännitteen taso tai tasonmuutos digitaalisessa tuloliittimessä), joka laukaisee toiminnosi välittömästi.

Yksi tärkeä asia, joka on pidettävä mielessä keskeytysten kanssa, on se, että kutsutun toiminnon tulisi olla mahdollisimman nopea (tyypillisesti ei saa olla delay() -kutsua tai muuta sovellusliittymää, joka riippuu delay() -palvelusta).

Jos sinulla on pitkä aktivoitava tehtävä ulkoisen tapahtuman laukaisun yhteydessä, voit mahdollisesti käyttää osuuskunnan ajastinta ja lisätä siihen uuden tehtävän keskeytystoiminnostasi.

Toinen tärkeä keskeytyskohdista on, että niiden lukumäärä on rajallinen (esim. vain 2 UNO: lla). Joten jos aloitat enemmän ulkoisia tapahtumia, sinun on toteutettava jonkinlainen multipleksointi kaikki tulot yhdeksi ja keskeytystoiminto on määritettävä, mikä multipleksoitu tulo oli todellinen laukaisu.

Vastaa

Yksinkertainen ratkaisu on käyttää ajastinta . Toteutuksia on useita. Tämä kuvaa lyhyesti yhden, joka on saatavana AVR- ja SAM-pohjaisille levyille. Pohjimmiltaan yksi puhelu aloittaa tehtävän; ”luonnos luonnoksessa”.

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

Scheduler.start () lisää uuden tehtävän, joka suorittaa taskSetup kerran ja kutsuu sitten toistuvasti taskLoop kuten Arduino-luonnos toimii. Tehtävällä on oma pino. Pinon koko on valinnainen parametri. Pinoa oletuskoko on 128 tavua.

Jotta kontekstinvaihto sallitaan, tehtävien on kutsuttava yield () – tai -viivettä ( ) . Ehdon odottamiseen on myös tukimakro.

await(Serial.available()); 

Makro on syntaktinen sokeri seuraaville:

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

Odottaa voi myös käytetään tehtävien synkronointiin. Alla on esimerkkikatkelma:

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); } 

Lisätietoja on -esimerkkeissä . On olemassa esimerkkejä useista LED-merkkivaloista vilkkumisesta purkamispainikkeeseen ja yksinkertaisesta kuoresta, jossa lukematon komentorivi. Lähdekoodin jäsentämisessä ja pienentämisessä voidaan käyttää malleja ja nimitiloja. -luonnoksen alla näkyy, kuinka mallitoimintoja käytetään monisilmäisyydessä. Se riittää 64 tavua pinolle.

#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(); } 

Mukana on myös -vertailu , joka antaa käsityksen suorituskyvystä eli ajasta aloittaaksesi tehtävän, kontekstikytkimen jne.

Viimeisenä on muutama tukiluokka tehtävän tason synkronointia ja viestintää varten; jono ja semafori .

vastaus

Tulin myös tähän aiheeseen toteuttaessani matriisin LED-näyttöä.

Yhdellä sanalla , voit rakentaa kyselyaikataulun käyttämällä millis () -funktiota ja ajastimen keskeytystä Arduinossa.

Ehdotan seuraavia artikkeleita Bill Earlilta:

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

Vastaus

Tämän foorumin edellisestä loitsusta seuraava kysymys / vastaus siirrettiin sähkötekniikkaan. Siinä on näyte arduino-koodista, joka vilkuttaa LEDiä ajastimen keskeytyksellä samalla kun käyttää pääpiiriä sarjaliitännän tekemiseen.

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

Lähetä uudelleen:

Keskeytykset ovat yleinen tapa saada aikaan asiat, kun jotain muuta tapahtuu. Alla olevassa esimerkissä LED vilkkuu käyttämättä delay(). Aina kun Timer1 käynnistyy, keskeytyspalvelurutiini (ISR) isrBlinker() kutsutaan. Se kytkee LED-valon päälle / pois päältä.

Osoittaakseen, että muita asioita voi tapahtua samanaikaisesti, loop() kirjoittaa toistuvasti foo / bar-sarjaliitäntään riippumatta LED-merkkivalon vilkkumisesta. .

#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); } 

Tämä on hyvin yksinkertainen esittely. ISR: t voivat olla paljon monimutkaisempia, ja ajastimet ja ulkoiset tapahtumat (nastat) voivat laukaista ne. Monet yleisistä kirjastoista toteutetaan ISR: ien avulla.

Answer

Voit myös kokeilla ThreadHandler-kirjastoni

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

Tuotto () tai viive ().

Loin kirjaston, koska tarvitsin kolme ketjua ja tarvitsin kaksi niistä toimiakseen tarkkaan aikaan riippumatta siitä, mitä muut tekivät. Ensimmäinen ketju käsitteli sarjaliikennettä. Toinen oli Kalman-suodattimen käyttö käyttäen floatmatriisikertomusta Eigen-kirjaston kanssa. Ja kolmas oli nopea virranohjaussilmukka, jonka oli kyettävä keskeyttämään matriisilaskelmat.

Kuinka se toimii

Jokaisella syklisellä langalla on prioriteetti ja jakso. Jos ketju, jolla on korkeampi prioriteetti kuin nykyisellä suorituslangalla, saavuttaa seuraavan suoritusajansa, ajoitin keskeyttää nykyisen säikeen ja vaihtaa korkeamman prioriteetin. Kun korkean prioriteetin säike on suoritettu loppuun, ajoitin siirtyy takaisin edelliseen säikeeseen.

Aikataulusäännöt

ThreadHandler-kirjaston ajoituskaavio on seuraava:

  1. Korkein prioriteetti ensin.
  2. Jos prioriteetti on sama, ensin suoritetaan ketju, jolla on aikaisin määräaika.
  3. Jos kahdella säikeellä on sama määräaika, ensimmäinen luotu ketju suoritetaan ensin.
  4. Viestiketjun voi keskeyttää vain korkeamman prioriteetin säikeet.
  5. Kun ketju on suorittamassa, se estää kaikkien alemman prioriteetin säikeiden suorittamisen, kunnes ajo-funktio palaa. > Loop-toiminnolla on prioriteetti -128 ThreadHandler-säikeisiin verrattuna.

Kuinka käyttää

Lankoja voidaan luoda c ++ -perinnön kautta

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

Tai createThread- ja lambda-funktioiden kautta

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

Säieobjektit muodostavat yhteyden automaattisesti ThreadHandleriin, kun ne luodaan.

Aloita luotujen lankaobjektien soitto seuraavasti: p>

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

An swer

Ja tässä on vielä yksi mikroprosessori-osuuskunnan monitoimikirjasto – PQRST: prioriteettijono yksinkertaisten tehtävien suorittamiseen.

Tässä mallissa ketju toteutetaan Task -alaluokana, joka on ajoitettu tulevaan aikaan (ja mahdollisesti ajoitettu säännöllisin väliajoin, jos, kuten tavallista, se alaluokkia LoopTask sen sijaan). Objektin run() -menetelmää kutsutaan, kun tehtävä erääntyy. run() -menetelmä tekee jonkin verran työtä ja palaa sitten (tämä on osuuskuntabitti); se ”tyypillisesti ylläpitää jonkinlaista tilakonetta hallitsemaan toimintojaan peräkkäisissä kutsumisissa (triviaali esimerkki on muuttuja light_on_p_ alla olevassa esimerkissä). Se vaatii hieman uudelleentarkastelua siitä, miten järjestä koodisi, mutta se on osoittautunut erittäin joustavaksi ja kestäväksi melko intensiivisessä käytössä.

Se on agnostinen aikayksiköistä, joten se on yhtä onnellinen käynnissä yksiköissä millis() as micros(), tai mikä tahansa muu rasti.

Tässä on blink -ohjelma, joka on toteutettu tämän kirjaston avulla. Tämä näyttää vain yhden käynnissä olevan tehtävän: muut tehtävät luodaan yleensä ja aloitetaan setup() -kohdassa.

#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()); } 

Kommentit

  • Nämä ovat suoritus loppuun-tehtäviä, eikö niin?
  • @EdgarBonet I ’ En ole varma, mitä tarkoitat. Kun run() -menetelmä on kutsuttu, sitä ei keskeytetä, joten sen vastuulla on lopettaa kohtuullisen nopeasti. Tyypillisesti se kuitenkin ’ tekee työnsä ja aikatauluttaa itsensä uudelleen (mahdollisesti automaattisesti, jos alakategorian LoopTask) tulevaa aikaa. Yleinen malli on tehtävän ylläpitää jotakin sisäistä tilakonetta (triviaali esimerkki on yllä oleva light_on_p_ -tila) niin, että se käyttäytyy sopivasti, kun se erääntyy.
  • Joten kyllä, nämä ovat suorituksen loppuun saakka tehtäviä tehtäviä: mitään tehtävää ei voida suorittaa, ennen kuin nykyinen suorittaa loppuun suorituksen palaamalla kohdasta run(). Tämä on toisin kuin yhteistyökierteet, jotka voivat tuottaa CPU: n esimerkiksi soittamalla yield() tai delay(). Tai ennaltaehkäisevät säikeet, jotka voidaan ajoittaa milloin tahansa. Minusta ero on tärkeä, koska olen nähnyt, että monet ihmiset, jotka tulevat tänne ympäri ketjuja, tekevät niin, koska he mieluummin kirjoittavat estokoodin kuin valtion koneet. CPU: ta tuottavien todellisten ketjujen estäminen on hienoa. RtC-tehtävien estäminen ei ole.
  • @EdgarBonet It ’ on hyödyllinen ero, kyllä. Pidän sekä tätä tyyliä että tuotto-tyylisiä ketjuja yksinkertaisesti erilaisina osuuskunnan säikeinä, toisin kuin ennakoivat säikeet, mutta totta, että ne edellyttävät erilaista lähestymistapaa koodaamalla niitä. Olisi mielenkiintoista nähdä harkittu ja perusteellinen vertailu tässä mainituista lähestymistavoista; yksi mukava kirjasto, jota ei ole mainittu yllä, on prototreads . Minusta on kritisoitavaa, mutta myös kiitettävää. Pidän (tietysti) mieluummin lähestymistavastani, koska se näyttää selkeimmältä ja ei vaadi ylimääräisiä pinoja.
  • (korjaus: protoketjut mainittiin kohdassa @sachleen ’ s vastaus )

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *