Kuinka suorittaa rinnakkaisia ​​tehtäviä (säikeitä) Arduino-ohjelmassa. Arduino keskeyttää attachInterruptilla Miksi tarvitsemme laitteistokeskeytyksiä

Ohje

Yleisesti ottaen Arduino ei tue todellista tehtävien rinnastusta tai monisäikeistystä.
Mutta on mahdollista määrittää jokaisella "silmukan ()"-silmukan toistolla, jotta voidaan tarkistaa, onko tullut aika suorittaa jokin ylimääräinen taustatehtävä. Tässä tapauksessa käyttäjä näyttää, että useita tehtäviä suoritetaan samanaikaisesti.
Räpytetään esimerkiksi tietyllä taajuudella ja annetaan samanaikaisesti nousevia ja laskevia ääniä kuin sireeni pietsosäteilijältä.
Ja LED, ja olemme jo muodostaneet yhteyden Arduinoon useammin kuin kerran. Kootaan piiri kuvan osoittamalla tavalla. Jos liität LEDin muuhun digitaaliseen nastaan ​​kuin "13", älä unohda 220 ohmin virranrajoitusvastusta.

Kirjoitetaan tämä luonnos ja ladataan se Arduinoon.
Taulun jälkeen voidaan nähdä, että sketsi ei toteuta aivan kuten tarvitsemme: ennen kuin sireeni toimii täysin, LED ei vilku, ja toivomme, että LED-valo sireenin äänen 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()"-käsky viivästyttää ohjelman suorittamista tietyn ajan, ja ennen kuin tämä aika on kulunut umpeen, ohjelman seuraavia komentoja ei suoriteta. Tästä johtuen emme voi asettaa eri kestoa jokaiselle ohjelman "loop()"-silmukan tehtävälle.
Siksi sinun täytyy jotenkin simuloida moniajoa.

Arduino-kehittäjät ehdottavat versiota, jossa Arduino suorittaa tehtäviä pseudo-rinnakkaisena artikkelissa https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay.
Menetelmän ydin on, että jokaisessa "silmukan ()"-silmukan toistossa tarkistamme, onko aika vilkkua LED-valoa (suorittaa taustatehtävä) vai ei. Ja jos on, niin käännämme LEDin tilan. Tämä on eräänlainen "delay()"-operaattorin ohitus.
Tämän menetelmän merkittävä haittapuoli on, että koodiosio ennen LED-ohjauslohkoa on suoritettava nopeammin kuin "ledInterval"-LEDin vilkkumisaikaväli. Muuten vilkkuminen tapahtuu harvemmin kuin on tarpeen, emmekä saa tehtävien rinnakkaisen suorittamisen vaikutusta. Erityisesti luonnoksessamme sireenin äänen muutoksen 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 koodissa käytetään "delay()"-operaattoria, niin näennäisen rinnakkaisuuden simulointi on vaikeaa, joten sitä kannattaa välttää.
Tässä tapauksessa sireenin äänenohjauslohkon olisi myös tarkistettava, onko aika tullut vai ei, eikä käytä "viivettä (). Mutta tämä lisäisi koodin määrää ja heikentäisi ohjelman luettavuutta.

Tämän ongelman ratkaisemiseksi käytämme upeaa ArduinoThread-kirjastoa, jonka avulla voit helposti luoda pseudo-rinnakkaisprosesseja. Se toimii samalla tavalla, mutta sallii sinun olla kirjoittamatta koodia ajan tarkistamiseksi - sinun on suoritettava tehtävä tässä syklissä vai ei. Tämä vähentää koodin määrää ja parantaa luonnoksen luettavuutta. Katsotaanpa kirjaston toimintaa.
Lataa ensin 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 on sama kuin sivupalkissa.
Ohjelmassa luomme kaksi säiettä, joista jokainen suorittaa oman toimintonsa: yksi vilkkuu LEDiä, toinen ohjaa sireenin ääntä. Jokaisen säikeen silmukan jokaisessa iteraatiossa 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.
Tarkemmat selitykset on annettu koodissa.
Lataa koodi Arduino-muistiin, suorita se. Nyt kaikki toimii juuri niin kuin pitääkin!

Tämä artikkeli käsittelee kysymyksiä useiden orjalaitteiden rinnakkais- ja sarjaliitännöistä SPI-väylään, vuororekisterien sarjaliitännät, työskentely kahden 7-segmenttisen näytön kanssa, itsenäisten prosessien toteuttaminen Arduinossa. Tämän seurauksena teemme piirin, jossa käärme juoksee kaksinkertaista 7-segmenttiä ja sekuntia tikittää toisessa, yksittäisessä, tällä hetkellä.

tutustuimme SPI-väylään ja opimme, että tarvitaan 4 johtoa orjan yhdistämiseen isäntään. Jos orjalaitteita on kuitenkin enemmän kuin yksi, meillä on jo mielenkiintoisia vaihtoehtoja.

Laitteiden kytkeminen rinnakkain SPI-väylään

Rinnakkain kytkettynä useat orjalaitteet käyttävät yhteisiä johtoja SCLK, MOSI Ja MISO, kun taas jokaisella orjalla on oma linjansa SS . Ohjaaja päättää vaihdettava laite, kirjoittaja muodostaen siihen matalan signaalin SS .
Voidaan nähdä, että yhteyden muodostamiseksi n tarvittavat laitteet n rivit SS , eli SPI-ympäristön toimintaan n tätä varten on osoitettava orjia n+3 mikro-ohjaimen jalat.

Laitteiden sarjaliitäntä SPI-väylään

