Keskeytyskäsittelijöiden kirjoittaminen¶
Sopivalla laitteistolla MicroPython tarjoaa mahdollisuuden kirjoittaa keskeytyskäsittelijöitä Pythonilla. Keskeytyskäsittelijät - tunnetaan myös nimellä keskeytyspalvelurutiinit (ISR:t) - määritellään takaisinkutsufunktioina. Ne suoritetaan vastauksena tapahtumaan, kuten ajastimen laukeamiseen tai jännitteen muutokseen nastassa. Tällaiset tapahtumat voivat sattua missä tahansa kohdassa ohjelmakoodin suoritusta. Tällä on merkittäviä seurauksia, joista osa on ominaisia MicroPython-kielelle. Muut taas ovat yhteisiä kaikille järjestelmille, jotka pystyvät reagoimaan reaaliaikaisiin tapahtumiin. Tämä dokumentti käsittelee ensin kielikohtaiset kysymykset ja sen jälkeen tarjoaa lyhyen johdannon reaaliaikaohjelmointiin niille, jotka ovat aihealueessa uusia.
Tämä johdanto käyttää epämääräisiä ilmaisuja kuten ”hidas” tai ”mahdollisimman nopea”. Tämä on tarkoituksellista, sillä nopeudet riippuvat sovelluksesta. Hyväksyttävät ISR:n kestot riippuvat keskeytysten tapahtumistaajuudesta, pääohjelman luonteesta ja muiden samanaikaisten tapahtumien olemassaolosta.
Vinkkejä ja suositeltuja käytäntöjä¶
Tämä tiivistää alla yksityiskohtaisesti esitetyt seikat ja luettelee keskeytyskäsittelijäkoodin tärkeimmät suositukset.
Pidä koodi mahdollisimman lyhyenä ja yksinkertaisena.
Vältä muistin varaamista: ei lisäystä listoihin tai sanakirjoihin, ei liukulukuja.
Harkitse
micropython.schedule-funktion käyttöä edellä mainitun rajoituksen kiertämiseksi.Kun ISR palauttaa useita tavuja, käytä esivarattua
bytearray-objektia. Jos ISR:n ja pääohjelman välillä halutaan jakaa useita kokonaislukuja, harkitse taulukkoa (array.array).Kun dataa jaetaan pääohjelman ja ISR:n välillä, harkitse keskeytysten poistamista käytöstä ennen datan käsittelyä pääohjelmassa ja niiden ottamista takaisin käyttöön välittömästi sen jälkeen (katso Kriittiset osiot).
Varaa hätäpoikkeuspuskuri (katso alla).
MicroPython-erityiskysymyksiä¶
Hätäpoikkeuspuskuri¶
Jos ISR:ssä tapahtuu virhe, MicroPython ei pysty tuottamaan virheraporttia, ellei tätä tarkoitusta varten ole luotu erityistä puskuria. Virheenkorjaus helpottuu, jos seuraava koodi sisällytetään mihin tahansa keskeytyksiä käyttävään ohjelmaan.
import micropython
micropython.alloc_emergency_exception_buf(100)
Hätäpoikkeuspuskuri voi sisältää vain yhden poikkeuksen pinojäljen. Tämä tarkoittaa, että jos toinen poikkeus heitetään poikkeuksen käsittelyn aikana keon ollessa lukittuna, tämän toisen poikkeuksen pinojälki korvaa alkuperäisen - vaikka toinen poikkeus käsiteltäisiin siististi. Tämä voi johtaa hämmentäviin poikkeusviesteihin, jos puskuri myöhemmin tulostetaan.
Yksinkertaisuus¶
Useista syistä on tärkeää pitää ISR-koodi mahdollisimman lyhyenä ja yksinkertaisena. Sen tulisi tehdä vain se, mikä on tehtävä välittömästi sen aiheuttaneen tapahtuman jälkeen: toimenpiteet, jotka voidaan lykätä, tulisi delegoida pääohjelman silmukalle. Tyypillisesti ISR käsittelee keskeytyksen aiheuttaneen laitteen ja saattaa sen valmiiksi seuraavaa keskeytystä varten. Se kommunikoi pääsilmukan kanssa päivittämällä jaettua dataa ilmaistakseen, että keskeytys on tapahtunut, ja palaa. ISR:n tulisi palauttaa hallinta pääsilmukalle mahdollisimman nopeasti. Tämä ei ole erityinen MicroPython-kysymys, joten se käsitellään tarkemmin alla.
ISR:n ja pääohjelman välinen viestintä¶
Normaalisti ISR:n on kommunikoitava pääohjelman kanssa. Yksinkertaisin tapa tehdä tämä on yhden tai useamman jaetun dataobjektin kautta, jotka on joko määritelty globaaleiksi tai jaettu luokan kautta (katso alla). Tähän liittyy useita rajoituksia ja vaaroja, joita käsitellään tarkemmin alla. Kokonaislukuja, bytes- ja bytearray-objekteja käytetään yleisesti tähän tarkoitukseen yhdessä taulukoiden kanssa (array-moduulista), jotka voivat tallentaa erilaisia datatyyppejä.
Olion metodien käyttö takaisinkutsuina¶
MicroPython tukee tätä tehokasta tekniikkaa, joka mahdollistaa ISR:n jakaa instanssimuuttujia pohjana olevan koodin kanssa. Se mahdollistaa myös laiteajuria toteuttavan luokan tukea useita laiteinstansseja. Seuraava esimerkki saa kaksi LED-valoa vilkkumaan eri tahtiin.
import machine
import micropython
micropython.alloc_emergency_exception_buf(100)
class Foo(object):
def __init__(self, freq, led):
self.led = led
self.timer = machine.Timer(-1, freq=freq, callback=self.cb, hard=True)
def cb(self, tim):
self.led.toggle()
red = Foo(1, machine.LED("LED_RED"))
green = Foo(0.8, machine.LED("LED_GREEN"))
Tässä esimerkissä red-instanssi ohjaa punaista LEDiä 1 Hz:n virtuaaliajastimesta: joka kerta kun ajastin laukeaa, red.cb() kutsutaan, mikä vaihtaa punaisen LEDin tilaa. green-instanssi toimii vastaavasti 0,8 Hz:n ajastimella vaihtaen vihreän LEDin tilaa. Instanssimetodien käyttö tarjoaa kaksi etua. Ensinnäkin yksi ainoa luokka mahdollistaa koodin jakamisen useiden laiteinstanssien välillä. Toiseksi, koska kyseessä on sidottu metodi, takaisinkutsufunktion ensimmäinen argumentti on self. Tämä mahdollistaa takaisinkutsun pääsyn instanssidataan ja tilan tallentamisen peräkkäisten kutsujen välillä. Esimerkiksi jos yllä olevassa luokassa olisi muuttuja self.count asetettuna nollaksi konstruktorissa, cb() voisi kasvattaa laskuria. red- ja green-instanssit ylläpitäisivät tällöin riippumattomia laskureita siitä, kuinka monta kertaa kukin LED on vaihtanut tilaa.
Python-olioiden luominen¶
ISR:t eivät voi luoda Python-olioiden instansseja. Tämä johtuu siitä, että MicroPythonin on varattava muistia oliolle vapaiden muistilohkojen varastosta, jota kutsutaan heap-keoksi. Tämä ei ole sallittua keskeytyskäsittelijässä, koska keon varaaminen ei ole uudelleenkäynnistettävää (re-entrant). Toisin sanoen keskeytys saattaa tapahtua, kun pääohjelma on kesken varauksen suorituksen - keon eheyden säilyttämiseksi tulkki ei salli muistin varaamista ISR-koodissa.
Tämän seurauksena ISR:t eivät voi käyttää liukulukuaritmetiikkaa; tämä johtuu siitä, että liukuluvut ovat Python-olioita. Vastaavasti ISR ei voi lisätä alkiota listaan. Käytännössä voi olla vaikeaa määrittää tarkalleen, mitkä koodirakenteet yrittävät suorittaa muistin varaamista ja aiheuttaa virheilmoituksen: tämä on toinen syy pitää ISR-koodi lyhyenä ja yksinkertaisena.
Yksi tapa välttää tämä ongelma on, että ISR käyttää esivarattuja puskureita. Esimerkiksi luokan konstruktori luo bytearray-instanssin ja boolean-lipun. ISR-metodi sijoittaa dataa puskurin sijainteihin ja asettaa lipun. Muistin varaaminen tapahtuu pääohjelman koodissa, kun olio instantioidaan, eikä ISR:ssä.
MicroPython-kirjaston I/O-metodit tarjoavat yleensä mahdollisuuden käyttää esivarattua puskuria. Esimerkiksi machine.I2C.readfrom_into() lukee kutsujan tarjoamaan muunneltavaan puskuriin: tämä mahdollistaa sen käytön ISR:ssä.
Tapa luoda olio käyttämättä luokkaa tai globaaleja on seuraava:
def set_volume(t, buf=bytearray(3)):
buf[0] = 0xa5
buf[1] = t >> 4
buf[2] = 0x5a
return buf
Kääntäjä instantioi oletusarvoisen buf-argumentin, kun funktio ladataan ensimmäistä kertaa (yleensä kun moduuli, jossa se sijaitsee, tuodaan).
Olion luominen tapahtuu, kun viittaus sidottuun metodiin luodaan. Tämä tarkoittaa, että ISR ei voi välittää sidottua metodia funktiolle. Yksi ratkaisu on luoda viittaus sidottuun metodiin luokan konstruktorissa ja välittää tämä viittaus ISR:ssä. Esimerkiksi:
class Foo():
def __init__(self):
self.bar_ref = self.bar # Allocation occurs here
self.x = 0.1
self.tim = machine.Timer(-1, freq=2, callback=self.cb, hard=True)
def bar(self, _):
self.x *= 1.2
print(self.x)
def cb(self, t):
# Passing self.bar would cause allocation.
micropython.schedule(self.bar_ref, 0)
Muita tekniikoita ovat metodin määrittely ja instantiointi konstruktorissa tai Foo.bar()-metodin välittäminen argumentin self kanssa.
Python-olioiden käyttö¶
Olioihin liittyy lisärajoitus Pythonin toimintatavan vuoksi. Kun import-lause suoritetaan, Python-koodi käännetään bytecode-tavukoodiksi, jolloin yksi koodirivi tyypillisesti vastaa useita tavukoodeja. Kun koodi suoritetaan, tulkki lukee jokaisen tavukoodin ja suorittaa sen sarjana konekielikäskyjä. Koska keskeytys voi tapahtua milloin tahansa konekielikäskyjen välillä, alkuperäinen Python-koodirivi saattaa olla vain osittain suoritettu. Näin ollen pääsilmukassa muokattu Python-olio, kuten joukko, lista tai sanakirja, saattaa olla sisäisesti epäjohdonmukainen sillä hetkellä, kun keskeytys tapahtuu.
Tyypillinen lopputulos on seuraavanlainen. Harvoissa tapauksissa ISR suoritetaan juuri sillä hetkellä, kun olio on osittain päivitetty. Kun ISR yrittää lukea oliota, seurauksena on kaatuminen. Koska tällaisia ongelmia esiintyy tyypillisesti harvoin ja satunnaisesti, niitä voi olla vaikea diagnosoida. On olemassa tapoja kiertää tämä ongelma, kuvattu Kriittiset osiot -kohdassa alla.
On tärkeää olla selvillä siitä, mikä muodostaa olion muokkaamisen. Taulukon tai bytearray-objektin sisällön muuttaminen on turvallista. Tämä johtuu siitä, että tavut tai sanat kirjoitetaan yhtenä konekielikäskynä, joka ei ole keskeytettävissä: reaaliaikaohjelmoinnin termein kirjoitus on atominen. Sama pätee sanakirjan alkion päivittämiseen, koska alkiot ovat konesanoja, jotka ovat kokonaislukuja tai osoittimia olioihin. Käyttäjän määrittelemä olio saattaa instantioida taulukon tai bytearray-objektin. On pätevää, että sekä pääsilmukka että ISR muuttavat näiden sisältöä.
Vaara syntyy, kun olion rakennetta muutetaan, erityisesti sanakirjojen tapauksessa. Avainten lisääminen tai poistaminen voi laukaista uudelleenhajautuksen (rehash). Jos kova ISR suoritetaan uudelleenhajautuksen ollessa käynnissä ja yrittää käyttää alkiota, voi tapahtua kaatuminen. Sisäisesti globaalit on toteutettu sanakirjana. Näin ollen pääohjelman tulisi luoda kaikki tarvittavat globaalit ennen kovia keskeytyksiä tuottavan prosessin käynnistämistä. Sovelluskoodin tulisi myös välttää globaalien poistamista.
MicroPython tukee mielivaltaisen tarkkuuden kokonaislukuja. Arvot välillä 230 -1 ja -230 tallennetaan yhteen konesanaan. Suuremmat arvot tallennetaan Python-olioina. Näin ollen pitkien kokonaislukujen muutoksia ei voida pitää atomisina. Pitkien kokonaislukujen käyttö ISR:issä on turvatonta, koska muistin varaamista saatetaan yrittää muuttujan arvon muuttuessa.
Liukulukurajoituksen kiertäminen¶
Yleisesti ottaen on parasta välttää liukulukujen käyttöä ISR-koodissa: laitteet käsittelevät tavallisesti kokonaislukuja ja muunnos liukuluvuiksi tehdään yleensä pääsilmukassa. On kuitenkin muutamia DSP-algoritmeja, jotka vaativat liukulukulaskentaa. Alustoilla, joissa on laitteistopohjainen liukulukulaskenta (kuten STM32-pohjaiset OpenMV Camit), voidaan käyttää sisäänupotettua ARM Thumb -assembleria tämän rajoituksen kiertämiseen. Tämä johtuu siitä, että prosessori tallentaa liukulukuarvot konesanaan; arvoja voidaan jakaa ISR:n ja pääohjelman koodin välillä liukulukujen taulukon kautta.
micropython.schedule-funktion käyttö¶
Tämä funktio mahdollistaa ISR:n ajoittaa takaisinkutsun suoritettavaksi ”hyvin pian”. Takaisinkutsu asetetaan jonoon suoritettavaksi, mikä tapahtuu ajankohtana, jolloin keko ei ole lukittu. Näin ollen se voi luoda Python-olioita ja käyttää liukulukuja. Takaisinkutsun on myös taattu suoritettavan ajankohtana, jolloin pääohjelma on saattanut päätökseen kaikki Python-olioiden päivitykset, joten takaisinkutsu ei kohtaa osittain päivitettyjä olioita.
Tyypillinen käyttötapa on sensorilaitteiston käsittely. ISR hankkii dataa laitteistolta ja mahdollistaa sen antaa uuden keskeytyksen. Sen jälkeen se ajoittaa takaisinkutsun datan käsittelyä varten.
Ajoitettujen takaisinkutsujen tulisi noudattaa alla esiteltyjä keskeytyskäsittelijöiden suunnitteluperiaatteita. Tämä on tarpeen niiden ongelmien välttämiseksi, jotka johtuvat I/O-toiminnasta ja jaetun datan muokkaamisesta ja jotka voivat syntyä missä tahansa koodissa, joka keskeyttää pääohjelman silmukan.
Suoritusaikaa on tarkasteltava suhteessa siihen taajuuteen, jolla keskeytyksiä voi tapahtua. Jos keskeytys tapahtuu edellisen takaisinkutsun suorituksen aikana, takaisinkutsusta asetetaan jonoon uusi instanssi suoritettavaksi; se suoritetaan nykyisen instanssin valmistuttua. Pitkäkestoinen korkea keskeytysten toistotaajuus aiheuttaa siten riskin hallitsemattomasta jonon kasvusta ja lopulta epäonnistumisesta RuntimeError-virheen kanssa.
Jos schedule()-funktiolle välitettävä takaisinkutsu on sidottu metodi, harkitse ”Python-olioiden luominen” -kohdan huomautusta.
Poikkeukset¶
Jos ISR nostaa poikkeuksen, se ei etene pääsilmukkaan. Keskeytys poistetaan käytöstä, ellei ISR-koodi käsittele poikkeusta.
Liittyminen asyncioon¶
Kun ISR suoritetaan, se voi keskeyttää asyncio-ajastimen. Jos ISR suorittaa asyncio-operaation, ajastimen toiminta voi häiriintyä. Tämä pätee riippumatta siitä, onko keskeytys kova vai pehmeä, ja pätee myös, jos ISR on siirtänyt suorituksen toiselle funktiolle micropython.schedule-funktion kautta. Erityisesti tehtävien luominen tai peruuttaminen on virheellistä ISR-kontekstissa. Turvallinen tapa olla vuorovaikutuksessa asyncio-ohjelman kanssa on toteuttaa korutiini synkronoinnilla, joka suoritetaan asyncio.ThreadSafeFlag-objektilla. Seuraava katkelma havainnollistaa tehtävän luomista vastauksena keskeytykseen:
tsf = asyncio.ThreadSafeFlag()
def isr(_): # Interrupt handler
tsf.set()
async def foo():
while True:
await tsf.wait()
asyncio.create_task(bar())
Tässä esimerkissä ISR:n suorituksen ja foo()-funktion suorituksen välillä on vaihteleva määrä viivettä. Tämä on luontaista yhteistoiminnalliselle ajoitukselle. Suurin viive riippuu sovelluksesta ja alustasta, mutta sitä voidaan tyypillisesti mitata kymmenissä millisekunneissa.
Yleisiä kysymyksiä¶
Tämä on vain lyhyt johdanto reaaliaikaohjelmoinnin aihealueeseen. Aloittelijoiden tulisi huomata, että reaaliaikaohjelmien suunnitteluvirheet voivat johtaa vikoihin, joita on erityisen vaikea diagnosoida. Tämä johtuu siitä, että ne voivat esiintyä harvoin ja olennaisesti satunnaisin väliajoin. On ratkaisevan tärkeää saada alkusuunnittelu oikein ja ennakoida ongelmat ennen niiden syntymistä. Sekä keskeytyskäsittelijät että pääohjelma on suunniteltava seuraavat kysymykset huomioiden.
Keskeytyskäsittelijän suunnittelu¶
Kuten edellä mainittiin, ISR:t tulisi suunnitella mahdollisimman yksinkertaisiksi. Niiden tulisi aina palata lyhyessä, ennustettavassa ajassa. Tämä on tärkeää, koska ISR:n suorituksen aikana pääsilmukka ei suoritu: pääsilmukka kokee väistämättä taukoja suorituksessaan satunnaisissa koodin kohdissa. Tällaiset tauot voivat olla vaikeasti diagnosoitavien bugien lähde, erityisesti jos niiden kesto on pitkä tai vaihteleva. Ymmärtääkseen ISR:n suoritusajan vaikutukset tarvitaan perusymmärrys keskeytysten prioriteeteista.
Keskeytykset on järjestetty prioriteettijärjestelmän mukaan. Korkeamman prioriteetin keskeytys voi itse keskeyttää ISR-koodin. Tällä on vaikutuksia, jos kaksi keskeytystä jakavat dataa (katso Kriittiset osiot alla). Jos tällainen keskeytys tapahtuu, se aiheuttaa viiveen ISR-koodiin. Jos matalamman prioriteetin keskeytys tapahtuu ISR:n suorituksen aikana, se viivästyy, kunnes ISR on valmis: jos viive on liian pitkä, matalamman prioriteetin keskeytys voi epäonnistua. Toinen hitaiden ISR:ien ongelma on tapaus, jossa saman tyypin toinen keskeytys tapahtuu sen suorituksen aikana. Toinen keskeytys käsitellään ensimmäisen päätyttyä. Jos saapuvien keskeytysten taajuus kuitenkin jatkuvasti ylittää ISR:n kyvyn palvella niitä, lopputulos ei ole onnellinen.
Näin ollen silmukkarakenteita tulisi välttää tai minimoida. I/O muille laitteille kuin keskeyttävälle laitteelle tulisi yleensä välttää: I/O kuten levynkäyttö, print-lauseet ja UART-käyttö on suhteellisen hidasta, ja sen kesto voi vaihdella. Toinen ongelma tässä on, että tiedostojärjestelmäfunktiot eivät ole uudelleenkäynnistettäviä: tiedostojärjestelmän I/O:n käyttö ISR:ssä ja pääohjelmassa olisi vaarallista. Ratkaisevan tärkeää on, että ISR-koodin ei tulisi odottaa tapahtumaa. I/O on hyväksyttävää, jos koodin voidaan taata palaavan ennustettavassa ajassa, esimerkiksi nastan tai LEDin tilan vaihtaminen. Keskeyttävän laitteen käyttäminen I2C:n tai SPI:n kautta voi olla tarpeen, mutta tällaisiin käyttöihin kuluva aika tulisi laskea tai mitata ja sen vaikutus sovellukseen arvioida.
Yleensä on tarve jakaa dataa ISR:n ja pääsilmukan välillä. Tämä voidaan tehdä joko globaalien muuttujien tai luokka- tai instanssimuuttujien kautta. Muuttujat ovat tyypillisesti kokonaisluku- tai boolean-tyyppejä, tai kokonaisluku- tai tavutaulukoita (esivarattu kokonaislukutaulukko tarjoaa nopeamman käytön kuin lista). Kun ISR muokkaa useita arvoja, on otettava huomioon tapaus, jossa keskeytys tapahtuu ajankohtana, jolloin pääohjelma on käyttänyt joitakin, mutta ei kaikkia arvoja. Tämä voi johtaa epäjohdonmukaisuuksiin.
Tarkastellaan seuraavaa suunnittelua. ISR tallentaa saapuvan datan bytearray-objektiin, ja lisää sitten vastaanotettujen tavujen määrän kokonaislukuun, joka edustaa käsittelyä odottavien tavujen kokonaismäärää. Pääohjelma lukee tavujen määrän, käsittelee tavut ja nollaa sitten valmiina olevien tavujen määrän. Tämä toimii, kunnes keskeytys tapahtuu juuri sen jälkeen, kun pääohjelma on lukenut tavujen määrän. ISR sijoittaa lisätyn datan puskuriin ja päivittää vastaanotettujen määrän, mutta pääohjelma on jo lukenut määrän, joten se käsittelee alun perin vastaanotetun datan. Vasta saapuneet tavut menetetään.
On olemassa useita tapoja välttää tämä vaara, yksinkertaisin on käyttää rengaspuskuria (circular buffer). Jos ei ole mahdollista käyttää rakennetta, jossa on luontainen säieturvallisuus, muita tapoja kuvataan alla.
Uudelleenkäynnistettävyys (Reentrancy)¶
Potentiaalinen vaara voi syntyä, jos funktio tai metodi jaetaan pääohjelman ja yhden tai useamman ISR:n välillä tai useiden ISR:ien välillä. Ongelma tässä on, että funktio saattaa itse keskeytyä ja kyseisen funktion uusi instanssi suorittua. Jos näin on tarkoitus tapahtua, funktio on suunniteltava uudelleenkäynnistettäväksi (reentrant). Se, miten tämä tehdään, on edistynyt aihe, joka ylittää tämän opetusohjelman laajuuden.
Kriittiset osiot¶
Esimerkki kriittisestä koodiosiosta on sellainen, joka käyttää useampaa kuin yhtä muuttujaa, johon ISR voi vaikuttaa. Jos keskeytys sattuu tapahtumaan yksittäisten muuttujien käyttöjen välillä, niiden arvot ovat epäjohdonmukaisia. Tämä on esimerkki vaarasta, joka tunnetaan kilpailutilanteena (race condition): ISR ja pääohjelman silmukka kilpailevat muuttujien muuttamisesta. Epäjohdonmukaisuuden välttämiseksi on käytettävä keinoa varmistaa, ettei ISR muuta arvoja kriittisen osion ajan. Yksi tapa saavuttaa tämä on antaa machine.disable_irq() ennen osion alkua ja machine.enable_irq() lopussa. Tässä on esimerkki tästä lähestymistavasta:
import machine
import micropython
import array
import random
import time
micropython.alloc_emergency_exception_buf(100)
class BoundsException(Exception):
pass
ARRAYSIZE = const(20)
index = 0
data = array.array('i', [0] * ARRAYSIZE)
def callback1(t):
global data, index
for x in range(5):
data[index] = random.getrandbits(30) # simulate input
index += 1
if index >= ARRAYSIZE:
raise BoundsException('Array bounds exceeded')
tim = machine.Timer(-1, freq=100, callback=callback1, hard=True)
for loop in range(1000):
if index > 0:
irq_state = machine.disable_irq() # Start of critical section
for x in range(index):
print(data[x])
index = 0
machine.enable_irq(irq_state) # End of critical section
print('loop {}'.format(loop))
time.sleep_ms(1)
tim.deinit()
Kriittinen osio voi koostua yhdestä koodirivistä ja yhdestä muuttujasta. Tarkastellaan seuraavaa koodikatkelmaa.
count = 0
def cb(): # An interrupt callback
count += 1
def main():
# Code to set up the interrupt callback omitted
while True:
count += 1
Tämä esimerkki havainnollistaa hienovaraista bugien lähdettä. Pääsilmukan rivi count += 1 kantaa erityistä kilpailutilannevaaraa, joka tunnetaan nimellä luku-muokkaus-kirjoitus (read-modify-write). Tämä on klassinen bugien aiheuttaja reaaliaikajärjestelmissä. Pääsilmukassa MicroPython lukee count-muuttujan arvon, lisää siihen 1 ja kirjoittaa sen takaisin. Harvoissa tapauksissa keskeytys tapahtuu luvun jälkeen ja ennen kirjoitusta. Keskeytys muokkaa count-muuttujaa, mutta sen muutos ylikirjoitetaan pääsilmukan toimesta, kun ISR palaa. Todellisessa järjestelmässä tämä voisi johtaa harvinaisiin, ennustamattomiin vikoihin.
Kuten edellä mainittiin, on syytä olla varovainen, jos Python-sisäänrakennetun tyypin instanssia muokataan pääkoodissa ja kyseistä instanssia käytetään ISR:ssä. Muokkauksen suorittavaa koodia tulisi pitää kriittisenä osiona sen varmistamiseksi, että instanssi on kelvollisessa tilassa ISR:n suorituksen aikana.
Erityistä varovaisuutta on noudatettava, jos aineisto jaetaan eri ISR:ien välillä. Vaara tässä on, että korkeamman prioriteetin keskeytys voi tapahtua, kun matalamman prioriteetin keskeytys on osittain päivittänyt jaetun datan. Tämän tilanteen käsittely on edistynyt aihe, joka ylittää tämän johdannon laajuuden, sitä lukuun ottamatta, että alla kuvattuja mutex-olioita voidaan joskus käyttää.
Keskeytysten poistaminen käytöstä kriittisen osion ajaksi on tavanomainen ja yksinkertaisin tapa edetä, mutta se poistaa käytöstä kaikki keskeytykset eikä vain sen, jolla on potentiaalia aiheuttaa ongelmia. On yleensä epätoivottavaa poistaa keskeytys käytöstä pitkäksi aikaa. Ajastinkeskeytysten tapauksessa se tuo vaihtelua siihen ajankohtaan, jolloin takaisinkutsu tapahtuu. Laitekeskeytysten tapauksessa se voi johtaa siihen, että laitetta palvellaan liian myöhään, mistä seuraa mahdollinen datan menetys tai ylivuotovirheitä laitteen laitteistossa. Kuten ISR:ien, myös pääkoodin kriittisen osion tulisi olla lyhytkestoinen ja ennustettava.
Lähestymistapa kriittisten osioiden käsittelyyn, joka vähentää radikaalisti aikaa, jonka keskeytykset ovat poissa käytöstä, on käyttää oliota nimeltä mutex (nimi johdettu poissulkemisen, mutual exclusion, käsitteestä). Pääohjelma lukitsee mutexin ennen kriittisen osion suorittamista ja avaa sen lopussa. ISR testaa, onko mutex lukittuna. Jos se on, se välttää kriittisen osion ja palaa. Suunnitteluhaaste on määritellä, mitä ISR:n tulisi tehdä siinä tapauksessa, että pääsy kriittisiin muuttujiin evätään. Yksinkertainen esimerkki mutexista löytyy täältä. Huomaa, että mutex-koodi poistaa keskeytykset käytöstä, mutta vain kahdeksan konekäskyn ajaksi: tämän lähestymistavan etu on, että muut keskeytykset jäävät käytännössä koskemattomiksi.
Keskeytykset ja REPL¶
Keskeytyskäsittelijät, kuten ajastimiin liittyvät, voivat jatkaa suoritustaan ohjelman päättymisen jälkeen. Tämä voi tuottaa odottamattomia tuloksia tilanteissa, joissa olisi voinut odottaa takaisinkutsun nostavan olion poistuneen näkyvyysalueelta. Esimerkiksi OpenMV Camissa:
def bar():
foo = machine.Timer(-1, freq=4, callback=lambda t: print('.', end=''), hard=True)
bar()
Tämä jatkuu, kunnes ajastin nimenomaisesti poistetaan käytöstä tai kortti nollataan Ctrl-D-näppäimillä.