Kuinka käyttää haihtuvia muuttujia oikein Arduinossa?

Tein pienen projektin Arduino Unon kanssa. Siihen sisältyi keskeytyksiä, koska käytän koodereita mittaamaan kuinka paljon tasauspyöräjärjestelmä liikkuu eteenpäin. Robotini liikkuu vain eteenpäin. Joten käytän vain yhtä kanavaa kustakin kooderista. Tässä ovat kaksi keskeytysrutiiniani:

ISR (INT0_vect){ encoderRPos = encoderRPos + 1; } ISR (INT1_vect){ encoderLPos = encoderLPos + 1; } 

Muuttujat encoderRPos ja encoderLPos ovat tyyppiä volatile int. Ymmärrän, että muuttujien, joille tehdään muutoksia missä tahansa keskeytysrutiinissa, on oltava tyypiltään haihtuvia. Tämän tarkoituksena on varoittaa koodin muita osia, jotka käyttävät näitä muuttujia, että se voi muuttua milloin tahansa.

Mutta koodissani tapahtui hieman outoa, enkä voinut selittää sitä. Näin lasken vasemman pyörän liikuttaman etäisyyden:

 #define distancePerCount 0.056196868 float SR = distancePerCount * (encoderRPos - encoderRPosPrev); float SL = distancePerCount * (encoderLPos - encoderLPosPrev); encoderRPosPrev = encoderRPos; encoderLPosPrev = encoderLPos; 

Mutta kun tulostan sarjaseurantaani seuraavan, huomaan poikkeaman:

kirjoita kuvan kuvaus tähän

Jos tarkastelet kolmatta sarake, (SL): n arvo on liian korkea jonkin aikaa. Tämä häiritsee kaikkia laskelmiani.

Ainoa vihje, jonka saan, jos otan saamani SL: n arvon ( 3682), joka on aina vakio, ja laske takaisin (encodeLPos - encoderLPosPrev), saan 65519,66, joka on lähellä unsigned int Mikä tarkoittaa, että (encoderLPos - encoderLPosPrev) aiheuttaa ylivuotoa, kun taas molemmat arvot, joiden ero on otettu, ovat vain noin 5000!

Ja onnistuin ratkaisemaan sen. onnea. Näin muokkain koodia:

 static int encoderRPosPrev = 0; static int encoderLPosPrev = 0; int diffL = (encoderLPos - encoderLPosPrev); int diffR = (encoderRPos - encoderRPosPrev); float SR = distancePerCount * diffR; float SL = distancePerCount * diffL; encoderRPosPrev = encoderRPos; encoderLPosPrev = encoderLPos; 

En ymmärrä mitä on tapahtunut. Onko haihtuvissa muuttujissa jotain, josta minun olisi pitänyt tietää?

Päivitys: Tässä on koko koodi, jos haluat koskaan katsoa sitä. Ja se toimii erittäin hyvin sen jälkeen, kun se on muutettu hyväksytyn vastauksen ehdotukseksi.

Kommentit

  • Kysymyksesi kertoo mitä kolmas sarake tuotos on … mitkä ovat muut sarakkeet? Muokkaa kysymystä ja lisää sarakeotsikot
  • @ jwpat7 Poistin ne tarkoituksella, koska se vain hämmentää lukijaa. Mutta Majenko on jo vastannut kysymykseen.
  • On vaikea antaa yksityiskohtaisia vastauksia katkelmistasi. could you explain why it is not happening randomly but at a specific time every time I run the code? Also why does it give the particular value? – Voisin todennäköisesti tehdä sen, jos näen koko koodin. Lue samalla: gammon.com.au/interrupts
  • @NickGammon Täältä: paste.ubuntu.com / 14085127
  • 3683 / .056196868 = 65537 joten näyttää siltä, että se kasvoi väärällä hetkellä, kyllä? Käytät muuttujaa, jota voidaan muuttaa keskeytyksessä useita kertoja kyseisessä koodissa, joten paikallisen kopion saaminen keskeytysten ollessa pois päältä olisi paljon turvallisempaa.

vastaus

Sinun on opittava kriittisistä osista .

Mitä todennäköisesti tapahtuu, että keskeytysrutiinit muuttavat muuttujia puolivälissä laskelmia. ”Korjauksesi” vähentää laskentaan käytettyä aikaa haihtuvilla muuttujilla, mikä vähentää törmäyksen todennäköisyyttä.

Sinun on tehtävä kopioimalla haihtuvat muuttujat paikallisiin muuttujiin, joiden keskeytykset on poistettu käytöstä tällöin. lyhyt jakso.

cli(); int l = encoderLpos; int r = encoderRpos; sei(); 

Koska Arduino on 8-bittinen CPU, matemaattisten toimintojen suorittaminen 16-bittisillä arvoilla vie useita asennusohjeita. Liukuluku on vielä huonompi käyttämällä monia monia ohjeita yksinkertaiseen lisäykseen. Jako ja kertolasku käyttävät huomattavasti enemmän. Keskeytyksellä on paljon mahdollisuuksia ampua tuon ohjeluettelon aikana. Kun teet tällaisen tehtävän ja käytät sitten uusia paikallisia muuttujia laskelmissasi, haihtuvien muuttujien käsittelemiseksi tarvittavat ohjeet pidetään ehdottomasti minimissä. Poistamalla keskeytykset käytöstä tehtävän aikana taat, että muuttujia ei voi koskaan muuttaa, kun käytät niitä. Tätä koodinpätkää kutsutaan kriittiseksi osaksi .

Kommentit

  • Näin voi olla vain. Mietitkö, voisitko selittää, miksi se ei tapahdu satunnaisesti, vaan tiettynä aikana joka kerta kun suoritan koodia? Miksi se antaa tietty arvo?
  • Tässä on hyvä viittaus cliin / seiin. nongnu.org/avr-libc/user-manual/… . Kun muistimuuri on epävakaata, ilmoitusta ei oikeastaan tarvita yllä olevassa koodissa. Tässä on hauskaa lukemista tästä aiheesta. ydin .org / doc / Documentation / volatile-uzskatettu-haitalliseksi.txt
  • @MikaelPatel Nice, mutta ei MCU: n kannalta merkityksellinen.Haihtuvia vaaditaan tässä tilanteessa estämään kääntäjää optimoimasta tapauksia, joissa sen mielestä ’ ei käytetä (arvo ei koskaan muutu). Cli / sei on tehty tekemään toiminnasta atomic WRT ainoa muu ketju (keskeytykset), joka suorittaa.
  • Yrititkö koota koodin epävakaalla ja ilman? Mutta kriittisen osan kanssa (cli / sei). Yritän keskustella muistimuurin käsitteestä ja siitä, miten se tarjoaa kääntäjän pääsyn (ja oikean järjestyksen) kääntäjälle julistamalla muuttujat epävakaiksi. Suurimmalle osalle ohjelmoijista opetetaan, että kaikki ISR: ssä käytettävät muuttujat on julistettava epävakaiksi, mutta tässä tarinassa on paljon muuta.
  • En usko kääntäjää on paljon käsitystä siitä, mitä cli () ja sei () tekevät ja miten se vaikuttaisi esimerkiksi optimointiin muuttujista, joita ei pitäisi optimoida ’. Kaikki sei () ja cli () tekevät manipuloimalla globaalin keskeytyksen mahdollistavaa lippua rekisterissään. He eivät tee mitään koodivirran hyväksi.

Vastaa

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