6.4. rauta Keskeytys-Ohjelmointia

34
1 6.4. AVR_rauta. Keskeytys-ohjelmointia 2.1.2008, pva, kuvat jma Pollaus ja keskeytysjärjestelmä ”Elintason nousu on sitä, että entistä useammalla on enemmän rahaa käytössä kuin järkeä.” - Pentti Vahtera Tavoite Oppia miten tapahtuu tiedonsiirto mikro-ohjaimen ja ulkomaailman välillä. Oppia ymmärtämään ero on ns. pollaus- ja keskeytysmenetelmillä ja valitsemaan paras menetelmä kulloiseenkin tapaukseen. Oppia tekemään kumpaakin järjestelmään laiteläheistä c-koodia. Sisältö Kyselymenetelmä eli pollaus, polling Kyselymenetelmä, Pollaus esimerkki 1, 2, 3, 4, 5 6.2. Keskeytysmenetelmä, Interrupt System 3.2.1. Vektoroitu keskeytys Keskeytyksiä on monta eri tyyppiä Vektoroitu keskeytys Keskeytyksessä suoritettavat toimenpiteet: Keskeytysten hallinta Keskeytyskäsittelyyn liittyviä ongelmia Keskeytyslatenssiaikaan vaikuttavia tekijöitä Pinomuisti Mitä pinoon pannaan? AVR ja ulkoinen keskeytys Ulkoisiin keskeytyksiin liittyvät rekisterit: AVR ATmega32, External Interrupt System Keskeytyksien ohjelmointi Ulkoinen keskeytys esimerkki 1, 2, 3, Sisäkkäiset keskeytykset Ulkoinen keskeytys esimerkki 4 Mitä tehdä, jos keskeytykset eivät toimi? JTAG ICE ja interrupt INT0 Yleistä Useimmiten sulautetun järjestelmän tehtävänä on valvoa ympäristönsä tapahtumia erilaisten anturien (optokytkin, lämpöanturi, venymäliuska) avulla ja välittömästi reagoida niihin toimilaitteilla (tarratulostin, sähkömoottori, hälytyssummeri). Valvonnasta ja ohjauksesta seuraa aina tiedonsiirtotapahtuma.

Transcript of 6.4. rauta Keskeytys-Ohjelmointia

Page 1: 6.4. rauta Keskeytys-Ohjelmointia

1

6.4. AVR_rauta. Keskeytys-ohjelmointia 2.1.2008, pva, kuvat jma

Pollaus ja keskeytysjärjestelmä ”Elintason nousu on sitä, että entistä useammalla on enemmän rahaa käytössä kuin järkeä.” - Pentti Vahtera Tavoite Oppia miten tapahtuu tiedonsiirto mikro-ohjaimen ja ulkomaailman välillä. Oppia ymmärtämään ero on ns. pollaus- ja keskeytysmenetelmillä ja valitsemaan paras menetelmä kulloiseenkin tapaukseen. Oppia tekemään kumpaakin järjestelmään laiteläheistä c-koodia. Sisältö Kyselymenetelmä eli pollaus, polling Kyselymenetelmä, Pollaus esimerkki 1, 2, 3, 4, 5 6.2. Keskeytysmenetelmä, Interrupt System 3.2.1. Vektoroitu keskeytys Keskeytyksiä on monta eri tyyppiä Vektoroitu keskeytys Keskeytyksessä suoritettavat toimenpiteet: Keskeytysten hallinta Keskeytyskäsittelyyn liittyviä ongelmia Keskeytyslatenssiaikaan vaikuttavia tekijöitä Pinomuisti Mitä pinoon pannaan? AVR ja ulkoinen keskeytys Ulkoisiin keskeytyksiin liittyvät rekisterit: AVR ATmega32, External Interrupt System Keskeytyksien ohjelmointi Ulkoinen keskeytys esimerkki 1, 2, 3, Sisäkkäiset keskeytykset Ulkoinen keskeytys esimerkki 4 Mitä tehdä, jos keskeytykset eivät toimi? JTAG ICE ja interrupt INT0 Yleistä Useimmiten sulautetun järjestelmän tehtävänä on valvoa ympäristönsä tapahtumia erilaisten anturien (optokytkin, lämpöanturi, venymäliuska) avulla ja välittömästi reagoida niihin toimilaitteilla (tarratulostin, sähkömoottori, hälytyssummeri). Valvonnasta ja ohjauksesta seuraa aina tiedonsiirtotapahtuma.

Page 2: 6.4. rauta Keskeytys-Ohjelmointia

2

Kun mikro-ohjain vaihtaa tietoa oheislaitteiden kanssa, on huomioitava mm: - mille laitteelle data siirretään tai mistä se luetaan - onko oheislaite valmis datan siirtoon, ellei, ”miten se saadaan yhteistyöhön” - oheislaitteen ominaisuudet vastaanottaa/lähettää dataa (nopeus, sarja/rinnakkaissiirto, erikoisväylä kuten IIC, 1-Wire, SPI, USB, Bluetooth) - mikä on palvelujärjestys, prioriteetti, jos usea laite tarvitsee huomiota samanaikaisesti Dataa voidaan siirtää mikro-ohjaimen ja oheislaitteen välillä vain silloin, kun se kummallekin osapuolelle sopii. Ei ole mitään järkeä lähettää tietoa, jos toinen osapuoli ei kykene sitä syystä tai toisesta ottamaan vastaan. Joskus data pitää siirtää juuri tietyllä ajanhetkellä. Palo- tai murtoanturin reagoidessa on palokunta ja poliisi hälytettävä välittömästi. Tässä ja yleensäkin sulautettujen järjestelmien yhteydessä: data on käsitettävä laajasti. Se voi tarkoittaa joko varsinaista 1. ’oikeaa’ dataa, - kuten esimerkiksi sarjaportin kautta tulevaa tekstiviestiä tai LCD-näyttöön kirjoitettavaa ohjetekstiä, jne. tai 2. tapahtumia (events), kuten - tietoa optokytkimen tilasta, tai moottorin ohjauskäskyä, jne. tai 3. ”raakadataa”, esim. - lämpötila-anturin lähettämä bittijono, jne.

Datan siirtoa varten mikro-ohjaimen ja I/O-laitteen välillä on olemassa kaksi perusmenetelmää: Kyselymenetelmä eli pollaus, polling Keskeytysmenetelmä, interrupt 6.4.1. Kyselymenetelmä eli pollaus, polling Kyselymenetelmässä mikro-ohjain tarkastaa säännöllisin väliajoin mahdollisen oheislaitteen palvelutarpeen. Sulautetun järjestelmän ohjelma pyörii yleensä ilman käyttöjärjestelmää ns. ikuisessa silmukassa. Ohjelma tekee ohjelmoijan määrittämät normaalit toiminnot, johon kuuluu osana myös oheislaitteiden tilojen kysely. Kysytään jokaiselta vuorollaan: ”Tarvitaanko palvelua?” Tällöin suuri osa ajasta kuluu laitteiden tilojen tutkimiseen. Jos on aihetta, mikro-ohjain käynnistää palvelua kaipaavan laitteen palvelurutiinin, palvelualiohjelman.

Page 3: 6.4. rauta Keskeytys-Ohjelmointia

3

Kyselymenetelmän etuna on yksinkertainen liitäntä. Mutta se on toisaalta hyvin hidas, koska mikro-ohjaimen aika kuluu turhan tutkimiseen. Kyselymenetelmä ei ole reaaliaikainen. Jos laitteita on hyvin monta, mikro-ohjain ei välttämättä ehdi juuri sen laitteen luo, joka kiireisemmin tarvitsee palvelua. (vanhasta) Communicatorista RS-232-sarjaväylää pitkin tuleva tekstiviesti-data ei paljoa jaksa odotella, se on luettava talteen välittömästi.

Aloitus

Alustukset

Haluaakolaite 1

palvelua?

kyllä

Laitteen 1palveluohjelmaei

Haluaakolaite 2

palvelua?

kyllä

Laitteen 2palveluohjelmaei

Haluaakolaite 3

palvelua?

kyllä

Laitteen 3palveluohjelmaei

Kuva 6.4.1. Kyselymenetelmä. Kyselymenetelmä, Pollaus esimerkki 1 /************************************************** ******** Project : input_1.c Hardware: PV-M32 (4 MHz) + PV-TINT D-portissa ja PV-LEDIT B-portissa Software: WinAVR 20060421 Date : 24.11.2006 Author : pva Comments: input-demo, painele INT0- ja INT1-nappeja INT0 = PD.2 INT1 = PD.3 INT2 = PB.2 ja tarkkaile B-portin ledejä Katso kortin kytkentäkaaviota, niin ymmärrät miksi bitti 4 ja 5 ovat pimeitä (transistorien kannat vetävät ko. pinnit maihin) *************************************************** *******/

Page 4: 6.4. rauta Keskeytys-Ohjelmointia

4

#include <avr/io.h> int main(void) { DDRB = 0xFF; DDRD = 0x00; // input PORTD = 0xFF; // bitit "ylös" while(1) { PORTB = PIND; // Port Input D // lukee D-portin tilat ja sijoitetaan tulos B -porttiin // painele INT0- ja INT1-nappeja } }

Analysointi Aluksi määritetään porttien suunta, B ulospäin (ledi-moduuli) ja D-portti sisäänpäin (painonapit). D-portin sisäiset ylösvetovastukset kytketään input-pinneihin, jolloin niiden arvo on looginen 1 eli +5 V. INT-nappien painallus maadoittaa pinnin, eli sen loogien arvo on nolla 0. DDRD = 0x00; // input PORTD = 0xFF; // bitit "ylös"

