Keskeytykset ja moniajo Arduinossa. Kuinka suorittaa rinnakkaisia ​​tehtäviä (säikeitä) Arduino-ohjelmassa

Yleisesti ottaen Arduino ei tue todellista tehtävien rinnastusta tai monisäikeistystä. Mutta se on mahdollista jokaisella syklin toistolla loop() ohjeista mikro-ohjainta tarkistamaan, onko aika suorittaa lisätoimia, taustatehtävä. Tässä tapauksessa käyttäjä näyttää, että useita tehtäviä suoritetaan samanaikaisesti.

Vilkutetaan esimerkiksi LEDiä tietyllä taajuudella ja toistetaan samaan aikaan nousevia ja laskevia ääniä kuin pietsosäteilijän sireeni. Olemme jo yhdistäneet sekä LEDin että pietsosäteilijän Arduinoon useammin kuin kerran. Kootaan piiri kuvan osoittamalla tavalla.

Jos liität LEDin muuhun digitaaliseen nastaan ​​kuin "13", älä unohda noin 220 ohmin virtaa rajoittavaa vastusta.

2 LED- ja pietsosäteilijäohjaus käyttämällä delay()-operaattoria

Kirjoitetaan tällainen luonnos ja ladataan se Arduinoon.

Const int soundPin = 3; /* ilmoittaa muuttujan kanssa pin numero, johon pietsosähköinen elementti on kytketty */ const int ledPin = 13; // ilmoittaa muuttuja LED-pinninumerolla void setup() ( pinMode(ääniPin, OUTPUT); // ilmoittaa nastan 3 lähdöksi. } pinMode(ledPin, OUTPUT); // ilmoittaa nastan 13 lähdöksi. void loop() ( }

// Äänensäätö: tone(soundPin, 700); // antaa äänen taajuudella 700 Hz delay(200); ääni (soundPin, 500); // 500 Hz:n taajuudella viive(200);ääni (soundPin, 300); // 300 Hz:n taajuudella viive(200);

ääni (soundPin, 200); // 200 Hz:n taajuudella viive(200); // LED-ohjaus: digitalWrite(ledPin, HIGH); // valoviive(200); digitalWrite(ledPin, LOW); // sammuta viive (200); loop() Sen päälle kytkemisen jälkeen on selvää, että luonnos ei ole suoritettu juuri niin kuin tarvitsemme: ennen kuin sireeni on täysin toimintavalmis, LED ei vilku, mutta haluaisimme LEDin vilkkuvan aikana

3 sireenin ääni. Mikä tässä on ongelmana? Tosiasia on, että tätä ongelmaa ei voida ratkaista tavallisella tavalla. Mikro-ohjain suorittaa tehtävät tiukasti peräkkäin. Operaattori

Arduinon kehittäjät ehdottivat vaihtoehtoa, jossa Arduino suorittaa tehtäviä pseudo-rinnakkaisina. Menetelmän ydin on, että jokaisella syklin toistolla loop() tarkistamme, onko aika vilkkua LED-valoa (suorittaa taustatehtävä) vai ei. Ja jos se on saapunut, käännämme LEDin tilan. Tämä on eräänlainen ohitusvaihtoehto operaattorille // LED-ohjaus: digitalWrite(ledPin, HIGH); // valoviive(200);.

Const int soundPin = 3; // muuttuja pietsosähköisen elementin pin-numerolla const int ledPin = 13; // muuttuja LED-pinnillä const long ledInterval = 200; // LED-valon vilkkumisväli, ms. int ledState = LOW; // LEDin alkutila etumerkitön pitkä edellinenMillis = 0; // tallentaa edellisen LED-aktivoinnin ajan void setup() ( pinMode(ääniPin, OUTPUT); // aseta nasta 3 lähdöksi. } pinMode(ledPin, OUTPUT); // ilmoittaa nastan 13 lähdöksi. pinMode(ledPin, OUTPUT); // aseta nasta 13 lähdöksi. // Äänensäätö: tone(soundPin, 700); viive (200); }

ääni (soundPin, 500); viive (200);ääni (soundPin, 300);

viive (200); // LED-ohjaus: digitalWrite(ledPin, HIGH); // valoviive(200);ääni (soundPin, 200);

viive (200); // Vilkkuva LED: // aika Arduinon käynnistämisestä, ms: etumerkitön pitkä virtaMillis = millis();// Jos vilkkumisen aika on tullut, if (currentMillis - previousMillis >= ledInterval) ( previousMillis = currentMillis; // muista sitten // LED-ohjaus: digitalWrite(ledPin, HIGH); // valoviive(200); nykyinen aika