Kun laitteet on kytketty sarjaan, ne käyttävät yhteisiä johtoja. SCLK Ja SS , ja toisen lähtö on kytketty toisen tuloon. MOSI isäntä muodostaa yhteyden ensimmäiseen laitteeseen ja MISO- viimeiseen. Eli SPI-väylän isännälle tämä on ikään kuin yksi laite.
Tällainen yhteys mahdollistaa esimerkiksi yhden 16-bittisen siirtorekisterin rakentamisen kahdesta 8-bittisestä siirtorekisteristä, minkä nyt teemme.
On syytä huomata tällaisen yhteyden viehätys: liitä vähintään 3, vähintään 8 laitetta, se vie vain 4 jalkaa ohjaimeen.

Kahden siirtorekisterin sarjaliitäntä
Katsotaanpa vielä 74HC595-vaihtorekisteriä:

Muistamme sen D.S.- on sarjatulonasta, ja Q0-Q7 sarjalähtönastat. Q7S, jota emme käyttäneet, kun piirissä oli vain yksi rekisteri, on rekisterin sarjalähtö. Se löytää käyttönsä, kun siirrämme yli 1 tavun rekistereihin. Tämän pinin kautta kaikki myöhemmille rekistereille tarkoitetut tavut työnnetään peräkkäin ja viimeinen lähetetty tavu jää ensimmäiseen rekisteriin.


Yhdistämällä yhden ensimmäisen rekisterin Q7S-nasta toisen (ja niin edelleen, tarvittaessa) DS-nastaan, saadaan kaksoisrekisteri (tms. kolminkertainen).

Kahden 7-segmenttisen näytön liittäminen

Kaksinkertainen 7-segmenttinen näyttö on tyypillisesti laite, jossa on 18 jalkaa, 9 kutakin merkkiä kohti. Katsotaanpa kaaviota (näyttöni on merkitty LIN-5622SR ja on suuri todennäköisyys, että sen kytkentäkaavio on ainutlaatuinen):

Tämä on yleinen anodinäyttö, mikä tarkoittaa, että com1:lle ja com2:lle on syötettävä korkea TTL-taso ja matala taso vastaavassa jalassa diodin sytyttämiseksi. Jos sinulla on yhteinen katodinäyttö, tee päinvastoin!

Liitä näyttö kaavion mukaisesti:

Vasen näyttö on kytketty ensimmäiseen rekisteriin: 1A Q0-osaan, 1B Q1-osaan, 1C Q2-osaan jne. Yhdistämme yhteisen koskettimen com1 maahan. Teemme saman oikean näytön kanssa: 2A Q0-haaraan, 2B Q1-haaraan jne., yhteinen kosketin com2 on maassa.

Piiri ei näytä kuvalta, jos näytön pinout on erilainen kuin minun, tässä sinun on vain oltava varovainen kytkettäessä. Jos näyttö on varustettu yhteisellä katodilla, com1 ja com2 on kytketty virtalähteeseen!

Yksinkertainen käärme kahdella 7-segmenttisellä näytöllä

Joten viime kerralla opimme valaisemaan numeroita yksimerkkiiselle näytölle, ja tänään piirrämme käärmeen kaksimerkkiselle näytölle. Aluksi teemme yksinkertaisen käärmeen, joka koostuu kolmesta segmentistä ja kulkee ympyrässä.

Syklimme koostuu kahdeksasta kehyksestä, joista jokainen sytyttää tietyt kolme LEDiä. Ensimmäisessä kehyksessä palavat 1E, 1F, 1A (katso kaavio), toisessa - 1F, 1A, 2A, kolmannessa - 1A, 2A, 2B ja niin edelleen, kahdeksannessa - 1D, 1E, 1F .

Tehdään jälleen kätevyyden vuoksi tavutaulukko muistaen, että oletusarvoisesti bitit lähetetään alkaen korkeimmasta, ts. 2h.

Kehys

1 abcd efgh

2 abcd efgh

hex

0111 0011

1111 1111

ECFF

0111 1011

0111 1111

ED EF

0111 1111

0011 1111

EF CF

1111 1111

0001 1111

FF 8F

1111 1111

1000 1111

FF 1F

1110 1111

1100 1111

7F 3F

1110 0111

1110 1111

7E 7F

1110 0011

1111 1111

7CFF


Isännän on asetettava langan matala (aktiivinen) taso SS , siirrä kaksi tavua ja vapauta johto. Tällä hetkellä tapahtuu salpa, jokaiseen rekisteriin kirjoitetaan tavu, kaksi merkkiä syttyy.

#sisältää<SPI .h> // yhdistä SPI-kirjasto
enum(reg=9); //valitse riviSS rekisteröidy Arduinon 9. nastalle

mitätön perustaa () {
SPI.begin(); //alustaa SPI
//Kääntää lähetystä varten valitun nastan lähtötilaan
pinMode(reg, OUTPUT);
}


mitätön silmukka () {
//Täytä matriisi tavuilla, jotka lähetämme
staattinen uint8_t numero =
(0xFF,0xCE,0xFF,0xDE,0xFC,0xFE,0xF8,0xFF,
0xF1,0xFF,0xF3,0xF7,0xF7,0xE7,0xFF,0xC7};
//siirtää kaksi tavua taulukosta ja lukita rekisterit
for (int i=0;i<16;i+=2){
digitalWrite(reg, LOW);
SPI .transfer(digit[i]);
SPI.siirto(numero);
digitalWrite(reg, HIGH);
viive(80); //tauko kehysten välillä
}
}


Video ohjelmasta:

Rinnakkaisprosessit Arduinossa

Miksi Arduino-kehittäjät kiinnittävät erityistä huomiota Blink viipymättä -esimerkkiin?

Yleensä Arduino-ohjelma on lineaarinen - ensin tehdään yksi asia, sitten toinen. Yllä olevassa esimerkissä käytimme funktiota viive (80) niin, että jokainen kehys piirretään 80 millisekuntia edellisen jälkeen. Kuitenkin loppujen lopuksi nämä 80 millisekuntia prosessori ei tee mitään eikä anna kenenkään tehdä mitään! Jotta voimme ajaa kahta tai useampaa rinnakkaista prosessia, meidän on muutettava ohjelman rakentamisen, luopumisen käsitettä viive() .

Uuden suunnittelumme ydin on ajastin. Ajastin laskee ajan, ja pakotamme tämän tai toisen tapahtuman tapahtumaan tietyin väliajoin. Esimerkiksi kellonäyttö tikittää sekunnin välein ja LED-valo vilkkuu 0,86 sekunnin välein.

Arduinossa on asia, joka laskee ajan ohjelman alkamisesta, sitä kutsutaan millis(). Sen avulla järjestetään tehtävien "rinnakkaisu".

Lopputyö: kello ja ovela käärme


Kootaan tämä kaavio:

Vasen ja keskimmäinen rekisteri toimii meille johtajan näkökulmasta yhtenä laitteena ja oikea rekisteri - toisena. Voidaan nähdä, että nämä kaksi laitetta käyttävät samaa johtoa. SCLK(Arduino-nasta 13, johto näkyy oranssina) ja MOSI(11. pin, keltainen), SS käytetään erilaisia ​​(nastat 8 ja 9, vihreä väri). 7-segmenttisten näyttöjen liittäminen rekistereihin näkyy tietyissä malleissani, eikä se todennäköisesti vastaa sinun malleitasi.


Tällä kertaa teemme käärmeestämme ovelamman: se juoksee kaikkien segmenttien läpi samalla tavalla kuin susi ajoi moottoripyörällä tienristeyksessä "Odota vain!"-sarjassa, joka alkaa siitä, että hän rullaa tämän ulos. sama moottoripyörä autotallista ja laittaa kypärän päähän.

Tämän käärmeen tavusekvenssi on:

Staattinen uint8_t käärme =


Nyt ydin: toiminto millis() istuu ja laskee millisekunteja alusta. Jokaisen silmukan alussa muistamme arvon millis() muuttujaksi ajastin. Muuttujien asettaminen snakeAjastinEd Ja digitTimerPrev, joka tallentaa edellisen tapahtuman hetken: for snakeAjastinEd on käärmeanimaation edellisen kehyksen sisällyttäminen digitTimerPrev- edellisen numeron sisällyttäminen. Heti kun nykyinen aikaero ( ajastin) ja edellinen ( snakeAjastinEd tai digitTimerPrev) tulee yhtä suureksi kuin määritetty aika (tapauksessamme 80 ja 1000 ms, vastaavasti), lähetämme seuraavan kehyksen/tavun.

Täten,

  • 80 ms välein ohjain laskee signaalia linjalla SS kaksoisnäyttö, lähetä kaksi tavua ja vapauta rivi.
  • joka sekunti ohjain laskee signaalia linjalla SS yksi näyttö, lähetä yksi tavu ja vapauta rivi.
Otetaan se käyttöön Arduinossa. Olen jo kuvaillut kaiken yksityiskohtaisesti aiemmin, mielestäni on turha kommentoida.

#sisältää<SPI .h>

enum ( snakePin = 9, digitPin = 8 );
unsigned long timer=0, snakeTimerPrev=0, digitTimerPrev=0;
int i = 0, j = 0;



mitätön perustaa () {
SPI.begin();
pinMode(digitPin, OUTPUT );
pinMode(snakePin, OUTPUT );
}


