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
- Huomaa, että Arduino DUE: lla on poikkeus tähän, ja sillä on useita ohjaussilmukoita: arduino.cc/en/Tutorial/MultipleBlinks
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.
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.
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:
- Korkein prioriteetti ensin.
- Jos prioriteetti on sama, ensin suoritetaan ketju, jolla on aikaisin määräaika.
- Jos kahdella säikeellä on sama määräaika, ensimmäinen luotu ketju suoritetaan ensin.
- Viestiketjun voi keskeyttää vain korkeamman prioriteetin säikeet.
- 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.
- etusivu
- Luokkatason ohjeet
- arkisto , jossa -lataukset ja ongelmaluettelo
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 alakategorianLoopTask
) tulevaa aikaa. Yleinen malli on tehtävän ylläpitää jotakin sisäistä tilakonetta (triviaali esimerkki on yllä olevalight_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 soittamallayield()
taidelay()
. 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 )