4 if (ledState == LOW) ( // ja käännä LEDin tila ledState = HIGH; ) else ( ledState = LOW; ) digitalWrite(ledPin, ledState); // vaihtaa LEDin tilaa) Merkittävä haitta

tätä menetelmää on, että koodin osa ennen LED-ohjauslohkoa on suoritettava nopeammin kuin LED-valon vilkkumisaikaväli "ledInterval". Muuten vilkkuminen tapahtuu harvemmin kuin on tarpeen, emmekä saa tehtävien rinnakkaisen suorittamisen vaikutusta. Erityisesti luonnoksessamme sireeniäänen vaihtumisaika on 200+200+200+200 = 800 ms ja asetamme LED-vilkkujen väliksi 200 ms. Mutta LED vilkkuu 800 ms:n ajan, mikä on 4 kertaa pidempi kuin asetimme., jonka avulla voit helposti luoda näennäisen rinnakkaisia ​​prosesseja. Se toimii samalla tavalla, mutta sallii sinun olla kirjoittamatta koodia ajoituksen tarkistamiseksi - onko sinun suoritettava tehtävä tässä silmukassa vai ei. Tämä vähentää koodin määrää ja parantaa luonnoksen luettavuutta. Katsotaanpa kirjaston toimintaa.


Lataa ensin kirjaston arkisto viralliselta verkkosivustolta ja pura se hakemistoon kirjastot/ kehitysympäristö Arduino IDE. Nimeä sitten kansio uudelleen ArduinoThread-master V on, että koodin osa ennen LED-ohjauslohkoa on suoritettava nopeammin kuin LED-valon vilkkumisaikaväli "ledInterval". Muuten vilkkuminen tapahtuu harvemmin kuin on tarpeen, emmekä saa tehtävien rinnakkaisen suorittamisen vaikutusta. Erityisesti luonnoksessamme sireeniäänen vaihtumisaika on 200+200+200+200 = 800 ms ja asetamme LED-vilkkujen väliksi 200 ms. Mutta LED vilkkuu 800 ms:n ajan, mikä on 4 kertaa pidempi kuin asetimme..

Kytkentäkaavio pysyy samana. Vain ohjelmakoodi muuttuu.

#sisältää // ArduinoThread-kirjaston yhdistäminen const int soundPin = 3; // muuttuja pietsosähköisen elementin pin-numerolla const int ledPin = 13; // muuttuja, jolla on LED-nastanumero Thread ledThread = Thread(); // luo LED-ohjaussäie Thread soundThread = Thread(); // luo sireenin ohjaussäie void setup() ( pinMode(ääniPin, OUTPUT); // ilmoittaa nastan 3 lähdöksi. } pinMode(ledPin, OUTPUT); // ilmoittaa nastan 13 lähdöksi. pinMode(ledPin, OUTPUT); // ilmoittaa nastan 13 lähdöksi. } ledThread.onRun(ledBlink); // määritä tehtävä säiettä ledThread.setInterval(1000); // aseta vastausväli, ms soundThread.onRun(ääni); // antaa tehtävän säiettä soundThread.setInterval(20); // aseta vastausväli, ms // Tarkista, onko LEDin aika vaihtaa: if (ledThread.shouldRun()) ledThread.run(); // aloita säiettä // Tarkista, onko aika vaihtaa sireeniääntä: if (soundThread.shouldRun()) soundThread.run(); // aloita ketju// LED-virta: } tyhjä ledBlink() ( static bool ledTila = false; // LED-tila Päällä/Pois ledStatus = !ledStatus; // kääntää tilan digitalWrite(ledPin, ledStatus); // kytke LED päälle/pois päältä// Sireenivirta:

tyhjä ääni () ( staattinen sävy = 100; // äänenkorkeus, Hz-ääni(soundPin, ton); // laita sireeni päälle taajuudella "ton" Hz, jos (ton ) Ohjelmassa luomme kaksi säiettä - ledThread Ja äänilanka, jokainen suorittaa oman toimintonsa: yksi vilkuttaa LEDiä, toinen ohjaa sireenin ääntä. Silmukan jokaisessa iteraatiossa, jokaiselle säikeelle, tarkistamme, onko sen suorittamisen aika tullut vai ei. Jos se saapuu, se käynnistetään suoritettavaksi menetelmällä // LED-ohjaus: digitalWrite(ledPin, HIGH); // valoviive(200); juokse ()


. Tärkeintä ei ole käyttää operaattoria

. Koodi antaa tarkempia selityksiä.

Ladataan koodi Arduino-muistiin ja suoritetaan se. Nyt kaikki toimii juuri niin kuin pitääkin!
Mutta joka kerta kun "loop()"-silmukka toistetaan, voit määrittää tarkistaaksesi, onko aika suorittaa jokin lisätehtävä taustalla. Tässä tapauksessa käyttäjä näyttää, että useita tehtäviä suoritetaan samanaikaisesti.
Vilkutetaan esimerkiksi tietyllä taajuudella ja toistetaan samaan aikaan nousevia ja laskevia ääniä kuin sireeni pietsosäteilijältä.
Olemme jo yhdistäneet sekä LEDin että Arduinon Arduinoon useammin kuin kerran. Kootaan piiri kuvan osoittamalla tavalla. Jos liität LEDin muuhun digitaaliseen nastaan ​​kuin "13", älä unohda noin 220 ohmin virtaa rajoittavaa vastusta.

Kirjoitetaan tällainen luonnos ja ladataan se Arduinoon.
Taulua tarkastellessamme on selvää, että luonnos ei ole suoritettu aivan kuten tarvitsemme: ennen kuin sireeni on täysin toimintavalmis, LED ei vilku, ja haluaisimme LEDin vilkkuvan sireenin AIKANA. Mikä tässä on ongelmana?
Tosiasia on, että tätä ongelmaa ei voida ratkaista tavallisella tavalla. Mikro-ohjain suorittaa tehtävät tiukasti peräkkäin. "Delay()"-operaattori viivästyttää ohjelman suorittamista tietyn ajan, ja ennen tämän ajan umpeutumista seuraavia ohjelman komentoja ei suoriteta. Tästä johtuen emme voi asettaa eri suoritusaikaa jokaiselle ohjelman "loop()"-silmukan tehtävälle.
Siksi sinun täytyy jotenkin simuloida moniajoa.

Arduino-kehittäjät ehdottavat vaihtoehtoa, jossa Arduino suorittaa tehtäviä pseudo-rinnakkaisina, artikkelissa https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay.
Menetelmän ydin on, että jokaisella silmukan () toistolla tarkistamme, onko aika vilkkua LED-valoa (suorittaa taustatehtävä) vai ei. Ja jos se on saapunut, käännämme LEDin tilan. Tämä on eräänlainen ohitus "delay()":lle.
Tämän menetelmän merkittävä haittapuoli on, että koodin osa ennen LED-ohjauslohkoa on suoritettava pidempään kuin LED-vilkkumisaikaväli "ledInterval". Muuten vilkkuminen tapahtuu harvemmin kuin on tarpeen, emmekä saa tehtävien rinnakkaisen suorittamisen vaikutusta. Erityisesti luonnoksessamme sireeniäänen vaihtuvuuden kesto on 200+200+200+200 = 800 ms ja asetamme LEDin vilkkumisväliksi 200 ms. Mutta LED vilkkuu 800 ms:n ajan, mikä on 4 kertaa erilainen kuin asetimme. Yleensä, jos koodisi käyttää "delay()"-operaattoria, näennäisen rinnakkaisuuden simulointi on vaikeaa, joten sitä kannattaa välttää.
Tässä tapauksessa sireenin ohjausyksikön olisi myös tarkistettava, onko aika tullut vai ei, eikä käytä "delay()" -toimintoa. Mutta tämä lisäisi koodin määrää ja tekisi ohjelmasta vähemmän luettavan.

Tämän ongelman ratkaisemiseksi käytämme upeaa ArduinoThread-kirjastoa, jonka avulla voit helposti luoda pseudo-rinnakkaisprosesseja. Se on samanlainen, mutta sallii sinun olla kirjoittamatta koodia ajan tarkistamiseksi - onko sinun suoritettava tehtävä tässä syklissä vai ei. Tämä vähentää koodin määrää ja parantaa luonnoksen luettavuutta. Katsotaanpa kirjaston toimintaa.
Ensinnäkin lataa kirjaston arkisto viralliselta verkkosivustolta https://github.com/ivanseidel/ArduinoThread/archive/master.zip ja pura se Arduino IDE -kehitysympäristön "kirjastot" -hakemistoon. Nimeä sitten "ArduinoThread-master" -kansio uudelleen "ArduinoThreadiksi".

Kytkentäkaavio pysyy samana. Vain ohjelmakoodi muuttuu. Nyt se tulee näyttämään sisäpuolelta.
Ohjelmassa luomme kaksi säiettä, joista jokainen suorittaa oman toimintonsa: yksi vilkkuu LEDiä, toinen ohjaa sireenin ääntä. Silmukan jokaisessa iteraatiossa, jokaiselle säikeelle, tarkistamme, onko sen suorittamisen aika tullut vai ei. Jos se saapuu, se käynnistetään suoritettavaksi "run()"-menetelmällä. Tärkeintä on olla käyttämättä "delay()"-operaattoria.
Koodi antaa tarkempia selityksiä.
. Tärkeintä ei ole käyttää operaattoria

Innostus, työnarkomaani tai lisääntynyt kunnianhimo pakottavat meidät kirjaimellisesti ottamaan vastaan ​​mahdollisimman monia tehtäviä ja velvoitteita. Seurauksena on, että raportointikauden aattona pää räjähtää keskeneräisistä tehtävistä, ja "tehtävälista" on yleensä tapettirullan mittainen.

Tarvitset

  • - järjestäjä
  • - ohjelmisto työajan organisointiin (esim. ChromoDoro ajastin - Google-sovellus http://clck.ru/9ZC1)
  • - Tehtäväaikataulu tai "tehtävälista" taulukon muodossa.
  • - tarrat ja merkit

. Koodi antaa tarkempia selityksiä.

"Sinulle ja minulle"

Kaikki tehtävät on jaettu suoritettaviin tai ne voidaan antaa jollekin, joka on vähemmän kiireinen. On olemassa joukko liian vastuullisia johtajia, jotka uskovat, että heidän on kannettava koko osaston työ itsellään. Vanhemmat eivät siis salli sinun sitoa kengännauhojasi, koska vanhemmat ovat siinä parempia. Et voi ryöstää ja riistää alaista mahdollisuutta esitellä kykyjään. Sama on kotona. Sen sijaan, että lupaisit itsellesi joka ilta korjata tippuvan hanan, sinun on soitettava putkimiehelle ja jätettävä tehtävä pois luettelosta.

"Alas viivytyksestä"

Tällä psykologisella ilmiöllä on monia lajikkeita ja vastaavasti määritelmiä, mutta yleensä se on haluttomuus aloittaa ongelman ratkaiseminen. Joskus ensimmäinen askel on vaikea ottaa alitajuisen epäonnistumisen pelon vuoksi, joskus se on suoraa haluttomuutta siirtyä mukavasta laiskuuden tilasta epämukavaan. Useimmiten ihminen sanoo itselleen: "Syön omenan, pelaan pasianssia ja sitten...". Meidän on vaihdettava mieltämme. Ja tee omenasta ja pasianssista palkinto ponnisteluistasi. Ja jokapäiväinen mantra kuulostaa: "Jos työskentelen 15 minuuttia, syön omenan, olen sen ansainnut." Mutta ajanhallinnan taito ei ole helppoa.

Tehtäväryhmät

Liiketoiminnassa nämä voivat olla työalueisiin liittyviä tehtäviä (logistiikka tai hinnoittelu). Opiskelijalla on temaattiset lohkot opiskeltavasta materiaalista. Edistyneet ihmiset jakavat asunnon vyöhykkeisiin. "Kylpyhuone", "käytävä", "tila television vieressä" - kaikilla on yksilöllinen lähestymistapa. Ja vietä enintään 15 minuuttia kullakin vyöhykkeellä järjestyksen palauttamiseksi. Tämä riittää helposti ylläpitämään puhtautta, säästämään aikaa ja olemaan hulluksi syyllisyyteen keskeneräisten tehtävien vuoksi. Jokaisen tehtäväryhmän viereen on hyvä kirjoittaa likimääräinen suoritettujen töiden prosenttiosuus. Työn näkyvät tulokset edistävät psykologinen mukavuus ja lisääntynyt ahdistus, joka estää tehtävien suorittamisen.

Motivaatio

Jos se on olemassa, niin tekniset temput optimoivat työn. Jos siinä on ongelmia, sähköiset muistutukset ja tarrat aiheuttavat ärsytystä. Siksi on tärkeää ymmärtää ongelmien ratkaisemisen hyödyt ja lähestyä työtä tietoisesti. Jos mieli vaeltelee miellyttävän hölynpölyn häiritsemänä, siinä ei ehkä ole tarpeeksi iloa. Masentuneen ihmisen on paljon vaikeampaa olla tehokas. Tämä tarkoittaa, että ongelmien ratkaisuun kannustimet, inspiraatiota ja erityistä positiivista luovaa asennetta on haettava ulkopuolelta.

Video aiheesta

Huomaa

Kaikki tehtävät voidaan jakaa neljään tunnettuun kategoriaan: tärkeät ja ei-kiireelliset, tärkeät ja kiireelliset, merkityksettömät ja kiireelliset, merkityksettömät ja ei kiireelliset. Silloin on helpompi laskea kunkin tehtävän suorittamiseen kuluva aika. Tärkeintä ei ole unohtaa sisällyttää viidentoista minuutin taukoja.

Hyödyllisiä neuvoja

Älä ruiskuta. Me kaikki yritämme täyttää muiden odotukset. Mutta meidän on opittava sanomaan: "Lopeta." Suorittaaksesi tehtävän tehokkaasti, sinun on keskityttävä siihen täysin. Ja tätä varten sinun on ilmeisesti työskenneltävä yhteen suuntaan ja rasitettava kaikki mielesi ponnistelut ongelmien ratkaisemiseksi.

Yleisesti ottaen Arduino ei tue todellista tehtävien rinnastusta tai monisäikeistystä. Mutta se on mahdollista jokaisella syklin toistolla loop() ohjeistaa mikro-ohjainta tarkistamaan, onko aika suorittaa jokin lisätehtävä taustalla. Tässä tapauksessa käyttäjä näyttää, että useita tehtäviä suoritetaan samanaikaisesti.

Vilkutetaan esimerkiksi LEDiä tietyllä taajuudella ja toistetaan samaan aikaan nousevia ja laskevia ääniä kuin pietsosäteilijän sireeni. Olemme jo yhdistäneet sekä LEDin että pietsosäteilijän Arduinoon useammin kuin kerran. Kootaan piiri kuvan osoittamalla tavalla.

Jos liität LEDin muuhun digitaaliseen nastaan ​​kuin "13", älä unohda noin 220 ohmin virtaa rajoittavaa vastusta.

2 LED- ja pietsosäteilijäohjaus käyttämällä delay()-operaattoria

Kirjoitetaan tällainen luonnos ja ladataan se Arduinoon.

Const int soundPin = 3; /* ilmoittaa muuttuja sen nastan numerolla, johon pietsosähköinen elementti on kytketty */ const int ledPin = 13; // ilmoittaa muuttuja LED-pinninumerolla void setup() ( pinMode(ääniPin, OUTPUT); // ilmoittaa nastan 3 lähdöksi. } pinMode(ledPin, OUTPUT); // ilmoittaa nastan 13 lähdöksi. void loop() ( }

// Äänensäätö: tone(soundPin, 700); // antaa äänen taajuudella 700 Hz delay(200); ääni (soundPin, 500); // 500 Hz:n taajuudella viive(200);ääni (soundPin, 300); // 300 Hz:n taajuudella viive(200);

ääni (soundPin, 200); // 200 Hz:n taajuudella viive(200); // LED-ohjaus: digitalWrite(ledPin, HIGH); // valoviive(200); digitalWrite(ledPin, LOW); // sammuta viive (200); loop() Sen päälle kytkemisen jälkeen on selvää, että luonnos ei ole suoritettu juuri niin kuin tarvitsemme: ennen kuin sireeni on täysin toimintavalmis, LED ei vilku, mutta haluaisimme LEDin vilkkuvan aikana

3 sireenin ääni. Mikä tässä on ongelmana? Tosiasia on, että tätä ongelmaa ei voida ratkaista tavallisella tavalla. Mikro-ohjain suorittaa tehtävät tiukasti peräkkäin. Operaattori

Arduinon kehittäjät ehdottivat vaihtoehtoa, jossa Arduino suorittaa tehtäviä pseudo-rinnakkaisina. Menetelmän ydin on, että jokaisella syklin toistolla loop() tarkistamme, onko aika vilkkua LED-valoa (suorittaa taustatehtävä) vai ei. Ja jos se on saapunut, käännämme LEDin tilan. Tämä on eräänlainen ohitusvaihtoehto operaattorille // LED-ohjaus: digitalWrite(ledPin, HIGH); // valoviive(200);.

Const int soundPin = 3; // muuttuja pietsosähköisen elementin pin-numerolla const int ledPin = 13; // muuttuja LED-pinnillä const long ledInterval = 200; // LED-valon vilkkumisväli, ms. int ledState = LOW; // LEDin alkutila etumerkitön pitkä edellinenMillis = 0; // tallentaa edellisen LED-aktivoinnin ajan void setup() ( pinMode(ääniPin, OUTPUT); // aseta nasta 3 lähdöksi. } pinMode(ledPin, OUTPUT); // ilmoittaa nastan 13 lähdöksi.// Äänensäätö: tone(soundPin, 700); }

viive (200);

viive (200); // LED-ohjaus: digitalWrite(ledPin, HIGH); // valoviive(200);ääni (soundPin, 200);

ääni (soundPin, 500); // LED-ohjaus: digitalWrite(ledPin, HIGH); // valoviive(200); nykyinen aika

4 if (ledState == LOW) ( // ja käännä LEDin tila ledState = HIGH; ) else ( ledState = LOW; ) digitalWrite(ledPin, ledState); // vaihtaa LEDin tilaa) Merkittävä haitta

tätä menetelmää on, että koodin osa ennen LED-ohjauslohkoa on suoritettava nopeammin kuin LED-valon vilkkumisaikaväli "ledInterval". Muuten vilkkuminen tapahtuu harvemmin kuin on tarpeen, emmekä saa tehtävien rinnakkaisen suorittamisen vaikutusta. Erityisesti luonnoksessamme sireeniäänen vaihtumisaika on 200+200+200+200 = 800 ms ja asetamme LED-vilkkujen väliksi 200 ms. Mutta LED vilkkuu 800 ms:n ajan, mikä on 4 kertaa pidempi kuin asetimme., jonka avulla voit helposti luoda näennäisen rinnakkaisia ​​prosesseja. Se toimii samalla tavalla, mutta sallii sinun olla kirjoittamatta koodia ajoituksen tarkistamiseksi - onko sinun suoritettava tehtävä tässä silmukassa vai ei. Tämä vähentää koodin määrää ja parantaa luonnoksen luettavuutta. Katsotaanpa kirjaston toimintaa.


Lataa ensin kirjaston arkisto viralliselta verkkosivustolta ja pura se hakemistoon kirjastot/ viive (200); ArduinoThread-master V on, että koodin osa ennen LED-ohjauslohkoa on suoritettava nopeammin kuin LED-valon vilkkumisaikaväli "ledInterval". Muuten vilkkuminen tapahtuu harvemmin kuin on tarpeen, emmekä saa tehtävien rinnakkaisen suorittamisen vaikutusta. Erityisesti luonnoksessamme sireeniäänen vaihtumisaika on 200+200+200+200 = 800 ms ja asetamme LED-vilkkujen väliksi 200 ms. Mutta LED vilkkuu 800 ms:n ajan, mikä on 4 kertaa pidempi kuin asetimme..

Kytkentäkaavio pysyy samana. Vain ohjelmakoodi muuttuu.

#sisältää // ArduinoThread-kirjaston yhdistäminen const int soundPin = 3; // muuttuja pietsosähköisen elementin pin-numerolla const int ledPin = 13; // muuttuja, jolla on LED-nastanumero Thread ledThread = Thread(); // luo LED-ohjaussäie Thread soundThread = Thread(); // luo sireenin ohjaussäie void setup() ( pinMode(ääniPin, OUTPUT); // ilmoittaa nastan 3 lähdöksi. } pinMode(ledPin, OUTPUT); // ilmoittaa nastan 13 lähdöksi. pinMode(ledPin, OUTPUT); // ilmoittaa nastan 13 lähdöksi. } ledThread.onRun(ledBlink); // määritä tehtävä säiettä ledThread.setInterval(1000); // aseta vastausväli, ms soundThread.onRun(ääni); // antaa tehtävän säiettä soundThread.setInterval(20); // aseta vastausväli, ms // Tarkista, onko LEDin aika vaihtaa: if (ledThread.shouldRun()) ledThread.run(); // aloita säiettä // Tarkista, onko aika vaihtaa sireeniääntä: if (soundThread.shouldRun()) soundThread.run(); // aloita ketju// LED-virta: } tyhjä ledBlink() ( static bool ledTila = false; // LED-tila Päällä/Pois ledStatus = !ledStatus; // kääntää tilan digitalWrite(ledPin, ledStatus); // kytke LED päälle/pois päältä// Sireenivirta:

tyhjä ääni () ( staattinen sävy = 100; // äänenkorkeus, Hz-ääni(soundPin, ton); // laita sireeni päälle taajuudella "ton" Hz, jos (ton ) Ohjelmassa luomme kaksi säiettä - ledThread Ja äänilanka, jokainen suorittaa oman toimintonsa: yksi vilkuttaa LEDiä, toinen ohjaa sireenin ääntä. Silmukan jokaisessa iteraatiossa, jokaiselle säikeelle, tarkistamme, onko sen suorittamisen aika tullut vai ei. Jos se saapuu, se käynnistetään suoritettavaksi menetelmällä // LED-ohjaus: digitalWrite(ledPin, HIGH); // valoviive(200); juokse ()


. Tärkeintä ei ole käyttää operaattoria

ääni (soundPin, 300); viive (200);ääni (soundPin, 200);

viive (200);

// Vilkkuva LED: // aika Arduinon käynnistämisestä, ms: etumerkitön pitkä virtaMillis = millis(); // Jos on tullut aika vilkkua, if (currentMillis - previousMillis >= ledInterval) ( previousMillis = currentMillis; // niin muista nykyinen aika if (ledState == LOW) ( // ja käännä LEDin tila ledState = HIGH ) else ( ledState = LOW; ) digitalWrite(ledPin, ledState); Tämän menetelmän merkittävä haittapuoli on, että koodin osa ennen LED-ohjauslohkoa on suoritettava nopeammin kuin LED-vilkkumisaikaväli "ledInterval". Muuten vilkkuminen tapahtuu harvemmin kuin on tarpeen, emmekä saa tehtävien rinnakkaisen suorittamisen vaikutusta. Erityisesti luonnoksessamme sireeniäänen vaihtumisaika on 200+200+200+200 = 800 ms ja asetamme LED-vilkkujen väliksi 200 ms. Mutta LED vilkkuu 800 ms:n ajan, mikä on 4 kertaa pidempi kuin asetimme. Tässä tapauksessa sireenin äänenohjausyksikön olisi myös tarkistettava, onko aika tullut vai ei, eikä käytä, joka vaikuttaa Arduinoon. Kun tämä koodi on suoritettu, prosessori palaa siihen, mitä se alun perin teki, ikään kuin mitään ei olisi tapahtunut!

Tässä on hämmästyttävää, että keskeytykset mahdollistavat ohjelman järjestämisen vastaamaan nopeasti ja tehokkaasti tärkeisiin tapahtumiin, joita ei niin helposti ennakoida ohjelmasilmukassa. Mikä parasta, keskeytykset antavat prosessorille mahdollisuuden tehdä muita asioita sen sijaan, että tuhlattaisiin aikaa odottamaan tapahtuman tapahtumista.

Painike keskeyttää

Aloitetaan yksinkertainen esimerkki: Käytä keskeytystä painikkeen painallusten seuraamiseen. Aluksi otamme luonnoksen, jonka olet todennäköisesti jo nähnyt: "Button"-esimerkki, joka sisältyy Arduino IDE:hen (löydät sen "Examples"-hakemistosta, tarkista valikosta Tiedosto → Esimerkit → 02. Digitaalinen → Painike ).

Const int buttonPin = 2; // pin numero painikkeella const int ledPin = 13; // PIN-numero LEDillä int buttonState = 0; // muuttuja painikkeen tilan lukemiseen void setup() ( // aseta LED-nasta lähtöön: pinMode(ledPin, OUTPUT); // aseta painikkeen nasta tuloon: pinMode(buttonPin, INPUT); ) void loop () ( // lue painikkeen tila: buttonState = digitalRead(buttonPin); // tarkista onko painiketta painettu. // jos painetaan, niin buttonState on HIGH: if (buttonState == HIGH) ( // kytke päälle LED: digitalWrite(ledPin, HIGH ) else ( // sammuta LED: digitalWrite(ledPin, LOW); ) )

Tässä näkemässäsi ei ole mitään järkyttävää tai yllättävää: ohjelma ei muuta kuin käy läpi loop():n ja lukee buttonPin :n arvon. Oletetaan hetkeksi, että haluaisit tehdä jotain enemmän loop(:lla), jotain muutakin kuin vain lukea tulosteen tilaa. Tässä keskeyttäminen on hyödyllistä. Sen sijaan, että valvoisimme jatkuvasti nastan tilaa, voimme määrittää tämän työn keskeytykselle ja vapaalle silmukalle () tehdäksemme mitä tarvitsemme sillä välin! Uusi koodi näyttää tältä:

Const int buttonPin = 2; // pin numero painikkeella const int ledPin = 13; // PIN-numero LED-valolla volatile int buttonState = 0; // muuttuja painikkeen tilan lukemiseen void setup() ( // aseta LED-nasta lähtöön: pinMode(ledPin, OUTPUT); // aseta painikkeen nasta tuloon: pinMode(buttonPin, INPUT); // liitä keskeytys ISR-vektoriin attachInterrupt (0, pin_ISR, CHANGE) void pin_ISR() ( buttonState = digitalRead(buttonPin); digitalWrite(ledPin, buttonState); )

Silmukat ja keskeytystilat

Huomaat tässä muutamia muutoksia. Ensimmäinen ja ilmeisin on, että loop() ei nyt sisällä ohjeita! Pärjäämme ilman niitä, koska kaikki työ, joka aiemmin tehtiin if/else-lauseessa, on nyt tehty uusi ominaisuus pin_ISR() . Tämän tyyppistä funktiota kutsutaan keskeytyskäsittelijäksi: sen tehtävänä on suorittaa nopeasti, käsitellä keskeytystä ja antaa prosessorin palata takaisin pääohjelmaan (eli silmukan () sisältöön). On useita asioita, jotka on otettava huomioon kirjoitettaessa keskeytyskäsittelijää: tärkeitä kohtia, jonka näet yllä olevassa koodissa:

  • Käsittelijöiden tulee olla lyhyitä ja ytimekkäitä. Et halua keskeyttää pääsilmukkaa pitkäksi aikaa!
  • Käsittelijöillä ei ole syöttöparametreja ja palauttaa arvot. Kaikki muutokset on tehtävä globaaleihin muuttujiin.

Ihmettelet todennäköisesti: mistä tiedämme, milloin keskeytys laukeaa? Mikä sen aiheuttaa? Kolmas funktio, jota kutsutaan setup()-funktiossa, asettaa keskeytyksen koko järjestelmälle. Tämä toiminto attachInterrupt() ottaa kolme argumenttia:

  1. keskeytysvektori, joka määrittää, mikä nasta voi tuottaa keskeytyksen. Tämä ei ole itse PIN-numero, vaan viittaus muistipaikkaan, jota Arduino-prosessorin on tarkkailtava nähdäkseen, onko keskeytys tapahtunut. Tämä tila tässä vektorissa vastaa tiettyä ulkoinen lähtö, eivätkä kaikki nastat voi aiheuttaa keskeytystä! Arduino Unossa nastat 2 ja 3 voivat tuottaa keskeytyksiä keskeytysvektoreilla 0 ja 1. Katso luettelo nastoista, jotka voivat tuottaa keskeytyksiä, Arduino attachInterrupt -funktion dokumentaatiosta.
  2. keskeytyskäsittelijän funktion nimi: määrittää koodin, joka suoritetaan, kun keskeytyksen liipaisuehto täyttyy;
  3. keskeytystila, joka määrittää, mikä pin-toiminto laukaisee keskeytyksen. Arduino Uno tukee neljää keskeytystilaa:
    • RISING - aktivoi nousevan reunan keskeytyksen keskeytysnastassa;
    • FALLING - aktivoi putoamisen keskeytyksen;
    • CHANGE - reagoi kaikkiin keskeytysnastan arvon muutoksiin;
    • LOW - Soittaa aina, kun nasta on alhainen.

Ja yhteenvetona totean, että attachInterrupt()-asetuksemme vastaa keskeytysvektorin 0 (nasta 2) seurantaa, jotta keskeytykseen vastaa pin_ISR() ja pin_ISR() kutsuminen aina tulee muutos tila nastassa 2.

Haihtuva

Toinen huomionarvoinen seikka on, että keskeytyskäsittelijämme käyttää buttonState-muuttujaa lähdön tilan tallentamiseen. Tarkista sen sijaan buttonState: määritelmä kirjoita int, määritimme sen haihtuvaksi int-tyypiksi. Mikä tässä on hätänä? volatile on C-kielen avainsana, joka koskee muuttujia. Se tarkoittaa, että muuttujan arvo ei ole alle täysi hallinta ohjelmia. Toisin sanoen buttonStaten arvo voi muuttua ja muuttua sellaiseksi, jota ohjelma itse ei voi ennustaa - tässä tapauksessa käyttäjän syötteeksi.

Vielä yksi hyödyllinen asia volatile-avainsanassa on suojata vahingossa tapahtuvalta optimoinnilta. Osoittautuu, että kääntäjät tekevät muutaman lisää lisätehtäviä kun muunnetaan ohjelman lähdekoodia konekoodiksi suoritettava koodi. Yksi näistä tehtävistä on poistaa käyttämättömät lähdekoodi muuttujat alkaen konekoodi. Koska buttonState-muuttujaa ei käytetä tai kutsuta suoraan sisään silmukkatoiminnot() tai setup() , on olemassa vaara, että kääntäjä saattaa poistaa sen käyttämättömänä muuttujana. Ilmeisesti tämä on väärin - tarvitsemme tämän muuttujan! avainsana epävakaa on sivuvaikutus, kertoo kääntäjälle, että tämä muuttuja tulee jättää yksin.

Käyttämättömien muuttujien poistaminen koodista on toiminnallinen ominaisuus, ei ole kääntäjävirhe. Joskus ihmiset jättävät koodiinsa käyttämättömiä muuttujia, jotka vievät muistia. Se ei ole niin iso ongelma, jos kirjoitat C-ohjelmaa tietokoneelle, jossa on gigatavua RAM-muistia. Kuitenkin Arduinossa RAM rajoitettu, etkä halua tuhlata sitä! Jopa C-kääntäjät tietokoneille tekevät täsmälleen saman asian käytettävissä olevan massasta huolimatta järjestelmämuisti. Mitä varten? Samasta syystä kuin ihmiset siivoavat jälkensä piknikin jälkeen – se on hyvä käytäntö, älä jätä roskia taaksesi.

Yhteenvetona

Keskeytykset ovat yksinkertainen tapa saada järjestelmä reagoimaan nopeammin aikaherkkään tehtäviin. Heillä on myös lisäetua- vapauttaa main loop() -silmukan, jolloin se voi keskittyä järjestelmän päätehtävään (huomasin, että keskeytysten käyttäminen pitää koodini hieman järjestyksessä: on helpompi nähdä, mihin pääkoodi on suunniteltu ja mitä jaksoittaisia ​​tapahtumia keskeytykset käsittelevät). Tässä esitetty esimerkki on keskeytysten peruskäyttötapaus; voit lukea tietoja I2C-laitteesta, langaton lähetys ja vastaanottaa tietoja tai jopa käynnistää tai sammuttaa moottorin.

Onko hienoja projekteja, joissa on keskeytyksiä? Jätä kommenttisi alle!