mitätön silmukka () {
staattinen uint8_t numero =
(0xC0.0xF9.0xA4.0xB0.0x99.0x92.0x82.0xF8,
0x80.0x90.0x88.0x83.0xC6.0xA1.0x86.0x8E);
staattinen uint8_t käärme =
(0xFF,0x9E,0xFF,0xDC,0xFF,0xF8,0xFF,0xF1,
0xFF,0xE3,0xFF,0xA7,0xBF,0xAF,0xBD,0xBF,
0xBC,0xFF,0xDC,0xFF,0xCE,0xFF,0xC7,0xFF,
0xE3,0xFF,0xB3,0xFF,0xBB,0xBF,0xBF,0x9F);


ajastin=millis();


jos (ajastin-käärmeTimerPrev>80)(
digitalWrite(snakePin, LOW);
SPI.siirto(käärme[j]);
SPI.siirto(käärme);
digitalWrite(snakePin, KORKEA);
j<30 ? j+=2: j=0;
snakeTimerPrev=ajastin;
}
if (ajastin-digitTimerPrev>1000)(
digitalWrite(digitPin, LOW);
SPI.siirto(numero[i]);


Laitteisto keskeyttää

En löytänyt hauskaa kuvaa tälle tunnille, löysin vain ohjelmointiluennon, ja aivan tämän luennon alku selittää meille täydellisesti, mitä keskeyttää. Arduinon keskeytys voidaan kuvata täsmälleen samalla tavalla: mikro-ohjain "pudottaa kaiken", siirtyy suorittamaan toimintolohkoa keskeytyskäsittelijässä, suorittaa ne ja palaa sitten tarkalleen pääkoodin kohtaan, jossa se pysähtyi.

Keskeytykset ovat erilaisia, eli eivät itse keskeytykset, vaan niiden syyt: keskeytys voi aiheuttaa analogia-digitaalimuuntimen, ajastinlaskurin tai kirjaimellisesti mikro-ohjaimen nastan. Tällaisia ​​keskeytyksiä kutsutaan ulkoisiksi. laitteisto ja siitä me puhumme tänään.

Ulkoinen laitteiston keskeytys- Tämä on keskeytys, joka johtuu jännitteen muutoksesta mikro-ohjaimen nastassa. Pääasia on, että mikro-ohjain (laskentaydin) ei pollaa nastaa Ja älä tuhlaa aikaa siihen, toinen "rautapala" on kiinnittämässä. Heti kun nastan jännite muuttuu (eli digitaalista signaalia, +5 kytketty / +5 poistettu) - mikro-ohjain vastaanottaa signaalin, lopettaa kaiken, käsittelee keskeytyksen ja palaa töihin. Miksi tätä tarvitaan? Useimmiten keskeytyksiä käytetään havaitsemaan lyhyitä tapahtumia - pulsseja tai jopa laskemaan niiden lukumäärää lataamatta pääkoodia. Laitteistokeskeytys voi napata lyhyen painikkeen painalluksen tai anturin liipaisimen monimutkaisten pitkien laskelmien tai koodin viiveiden aikana, ts. karkeasti sanottuna - pin on pollattu rinnakkain pääkoodin kanssa. Myös keskeytykset voivat herättää mikro-ohjaimen virransäästötiloista, kun lähes kaikki oheislaitteet on kytketty pois päältä. Katsotaanpa, miten Arduino IDE:n laitteistokeskeytyksiä käytetään.

Keskeytyksiä Arduinossa

Aloitetaan siitä, että kaikki nastat eivät "voi" keskeyttää. Kyllä, on olemassa sellainen asia kuin pinChangeInterrupts, mutta puhumme siitä edistyneillä tunneilla. Nyt meidän on ymmärrettävä, että laitteistokeskeytykset voivat tuottaa vain tietyt nastat:

MK / keskeytysnumero INT 0 INT 1 INT 2 INT 3 INT 4 INT 5
ATmega 328/168 (Nano, UNO, Mini) D2 D3
ATmega 32U4 (Leonardo, Micro) D3 D2 D0 D1 D7
ATmega 2560 (Mega) D2 D3 D21 D20 D19 D18

Kuten taulukosta ymmärsit, keskeytyksillä on oma numeronsa, joka eroaa pin-numerosta. Siinä on kätevä ominaisuus digitaalinenPinToInterrupt(pin), joka ottaa PIN-koodin ja palauttaa keskeytysnumeron. Syöttämällä tälle funktiolle Arduino nanon numero 3, saamme 1. Kaikki on yllä olevan taulukon mukaan, laiskoille tarkoitettu toiminto.

Toiminnon avulla kytketään keskeytys attachInterrupt (pin, käsittelijä, tila):

  • pin- keskeytysnumero
  • käsittelijä- keskeytyskäsittelijän toiminnon nimi (se täytyy luoda itse)
  • -tilassa– keskeytä toimintatila:
    • MATALA(matala) - laukaisee signaali MATALA pinnässä
    • NOUSEMINEN(kasvu) - laukeaa, kun nastan signaali muuttuu MATALA päällä KORKEA
    • PUTOUTUMINEN(pudota) - laukeaa, kun nastan signaali muuttuu KORKEA päällä MATALA
    • MUUTTAA(muuta) - laukeaa, kun signaali muuttuu (ja MATALA päällä KORKEA ja päinvastoin)

Keskeytys voidaan myös poistaa käytöstä toiminnolla irrota keskeytys (pin), missä pin on taas keskeytysnumero.

Voit myös poistaa keskeytykset käytöstä maailmanlaajuisesti toiminnolla noInterrupts() ja ratkaise ne uudelleen keskeyttää (). Ole varovainen niiden kanssa! noInterrupts() se myös pysäyttää ajastimen keskeytykset, ja kaikki aikatoiminnot ja PWM-sukupoltto "rikkoutuvat" puolestasi.

Katsotaanpa esimerkkiä, jossa näppäinpainallukset lasketaan keskeytyksessä ja pääsilmukassa ne lähetetään 1 sekunnin viiveellä. Työskentely painikkeella normaalitilassa on mahdotonta yhdistää tällaista karkeaa lähtöä viiveeseen:

Haihtuva int-laskuri = 0; // laskurimuuttuja void setup() ( Serial.begin(9600); // avattu portti tiedonsiirtoa varten // kytketty painike D2- ja GND pinMode(2, INPUT_PULLUP); \ // D2 on keskeytys 0 // käsittelijä - toimintopainikeTick // FALLING - kun painiketta napsautetaan, signaali on 0, ja saamme sen kiinni attachInterrupt(0, buttonTick, FALLING); ) void buttonTick() ( counter++; // + pressing ) void loop() ( Serial. println (laskuri); // lähtöviive (1000); // odota )

Joten koodimme laskee napsautukset myös viiveen aikana! Loistava. Mutta mikä on haihtuvia? Olemme ilmoittaneet globaalin muuttujan laskuri, joka tallentaa painikkeen napsautusten määrän. Jos muuttujan arvo muuttuu keskeytyksessä, sinun tulee ilmoittaa tästä mikrokontrollerille käyttämällä spesifiä haihtuvia, joka kirjoitetaan ennen muuttujan tietotyypin määrittämistä, muuten työ on virheellinen. Sinun tarvitsee vain muistaa tämä: jos muuttuja muuttuu keskeytyksen aikana, tee se haihtuvia.

Muutama tärkeä seikka:

  • Keskeytyksessä muokatut muuttujat on ilmoitettava muodossa haihtuvia
  • Keskeytyksillä ei ole viiveitä, kuten viive()
  • Ei muuta sen arvoa keskeytyksen aikana millis() Ja micros()
  • Keskeytyksessä lähtö porttiin ei toimi oikein ( Serial.print()), älä myöskään käytä sitä siellä - se lataa ytimen
  • Keskeytyksissä sinun on yritettävä tehdä mahdollisimman vähän laskelmia ja yleensä "pitkiä" toimia - tämä hidastaa MC:n työtä toistuvilla keskeytyksillä! Mitä tehdä? Lue alla.

Jos keskeytys havaitsee jonkin tapahtuman, jota ei tarvitse käsitellä välittömästi, on parempi käyttää seuraavaa keskeytyksen käsittelyalgoritmia:

  • Nosta vain lippu keskeytyskäsittelijässä
  • Ohjelman pääsilmukassa tarkistamme lipun, jos se nostetaan, nollaamme sen ja suoritamme tarvittavat toimenpiteet
volatile boolean intFlag = false; // lippu void setup() ( Serial.begin(9600); // avattu portti tiedonsiirtoa varten // kytketty painike D2:ssa ja GND pinMode(2, INPUT_PULLUP); // D2 on keskeytys 0 // käsittelijä - buttonTick-toiminto // FALLING - kun painiketta painetaan, signaali on 0 ja saamme sen kiinni attachInterrupt(0, buttonTick, FALLING); ) void buttonTick() ( intFlag = true; // nosti keskeytyslippua ) void loop() ( jos (intFlag) ( intFlag = false; // nollaa // tee jotain Serial.println("Keskeytä!"); ) )

Tässä on periaatteessa kaikki mitä sinun tulee tietää keskeytyksistä, analysoimme tarkempia tapauksia jatkotunneilla.

Video

Kun asennat tämän ohjelman, tulet yllättymään kuinka samanlainen se on Arduino IDE:n kanssa. Älä ihmettele, molemmat ohjelmat on tehty samalla moottorilla.

Sovelluksessa on paljon ominaisuuksia, mukaan lukien kirjasto Sarja, jotta voimme linkittää tiedonsiirron levyn ja .

Aloitetaan Arduino IDE ja valitaan yksinkertaisin esimerkki tietojen tuottamisesta Sarjaportti:

void setup() ( Serial.begin(9600); ) void loop() ( Serial.println("Hello Kitty!"); // odota 500 millisekuntia ennen kuin lähetät uudelleen delay(500); )

Suoritetaan esimerkki ja varmista, että koodi toimii.

Haetaan tietoja

Nyt haluamme saada saman tekstin sisään . Aloitamme uuden projektin ja kirjoitamme koodin.

Ensimmäinen vaihe on tuoda kirjasto. Mennään luonnos | Tuo kirjasto | Sarja. Viiva näkyy luonnoksessa:

Tuo käsittely.sarja.*; Sarja sarja; // luo sarjaporttiobjekti Merkkijono vastaanotettu; // sarjaportista saadut tiedot void setup() ( String port = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (serial.available() > 0) ( // jos on dataa, vastaanotettu = serial. readStringUntil("\n"); // lue tiedot ) println(received); // näytä tiedot konsolissa )

Varmistaaksemme tietojen vastaanottamisen sarjaportista tarvitsemme luokan objektin Sarja. Koska lähetämme merkkijonotietoja Arduinosta, meidän on saatava merkkijono myös käsittelyssä.

Menetelmässä perustaa() sinun on hankittava vapaa sarjaportti. Yleensä tämä on luettelon ensimmäinen käytettävissä oleva portti. Tämän jälkeen voimme asettaa kohteen Sarja, joka osoittaa portin ja tiedonsiirtonopeuden (on toivottavaa, että nopeudet täsmäävät).

On vielä kytkettävä kortti uudelleen, suoritettava luonnos käsittelystä ja tarkkailtava saapuvia tietoja sovelluskonsolissa.

Käsittelyn avulla voit työskennellä paitsi konsolin kanssa, myös luoda vakioikkunoita. Kirjoitetaan koodi uudelleen.

Tuo käsittely.sarja.*; Sarja sarja; // luo sarjaporttiobjekti Merkkijono vastaanotettu; // sarjaportista saadut tiedot void setup() ( koko(320, 120); merkkijonoportti = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (serial .available() > 0) ( // jos dataa on, // lue se ja kirjoita se muuttujaan vastaanotettu vastaanotettu = serial. readStringUntil("\n"); ) // Tekstin asetukset textSize(24); tyhjennä (); if (vastaanotettu != tyhjä) ( teksti(vastaanotettu, 10, 30); ) )

Suoritetaan esimerkki uudelleen ja nähdään ikkuna, jossa on kirjoitus, joka on piirretty uudelleen yhteen paikkaan.

Siten opimme vastaanottamaan tietoja Arduinosta. Näin voimme piirtää kauniita kaavioita tai luoda ohjelmia anturin lukemien seurantaan.

Lähetetään tietoja

Emme voi vain vastaanottaa tietoja levyltä, vaan myös lähettää tietoja levylle, pakottamalla meidät suorittamaan komentoja tietokoneelta.

Oletetaan, että lähetämme merkin "1" käsittelystä. Kun kortti havaitsee lähetetyn merkin, sytytä portin 13 LED (sisäänrakennettu).

Luonnos on samanlainen kuin edellinen. Luodaan esimerkiksi pieni ikkuna. Kun napsautat ikkuna-aluetta, lähetämme "1" ja kopioimme sen konsolissa vahvistusta varten. Jos napsautuksia ei ole, komento "0" lähetetään.

Tuo käsittely.sarja.*; Sarja sarja; // luo sarjaporttiobjekti Merkkijono vastaanotettu; // sarjaportista saadut tiedot void setup() ( koko(320, 120); merkkijonoportti = Serial.list(); serial = new Serial(this, port, 9600); ) void draw() ( if (mousePressed) == true) (//jos napsautimme ikkunassa serial.write("1"); //lähetä 1 println("1"); ) else ( //jos ei ollut napsautusta serial.write(" 0" ); // lähetä 0 ) )

Nyt kirjoitetaan luonnos Arduinolle.

Char commandArvo; // sarjaportista tulevat tiedot int ledPin = 13; // sisäänrakennettu LED void setup() ( pinMode(ledPin, OUTPUT); // tiedonantotila Serial.begin(9600); ) void loop() ( if (Serial.available()) ( commandValue = Serial.read ( ); ) if (commandValue == "1") ( digitalWrite(ledPin, HIGH); // laita LED päälle ) else ( digitalWrite(ledPin, LOW); // muuten sammuta ) viive(10); // viive ennen seuraavaa tietojen lukemista)

Suoritetaan molemmat sketsit. Napsautamme ikkunan sisään ja huomaamme, että LED syttyy. Et voi edes napsauttaa, vaan pitää hiiren painiketta painettuna - LED palaa jatkuvasti.

Tiedonvaihto

Yritetään nyt yhdistää molemmat lähestymistavat ja vaihtaa viestejä hallituksen ja sovelluksen välillä kahteen suuntaan.

Parhaan tehokkuuden saavuttamiseksi lisätään looginen muuttuja. Tämän seurauksena meidän ei enää tarvitse jatkuvasti lähettää 1 tai 0 käsittelystä ja sarjaportti puretaan eikä lähetä tarpeettomia tietoja.

Kun kortti havaitsee lähetetyn yksikön, muutamme boolen arvon päinvastaiseksi suhteessa nykyiseen tilaan ( MATALA päällä KORKEA ja päinvastoin). SISÄÄN muu käytämme merkkijonoa "Hello Kity", jonka lähetämme vain, jos emme löydä "1".

Toiminto muodosta yhteys () lähettää merkkijonon, jonka odotamme saavamme käsittelyssä. Jos vastaus tulee, käsittely voi vastaanottaa tiedot.

Char commandArvo; // sarjaportista tulevat tiedot int ledPin = 13; boolean ledState = ALHAINEN; //ohjaa LEDin tilaa void setup() ( pinMode(ledPin, OUTPUT); Serial.begin(9600); createContact(); // lähetä tavu kontaktille, kun vastaanotin vastaa ) void loop() ( // jos dataa voidaan lukea if (Serial.available() > 0) ( // lue dataa commandValue = Serial.read(); if (commandValue == "1") ( ledState = !ledState; digitalWrite(ledPin, ledState ); ) delay(100) ; ) else ( // Lähetä takaisin Serial.println("Hello Kitty"); ) delay(50); ) void createContact() ( while (Serial.available()<= 0) { Serial.println("A"); // отсылает заглавную A delay(300); } }

Siirrytään käsittelyluonnokseen. Käytämme menetelmää sarjatapahtuma(), jota kutsutaan aina, kun tietty merkki löytyy puskurista.

Lisää uusi looginen muuttuja ensimmäinen yhteys, jonka avulla voit määrittää, onko yhteys Arduinoon.

Menetelmässä perustaa() lisää rivi serial.bufferUntil("\n");. Tämä antaa meille mahdollisuuden tallentaa saapuvat tiedot puskuriin, kunnes löydämme tietyn merkin. Tässä tapauksessa palaamme (\n), koska olemme lähettämässä Serial.println() Arduinosta. "\n" lopussa tarkoittaa, että aktivoimme uuden rivin, eli tämä on viimeinen tieto, jonka näemme.

Koska lähetämme jatkuvasti tietoja, menetelmä sarjatapahtuma() suorittaa pyöräilytehtäviä piirrä (), voit jättää sen tyhjäksi.

Harkitse nyt päämenetelmää sarjatapahtuma(). Joka kerta kun syötämme uuden rivin (\n), tätä menetelmää kutsutaan. Ja joka kerta, kun seuraava toimintosarja suoritetaan:

  • Saapuvat tiedot luetaan;
  • Tarkistetaan, sisältävätkö ne arvoja (eli välitettiinkö meille tyhjä tietotaulukko tai "nolla");
  • tilojen poistaminen;
  • Jos olemme saaneet tarvittavat tiedot ensimmäistä kertaa, muutamme loogisen muuttujan arvoa ensimmäinen yhteys ja kerro Arduinolle, että olemme valmiita vastaanottamaan uutta dataa;
  • Jos tämä ei ole ensimmäinen vaaditun tietotyypin vastaanotto, näytämme ne konsolissa ja lähetämme tiedot tehdystä napsautuksesta mikrokontrolleriin;
  • Kerromme Arduinolle, että olemme valmiita vastaanottamaan uuden datapaketin.
tuontikäsittely.sarja.*; Sarja sarja; // luo sarjaporttiobjekti Merkkijono vastaanotettu; // sarjaportista saadut tiedot // Tarkista tiedot Arduinon loogisesta firstContact = false; void setup() ( koko(320, 120); merkkijonoportti = Serial.list(); serial = new Serial(this, port, 9600); serial.bufferUntil("\n"); ) void draw() ( ) void serialEvent(Serial myPort) ( // muodostaa merkkijonon vastaanotetuista tiedoista // "\n" - erotin - vastaanotetun datapaketin loppu = myPort. readStringUntil("\n"); // varmista, että että tietomme eivät ole tyhjiä ennen kuin jatkamme if (saanut != null) ( //poista välilyönnit vastaanotettu = trim(received); println(received); //etsi "A"-merkkijonoamme kättelyn aloittamiseksi //jos löytyi, tyhjennä puskuri ja lähetä tietopyyntö if (firstContact == false) ( if (received.equals("A")) ( serial.clear(); firstContact = true; myPort.write("A"); println ("yhteys"); ) ) else ( //jos yhteys on muodostettu, hae ja jäsennä tiedot println(vastaanotettu); if (mousePressed == true) (//jos napsautimme ikkunaa serial.write( "1"); //lähetä 1 println(" 1"); ) // kun sinulla on kaikki tiedot, tee pyyntö uudesta paketista serial.write("A"); ) ) )

Kun yhteys on kytketty ja käynnistetty, ilmaisu "Hello Kitty" pitäisi näkyä konsolissa. Kun napsautat Käsittely-ikkunaa, nastan 13 LED syttyy ja sammuu.

Prosessoinnin lisäksi voit käyttää PuTTy-ohjelmia tai kirjoittaa omia C#-ohjelmia käyttämällä valmiita luokkia porttien kanssa työskentelyyn.

04. Viestintä: Himmennin

Esimerkki osoittaa, kuinka voit lähettää tietoja tietokoneesta levylle LED-valon kirkkauden säätämiseksi. Tiedot tulevat yksittäisinä tavuina 0–255. Tiedot voivat tulla mistä tahansa tietokoneen ohjelmasta, jolla on pääsy sarjaporttiin, mukaan lukien Processing.

Tarvitset esimerkiksi vakiopiirin, jossa on vastus ja LED nastassa 9.

Luonnos Arduinolle.

Const int ledPin = 9; // LED nastassa 9 void setup() ( Serial.begin(9600); // aseta nastan tila pinMode(ledPin, OUTPUT); ) void loop() (tavun kirkkaus; // tarkista onko dataa tietokone if (Serial.available()) ( // lue viimeksi vastaanotetut tavut 0 - 255 brightness = Serial. read(); // aseta LEDin kirkkaus analogWrite(ledPin, kirkkaus); ) )

Koodi käsittelyä varten

Tuo käsittely.sarja.*; sarjaportti; void setup() ( koko(256, 150); println("Saatavilla olevat sarjaportit:"); println(Serial.list()); // Käyttää luettelon ensimmäistä porttia (numero 0). Muuta tätä valitaksesi portti // joka vastaa Arduino-korttiasi. Viimeinen parametri (esim. 9600) on // tiedonsiirron nopeus. Sen on vastattava arvoa, joka välitetään // Serial.begin() Arduino-luonnoksessasi. port = new Serial (this, Serial.list(), 9600); // Jos tiedät Arduino-levyn käyttämän portin nimen, määritä nimenomaisesti //port = new Serial(this, "COM1", 9600); ) void draw( ) ( // piirrä gradientti mustasta valkoiseksi kohteelle (int i = 0; i

Käynnistä ja siirrä hiiren osoitin luodun ikkunan päälle mihin tahansa suuntaan. Vasemmalle siirrettäessä LEDin kirkkaus pienenee, oikealle siirrettäessä se kasvaa.

04. Viestintä: PhysicalPixel (sytytä LED-valo hiirellä)

Muutetaan vähän ongelmaa. Siirrämme hiiren neliön päälle ja lähetämme merkin "H" (korkea) sytyttämään LEDin laudalla. Kun hiiri poistuu neliön alueelta, lähetämme merkin "L" (matala) sammuttamaan LED.

Koodi Arduinolle.

Const int ledPin = 13; // nasta 13 LED int incomingByte; // muuttuja tiedon vastaanottamiseen void setup() ( Serial.begin(9600); pinMode(ledPin, OUTPUT); ) void loop() ( // jos on dataa if (Serial.available() > 0) ( // lue tavuja puskurissa incomingByte = Serial.read(); // jos tämä on H-merkki (ASCII 72), laita merkkivalo päälle if (incomingByte == "H") ( digitalWrite(ledPin, HIGH); ) // jos tämä on L-merkki (ASCII 76), sammuta merkkivalo if (incomingByte == "L") ( digitalWrite(ledPin, LOW); ) ) )

Koodi käsittelyä varten.

Tuo käsittely.sarja.*; floatboxX; floatboxY; intboxSize=20; booleanmouseOverBox = false; sarjaportti; void setup() ( koko(200, 200); boxX = leveys / 2.0; boxY = korkeus / 2.0; rectMode(RADIUS); println(Serial.list()); // Avaa portti, johon Arduino-kortti on kytketty (tässä tapauksessa #0) // Varmista, että avaat portin samalla nopeudella, jota Arduino käyttää (9600 bps) portti = new Serial(this, Serial.list(), 9600); ) void draw() ( background(0) ); // Jos osoitin on neliön päällä if (hiiriX > laatikkoX - laatikon koko && hiiriX laatikkoY - laatikon koko && hiiriY

04. Viestintä: Kaavio (Piirrä kaavio)

Jos edellisessä esimerkissä lähetimme tietoja tietokoneelta taululle, nyt suoritamme käänteisen tehtävän - vastaanotamme tiedot potentiometristä ja näytämme ne kaavion muodossa.

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 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 annetaan samanaikaisesti nousevia ja laskevia ääniä kuin sireeni pietsosäteilijältä. 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 220 ohmin virranrajoitusvastusta.

2 LED- ja pietsosummeriohjaus käyttämällä delay()-operaattoria

Kirjoitetaan tämä 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 viiveellä(200); ääni (soundPin, 300); // 300 Hz:n viiveellä(200); ääni (soundPin, 200); // 200 Hz:n viiveellä(200); // LED-ohjaus: digitalWrite(ledPin, HIGH); // paloviive(200); digitalWrite(ledPin, LOW); // sammutusviive(200); }

Kun se on kytketty päälle, voidaan nähdä, että luonnos ei ole suoritettu täsmälleen niin kuin tarvitsemme: ennen kuin sireeni on täysin valmis, LED ei vilku, ja haluaisimme LEDin vilkkuvan aikana 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 viive() viivästyttää ohjelman suorittamista tietyn ajan, ja ennen kuin tämä aika on kulunut umpeen, ohjelman seuraavia komentoja ei suoriteta. Tästä syystä emme voi asettaa eri suoritusaikaa kullekin silmukan tehtävälle. loop() ohjelmia. Siksi sinun täytyy jotenkin simuloida moniajoa.

3 Rinnakkaiset prosessit ilman "delay()"-operaattoria

Arduinon kehittäjät ovat ehdottaneet 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 on, niin käännämme LEDin tilan. Tämä on eräänlainen ohitusoperaattori viive().

Const int soundPin = 3; // muuttuja pietsosähköisen elementin pin-numerolla const int ledPin = 13; // muuttuja LED-pinnillä const pitkä ledInterval = 200; // LEDin vilkkumisväli, ms. int ledState = LOW; // LEDin alkutila etumerkitön pitkä edellinenMillis = 0; // tallentaa edellisen LED-sytytyksen ajan void setup()( pinMode(ääniPin, OUTPUT); // aseta nasta 3 ulostuloksi. pinMode(ledPin, OUTPUT); // aseta nasta 13 ulostuloksi. } void loop() (// Äänensäätö: tone(soundPin, 700); viive (200); ääni (soundPin, 500); viive (200); ää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 aika vilkkua, if (currentMillis - previousMillis >= ledInterval) ( previousMillis = currentMillis; // tallenna sitten nykyinen aika if (ledState == LOW) ( // ja käännä LED-tila ledState = HIGH; ) else ( ledState = LOW; ) digitalWrite(ledPin, ledState); // vaihda LED-tila ) }

Tämän menetelmän merkittävä haittapuoli on, että koodiosio ennen LED-ohjauslohkoa on suoritettava nopeammin kuin "ledInterval"-LEDin vilkkumisaikaväli. Muuten vilkkuminen tapahtuu harvemmin kuin on tarpeen, emmekä saa tehtävien rinnakkaisen suorittamisen vaikutusta. Erityisesti luonnoksessamme sireenin äänen muutoksen kesto on 200+200+200+200 = 800 ms ja asetamme LEDin vilkkumisväliksi 200 ms. Mutta LED vilkkuu 800 ms:n välein, mikä on 4 kertaa enemmän kuin asetimme.

Yleensä, jos koodi käyttää operaattoria viive(), tässä tapauksessa näennäisen rinnakkaisuuden simulointi on vaikeaa, joten sitä kannattaa välttää.

Tässä tapauksessa sireenin äänenohjausyksikön olisi myös tarkistettava, onko aika tullut vai ei, eikä käytä viive(). Mutta tämä lisäisi koodin määrää ja heikentäisi ohjelman luettavuutta.

4 ArduinoThread-kirjaston käyttäminen luodaksesi rinnakkaisia ​​lankoja

Ongelman ratkaisemiseksi käytämme upeaa kirjastoa Arduino lanka, jonka avulla voit helposti luoda näennäisen rinnakkaisia ​​prosesseja. Se toimii samalla tavalla, mutta sallii sinun olla kirjoittamatta koodia ajan tarkistamiseksi - sinun on suoritettava tehtävä tässä syklissä 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/ Arduino IDE -kehitysympäristö. Nimeä sitten kansio uudelleen Arduino Thread-master V Arduino lanka.

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 LED-nastanumerolla Thread ledThread = Thread(); // luo säikeen LEDin ohjaamiseksi Thread soundThread = Thread(); // luo ohjausvirta sireenille void setup()( pinMode(ääniPin, OUTPUT); // ilmoittaa nastan 3 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äikeelle soundThread setInterval(20); // aseta vastausväli, ms } void loop() (// Tarkista, onko aika vaihtaa LED: if (ledThread.shouldRun()) ledThread.run(); // aloita säiettä // Tarkista, onko aika muuttaa sireenin ää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; // ääniääni, 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ä. Jokaisen säikeen silmukan jokaisessa iteraatiossa tarkistamme, onko sen suorittamisen aika tullut vai ei. Jos se saapuu, se käynnistetään suoritettavaksi menetelmällä juosta(). Tärkeintä ei ole käyttää operaattoria viive(). Tarkemmat selitykset on annettu koodissa.


Lataa koodi Arduino-muistiin, suorita se. Nyt kaikki toimii juuri niin kuin pitääkin!



Copyright © 2023 Hieman tietokoneesta.