Komentorivillä PORTB = PIND; // Port Input D

luetaan D-portin input-rekisterin PIND sisältö ja se kopioidaan B-rekisteriin (output-rekisteri). Kyselymenetelmä, Pollaus esimerkki 2 /************************************************** ******** Project : input_2.c Hardware: PV-M32 + PV-TINT D-portissa ja PV-LEDIT B -portissa Software: WinAVR 20060421 Date : 24.11.2006 Author : pva Comments: lukee D-portin painokytkimiä, jos painett u sytyttää B-portissa LEDi-kuvion input-demo, painele INT0- ja INT1-nappeja INT0 = PD.2 INT1 = PD.3 INT2 = PB.2 ja tarkkaile B-portin ledejä Katso kortin kytkentäkaaviota, *************************************************** *******/ #include <avr/io.h> int main(void) { DDRB = 0xFF; PORTB = 0x0F; DDRD = 0x00; // suunta input PORTD = 0xFF; // pinnit 'ylös', // jotta napin painallus voidaan lukea

Page 5: 6.4. rauta Keskeytys-Ohjelmointia

5

while(1) { if(~PIND & (1 << PD2)) // jos INT0 painettu, eli PD2 == 0 (INT0 == P D2) PORTB = 1<<7; else if(~PIND & (1 << PD3)) // jos INT1 painettu, eli PD3 == 0 (INT1 == P D3) PORTB = 1<<0; else PORTB = 0x18; // jos ei mitään painettu } } Analysointi Tämän harjoituksen vaikein asia on komentorivit: if(~PIND & (1 << PD2)) // jos INT0 painettu, eli PD2 == 0 (INT0 == P D2) PORTB = 1<<7;

if-lauseessa testataan onko PD2-portin 2-bitti nolla (painettu) vai ei. Jos D-portin suunta on ulos ja kaikki ylösvetovastukset on kytketty, PIND-rekisterin tila on 0xCF, eli binäärisenä 1100 1111, koska kortin transistorit vetävät PD4 ja PD5-pinnit alas = 0. jos PD2 on painettu PIND 1100 1011 ~PIND 0011 0100 jonka kanssa otetaan AND-operaatio bitti bitiltä 1<<PD2-lauseen, eli

0000 0100, kanssa

~PIND 0011 0100 1<<PD2 0000 0100 ----------------------------- 0000 0100 C-kielessä kaikki nollasta poikkeavat luvut ovat tosi, joten jos INT0-PD2 on painettu, silloin if-lause on tosi, true ja PORTB = 1<<7;

muussa tapauksessa if-lause on epätosi, false, siloin else PORTB = 0x18; // jos ei mitään painettu

Page 6: 6.4. rauta Keskeytys-Ohjelmointia

6

Kyselymenetelmä, Pollaus esimerkki 3 /************************************************** ******** Project : input_3.c Hardware: PV-M32 + PV-TINT D-portissa ja PV-LEDIT B -portissa Software: WinAVR 20060421 Date : 24.11.2006 Author : pva Comments: lukee D-portin painokytkimiä, jos INT1 on painettu

sytyttää B-portissa LEDi-kuvion INT1 = PD.3 Katso kortin kytkentäkaaviota, *************************************************** *******/ #include <avr/io.h> #define LUKU 0xC7 // vakion määritys // transistorit vetävät PD.4 ja PD5 alas = 0 int main(void) { DDRB = 0xFF; PORTB = 0x00; DDRD = 0x00; // suunta input PORTD = 0xFF; // pinnit 'ylös', // jotta napin painallus voidaan lukea while(1) { if(PIND == LUKU) PORTB = LUKU; // jos INT1 painettu, eli PD3 == 0 (INT1 == P D3) else PORTB = 0x81; } } Analysointi Ensin määritetään vakio: #define LUKU 0xC7 // vakion määritys

Kuten aiemmin opittiin, C-käännin korvaa koodissa esiintyvän vakion LUKU heksaluvulla 0xC7. Se on D-portin tilan arvo silloin, kun painokytkintä INT1 on painettu samaan aikaan, kun portin tila luetaan inpu-muuttujan arvoksi. while(1) { if(PIND == LUKU) PORTB = LUKU; // jos INT1 painettu, eli PD3 == 0 (INT1 == P D3) else PORTB = 0x81;

Ohjelman while-silmukassa luetaan D-portin tila PIND-rekisteristä. Jos kortin painokytkintä INT1 painetaan, ohjelma lukee sen.

Page 7: 6.4. rauta Keskeytys-Ohjelmointia

7

Kyselymenetelmä, Pollaus esimerkki 4 /************************************************** ******** Project : inpu4_4.c Hardware: PV-M32 + PV-TINT D-portissa + PV-LEDIT B- portissa Software: WinAVR 20060421 Date : 24.11.2006 Author : pva Comments: input-demo, painele INT0 INT0 = PD.2 INT1 = PD.3 avr-libc kirjastosta löytyy mm. seuraavat makrot: bit_is_set(sfr, bit) ==> rekisterin bitti 1 bit_is_clear(sfr, bit) ==> rekisterin bitti 0 loop_until_bit_is_set(sfr, bit) ==> Odota kunnes bitti on 1 loop_until_bit_is_clear(sfr, bit) ==> Odota kunnes bitti on 0 *************************************************** *******/ #include <avr/io.h> int main(void) { DDRB = 0xFF; PORTB = 0x00; DDRD = ~(1 << 3); // 3.bitti "0" in, muut "1" o ut // ~ on tilde, invertoi bitit PORTD = 1 << 3; // 3. bitti ylös, muuta alas while(1) { if(bit_is_clear(PIND, 3)) // jos INT1 painettu PORTB = 1 << 0; // 0-bitti ON else PORTB = 1 << 7; // 7-bitti ON } }

Analysointi WinAVR-paketin kirjastoista löytyy sfr_defs.h-kirjasto (C:\WinAVR\avr\include\avr\ jossa on määritetty mielenkiintoisia ja varsin käyttökelpoisia makroja. Ne näyttävät funktioilta määrityksen perusteella, mutta tosiasiassa ne ovat makroja. Katso sivu wwwwwwww mikä on makro. Yksi niistä on esitetty tässä. bit_is_set(sfr, bit) ==> rekisterin bitti 1 makrolla voidaan testata on rekisterin tai portin bitti asetettu (set) ykköseksi bit_is_clear(sfr, bit) ==> rekisterin bitti 0 tai tällä onko se nollattu, clear. Koodissa makro on if-lauseen ehto-osana seuraavasti: if(bit_is_clear(PIND, 3)) // jos INT1 painettu PORTB = 1 << 0; // 0-bitti ON

Page 8: 6.4. rauta Keskeytys-Ohjelmointia

8

Jos ehto on tosi, asetetaan 0-bitti ykköseksi ja muut nollataan. ellei, else PORTB = 1 << 7; // 7-bitti ON

niin asetetaan B-portin 7-bitti ykköseksi ja muut nollataan. Näitä makroja voidaan käyttää portin tai rekisterin yksittäisen bitin tilan selvittämiseen. Kyselymenetelmä, Pollaus esimerkki 5 /************************************************** ******** Project : input_5.c Hardware: PV-M32 + PV-TINT D-portissa + PV-LEDIT B- portissa Software: WinAVR 20060421 Date : 24.11.2006 Author : pva Comments: input-demo, painele INT0 ja INT1 INT0 = PD.2 INT1 = PD.3 avr-libc kirjastosta löytyy mm. seuraavat makrot: bit_is_set(sfr, bit) ==> rekisterin bitti 1 bit_is_clear(sfr, bit) ==> rekisterin bitti 0 loop_until_bit_is_set(sfr, bit) ==> Odota kunnes bitti on 1 loop_until_bit_is_clear(sfr, bit) ==> Odota kunnes bitti on 0 *************************************************** *******/ #include <avr/io.h> #include <util/delay.h> // function prototype void wait(uint16_t time); int main(void) { DDRB = 0xFF; PORTB = 0x01; DDRD = ~(1 << 3); // 3.bitti "0" in, muut "1" o ut // ~ on tilde, invertoi bitit PORTD = 1 << 3; // 3. bitti ylös, muuta alas while(1) { loop_until_bit_is_clear(PIND, 3); // kunnes IN T1 painettu PORTB = 1 << 7; // 7-bitti ON wait(1000); PORTB = 1 << 0; // 0-bitti ON } }

Analysointi

Toinen tarpeellinen makropari on tässä: loop_until_bit_is_set(sfr, bit) ==> Odota kunnes bitti on 1 loop_until_bit_is_clear(sfr, bit) ==> Odota kunnes bitti on 0

Kuten makron nimitekstistä käy ilmi, tutkitaan onko portin tai rekisterin tietty bitti asettu tai nollattu ja jäädään odottamaan, loop_until, kunnes ehto on tosi.

Page 9: 6.4. rauta Keskeytys-Ohjelmointia

9

Tässä on esitetty muutama yksinkertainen idea pollauksesta eli kyselymenetelmästä. Jos tutkittavia kohteita on monia ja kun jokainen suoritettava tehtävä vaatii oman aikansa, saattaa olla, että joku kiireellinen palveluntarve joutuu odottamaan liian kauan. Siksi ’pollaus’ ei sovellu suuriin järjestelmiin, mutta pienissä systeemeissä se on ihan käyttökelpoinen tapa valvoa, mitä mikro-ohjaimen ympärillä tapahtuu.

Pollaus vs. keskeytys esim. Softa tekee jotain napin painalluksesta Vaihtoehdot napin painalluksen tunnistukseen 1. Tarkistetaan jatkuvasti onko nappia painettu 2. Laitetaan napin painamisesta aiheutumaan keskeytys Pollaus vie resursseja

6.4.2. Keskeytysmenetelmä, Interrupt System Kaikkiin erikseen määriteltyihin ulkoisiin tapahtumiin mikro-ohjaimen tulee reagoida jollain lailla. Sitä vartenhan se on laitteeseen asennettu. MCU voi ”pollata” kaikkia tiedosssaan olevia ulkoisia tapahtumia, eli kysellä vuorotellen tietyssä järjestyksessä ”tarvitaanko palvelua?”. Kysely on hidasta ja se saattaa tulla väärään aikaan (liian myöhään). Tehokkaampi tapa on reagoida ulkoisiin tapahtumiin keskeytyksellä, interrupt. Kukin huomiota kaipaava tapahtuma ilmoittaa mikro-ohjaimelle tarpeestaan ns. keskeytys-pyynnöllä. Silloin mikro-ohjain keskeyttää senhetkisen varsinaisen työnsä ja rientää välittömästi palvelua pyytäneen avuksi. Useimmiten oheislaite halutessaan mikro-ohjaimen huomiota tarvitsee sitä välittömästi, ’as soon as possible’. Keskeytyspyyntö on ulkoisen (tai sisäisen, se voi sijaita myös mikro-ohjaimessa) laitteen antama signaali, keskeytyssignaali, jolla se ilmoittaa haluavansa prosessorin palvelua. Ulkoinen keskeytyssignaali tuodaan mikro-ohjaimen ’liitäntäjalkaan’ (pinni) ja sisäiset keskeytyssignaalit kytketään tietysti suoraan keskeytysyksikköön. Tyypillisesti näitä keskeytyssignaaleja, IRQ, Interrupt request, on useita. Keskeytyksen tullessa prosessori suorittaa kullekin oheislaitteelle ominaisen ohjelman, eli ns. keskeytyksen palveluohjelman, interrupt routine, ISR, interrupt service routine. Esim. PC:n hiiren liike aikaansaa keskeytyspyynnön ja Windows rientää siirtämään kuvaruudulla osoitinta haluttuun suuntaan.

Page 10: 6.4. rauta Keskeytys-Ohjelmointia

10

Keskeytysjärjestelmän ohjelma- ja laiterakenne on paljon monimutkaisempi kuin kysely-menetelmän. Toiminta on myös vaikeampi testata. Mutta, mikä tärkeintä, keskeytysjärjestelmä on reaaliaikainen. Palvelu tarjotaan välittömästi, heti kun oheislaite sitä kaipaa.

Aloitus

Alustukset

Prosessorinpääohjelma

Keskeytys-palvelu -ohjelma

Sallitun keskeytyksen tullessa siirrytäänkeskeytyksen palvelu -ohjelmaan

Paluu keskey-tyspalvelusta

Kuva 6.4.2. Keskeytysmenetelmä. Keskeytykset ovat tärkeä osa sulautetun älyn reagointimekanismia. Ohjelmoijan on ymmärrettävä keskeytysprosessin kulku, siihen kuuluvat termit, sekä keskeytykseen liittyvät rekisterit ja niiden eri bittien merkitys. Muuten on vaikeaa hallita sulautetun järjestelmän toimintaa. Ja sehän ohjelmoijalla on tavoitteena. Mikro-ohjainten opiskelu ja oppiminen jos mikä on kumulatiivista. Erilaiset keskeytykset ovat älyelektroniikan tärkeä ominaisuus, sanoisin että tärkein. Sulautettua järjestelmää oikeastaan ei voi olla ilman keskeytyssysteemiä. Mikro-ohjain ilman keskeytyssysteemia on aika hyödytön.

Keskeytyksessä pysäytetään ”normaalin” koodin ajaminen ja siirrytään tekemään

- jotain tärkeämpää - tai ajallisesti kriittistä ohjelmaa.

Useimmiten oheislaite halutessaan mikro-ohjaimen huomiota tarvitsee sitä välittö-mästi. Se ilmoittaa avun tarpeensa erityisellä signaalilla, keskeytyssignaalilla. Esim. kun sarjaportti on vastaanottanut merkin, portti tallettaa sen omaan vastaanottorekiste-riinsä. Jotta seuraava merkki voidaan ottaa vastaan, edellinen pitää tallettaa johonkin turvalliseen paikkaan, koska muuten uusi merkki tulee edellisen ’päälle’. Merkin tullessa sarjaporttiin, se ilmoittaa tapahtumasta välittömästi keskeytyslinjalla prosessorille (jos toimi on ohjelmoimalla mahdollistettu), jonka tehtävä on käydä tallettamassa vastaanotettu merkki turvaan ’as soon as possible’. Keskeytyspyyntö on siis ulkoisen (tai sisäisen, se voi sijaita myös mikro-ohjaimessa) laitteen antama signaali, keskeytyssignaali, jolla se ilmoittaa haluavansa prosessorin palvelua. Ulkoinen keskeytyssignaali tuodaan mikro-ohjaimen ’liitäntäjalkaan’ (pinni) ja sisäiset keskeytyssignaalit kytketään tietysti suoraan keskeytysyksikköön. Tyypillisesti näitä keskeytyssignaaleja, IRQ, Interrupt request, on useita.

Page 11: 6.4. rauta Keskeytys-Ohjelmointia

11

Keskeytyksen tullessa prosessori suorittaa kullekin oheislaitteelle ominaisen ohjelman, eli ns. keskeytyksen palveluohjelman, interrupt routine, ISR interrupt service routine. Esim. PC:n hiiren liike aikaansaa keskeytyspyynnön ja Windows rientää siirtämään kuvaruudulla osoitinta haluttuun suuntaan. Keskeytysohjelma on oikeastaan aliohjelma joka kutsutaan käyttöön kun jotain odottamatonta tapahtuu, jokin MCU-pinnin tilaa vaihtuu, timer-rekisteri saavuttaa tietyn tilan, merkki on tullut sarjaportin rekisteriin, jne..

C-kieli ”yrittää pysyä mahdollisimman kaukana laiteläheisistä keskeytysrutiineista”. Syykin on selvä: Keskeytysrutiinit ovat jokaisessa mikro-prosessorissa erilaiset. Siksi C-kääntimen tekijä kirjoittaa omat keskeytyskäsittelyyn liittyvät ohjelmat.

Kaikilla prosessoreilla keskeytyssysteemi toimii samalla periaatteella, vain rekisterien ja bittien nimet vaihtelevat.

Keskeytyksiin liittyy aluksi oudolta vaikuttava terminologia. Aikaa myöten se tulee tutuksi. Tässä vaiheessa viimeistään Sinun tulee ottaa esille Atmelin ATmega32-ohjainta käsittelevä dokumentti.

ATmega32

datasheet

doc2503.pdf

(~3,2 MB)

The datasheet. Se kertoo kaikki tietämisen arvoiset tekniset yksityiskohdat käytössämme olevasta mikro-ohjaimesta. Keskeytyksistä kerrotaan mm. alkaen sivulta 44. Dokumentti löytyy luonnollisesti valmistajan Atmelin sivuilta, mutta helpoiten www.avrfreaks.net, device.

Keskeytyksiä on monta eri tyyppiä Rautakeskeytys

- ulkoinen laite (tai mikro-ohjaimen sisäinen) tarvitsee huomiota ja pyytää keskeytystä, se aktivoi erityisen keskeytysbitin (esim. INT0)

- keskeytyspynnön tultua (keskeytyslippu asetettu) hypätään vektoritaulukon määrittämään kiinteään osoitteeseen (vektoroitu keskeytys) ja lippu nollataan automaattisesti by rauta. Keskeytyslippu voidaaan nollata myös ohjelmalla kirjoittamalla ko. bitti ykköseksi.

- reset, ulkoinen tai sisäinen - erikoistapaus, kuten esim BOD, Brown Out Detector - esim. timerin, analogia-komparaattorin tai sarjaliikenteen keskeytys

o jotain tapahtuu raudassa joka ohjelman tulee tietää, kuten esim. ADC-muunnos on valmis, jännite ylittää komparaattoriin määritetyn tason...

o merkki saapunut USARTiin o timer/counter saavuttanut tietyn arvon o Watch Dog Timer-keskeytys

- rautatoimintoihin liittyvät keskeytykset käsitellään ko. toiminnon yhteydessä.

Page 12: 6.4. rauta Keskeytys-Ohjelmointia

12

Reset Kuten taulukosta 6.4.1 näkyy, reset on keskeytys, jonka vektorinumero (osoite) on pienin. Se on samalla myös ’voimakkain’ keskeytys, sillä se ohittaa kaikki muut. Resetin tullessa ohjelmalaskurin arvoksi asetetaan osoite 0x00, joten siinä osoitteessa tulee olla flash-muistia ja ohjelman ensimmäinen käsky. Mitä muuta tapahtuu?

- kaikkien oheispiirien toiminta estetään, mukaan lukien vahtiajastin-WDT - kaikki rinnakkaisportit asetetaan tuloiksi - kaikki keskeytykset estetään

Reset-keskeytyksen voi aiheuttaa usea eri lähde:

- External Reset, pidetään 0-taso ulkoisessa reset-pinnissä 50 ns tai pidempään - Power-on Reset, käyttöjännitteen kytkemisen yhteydessä - Watchdog Reset, vahtiajastimen aiheuttama - Brown-out reset - JTAG reset

Poikkeustila

- esim. nollalla jakaminen (ei käytössä AVR-ohjaimissa), väärä koodi, jne Ohjelmallinen keskeytys

- tyypillisesti käyttöjärjestelmäkutsu (jos käytössä on käyttöjärjestelmä) - funktion kutsu

6.4.2.1. Vektoroitu keskeytys Ohjelmamuistin ”alapää”, alkaen osoitteesta 0x0000, on varattu ”ohjelmamuistin vektori-taulukolle”. Ohjelman suoritus alkaa aina osoitteesta nolla, 0x0000, siellä on oltava ohjelman ensimmäinen konekielinen käsky. Resetissä osoiterekisterin eli ohjelmalaskurin arvoksi tulee 0x0000, sieltä alkaa ohjelma. Vektoritaulukko sisältää kyseisen AVR:n kaikkien keskeytysaliohjelmien osoitteet. Keskeytys-pyynnön tullessa ohjelma ”hyppää” osoitteeseen, jossa varsinainen keskeytysaliohjelma sijaitsee.

ATmega32 Vector table Example; vain pari ensimmäistä vektoria on esitetty. Katso

tarkemmin Atmelin dokumentista.

Ohjelmamuistin osoite Vecktori Comment

$0000 Reset Reset-osoite josta kaikki ohjelmat

alkavat.

$0001 INT0 INT0-keskeytyksen osoite sijaitsee tässä

$0002 INT1 INT1-keskeytyksen osoite sijaitsee tässä

etc... ... ...

Keskeytysten määrä vaihtelee eri mikro-ohjaimissa.

Taulukko 6.4.1. AVR keskeytysvektorit.

Page 13: 6.4. rauta Keskeytys-Ohjelmointia

13

Vektoroidussa keskeytyksessä jokainen keskeytyslähde aiheuttaa ohjelman suorituksen jatkumisen omasta kiinteästä osoitteesta. Siinä osoitteessa tulee olla hyppykäsky varsinaiseen keskeytys-aliohjelmaan. Vektorin määritys tehdään kirjoittamalla keskeytysaliohjelman tunnisterivi tietyn formaatin mukaan. Siinä kerrotaan epäsuorasti, mikä on ko. keskeytysohjelman alkuosoite. Itse absoluuttisen osoitteen laskemisen hoitaa käänninohjelma. C-koodaajalle riittää, että hän formuloi keskeytysaliohjelman oikein. Assembly on eri juttu. Keskeytyksiä voi tulla kuinka monta tahansa ja milloin tahansa, ne palvellaan yksi kerrallaan. Jos halutaan, että mikään toinen keskeytys ei pääse häiritsemään parhaillaan palveltavaa keskeytystä, pannaan keskeytysohjelman alkuun muut keskeytykset kieltävä käsky.

Keskeytysvektoritaulukko. 6.4.2.2 Keskeytyksessä suoritettavat toimenpiteet Normaalisti ohjelma etenee lineaarisesti ja ohjelmalaskuri PC osoittaa seuraavaksi haettavan käskyn osoitteeseen. Kun käsky on haettu prosessorin käskynkäsittely-yksikköön ja sitä toteutetaan, samanaikaisesti PC inkrementoituu automaattisesti osoittamaan seuraavaa muistipaikkaa, josta haetaan seuraava käsky.

Page 14: 6.4. rauta Keskeytys-Ohjelmointia

14

Keskeytys merkitsee poikkeamista tästä käskyjen suorittamisen suoraviivaisuudesta. Silloin - kun MCU havaitsee keskeytyspyynnön, meneillään oleva konekielikäsky suoritetaan loppuun - kielletään mahdolliset uudet keskeytykset - talletetaan paluuosoite ja mahdolliset muut CPU:n rekisterit pinomuistiin - selvitetään keskeytystä pyytänyt laite keskeytyslinjan aktivoitumisen perusteella. - hypätään keskeytyspalveluohjelmaan ja palvellaan keskeytys - palautetaan pääohjelman osoite pinosta ohjelmalaskuriin pääohjelmaa varten - jatketaan keskeytyneen ohjelmaosan suoritusta 6.4.2.3. Keskeytyskäsittelyyn liittyviä ongelmia Keskeytys saadaan käyttöön initialisoimalla kaikki tarvittavat rekisterit. Niistä esimerkkikoodeja selityksin edempänä. Joitakin keskeytyksiä ei voi kieltää (Non Maskable Interrupts), AVR:ssä ei ole sellaisia (?). Keskeytykset voivat olla priorisoitu. Samanaikaisista keskeytyspyynnöistä palvellaan korkeampaa prioriteettia Ongelmia syntyy jos keskeytyspyyntö tulee käsiteltäessä rekisteriä jota ISR:kin (Interrupt Service Routine) käyttää. Käsite on nimeltään ”jaetun datan ongelma”. Pahimmillaan sekä pääohjelma että ISR toimivat virheellisesti Tässä yhteydessä kannattaa palauttaa mieleen, että C:llä ohjelmoitaessa yksi käsky kääntyy (tavallisesti) useaksi konekielikäskyksi. Jos keskeytyspyyntö sattuu ennen kuin kaikki ko. konekäskyt on saatu suoritettua, (todennäköisesti) syntyy ongelmia. Helpoin tapa välttää tällaiset ongelmat on kieltää keskeytykset silloin kun käsitellään jaettua dataa. 6.4.2.4. Keskeytyslatenssiaikaan vaikuttavia tekijöitä Latenssiaika, on aika joka kuluu kun järjestelmä vastaa keskeytyspyyntöihin. vasteeseen vaikuttavat:

- pisin aika, jonka tietty keskeytys (tai kaikki) on kielletty - korkeamman priotiteetin keskeytysohjelman suorituksen vaatima aika. - cpu:lta kuluva aika keskeytyspyynnön aiheuttaman toiminnan

keskeyttämiseen ja keskeytysaliohjelmaan siirtymiseen - kuinka kauan keskeytysaliohjelmalta kestää tallettaa tarvittavat

rekisterit ja kuinka kauan varsinainen toiminta kestää

Keskeytykseen käytetty aika Sulautettu koodi on useimmiten aikakriittistä, siksi keskeytyksessä vietetty aika ei saa kestää liian kauan. Perusperiaate: - keskeytysrutiinissa ollaan niin lyhyt aika kuin suinkin.

Jokaiseen keskeytysaliohjelmaan liittyy kaksi aikaparametriä:

- minimiaika keskeytysten välillä - maksimiaika, joka kuluu keskeytysaliohjelman koko suoritukseen.

Page 15: 6.4. rauta Keskeytys-Ohjelmointia

15

6.4.2.5. Pinomuisti Ohjelma, joka käyttää keskeytyksiä on aina aloitettava siten, että pino-osoittimen sisällöksi asetetaan se osoite, johon pino halutaan sijoittaa käyttömuistissa. Tällöin suoritetaan ns. Stack Pointerin initialisointi. Kun käytetään mikro-ohjaimen ohjaukseen C-kieltä, niin käänninohjelma hoitaa tarvittavat pino-osoittimen asetukset ns. start-up-rutiinissa (jonka se lisää koodin alkuun). Niistä ei ohjelmoijan tarvitse huolehtia. Mutta keskeytysohjelmien muoto, formaatti, täytyy tuntea, koska siinä kerrotaan aliohjelman osoite. Kun ohjelmassa suoritetaan hyppy toiseen funktioon, joko keskeytyksen tapahduttua, tai aliohjelmakutsujen tullessa, täytyy paluuosoite tallentaa jonnekin. Sitä SRAM-muistin osaa, johon paluuosoite eli ohjelmalaskurin arvo, mahdolliset muut tarvittavat rekisterit ja paikalliset muuttujat ja parametrit talletetaan, kutsutaan pinoksi, stack. Pino-osoitin, SP, stack pointer, on rekisteri, joka ilmoittaa pinomuistin sijaintipaikan. Se osoittaa aina pinon päällimmäiseen osoitteeseen. Pino sijaitsee SRAM-muistin yläpäässä ja ”kasvaa” alaspäin. Pino on dynaaminen muistialue jonka koon määrittää ohjelmakoodin alkuun sijoitetut start-up-tiedoston alkurutiinit. Pinon sisältö kasvaa ja pienenee tarpeen mukaan ja joskus se ”ylivuotaa” aiheuttaen ongelmia. Pino-osoitin käsittää kaksi I/O-avaruudessa olevaa 8-bitin rekisteriä. Joissakin AVR-ohjaimissa on niin pieni SRAM-muisti (AT90S2313 vain 128 tavua), että vain toinen eli SPL-rekisteri riittää osoitteen tekoon. Aivan pienissä mikro-ohjaimissa (esim. AT90S1200), ei ole yhtään tavua SRAM-muistia. Niissä pino tehdään käyttämällä CPU:n rekistereitä. Tällöin ohjelman kehitykseen ei voi käyttää C-käännintä. Pinon käyttö on tilapäistä (se ”elää” vain keskeytyksen tai funktion ajan) ja se tyhjennetään automaattisesti kun funktio päättyy. Mutta kun samaa pinoa käytetään uudelleen ja uudelleen, selvitään pienellä SRAM-muistilla.

avr-gcc-käännin käyttää yhtä pinoa, jotkut muut c-kääntimet kahta.

6.4.2.6. Mitä pinoon pannaan? Sinne talletetaan prosessorin keskeiset rekisterit joiden arvo saattaa keskeytysrutiinissa muuttua.

- funktion käyttämät muuttujat ja parametrit - funktion paluusosoite eli ohjelmalaskurirekisteri, Program Counter - tilarekisteri, statusrekisteri, State Register, SREG - mahdolliset yleisrekisterit, General Purpose Register

C-käännin tekee tarvittavat talletukset oletuksena puolestamme, joten asia täytyy tuntea kun koodaamme assemblyä. Pinoa hallitaan assembly-käskyillä

- push, joka taltio arvoja pinoon - pull, joka ottaa arvoja pinosta

Muutenkin pinon käsittely tulee tuntea, koska sen avulla voidaan tarvittaessa ohjailla muistin käyttöä ja debuggaus on helpompaa. Joskus pinon ylivuoto aiheuttaa ongelmia, josta ei selviä ilman pinomekanismin osaamista.

Page 16: 6.4. rauta Keskeytys-Ohjelmointia

16

Keskeytys on signaali, jolla oheislaite kertoo kaipaavansa huomiota.

Jos funktion kutsussa on paljon argumentteja käytössä, on viisainta käyttää osoitinta. Esim. sturctin jäseniä voidaan kutsuttavasta funktiosta lukea osoittimen avulla. Pinossa on vain paluuosoite ja struktin osoite. ”Mitä enemmän funktiokutsussa on argumentteja, sitä enemmän on osoittimista hyötyä”. Tästä enemmän osoittimien yhteydessä.

6.4.3. AVR ja ulkoinen keskeytys Seuraavassa esitellään ulkoisten keskeytysten käytössä tarvittavat rekisterit. Niissä kerrotaan vain ne rekisterit ja bitit, jotka on tarpeen nimenomaan tässä yhteydessä. Eräät rekisterit sisältävät ohjausbittejä myös muihin tarkoituksiin. Jos samaa rekisteriä tarvitaan myöhemmin toisessa yhteydessä, se esitellään uudelleen, mutta vain ne bitit, jotka ko. työssä ovat tarpeen. Näin kaikki oleellinen tieto löytyy yhdestä paikasta, eikä Sinun tarvitse selata kirjaa edestakaisin. Yleistä Sulautetun järjestelmän prosessoreihin on haluttu tehdä paljon erilaisia toimintoja, mutta samalla ei ole haluttu kasvattaa prosessoripiirin pinnimäärää 40 (DIL-kotelo) tai 44 (TQFP pintaliitoskotelo) suuremmaksi. Tämän ratkaisun seurauksena prosessorin porttien (A-, B-, C- ja D-portit) pinnejä käytetään useampaan tarkoitukseen.

• Yksinkertaisin käyttö kaikille porttien pinneille on käyttää niitä joko tulona tai lähtönä. Näin saadaan prosessoriin kytkettyä esim. 8 tai enemmän tuloa, joissa esiintyy vain tasot 0 ja 1 (esim. ovikytkin) ja 8/16 lähtöä, joissa esiintyy vain tasot 0 tai 1 (esim. taulu, jossa LEDit osoittavat ovatko ovet auki vai kiinni)

• Porttiin D on sijoitettu ajastimien ohjauksia (PD4 … PD7) ja sarjaliikenteen tarvitsemat liitännät TXD ja RXD (PD1 ja PD0). D-portissa on lisäksi kaksi ulkoisen keskeytyksen tuloa INT0 ja INT1.

Kaikkien porttien käsittely tuloina tai lähtöinä on varsin yksinkertaista, sillä jokaisella portilla on oma suuntarekisteri (DDRx) johon kirjoitetaan 8-bittinen luku siten, että 1-bitti määrää portin vastaavan bitin lähdöksi ja 0-bitti tuloksi. Kaikkien porttien vaihtoehtoisien toimintojen käyttöön liittyy useampia rekistereitä, joilla määritellään kyseisen toiminnon ominaisuuksia.

Page 17: 6.4. rauta Keskeytys-Ohjelmointia

17

6.4.3.1. Ulkoisiin keskeytyksiin liittyvät rekisterit Jotta keskeytykset saadaan käyttöön, kaikki tarvittavat rekisterit on ensin asetettava, initialisoitava, sellaisiin arvoihin, jotka mahdollistavat halutun keskeytyksen tiettyjen ehtojen vallitessa. Pinorekisteri, Stack Pointer Pino-osoitin, SP, stack pointer, on rekisteri, joka ilmoittaa pinomuistin sijaintipaikan. Se sisältää pinon päällimmäisen osoitteen, pinomuistin alkuosoitteen. AVR:n pino kasvaa alaspäin, kohti pienempiä osoitteita.

SPH Stack Pointer, High

Bit 15 14 13 12 11 10 9 8 SP15 SP14 SP13 SP12 SP11 SP10 SP9 SP8

Read/write R/W R/W R/W R/W R/W R/W R/W R/W Init. Value 0 0 0 0 0 0 0 0

Pinon osoitusrekisteri, ylätavu

SPL Stack Pointer, Low

Bit 7 6 5 4 3 2 1 0 SP7 SP6 SP5 SP4 SP3 SP2 SP1 SP0

Read/write R/W R/W R/W R/W R/W R/W R/W R/W Init. Value 0 0 0 0 0 0 0 0

Pinon osoitusrekisteri, alatavu

Ohjelma, joka käyttää funktioita, on aina aloitettava siten, että pino-osoittimen sisällöksi asetetaan se osoite, johon pino halutaan sijoittaa käyttömuistissa. Tällöin suoritetaan ns. Stack Pointerin initialisointi. Kun käytetään mikro-ohjaimen ohjaukseen C-kieltä, niin käänninohjelma hoitaa tarvittavat pino-osoittimen asetukset ns. start-up-rutiinissa (jonka se lisää koodin alkuun). Niistä ei ohjelmoijan tarvitse huolehtia. Mutta keskeytysohjelmien muoto, formaatti, täytyy tuntea, koska siinä kerrotaan aliohjelman osoite. Tilarekisteri SREG, Status Register Tilarekisterin 7. eli eniten merkitsevä bitti, I-bitti, kieltää tai antaa luvan saako yleensäkään käyttää keskeytystä. Eli se estää tai sallii kaikki keskeytykset. I-bitti on nimeltään globaali keskeytysbitti. Jotta yleensä keskeytykset olisivat mahdollisia, pitää globaali keskeytysbitti asettaa ykköseksi. Huomaa, että myös kyseisen keskeytyksen erikoisrekisterit on oltava oikein initialisoitu. AVR:n rauta nollaa I-bitin keskeytyksen tullessa ja asettaa sen ykköseksi, kun palataan keskeytysaliohjelmasta RETI-komennolla.

SREG Status register

Bit 7 6 5 4 3 2 1 0 I T H S V N Z C

Init. value 0 0 0 0 0 0 0 0

The bits in SREG indicate the

current state of the processor.

Rekisterin muut bitin kertovat mitä varsinaisessa tiedon käsittelyssä (accu ja ALU yhteistyö) on tapahtunut. Se selvitetään myöhemmin.

Page 18: 6.4. rauta Keskeytys-Ohjelmointia

18

Huomaa että SREG-rekisteriä ei talleteta pinoon automaattisesti kesketyksen tullessa, eikä palauteta lopussa, se on hoidettava softalla. I-bitti voidaan asettaa tai nollata ohjelmallisesti SEI ja CLI-komennoilla, tai avr-gcc:n C-kielen komennoilla sei(), cli(). sei(); // set interrupts cli(); // clear interrupts

Käskyt reti ja ret eroavat vain siten, että reti asettaa Global interrupt enable-bitin status-rekisterissä, ret ei. Keskeytysten salliminen ja kieltäminen SREG = 0x80; SREG |= 1<<7; // asettaa vain I-bitin sei(); // set interrupts, sallii keskeytykset SREG = 0x00; SREG &= ~(1<<7); // nollaa vain I-bitin cli(); // clear interrupts, estää keskeytykset

Et voi globaalisti estää keskeytystä käyttäen cli()-makroa kun olet sisällä keskeytysrutiinissa. Keskeytykset ovat kielletyt silloin muutenkin. Mutta ne voidaan uudelleen sallia kun olet pois ISR:stä.

Suositeltavin tapa: sei(); // set interrupts, sallii keskeytykset

Suositeltavin tapa: cli(); // clear interrupts, estää keskeytykset

GICR, General Interrupt Control Register Keskeytysmaskilla määritetään mitkä ulkoiset keskeytykset sallitaan.

GICR General Interrupt Control

Register

Bit 7 6 5 4 3 2 1 0 INT1 INT0 INT2 - - - IVSEL IVCE

Read/write R/W R/W R/W R R R R/W R/W Init. Value 0 0 0 0 0 0 0 0

R tarkoittaa reserved, varattu, ja ne bitit ovat vain luettavissa ja

luetaan aina nollaksi.

Keskeytys on sallittu (enabled), kun vastaava bitti on 1 (INT1, INT0, INT2). Keskeytys on kielletty (disabled), kun vastaava bitti on 0.

Page 19: 6.4. rauta Keskeytys-Ohjelmointia

19

MCUCR, MCU General Control Register Keskeytystulojen INT0 ja INT1 keskeytystavan valinta hoidetaan MCUCR-rekisterillä. Sen biteillä määritetään millainen tapahtuma aktivointipinnissä aiheuttaa keskeytyksen.

- reunaherkkä, liipaisu nousevasta tai laskevasta reunasta - tasoherkkä, liipaisu kun signaali on aktiivinen

MCUCR MCU General Control

Register

Bit 7 6 5 4 3 2 1 0 SE SM2 SM1 SM0 ISC11 ISC10 ISC01 ISC00

Init. value 0 0 0 0 0 0 0 0

Kaikki bitit ovat R/W.

The bits in MCUCR allow

general processor control. Consult the datasheet for an

in-depth description of the registers and the individual

bits.

INT0:n keskeytystapa valitaan biteillä ISC01 ja ISC00:

INT1:n keskeytystapa valitaan biteillä ISC11 ja ISC10:

INT2:n keskeytystapa valitaan rekisterin MCUCSR bitillä ISC2: Jos ISC2 = 0 -> laskeva reuna Jos ISC2 = 1 -> nouseva reuna Keskeytyspyyntö indikoidaan GIFR-rekisterillä.

GIFR General Interrupt Flag

Register

Bit 7 6 5 4 3 2 1 0 INTF1 INTF0 INTF2 - - - - -

Read/write R/W R/W R/W R R R R R Init. Value 0 0 0 0 0 0 0 0

R tarkoittaa reserved, varattu, ja

ne bitit ovat vain luettavissa ja

luetaan aina nollaksi.

Kun keskeytys on tullut, asettuu (=1) vastaava lippu (INTF1, INTF0, INTF2).

Page 20: 6.4. rauta Keskeytys-Ohjelmointia

20

Kuten huomasit, ennen kuin keskeytyksiä saadaan käyttöön on asetettava monta rekisteriä ja niissä monta bittiä. Viimeistään nyt lienee selvää, että sulautettujen ohjelmoijan tulee tuntea käyttämänsä mikro-ohjaimen rekisterirakenne.

Ulkoiset keskeytykset ATmega32

INT2(PB2)

INT0(PD2)

INT1(PD3)

01

01

01

RekisteriGICR

Bit

7B

it 6

Bit

5 01

01

01

StatusrekisteriSREG

Bit

7

I

INT

0IN

T1

INT

2

Keskeytysosoite 0

Keskeytysosoite 2

Keskeytysosoite 1

Ohjelmamuisti

Pääohjelma”main”

Pinomuisti

Prosessori

KeskeytysohjelmaISR(INT2_vect)

KeskeytysohjelmaISR(INT0_vect)

KeskeytysohjelmaISR(INT1_vect)

Keskeytystapa:

RekisteriMCUCR

Bit 6Bit 1Bit 2Bit 3

ISC11 ISC10ISC01ISC00

INT2INT1

ISCx1 ISCx00 0 ”0"0 11 01 1

RekisteriMCUCSR

Bit 0

ISC2

01

ISC2

INT0

Yllä oleva kaaviokuva täydentää ja selventää eri rekisterien merkitystä keskeytystapahtumassa. Seuraavassa esitetään erilaisia ulkoiseen keskeytyksen käyttöön liittyviä mallikoodeja. Ne on kommentoitu niin hyvin, että kunkin malliin liittyvä oleellinen idea selviää viimeistään kun ajat ko. koodin ja seuraan ohjelman etenemistä PV-M32-kortilla.

Page 21: 6.4. rauta Keskeytys-Ohjelmointia

21

AVR ATmega32, External Interrupt System pva 01062006 www.microsalo.com

Ulkoinen keskeytys tarvitsee toimiakseen seuraavien rekistereiden asetukset.

1. GICR 2. GIMSK 3. MCUCR 4. MCUCSR 5. SREG

GICR, General Interrupt Control Register INT1 INT0 INT2 - - - IVSEL IVCE

Bitti Nimi Merkitys

7 INT1 Ulkoinen keskeytys INT1 0: Keskeytys estetty, 1: Keskeytys sallittu 6 INT0 Ulkoinen keskeytys INT0 0: Keskeytys estetty, 1: Keskeytys sallittu 5 INT2 Ulkoinen keskeytys INT2 0: Keskeytys estetty, 1: Keskeytys sallittu INT1 = PD3, INT0 = PD2, INT2 = PB2 SREG, Status Register, Tilarekisteri, Globaali keskeytys Bitti Nimi Merkitys Kaikki keskeytykset Käskyt 7 I Globaali keskeytys 0: estetty, 1: sallittu CLI clear, SEI set Huom! Muutkin bitit ovat käytössä (akku + ALU yhteistyön tulokset), mutta niitä ei esitellä tässä yhteydessä. MCUCR, MCU Control Register, MCU ohjausrekisteri SE SM2 SM1 SM0 ISC11 ISC10 ISC01 ISC00

ISCx1 ISCx0 Keskeytyksen aiheuttaja 0 0 keskeytys x liipaistaan pulssin nollatasolla 0 1 varalla (any change?) 1 0 keskeytys x liipaistaan pulssin laskevalla reunalla 1 1 keskeytys x liipaistaan pulssin nousevalla reunalla

MCUCSR, MCU Control and Status Register JTD ISC2 - JTRF WDRF BORF EXTRF PORF

Bitti Nimi Merkitys Keskeytys aktivoituu, kun 6 ISC2 INT2 keskeytys 0: Laskeva reuna in INT2, 1: Nouseva reuna in INT2

SREG, Status Register, Tilarekisteri, Globaali keskeytys Bitti Nimi Merkitys Kaikki keskeytykset Käskyt 7 I Globaali keskeytys 0: estetty, 1: sallittu CLI clear, SEI set

Page 22: 6.4. rauta Keskeytys-Ohjelmointia

22

6.4.4.1. Keskeytyksien ohjelmointi avr-gcc-kääntimen ISR, Interrupt Service Routine, keskeytysaliohjelmien perusformaatti on: ISR (_vector_default) { // your code here; }

int main(void){

DDRB = 0xFF; PORTB = 0x80;

DDRD = 0x00; PORTD = 0xFF;

GICR |= 1 << INT0; MCUCR = (1<<ISC01);

SREG = SREG | (1 << SREG_I);

while(1) { PORTB = PORTB ^ MASKI;//odotus

wait(100);}

}

ISR(INT0_vect ) // INT0 (PD.2){

uint8_t i = 0;

for (i = 0; i < 6; i++) { PORTB = 0x18; wait(50); PORTB = 0x81; wait(50); } PORTB = 0x00; }

Kun ulkoinen keskeytys INT0 aktivoituu (tässä laskeva reuna portin D bitissä 2), program counterin uudeksi sisällöksi ladataan keskeytysohjelman osoite muistipaikoista $002 ja $003.

Prosessori jatkaa ohjelman suorittamista keskeytysohjelmassa

Keskeytyksen palvelun jälkeen palataan pääohjelmaan.

Kuva 6.4.2. Ulkoinen keskeytyskoodi. The AVR core disables interrupts when it vectors to an ISR. Kaikki keskeytykset on estetty AVR raudan toimesta kun ohjelma siirtyy ISR-keskeytysrutiiniin.

Page 23: 6.4. rauta Keskeytys-Ohjelmointia

23

ATmega32-ohjaimessa on kolme ulkoista keskeytysmekanismia. Kaksi niiden aktivointipinneistä on liitetty D-porttiin ja kolmas on B-portissa. PV-TINT-kortissa on D-portin keskeytyksiä varten kaksi painonappia, ne on kytketty kuvan 6.4.3. osoittamalla tavalla.

R10k

R1010k

+5V +5V

SV1(5)

SV1(6)

S2(INT1)

S1(INT0)

Kuva 6.4.3. Ulkoiset keskeytykset INT0 ja INT1. Kuva 6.4.3. osoittaa miten painokytkimet S1 ja S2, jotka ovat kortin input-kytkimiä, toimivat myös INT0- ja INT0-keskeytyssignaalien antajana. Normaalisti mikro-ohjaimen PDx-pinni on vedetty ylösvetovastuksella R = 10k ykköseksi (+5V). Painamalla kytkintä S, saadaan keskeytyspyynnöstä kertova jännite nollatilaan, (keskeytys voidaan liipaista myös pulssin laskevasta tai nousevasta reunasta). Samoin toimii INT1-kytkin.

Kiellä keskeytykset kun käsittelet jaettua dataa. Sulautettujen järjestelmien koodaajan on tunnettava raudan lisäksi mahdollisimman hyvin käyttämänsä C-kääntimen ominaisuudet. Keskeytykset ovat määritelty interrupt.h-tiedostossa. Se löytyy C:\WinAVR\avr\include\avr\

Kytkinvärähtelyt

Did you remember that your switches may bounce? It's usually a good idea to immediately disable the external interrupt within the interrupt handler, and launch a timer (e. g. 10 ms) that only re-enables it after that time.

Page 24: 6.4. rauta Keskeytys-Ohjelmointia

24

Ulkoinen keskeytys esimerkki 1 /************************************************** ******** Korjaa koodi niin, että käytössä on vain INT1-keske ytys Project : interrupt_eka.c Hardware: PV-M32 (4 MHz) + PV-EMO + PV-LEDIT Software: WinAVR-20071221 + AVRStudio4.13 SP2 Date : 31.12.2007 Author : pva Comments: interrupt-demo, painele S2-nappia S2 = INT0 = PD.2 S3 = INT1 = PD.3 *************************************************** ********/ #include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> // function prototype void wait(uint16_t time); ISR(INT0_vect) // Ulkoinen keskeytys INT0 { PORTC |= 1<<0; // GREEN-LED ON + taustavalo wait(1000); PORTC &= ~(1<<0); // GREEN-LED OFF } int main(void) { DDRC |= 1<<0; DDRB = 0xFF; // DDRD = 0xF0; // PORTD = 0x0F; // D-portin pinnit 'ylös' GICR |= 1 << INT0; // INT0 sallittu MCUCR = 1<<ISC01; // laskeva reuna generoi keskeytyksen sei(); // globaali keskeytys sallittu while(1) { PORTB = 0x01; wait(50); PORTB = 0x80; wait(50); } } // *** Primitive wait() *** void wait(uint16_t time) { volatile uint16_t i; for(i=0;i<2000;i++) _delay_loop_2(time); }

Analysointi Keskeytysfunktion otsikkorivi on oltava juuri mallin mukainen. Sillä kerrotaan kääntimelle keskeytysohjelman nimen lisäksi, missä osoitteessa keskeytysaliohjelma

Page 25: 6.4. rauta Keskeytys-Ohjelmointia

25

sijaitsee, ja että sinne hypätään nimenomaan vasta keskeytyspyynnön tultua. Edellyttää tietysti, että ulkoinen keskeytys on rekisterimäärityksin sallittu ja vielä, että globaali keskeytyksen sallinta sen mahdollistaa. Keskeytysten sallinta on tehty main()-funktiossa initialisoimalla kolme keskeytyksiä ohjaavaa rekisteriä; GICR |= 1 << INT0; // INT0 sallittu MCUCR = 1<<ISC01; // laskeva reuna generoi keske ytyksen sei(); // globaali keskeytys sallittu

Viisainta on koota rekisterien asetukset yhteen funktioon ja kutsua sitä main-funktiosta aivan ohjelman alussa. Tämä on hyvä koodaustapa varsinkin, kun ohjelmien monipuolistuessa asetettavien rekisterimääritysten määrä kasvaa. Silloin mahdolliset asetusten virheet tai muutokset ovat helposti tavoitettavissa. Kun GICR-rekisterin INT0-bitti asetetaan ykköseksi, se mahdollistaa INT0-keskeytyksen. Keskeytyksen aiheuttavan signaalin muoto määritetään MCUCR-rekisterissä. Tässä määrityksessä keskeytyslinjan alaslaskeva reuna huomioidaan. Vielä tarvitaan keskeytysten globaali sallinta, se tehdään tilarekisterissä SREG, asettamalla sen eniten merkitsevä bitti ykköseksi. Tämä tapahtuu joko komennolla SREG = 0x80; // globaali keskeytyksen sallinta tai sei(); // sekin asettaa I-bitin status-rekisterissä , SREG

Tämä makro vaati toimiakseen prototyypin, joka saadaan mukaan rivillä #include <avr/interrupt.h>

Itse ohjelma pyörii normaalisti ikuisessa silmukassa while(1) { PORTB = 0x01; wait(50); PORTB = 0x80; wait(50); }

Keskeytyspyynnön tullessa, eli kun INT0-kytkimellä aiheutetaan keskeytyslinjaan alaslaskeva reuna, ensin ohjelma panee paluuosoitteen talteen pinoon ja hyppää sitten keskeytysaliohjelmaan ja suorittaa sen käskyt. Kun keskeytysaliohjelmafunktio on suoritettu, haetaan pääohjelman osoite pinomuistista ja jatketaan siitä mihin ennen keskeytystä jäätiin.

Tutki tarkemmin MCUCR-rekisteriä. Kaikki rekisterin bitit ovat käynnistyksen (resetin) jälkeen nollia (init-arvo on 0). Joten, jos keskeytys liipaistaan ulkoisen pinnin 0-tasolla, niin tätä rekisteriä eri tarvitse erityisemmin asettaa.

Koska keskeytysasetuksia on monta ja toistuvat suurin piirtein samanlaisina eri ohjelmissa, asetuksista kannattaa tehdä oma funktio ja kopioida se uuteen ohjelmaan aina kun tarvetta on. Helpottaa työtä ja kunnolla testattu koodi on toimintavarma.

Page 26: 6.4. rauta Keskeytys-Ohjelmointia

26

INT0 on ulkoisen keskeytyksen esto/sallinta-bitti. Se on GICR-rekisterin 6.bitti. Sillä ei ole mitään tekemistä D-portin suuntarekisterin kanssa. Olen kommentoinut pois määritykset, joilla D-portin alaosan bitit on määritetty input-sisään ja ko. bitit on ohjelmallisesti vedetty ylös. Määritystä tarvitaan, jos keskeytyspinniä ei ole vedetty ulkoisella ylösvetovastuksella loogiseen 1-tilaan. // DDRD = 0xF0; // PORTD = 0x0F; // D-portin pinnit 'ylös'

Jos haluaa kieltää ulkoisen INT0-keskeytyksen toiminta, siihen on kaksi tapaa:

- nollataan GICR-rekisterin INT0-bitti - kielletään kaikki keskeytykset nollaamalla I-bitti SREG-rekisterissä, eli

kirjoittamalla makro cli(); Ei kannata kirjoittaa cli()-makroa ISR-rutiinin sisälle (ja yrittää siten estää globaalisti kaikki keskeytykset), oletuksena kaikki keskeytykset on silloin muutenkin kielletty. Ulkoinen keskeytys esimerkki 2 /************************************************** ******** Project : interrupt_2.c Hardware: PV-M32 + PV-TINT D-portissa + PV-LEDIT B- portissa Software: WinAVR 20060421 Date : 25.11.2006 Author : pva Comments: ulkoinen keskeytys INT0-demo Paina monta kertaa INT0-nappia, tulostaa painalluks et B-porttiin INT1 = PD3 INT0 = PD2 INT2 = PB2 *************************************************** *******/ #include <avr/io.h> #include <avr/interrupt.h> // keskeytyskirjasto #include <util/delay.h> #define MASKI 1<<4 // function prototype void wait(uint16_t time); void Tulosta(void); uint8_t count=0; // globaali muuttuja int main(void) { DDRD |= 1<<4; // PD.4 output DDRD &= ~(1<<2); // PD.2-port input, kokeile 0xF F PORTD = 0x0F; // D-portin pinnit 'ylös' DDRB = 0xFF; GICR |= 1 << INT0; // INT0 mahdollinen MCUCR = 1<<ISC01; // laskeva reuna (PD.2) generoi keskeytyksen uint8_t luku = 1<<4; SREG = SREG | (1 << SREG_I); // globaali keskeytysten sallinta while(1)

Page 27: 6.4. rauta Keskeytys-Ohjelmointia

27

{ Tulosta(); luku = luku ^ MASKI; // XOR-operaatio PORTD = luku; wait(50); } } ISR(INT0_vect) // Ulkoinen keskeytys INT0 { count++; } void Tulosta(void) { PORTB = count; }

Analysointi

Globaali keskeytysbitti nollaantuu automaattisesti (estää kaikki keskeytykset) keskeytyksen tapahtuessa (siis kun hypätään keskeytysohjelmaan) ja asettuu ykköseksi (sallii keskeytykset), kun keskeytysaliohjelmasta poistutaan RETI-konekielikomennolla

.

ISR:n koodi on oltava mahdollisimman lyhyt: as *short as possible*. Useimmiten on parasta, että keskeytysrutiinissa ainoastaan käydään asettamassa lippu, jonka aiheuttama toimenpide käsitellään sitten pääkoodissa. Käytä muuttujan määritykseen volatile-määrettä.

Keep your ISR short

ISR(INT0_vect) { flag = true; return; }

ISR-funktiossa ei tule käyttää wait-odotus-silmukkaa. NEVER. ISR kutsutaan kun jokin tapahtuma vaatii palvelua. Siitä on päästävä pois nopeasti, että annetaan mahdollisuus uusille keskeytyksille.

Pidä keskeytykset lyhyinä Sulautettu koodi on useimmiten aikakriittistä. Siksi keskeytys ei saa kestää liian kauan. Yksi tapa laskea suoritusaika konekielikäskyistä

Page 28: 6.4. rauta Keskeytys-Ohjelmointia

28

Ulkoinen keskeytys esimerkki 3 /************************************************** ******** Project : interrupt_3.c Hardware: PV-M32 + PV-TINT D-portissa + PV-LEDIT B- portissa Software: WinAVR 20060421 Date : 25.11.2006 Author : pva Comments: "oikeaoppinen_keskeytys" Kun käytetään keskeytystä, keskeytysohjelman koodin tulisi olla niin lyhyt kuin mahdollista. Hyvä tapa on käydä keskeytysohjelmassa vain asettam assa lippu (merkki) tiedoksi, että keskeytyspyyntö on ta pahtunut. *************************************************** *******/ #include <avr/io.h> #include <avr/interrupt.h> // keskeytyskirjasto #include <util/delay.h> #define TOSI 1 #define EPATOSI 0 // function prototype void wait(uint16_t time); void Taski(void); volatile uint8_t lippu = 0; // globaali muuttuja // volatie=kääntäjän optimoija ei vaikuta int main(void) { DDRD &= ~(1<<2); // PD.2-port input, kokeile 0xF F PORTD = 0x0F; // D-portin pinnit 'ylös' DDRB = 0xFF; GICR |= 1 << INT0; // INT0 mahdollinen MCUCR = 1<<ISC01; // laskeva reuna (PD.2) generoi keskeytyksen sei(); // globaali keskeytysten sallinta while(1) { Taski(); PORTB = 0x01; wait(50); PORTB = 0x00; wait(50); } } ISR(INT0_vect) // Ulkoinen keskeytys INT0 { lippu = TOSI; } void Taski(void) { if(lippu) // jos keskeytyspyyntö tapahtunut { PORTB = 0x18; wait(500); lippu = EPATOSI; } }

Page 29: 6.4. rauta Keskeytys-Ohjelmointia

29

Sisäkkäiset keskeytykset Sisäkkäiset keskeytykset ovat vaikeasti hallittavia. Jos ei ole pakko, älä käytä. Jos sisäkkäiset keskeytykset sallitaan, keskeytyskoodien oltava uudelleen kutsuttavia = re-entrant. Re-entrant: funktio ei voi käyttää muuttujia ei-atomaarisesti (atomaarinen koodin osa: sitä ei voi keskeyttää). Jos haluat, että toinen keskeytys voi keskeyttää edellisen, nested interrupt, keskeytyksen salliva bitti on asetettava ykköseksi ohjelmassa. Silloin mikä tahansa erikseen määritetty keskeytys voi sen keskeyttää. Pienissä sulautetuissa järjestelmissä sisäkkäiset keskeytykset ovat hankalia käyttää, varsinkin jos niitä tulee useita peräkkäin. Pino loppuu, koska yleensä käytettävä SRAM on pieni. Paras tapa on (useimmiten) keskeytyskielto ed. keskeytyskäsittelyn ajaksi.

Pinomuistin ylivuotoa voi välttää - välttämällä sisäkkäisiä funktiokutsuja (ts. pääohjelma kutsuu eka():ta, joka kutsuu toka():tä, joka kutsuu kolmas():tä, jne.)

Lisäksi sulautetussa koodissa funktion kutsussa ei kannata siirtää kovin isoja määriä dataa, eli vältä argumentti/parametri kuormitusta.

Sisäkkäiset keskeytykset, mallikoodi /************************************************** ***************** Project : interrupt_4.c Sofware : WinAVR 20060421 Hardware: PV-M32 + PV-TINT + PV-LEDIT Date : 25.11.2006 Author : pva Comments: ulkoinen keskeytys INT0 + INT1 Aktivoi ensin INT1, loistava LED siirtyy B-portin kortilla oikealta vas emmalle kun (noin) kolmas LED loistaa, niin aktivoi INT0, sen prioritetti on korkeampi, joten se keskeyttää I NT1:n ja kun INT0 suoritettu, palataan INT1-keskeytysfunktioon ja jatketaan siitä mihin jäätiin kunnes palataan main-funktioon INT1 = PD3 INT0 = PD2 INT2 = PB2 *************************************************** *****************/ #include <avr/io.h> #include <avr/interrupt.h> // keskeytyskirjasto #include <util/delay.h> // function prototype void wait(uint16_t time);

Page 30: 6.4. rauta Keskeytys-Ohjelmointia

30

// Ulkoinen keskeytys INT0 ISR(INT0_vect) { uint8_t i; for (i = 0; i < 7; i++) { PORTD = 1<<4; // punainen LED ON wait(50); PORTD &= ~(1<<4); // punainen LED EI wait(50); } } // Ulkoinen keskeytys INT1 ISR(INT1_vect) { uint8_t bitti = 0; sei(); // sallitaan keskeytykset // kokeile poistaa tämä käytöstä while(bitti<8) { PORTB = 1 << bitti; wait(400); bitti++; } } int main(void) { DDRD = 1<<4; // PV-TINT-kortin RED-LED PORTD = 0x0F; // portin "ala-nibblen" pinnit 'y lös' DDRB = 0xFF; GICR |= (1<<INT0 | 1<<INT1); // INT0 ja INT1 mah dollinen MCUCR |= (1<<ISC01 | 1<<ISC11); // laskevasta reunasta keskeytykset (PD.2 ja PD. 3) sei(); // globaali keskeytysten sallinta while(1) { PORTB = 0x01; wait(100); PORTB = 0x00; wait(100); } }

Analysointi Vinkki 1 Ovivalvonta. Jos painonapin tilalle kytketään vaikkapa valonsäde tai mikrokytkin, joka oikosulkee mikro-ohjaimen pinnin PD.2 maahan, eli aktivoi keskeytyksen, saamme hälytyksen, jos ikkuna on auki, joku tulee ovesta, jne.

Page 31: 6.4. rauta Keskeytys-Ohjelmointia

31

Vinkki 2 Joskus, esimerkiksi ohjelmavikoja etsittäessä, on tarpeen tietää, mikä on tietyn rekisterien sisältö. Se selviää parilla määrityksellä. Jos esimerkiksi halutaan tietää, mikä on GIMSK-rekisterin sisältö initialisoinnin jälkeen, kirjoita seuraavat rivit määritysten jälkeen; PORTB = GICR; wait(4000);

GICR-rekisterin sisältö asetetaan näyttönä toimivan B-portin arvoiksi ja pidetään 4 sekunnin viive, jotta ehdimme tajuta mikä arvo on. Muista poistaa tällaiset ohjelmaan kuulumattomat testirivit. Tätä samaa menetelmää kannattaa käyttää aina, kun epäilee jonkun rekisterin tai muuttujan sisältöä.

Koska keskeytysasetuksia on monta ja toistuvat suurin piirtein samanlaisina eri ohjelmissa, asetuksista kannattaa tehdä oma funktio ja kopioida se uuteen ohjelmaan aina kun tarvetta on. Helpottaa työtä ja kunnolla testattu koodi on toimintavarma.

Globaali keskeytysbitti nollaantuu automaattisesti keskeytyksen tapahtuessa ja asettuu ykköseksi, kun keskeytysaliohjelmasta poistutaan RETI-konekielikomennolla

Kuten huomasit, ennen kuin keskeytyksiä saadaan käyttöön on asetettava monta rekisteriä ja niissä monta bittiä. Viimeistään nyt lienee selvää, että sulautettujen ohjelmoijan tulee tuntea käyttämänsä mikro-ohjaimen rekisterirakenne.

Koska keskeytysasetuksia on monta ja toistuvat suurin piirtein samanlaisina eri ohjelmissa, asetuksista kannattaa tehdä oma funktio ja kopioida se uuteen ohjelmaan aina kun tarvetta on. Helpottaa työtä ja kunnolla testattu koodi on toimintavarma.

Tutki tarkemmin MCUCR-rekisteriä. Kaikki rekisterin bitit ovat käynnistyksen (resetin) jälkeen nollia (init-arvo on 0). Joten, jos keskeytys liipaistaan ulkoisen pinnin 0-tasolla, niin tätä rekisteriä eri tarvitse erityisemmin asettaa.

Mitä tehdä, jos keskeytykset eivät toimi?

1. Onko ohjelman alussa #include-komento, jossa kerrotaan kaikki prosessorin vaatimat kirjasto-ohjelmat?

2. Onko kaikki tarvittavat rekisterit alustettu? Myös globaali sallintabitti? 3. Onko keskeytyssignaalin formaatti oikein (nouseva/laskeva reuna, nollataso)? 4. Kutsutaanko initialisointi-funktiota ohjelman eli main-funktion alussa? 5. Onko tehty keskeytyspalveluohjelma (oma funktio) ja sille oikea vektori,

osoite, kirjoittamalla se oikealla formaatilla?

Page 32: 6.4. rauta Keskeytys-Ohjelmointia

32

JTAG ICE ja interrupt INT0 Tässä malliesimerkki, miten hyödynnetään AVRStudion debuggaus-ominaisuuksia ja PV-M32-kortin JTAG-liitäntää. Avaa projektitiedosto interupt_1.aps. Se syntyi, kun tutkit aiemmin interrupt_1.c- mallikoodia. Siirry debuggaus-tilaan; Debug, Start Debugging. Keltainen nuoli osoittaa main-funktion alkuun debuggauksen aloittamisen merkiksi. Avaa I/O View-ikkuna niin, että se on suunnilleen alla olevan kuvan näköinen. Siis niin, että saat näkyviin CPU:n rekisterit, B- ja D-portit, Data-muistin ja Watch-ikkunaan muuttujan i. Kelaa Memory-Data-muisti-ikkuna muistin loppuun eli viimeiselle riville.

Aja koodia käsky kerrallaan by F10. Tarkkaile samalla porttien ja rekisterien arvojen muuttumista sekä vertaa niitä B-portissa olevaan PV-LEDIT-moduulin toimintaan.

Page 33: 6.4. rauta Keskeytys-Ohjelmointia

33

Kun olet ajanut koodin mainin while-looppiin, on näkymä tällainen.

Huomaa, että SREG-rekisterin I-bitti on asetettu, eli globaali keskeytys on sallittu. Tämä tarkoittaa että kaikki ne keskeytykset ovat mahdollisia, jotka on erikseen määritetty. Ulkoisen keskeytyksen, External Interrupt, asetukset näkyvät koodissa ja I/O-ikkunassa. Muuttuja i ei ole vielä näkyvissä/käytössä, Not in scope, koska se on paikallinen muuttuja ja käytössä vain INT0-keskeytysfunktiossa. INT0-keskeytysfunktio Paina D-portissa olevan PV-TINT-kortin INT0-nappia, pidä se alhaalla ja paina samanaikaisesti F10. Ohjelma hyppää keskeytysohjelmaan ISR(INT0_vect){}. Muutaman F10 jälkeen, myös Watch-ikkunan i-muuttuja saa arvon ja huomaa, että se sijaitsee Pino-muistissa (SRAM) osoitteessa 0x084C. Huomaa, samanaikaisesti itse SP-rekisteri osoittaa 0x084B, eli seuraavaa muistipaikkaa (koska pino kasvaa alaspäin).

Page 34: 6.4. rauta Keskeytys-Ohjelmointia

34

Muutaman käsky-kerrallaan ajon jälkeen näkymä on suunnilleen tällainen.

SREGin I-bitti on nolla, eli kaikki keskeytykset on kielletty. SP-pinorekisteri osoittaa 0x084B. Muuttuja i on saanut arvon ja tyypin. B-portissa on 0x81. Pinossa, viimeisessä muistipaikassa on tallessa paluuosoite 0x00AD. Kokeile: Stop Debugging ja Start Debugging, ohjelma muistaa asetukset. Kiinnitä huomiota: SREG:in I-bitti on asetuksen jälkeen 1, mutta keskeytyksen tultua 0. Miksi? SP on jaettu kahteen rekisteriin SPH ja SPL. Miksi? Mikä on osoitteen arvo ja miksi? Mikä on SP:n arvo kun ollaan keskeytysohjelmassa? Milloin MCUCR rekisterin bitti asetetaan? Miksi muuttuja i on valid vain silloin kun ollaan keskeytysohjelmassa ja silloinkin vain tiettynä aikana?