Katkestused Arduino sisemise taimeriga. Katkestused ja multitegumtöötlus Arduinos. Mida saavad taimerid teha?

Õpime taimeri katkestustega töötamist. Kirjutame lihtsa programmi paralleelsete protsessidega.

Reaalses programmis tuleb korraga teha palju toiminguid. Sissejuhatuses tõin näite. Lubage mul loetleda, milliseid toiminguid ta teeb:

Operatsioon

Tsükli aeg
Küsib 3 nuppu, töötleb nendelt saadavaid signaale põrke kõrvaldamiseks 2 ms
Taastab andmed seitsme segmendi LED-indikaatoritelt 2 ms
Genereerib juhtsignaalid 2 DS18B20 temperatuuriandurile ja loeb neist andmeid. Anduritel on 1-juhtmeline jadaliides. 100 µs iga biti kohta,
1 s kogu lugemistsükkel
Voolu ja pinge analoogväärtuste lugemine Peltieri elemendil, toitepinge 100 µs
Analoogvoolu ja pinge väärtuste digitaalne filtreerimine 10 ms
Võimsuse arvutamine Peltieri elemendil 10 ms
PID (proportsionaalne integraaldiferentsiaal) voolu ja pinge stabiliseerimiskontroller 100 µs
Võimsuse regulaator 10 ms
Temperatuuri regulaator 1 sek
Turvafunktsioonid, andmete terviklikkuse jälgimine 1 sek
Juhtimine, süsteemi töö üldine loogika 10 ms

Kõik need toimingud tehakse tsükliliselt, igaühel on erinev tsükliperiood. Ühtegi neist ei saa peatada. Igasugune, isegi lühiajaline tööperioodi aja muutus toob kaasa probleeme: märkimisväärne mõõtmisviga, stabilisaatorite ebaõige töö, vilkuvad indikaatorid, nupuvajutuste ebastabiilne reaktsioon jne.

Külmiku kontrolleri programmis on mitu paralleelset protsessi, mis sooritavad kõiki neid toiminguid, igaüks tsüklis oma perioodiajaga. Paralleelprotsessid on protsessid, mille toimingud sooritatakse samaaegselt.

Eelmistes tundides lõime nupuobjekti jaoks klassi. Me ütlesime, et see on klass signaali töötlemiseks paralleelprotsessis. Et selle normaalseks tööks on vaja signaalitöötlusfunktsiooni (meetodit) kutsuda regulaarse perioodiga tsüklis (valisime aja 2 ms). Ja siis on kõikjal programmis saadaval märgid, mis näitavad nupu või signaali hetkeseisu.

Ühte ahelasse paigutasime nuppude oleku töötlemise ja LED-ide juhtimise koodi. Ja tsükli lõpus määrame viivitusfunktsiooni delay(2). Kuid aeg, mis kulub programmi käivitamiseks tsüklis, muudab tsükli koguaega. Ja tsükli periood ei ole selgelt võrdne 2 ms-ga. Lisaks programm hangub funktsiooni delay() täitmise ajal ega saa teha muid toiminguid. Keeruline programm toob kaasa täieliku kaose.

Välju – riistvarataimeri katkestamisel nupu oleku töötlemise funktsiooni kutsumine. Iga 2 ms järel tuleb põhiprogrammi silmus katkestada, nupu signaal töödeldakse ja juhtimine naaseb põhiahelasse koodi juurde, kus see katkestati. Lühike aeg nupu signaali töötlemiseks ei mõjuta oluliselt põhiahela täitmist. Need. Nuppude töötlemine toimub paralleelselt, põhiprogrammile märkamatult.

Riistvara taimeri katkestus.

Riistvarakatkestus on signaal, mis teatab mõnest sündmusest. Selle saabumisel programmi täitmine peatatakse ja juhtimine läheb üle katkestuste töötlejale. Pärast töötlemist naaseb juhtimine katkestatud programmikoodi juurde.

Programmi seisukohalt on katkestus funktsiooni kutsumine välise sündmuse tõttu, mis ei ole programmi koodiga otseselt seotud.

Taimeri katkestussignaal genereeritakse tsükliliselt, kindla perioodiga. Selle genereerib riistvarataimer – loogikaga loendur, mis teatud väärtuse saavutamisel lähtestab oma koodi. Seades programmiliselt lähtestusloogika koodi, saame määrata taimeri katkestuse perioodi aja.

Arduino taimeri perioodi režiimi ja aja seadistamine toimub mikrokontrolleri riistvararegistrite kaudu. Soovi korral saate välja mõelda, kuidas seda tehakse. Kuid ma pakun välja lihtsama võimaluse - kasutada MsTimer2 teeki. Veelgi enam, taimeri režiimi seadistamine toimub harva, mis tähendab, et teegi funktsioonide kasutamine ei aeglusta programmi.

Raamatukogu MsTimer2.

Teek on mõeldud mikrokontrolleri taimeri 2 riistvarakatkestuse konfigureerimiseks. Sellel on ainult kolm funktsiooni:

  • MsTimer2::set(allkirjata pikk ms, tühine (*f)())

See funktsioon määrab katkestuse perioodi ms-des. Selle perioodiga kutsutakse välja katkestuse töötleja f. See tuleb tunnistada kehtetuks (ei tagasta midagi) ja sellel ei tohi olla argumente. * f on funktsiooni osuti. Selle asemel peate kirjutama funktsiooni nime.

  • MsTimer2::start()

Funktsioon võimaldab taimeri katkestusi.

  • MsTimer2::stopp()

Funktsioon keelab taimeri katkestused.

Funktsioonide nime ette tuleb kirjutada MsTimer2::, sest Teek on kirjutatud nimeruumi direktiivi abil.

Teegi installimiseks kopeerige MsTimer2 kataloog Arduino IDE töökausta teekide kausta. Seejärel käivitage Arduino IDE programm, avage Sketch -> Connect raamatukogu ja vaadake, et MsTimer2 teek on teekide loendis olemas.

MsTimer2 teegi saate alla laadida ZIP-arhiivis. Selle installimiseks peate selle lahti pakkima.

Lihtne programm nupu signaali paralleelse töötlemisega.

Nüüd kirjutame ühe nupu ja LED-iga lihtsa programmi 6. õppetunnist. Üks nupp on ühendatud Arduino plaadiga vastavalt skeemile:

See näeb välja selline:

Iga nupuvajutuse korral muudab Arduino plaadi LED oma olekut. On vaja installida MsTimer2 ja Button teegid:

MsTimer2

Ja maksa. Ainult 25 rubla. kuus kõigile saidiressurssidele juurdepääsu saamiseks!

// sketch_10_1 10. õppetund
// Nupu vajutamine muudab LED-i olekut

#kaasa
#kaasa

#define LED_1_PIN 13 //
#define BUTTON_1_PIN 12 // nupp on ühendatud kontaktiga 12

Nupu nupp1(BUTTON_1_PIN, 15); // objekti loomine - nupp

void setup() (

MsTimer2::set(2, timerInterupt); // seadke taimeri katkestuse perioodiks 2 ms
MsTimer2::start(); //
}

void loop() (

// LED juhtimine
if (button1.flagClick == true) (
// nuppu klõpsati



}
}

// katkestuse töötleja
void timerInterrupt() (
nupp1.scanState(); // nupu stabiilse oleku ootemeetodi kutsumine
}

Funktsioonis setup() määrame taimeri katkestuse tsükli ajaks 2 ms ja määrame katkestuse käitleja nime timerInterrupt . Nupu signaali töötlemise funktsioon button1.scanState() kutsutakse taimeri katkestuse töötlejas iga 2 ms järel.

Seega töötleme nupu olekut paralleelprotsessis. Ja programmi põhiahelas kontrollime nupuklõpsu märki ja muudame LED-i olekut.

Muutuv kvalifikaator.

Muudame eelmises programmis loop() tsüklit.

void loop() (

kuigi (tõsi) (
if (button1.flagClick == true) break;
}

// nuppu klõpsati
button1.flagClick= false; // lähtesta märk
digitalWrite(LED_1_PIN, ! digitalRead(LED_1_PIN)); // LED inversioon
}

Loogiliselt pole midagi muutunud.

  • Esimeses versioonis läbis programm tsükli lõpuni ja analüüsis selles olevat nuppu button1.flagClick.
  • Teise variandi puhul analüüsib programm lippu button1.flagClick lõputus while-tsüklis. Kui lipp muutub aktiivseks, väljub see while-ahelast läbi katkestuse ja muudab LED-i oleku ümber.

Ainus erinevus seisneb selles, millises tsüklis programm töötab tsüklis või while.

Aga kui käivitame programmi uusima versiooni, siis näeme, et LED ei reageeri nupuvajutustele. Eemaldame klassi ja lihtsustame programmi.

#kaasa
#define LED_1_PIN 13 // LED ühendatud kontaktiga 13
int count=0;

void setup() (
pinMode(LED_1_PIN, VÄLJUND); // defineerige LED-i viik väljundina
MsTimer2::set(500, timerInterupt); // seadke taimeri katkestuse perioodiks 500 ms
MsTimer2::start(); // lubage taimeri katkestamine
}

void loop() (

samas (tõsi) (
if (count != 0) break;
}

arv= 0;
digitalWrite(LED_1_PIN, ! digitalRead(LED_1_PIN)); // LED-oleku inversioon
}

// katkestuse töötleja
void timerInterrupt() (
count++;
}

Selles programmis suurendatakse loendurit katkestuste töötlejas iga 500 ms järel 1 võrra. While-tsüklis analüüsitakse seda, katkestusega väljume ahelast ja pöörame LED-i oleku ümber. Lihtsamat programmi ei kujutaks ettegi, aga see ka ei tööta.

Fakt on see, et C++ keele kompilaator optimeerib programmi vastavalt selle intelligentsusele. Mõnikord ei tule see hästi välja. Kompilaator näeb, et while-tsüklis loendusmuutujaga ei tehta ühtegi toimingut. Seetõttu usub ta, et piisab loenduse seisu kontrollimisest ainult üks kord. Miks kontrollida silmuses midagi, mis ei saa kunagi muutuda. Kompilaator parandab koodi, optimeerides selle täitmisaja jaoks. Lihtsamalt öeldes eemaldab see tsüklist muutuja kontrollkoodi. Kompilaator ei saa aru, et loendusmuutuja muudab oma olekut katkestuste käitlejas. Selle tulemusena takerdume while-tsüklisse.

Programmi versioonides, mis täidavad tsüklit lõpuni, eeldab kompilaator, et kõik muutujad võivad muutuda, ja jätab kontrollkoodi. Kui sisestate while-tsüklisse mis tahes süsteemifunktsiooni kutse, otsustab ka kompilaator, et muutujad võivad muutuda.

Kui lisate while-tsüklisse näiteks funktsiooni delay() kutse, siis programm töötab.

samas (tõsi) (
if (count != 0) break;
viivitus(1);
}

Hea stiil on arendada programme, milles tsükkel täidetakse lõpuni ja programm ei hangu kuskil. Järgmine õppetund sisaldab ainsat koodi, mis parsib lippe lõpututes tsüklites. Järgmisena kavatsen tsükli kõigis programmides lõpuni täita.

Mõnikord pole see lihtne ega tõhus. Seejärel peate kasutama volatile kvalifikaatorit. See määratakse kindlaks muutuja deklareerimisel ja käsib kompilaatoril mitte proovida selle kasutamist optimeerida. See takistab kompilaatoril muutuja väärtuse kohta oletusi tegemast, kuna muutujat saab muuta mõnes teises programmiüksuses, näiteks paralleelprotsessis. Samuti paigutab kompilaator muutuja RAM-i, mitte üldotstarbelistesse registritesse.

Programmis loenduse deklareerimisel piisab kirjutamisest

lenduvate int count=0;

ja kõik võimalused töötavad.

Nuppu kontrolliva programmi puhul peate deklareerima, et klassi Button eksemplari omadused võivad muutuda.

volatile Button button1(BUTTON_1_PIN, 15); // objekti loomine - nupp

Minu tähelepanekute kohaselt ei suurenda volatiilse kvalifikaatori kasutamine kuidagi programmi koodi pikkust.

Nupu signaalitöötlusmeetodi võrdlus Bounce teegiga.

Põrkenuppude tagasilükkamiseks on valmis teek. Nupu olekut kontrollitakse funktsiooni update() kutsumisel. Selles funktsioonis:

  • nupu signaali loetakse;
  • võrreldes olekuga eelmise update();
  • kontrollib funktsiooni millis() abil, kui palju aega on möödunud eelmisest kõnest;
  • tehakse otsus, kas nupu olek on muutunud.
  • Kuid see ei ole paralleelne signaalitöötlus. Funktsiooni update() kutsutakse tavaliselt välja peamises asünkroonses programmitsüklis. Kui teatud aja jooksul ei helistata, läheb nupu signaali teave kaotsi. Ebaregulaarsed kõned põhjustavad algoritmi ebaõiget toimimist.
  • Funktsioonil endal on üsna palju koodi ja selle käivitamine võtab palju kauem aega kui Button() teegi funktsioonid.
  • Digitaalne signaalide filtreerimine keskmise väärtuse alusel puudub.

Parem on mitte kasutada seda teeki keerukates programmides.

Järgmises tunnis kirjutame keerukama programmi paralleelsete protsessidega. Õpime ühest taimeri katkestusest rakendama programmiplokkide täitmist erinevate ajavahemikega tsüklites.

Kategooria: . Saate selle järjehoidjatesse lisada.

Sisuliselt on mikrokontrolleri taimer digitaalne loendur, ainult "keeruline". Loenduri sisendisse antakse kella signaal, mille langemise põhjal loendur oma väärtust suurendab. Sündmuste ilmnemisel – loenduri ületäitumine või selle väärtus ühtib antud väärtusega – genereeritakse katkestusnõue.

Vaatame, kuidas kasutada T0 taimerit tavarežiimis. Selles režiimis loendab taimer loendusregistri mõnest algväärtusest maksimaalse võimaliku väärtuseni (kuni 255 või 0xFF). Kui taimer T0 loeb maksimumini, siis järgmises taktitsüklis ajab loendusregister TCNT0 üle – see lähtestatakse ja seatakse TOV0 lipp. Kui programm lubab katkestusi globaalselt (SREG registri lipp I) ja T0 taimeri ülevoolukatkestust (TIMSK registri lipp TOIE0), siis mikrokontroller kutsub vastava töötleja. Kui loendusregistri väärtus ühtib võrdlusregistriga OCR0, siis seatakse OCF0 lipp ja kui vaste sündmuse katkestus on lubatud, käivitub selle töötleja.

Taimer T0 tavarežiimis

Vaatleme praktilist probleemi – peame iga 20 ms tagant ühe nupu pollima. Mikrokontrolleri sagedus 8 MHz, ATmega16 mikrokontroller.

Esimese asjana tuleb otsustada taimeri eelskaala koefitsiendi valik ja arvutada TCNT0 loenduri registri algväärtus.

Taimerit T0 saab kellatada mikrokontrolleri sisemisest või välisest kellasignaalist, mis antakse T0 viigule. Sisemisest kellasignaalist töötades saab kasutaja valida selle signaali sagedusjaotuse suhted. Taimeril T0 on viis võimalikku eelskaala koefitsiendi valikut – 1, 8, 64, 256, 1024.

Selle probleemi lahendamiseks põhjendan järgmist. Kui taimeri T0 ühe linnukese periood oleks 1 ms, siis see sobiks mulle. 20 taktitsüklit annavad 20 ms. Milline taimeri eelskaala koefitsient võimaldab teil saada 1 ms lähedase kellaperioodi? Sa oskad arvestada.

Mikrokontrolleri taktsagedus Fcpu = 8000000 Hz
Mikrokontrolleri kella periood Tcpu = 1/Fcpu
Taimeri T0 kellaperiood on võrdne Tt0 = (1/Fcpu)/k = k/Fcpu

Kui k = 1024, on taimeri T0 kellaperiood võrdne Tt0 = 1024/8000000 = 0,128 ms

See on maksimaalne taimeri kella periood, mille saame meie tingimustes (Fcpu = 8 MHz). Madalamate koefitsientide korral on periood veelgi lühem.

Olgu, olgu, oletame, et üks taimeri kell on 0,128 ms, kas loendusregister on selle ajaintervalli loendamiseks piisavalt lai ja mitu kellatsüklit see võtab? Jagame vajaliku ajaintervalli (20 ms) ühe taimeri linnukese kestusega ja saame vastuse.

n = t/Tto = 20 ms/ 0,128 ms = 156,25

Lähima tervikuni ümardades saame 156 taktitsüklit. See on väiksem kui 255 (loendusregistri maksimaalne väärtus), mis tähendab, et TCNT0 loendusregistrist piisab.

Loendusregistri TCNT0 algväärtus arvutatakse taimeri T0 maksimaalse taktitsüklite arvu ja vajaliku erinevusena, st 256 - 156 = 100. (256 on maksimaalne ajavahemike arv, mida iga 8- bititaimer võib lugeda.)

Ma arvan, et nüüd on selge, kuidas arvutada tavarežiimi TCNT0 algväärtus:

Arvutame ühe taimeritsükli perioodi Tt0 = k/Fcpu,
- arvutada vajalik arv taktisagedusi antud intervalli jaoks n = t/Tto,
- arvutage loendusregistri algväärtus TCNT0 = 256 - n.

Saate selle protseduuri automatiseerida makrode abil. Näiteks nii:

#define F_CPU 8000000UL
#define AEG_MS(aeg, k) (256L - ((aeg)*(F_CPU))/(1000L*(k)))

Kuid sellise makroga peate olema ettevaatlik, teatud aja ja k väärtuste korral võivad tekkida vead.

Liigume nüüd koodi juurde. Taimeri T0 (ja ka teiste) kasutamiseks peate selle konfigureerima (initsialiseerima) ja kirjeldama katkestuste töötlejat (kui seda kasutatakse).

Taimeri käivitamine koosneb järgmistest sammudest.

Peatage taimer,
- normaalrežiimi seadistamine TCCR0-s ilma käivitamiseta,
- algväärtuse TCNT0 määramine,
- lippude lähtestamine TIFR-registris,
- lubage TIMSKis katkestus ületäitumisel,
- eelskaalaja seadistamine TCCR0-s, st taimeri käivitamine

Selles järjestuses on võimalikud variatsioonid.

Meie ülesande jaoks näeb lähtestamiskood välja järgmine:


/*loendusregistri väärtus*/
#define T_POLL 100

TCCR0 = 0;
TCCR0 = (0<TCNT0 = T_POLL;
TIFR = (1<TIMSK |= (1<TCCR0 |= (1<

Teine lähtestamisrida on sisuliselt kasutu, see lisati selguse huvides. Et näha selgelt, milline taimeri režiim on seadistatud.

Katkestuste lippude lähtestamine TIFR-registris toimub kirjutades vastavale bitile 1. See toiming tuleb sooritada registri ülekirjutamise teel, mitte bitipõhise VÕI kasutamisega. Ja sellepärast.

Oletame, et TIFR-registris on seatud kaks katkestuslippu – TOV1 ja TOV0. TOV0 peame lähtestama. Vajaliku numbri määramisel VÕI abilJuhtub midagi järgmist.


//TIFR-i väärtus on 0b00000101
//lipud on seatud TOV1 ja TOV0
//kood käivitatakse TIFR |= (1<
//TIFR on kopeeritud R16-sse
IN R16, 0x38

//R16-s on määratud TOV0 bitt
//kuigi see on juba installitud
ORI R16, 0x02

//R16, mis võrdub 0b00000101, kirjutatakse TIFR-registrisse
OUT 0x38, R16

Selle tulemusena lähtestati mõlemad lipud, kuid me tahtsime lähtestada ühte.

Jätkame.

Katkestuste töötlejate kirjeldamise süntaks on erinevate kompilaatorite puhul veidi erinev. IAR'a puhul näeb ülevoolusündmuse T0 taimeri katkestuse töötleja välja järgmine:



{
TCNT0 = T_POLL;

/*siin peaks olema nupuküsitlus*/

TIMER0_OVF_vect on ülevoolukatkestuse vektori aadress. See on võetud mikrokontrolleri päisefailidest. Antud juhul võtsin selle failist iom16.h.

Käsitleja esimene rida (TCNT0 = T_POLL;) kirjutab loendusregistri üle ja määrab selle algväärtuse. Kui seda ei tehta, jätkab taimer loendamist 0-st. Loendusregistri ümberkirjutamine tuleb teha katkestuste käitleja alguses.

Kogu meie ülesande kood näeb välja umbes selline. (Koodi kuvatakse IAR-i jaoks. Teiste kompilaatorite puhul peate muutma päisefaile ja katkestuste töötlejat.)

#kaasa
#kaasa
#kaasa

#define T_POLL 100

int main(tühine)
{
/*initsialiseeri taimer*/

TCCR0 = 0;
TCCR0 = (0<TCNT0 = T_POLL;
TIFR |= (1<TIMSK |= (1<TCCR0 |= (1<

/*initsialiseeri ülejäänud välisseadmed*/
DDRB |= (1<

Enable_interrupt();
samas(1);

/*katkestuse töötleja T0
ületäitumise sündmuse järgi*/
#pragma vektor = TIMER0_OVF_vect
__interrupt void TimerT0Ovf(void)
{
/*loendusregistri ülekirjutamine*/
TCNT0 = T_POLL;

/*küsitluse nupp*/

/*PB0 inversioon silumiseks*/
PORTB ^= (1<

OC0 väljundi juhtimine

Tavarežiimis saab taimer T0 muuta OC0 viigu olekut, kui loendusregister ja võrdlusregister kattuvad. Ja isegi ilma katkestusteta. Juhtimisvalikud määratakse TCCR0 registri bittide COM01 ja COM00 abil.

Siin on näide programmist, mis genereerib tihvti OC0 juures ruutlaine.

#kaasa
#kaasa

int main(tühine)
{
/*initsialiseeri taimer T0*/

TCCR0 = 0;
TCCR0 = (0<TCNT0 = 0;
OCR0 = 0;
TIMSK = 0;
TCCR0 |= (1<

/*initsialiseeri OC0*/
DDRB |= (1<

Kuigi(1);
tagasi 0;
}

OS0 viik muudab oma oleku vastupidiseks, kui loendusregister on null.

Mõned punktid taimeri kasutamise kohta

Taimeri katkestuse töötleja (ja kõik muud välisseadmed) tuleks teha võimalikult lühikeseks.

Kui loendusregistri (või võrdlusregistri) arvutatud väärtus ümardatakse, loeb taimer ajavahemikku veaga.

Ja viimane asi. Võib juhtuda, et taimeri katkestuse töötlemine viibib (näiteks mõne teise töötleja süül) ja TCNT0 register hakkab juba mitu taktitsüklit loendama. Kui kirjutate lihtsalt TCNT0 väärtuse üle, kutsutakse järgmine katkestus hiljem kui vaja. Selgub, et eelmised (viivitatud) ja uued katkestused ei pea nõutavale intervallile vastu.

Seda olukorda saab tasandada, kui kirjutate loendusregistri järgmiselt:

TCNT0 = TCNT0 + algusväärtus;

Loendusregistri praeguse väärtuse lisamine lähtestatud registriga võtab arvesse neid täiendavaid kellatsükleid.On tõesti üks AGA! Kui startValue on suur, võib liitmise toiming põhjustada loenduri registri ületäitumise.

Näiteks startValue = 250 ja taimer suutis lugeda 10-ni. Seejärel annab liitmise toiming järgmise tulemuse:

10 + 250 = 260

260-st võtame 8 numbrit ja saame 4. 4 kirjutatakse TCNT0-sse.

Katkestused võimaldavad mikrokontrolleritel sündmustele reageerida, ilma et nad peaksid pidevalt kontrollima mingeid tingimusi, et teha kindlaks, millal olulised muudatused on toimunud. Lisaks võimalusele ühendada katkestuste allikaid mõne viiguga, saate kasutada ka taimeriga genereeritud katkestusi.

Riistvara katkestab

Katkestuste kasutamise demonstreerimiseks pöördume tagasi digitaalsete sisendite juurde. Sageli kasutatakse mõne sisendsündmuse hetke määramiseks (näiteks nupu vajutamine) järgmist koodi:

if (digitalRead(inputPin) == LOW)

// Tehke mõned toimingud

See kood kontrollib pidevalt sisendPin pingetaset ja kui digitalRead tagastab LOW, teeb see midagi, millele viitab kommentaar // Tee midagi. See on täiesti toimiv lahendus, aga mis siis, kui silmusfunktsiooni sees on vaja teha palju muid toiminguid? Kõik need toimingud võtavad aega, mistõttu on võimalik lühikesest nupuvajutusest ilma jääda, kui protsessor on millegi muuga hõivatud. Tegelikult on peaaegu võimatu märkamata jätta, et nuppu vajutati, sest mikrokontrolleri standardite järgi jääb see väga pikaks ajaks alla vajutama.

Aga kuidas on lood anduri lühikeste impulssidega, mis võivad kesta sekundi miljondikuid? Selliste sündmuste vastuvõtmiseks peaksite kasutama katkestusi, määratledes funktsioonid, mida nende sündmuste puhul kutsutakse, olenemata sellest, mida mikrokontroller teeb. Selliseid katkestusi nimetatakse riistvara katkestused(riistvarakatkestused).

Arduino Unos on riistvarakatkestustega seotud vaid kaks kontakti, mistõttu kasutatakse neid väga säästlikult. Leonardol on neid tihvte viis, suurematel tahvlitel nagu Mega2560 on palju rohkem ja Due kontaktid on kõik katkestamisvõimelised.

Järgnevalt selgitatakse, kuidas riistvarakatkestused töötavad. Näidatud näite proovimiseks vajate täiendavat leivatahvlit, nuppu, 1k oomi takistit ja mõningaid hüppaja juhtmeid.

Joonisel fig. Joonis 3.1 näitab kokkupandud vooluringi. Läbi takistuse rakendatakse tihvtile D2 KÕRGE pinge, kuni nuppu vajutatakse, mille järel kontakt D2 maandatakse ja selle pingetase langeb tasemele LOW.

Laadige oma Arduino tahvlile üles järgmine visand:

// visand 03_01_katkestused

int ledPin = 13;

pinMode(ledPin, OUTPUT);

tühi värkHapenned()

digitalWrite(ledPin, HIGH);

Riis. 3.1. Katkestuse testimise skeem

Lisaks LED-viigu seadistamisele töötama digitaalväljundina, kasutab häälestusfunktsioon funktsiooni katkestusega seostamiseks teist rida. Nüüd kutsutakse seda funktsiooni automaatselt vastuseks igale katkestusele. Vaatame seda rida lähemalt, sest siin kutsutud funktsiooni argumendid tunduvad veidi ebatavalised:

attachKatkestus(0, asjad juhtunud, KUKKUV);

Esimene argument – ​​0 – on katkestuse number. See oleks selgem, kui katkestuse number langeks kokku pin numbriga, kuid see pole nii. Arduino Unos on katkestus 0 seotud viiguga D2 ja katkestus 1 seotud viiguga D3. Olukord muutub veelgi segasemaks tänu sellele, et teistes Arduino mudelites on need katkestused seotud erinevate viigudega ning lisaks tuleb Arduino Due's määrata pin number. Arduino Due plaadil on kõik kontaktid seotud katkestustega.

Ma tulen selle probleemi juurde hiljem tagasi, kuid läheme nüüd teise argumendi juurde. See argument stuffHappened tähistab funktsiooni nime, mida tuleks katkestamise käsitlemiseks kutsuda. Seda funktsiooni on visandil täpsemalt määratletud. Selliseid funktsioone nimetatakse katkestada rutiinid(Interrupt Service Routine, ISR), kehtivad erinõuded. Neil ei saa olla parameetreid ja nad ei tohi midagi tagastada. Sellel on omajagu mõtet: kuigi neid kutsutakse visandil erinevates kohtades, pole ühtegi koodirida, mis otse ISR-i kutsuks, seega pole mingit võimalust neile parameetreid edastada ega tagastusväärtust saada.

Funktsiooni viimane parameeter attachInterrupt on konstant, antud juhul FALLING. See tähendab, et katkestusrutiini kutsutakse välja ainult siis, kui pinge kontaktil D2 muutub HIGH tasemelt LOW tasemele (st langemisel), mis toimub nupu vajutamisel.

Pange tähele, et silmusfunktsioonis pole koodi. Üldiselt võib see funktsioon sisaldada koodi, mis töötab kuni katkestuse tekkimiseni. Katkestusrutiin ise lülitab lihtsalt L LED-i sisse.

Katsetamisel peaks pärast Arduino lähtestamist L LED-tuli välja lülituma. Ja pärast nupu vajutamist süttib see kohe ja jääb põlema kuni järgmise lähtestamiseni.

Pärast katsetamist proovige muuta viimase argumendiks attachInterrupt kõnes RISING ja laadige muudetud visand üles. Pärast Arduino taaskäivitamist peaks LED põlema jääma, sest pinge pindal on küll HIGH tasemel, kuid püsinud sellel tasemel alates taaskäivitusest. Kuni selle hetkeni ei langenud kontakti pinge madalale tasemele ja seejärel tõusnud (tõusnud) tasemele HIGH.

Kui vajutate ja hoiate nuppu all, peaks LED-tuli põlema, kuni selle vabastate. Nupu vabastamine põhjustab kontaktiga D2 seotud katkestuse, kuna nuppu all hoides oli kontakti pingetase MADAL ja vabastamisel tõusis see HIGH-ni.

Kui testimise käigus selgub, et teiega toimuv ei vasta varem antud kirjeldusele, on see tõenäoliselt tingitud nupul olevate kontaktide põrgatamisest. Selle efekti põhjustab asjaolu, et nupp ei anna selget üleminekut "sees" ja "väljas" olekute vahel, selle asemel toimub vajutamise hetkel korduv üleminek nende olekute vahel kuni "sees" olekuni fikseeritud. Proovige nuppu tugevamalt vajutada, see peaks aitama saavutada selget üleminekut olekute vahel ilma põrkeefektita.

Teine viis selle visandi proovimiseks on nuppu vajutada ja hoida all, samal ajal vajutades ja vabastades Arduino tahvli nuppu Reset. Seejärel, kui sketš algab, vabastage leivalaua nupp ja L LED süttib.

Katkestustega tihvtid

Tuleme nüüd tagasi katkestuste nimetamise probleemi juurde. Tabelis 3.1 loetleb Arduino plaatide levinumad mudelid ning näitab nendes olevate katkestuste ja kontaktide numbrite vastavust.

Tabel 3.1. Katkestustega tihvtid erinevates Arduino mudelites

Mudel Katkestuse number Märkmed
0 1 2 3 4 5
Uno D2 D3 - - - -
Leonardo D3 D2 D0 D1 D7 - Tõepoolest, võrreldes Unoga on esimesed kaks katkestust määratud erinevatele kontaktidele
Mega2560 D2 D3 D21 D20 D19 D18
Tähtaeg - - - - - - Katkestuste numbrite asemel tuleks funktsioonile attachInterrupt edastada PIN-koodid

Kahe esimese katkestuse tihvtide vahetamine Unos ja Leonardos tekitab lõksu, millesse on kerge kukkuda. Due mudelis tuleks katkestamisnumbrite asemel anda funktsioonile attachInterrupt pin-numbrid, mis näeb loogilisem välja.

Katkestusrežiimid

Eelmises näites kasutatud katkestusrežiimid RISING (tõusev serv) ja FALLING (langev serv) on praktikas kõige sagedamini kasutatavad. Siiski on mitmeid teisi režiime. Need režiimid on loetletud ja kirjeldatud tabelis. 3.2.

Tabel 3.2. Katkestusrežiimid

Režiim Tegevus Kirjeldus
MADAL Katkestus genereeritakse pingetasemel LOW Selles režiimis kutsutakse katkestusrutiini pidevalt nii kaua, kuni viik jääb madalaks.
TÕUSMINE Katkestus genereeritakse, kui pinge langeb positiivselt, madalast väärtusest HIGH. -
KUKKUMINE Katkestus genereeritakse negatiivse pingelanguse korral HIGH-lt LOW-le. -
KÕRGE Katkestus genereeritakse pingetasemel HIGH Seda režiimi toetatakse ainult Arduino Due mudelis ja sarnaselt LOW-režiimile kasutatakse seda praktikas harva.

Luba sisemine takistus

Eelmise näite vooluringis kasutati välist tõmbetakistit. Kuid praktikas juhitakse katkestusi tekitavaid signaale sageli andurite digitaalsetest väljunditest, mille puhul ei ole vaja kasutada tõmbetakistit.

Kuid kui anduri rolli mängib nupp, mis on ühendatud täpselt samamoodi nagu joonisel fig. 3.1, on takistusest võimalik vabaneda, lülitades sisse sisemise "tõmbetakistuse" nimiväärtusega umbes 40 kOhm. Selleks peate katkestusega seotud viigu jaoks selgesõnaliselt määrama režiimi INPUT_PULLUP, nagu on näidatud rasvases kirjas:

pinMode(ledPin, OUTPUT);

pinMode(2, INPUT_PULLUP);

attachKatkestus(0, asjad juhtunud, KUKKUV);

Katkesta rutiinid

Mõnikord võib tunduda, et katkestuste käsitlemise võimalus silmusfunktsiooni töötamise ajal on lihtne viis sündmuste, näiteks klahvivajutuste käsitlemiseks. Kuid tegelikkuses on väga ranged piirangud sellele, mida katkestamisrutiinid võivad teha ja mida mitte.

Katkestusrutiinid peaksid olema võimalikult lühikesed ja kiired. Kui katkestusrutiini töötamise ajal tekib veel üks katkestus, siis rutiini ei katkestata ja tekkivat signaali lihtsalt ignoreeritakse. See tähendab näiteks, et kui sageduse mõõtmiseks kasutatakse katkestusi, võite saada vale väärtuse.

Lisaks on katkestusrutiini töötamise ajal silmusfunktsiooni kood jõude.

Katkestused keelatakse töötlemise ajal automaatselt. See lahendus hoiab ära segaduse alamprogrammide vahel, mis üksteist katkestavad, kuid millel on soovimatud kõrvalmõjud. Viivitusfunktsioon kasutab taimereid ja katkestusi, nii et see ei tööta katkestusrutiinide korral. Sama kehtib ka millis-funktsiooni kohta. Kui proovite kasutada millisekundeid, et saada sel viisil viivituse tegemiseks plaadi viimasest lähtestamisest möödunud millisekundite arv, ei õnnestu, kuna see tagastab sama väärtuse, kuni katkestusrutiin on lõppenud. Siiski saate kasutada funktsiooni delayMicroseconds, mis ei kasuta katkestusi.

Katkestusi kasutatakse ka jadasideses, seega ärge proovige kasutada Serial.printi ega jadapordi lugemisfunktsioone. Siiski võite proovida ja mõnikord need isegi töötavad, kuid ärge oodake selliselt ühenduselt suurt töökindlust.

Operatsioonimuutujad

Kuna katkestusrutiinil ei saa olla parameetreid ja see ei saa midagi tagastada, on vaja mingit viisi teabe edastamiseks selle ja ülejäänud programmi vahel. Seda tehakse tavaliselt globaalsete muutujate abil, nagu on näidatud järgmises näites.

// visand 03_02_interrupt_flash

int ledPin = 13;

volatile Boolean flashFast = vale;

pinMode(ledPin, OUTPUT);

attachKatkestus(0, asjad juhtunud, KUKKUV);

int periood = 1000;

kui (flashFast) periood = 100;

digitalWrite(ledPin, HIGH);

digitalWrite(ledPin, LOW);

tühi värkHapenned()

flashFast = ! flashFast;

Selles visandis kasutab silmusfunktsioon viivitusperioodi määramiseks globaalset muutujat flashFast. Töötlemisrutiin muudab selle muutuja väärtuse tõeseks ja valeks.

Pange tähele, et flashFast muutuja deklaratsioon sisaldab sõna volatile. Saate edukalt arendada visandit ka ilma muutuva spetsifikaatorita, kuid see on hädavajalik, sest ilma muutliku spetsifikaatorita saab C-kompilaator genereerida masinkoodi, mis salvestab muutuja väärtused registrisse jõudluse parandamiseks. Kui, nagu antud juhul, vahemällu salvestamise kood katkestatakse, ei pruugi see muutuja väärtuse muutust märgata.

Kokkuvõtteks katkestusrutiinide kohta

Katkestusrutiinide kirjutamisel pidage meeles järgmisi reegleid.

Alamprogrammid peavad tegutsema kiiresti.

Andmete edastamiseks katkestusrutiini ja ülejäänud programmi vahel tuleb kasutada muutujaid, mis on deklareeritud volatiilse spetsifikaatoriga.

Ärge kasutage viivitust, kuid võite kasutada delayMicroseconds.

Ärge oodake väga usaldusväärset sidet jadaportide kaudu.

Ärge oodake, et millis-funktsiooni tagastatud väärtus muutub.

Katkestuste lubamine ja keelamine

Vaikimisi on katkestused visandites lubatud ja, nagu varem mainitud, on katkestusrutiini töötamise ajal automaatselt keelatud. Siiski on võimalik katkestusi programmikoodis selgesõnaliselt keelata ja lubada, kutsudes välja funktsioonid noInterrupts ja interrupts. Nendel funktsioonidel pole parameetreid ja esimene neist keelab katkestused ja teine ​​lubab need.

Selgesõnaline juhtimine võib olla vajalik tagamaks, et koodijuppi, näiteks sellist, mis väljastab andmejada või genereerib impulsside jada ja mis on funktsiooni delayMicroseconds abil täpselt ajastatud, ei saaks katkestada.

Taimer katkestab

Katkestuste käsitlemise rutiinide kõnet saab korraldada mitte ainult väliste sündmuste, vaid ka sisemiste ajamuutuste sündmuste kaudu. See funktsioon on eriti kasulik, kui peate teatud ajavahemike järel teatud toiminguid tegema.

TimerOne'i teek muudab taimeri katkestuste konfigureerimise lihtsaks. Selle leiate ja saate alla laadida aadressilt http://playground.arduino.cc/Code/Timer1.

Järgmine näide näitab, kuidas kasutada TimerOne'i 1 kHz ruutlaine impulsi jada genereerimiseks. Kui teil on sageduse mõõtmise võimalusega ostsilloskoop või multimeeter, ühendage see signaali nägemiseks kontaktiga 12 (joonis 3.2).

Riis. 3.2. Taimeri abil genereeritud ristkülikukujuline impulsside jada

// sketch_03_03_1kHz

#kaasa

int outputPin = 12;

volatiilne sisendväljund = LOW;

pinMode(12, VÄLJUND);

Timer1.initialize(500);

Timer1.attachInterrupt(toggleOutput);

void toggleOutput()

digitalWrite(väljundPin, väljund);

väljund = ! väljund;

Sama saab rakendada ka viivituse abil, kuid taimeri katkestuste kasutamine võimaldab korraldada tsükli sees mis tahes muude toimingute täitmist. Lisaks ei saavuta viivitusfunktsiooni kasutamine suurt täpsust, kuna kontakti pingetaseme muutmiseks kuluvat aega ei võeta viivituse väärtuses arvesse.

MÄRGE

Kõik piirangud välistele katkestusrutiinidele, millest oli varem juttu, kehtivad ka taimeri katkestusrutiinide kohta.

Esitatud meetodi abil saate määrata mis tahes intervalli katkestuste vahel vahemikus 1 kuni 8 388 480 μs, see tähendab kuni ligikaudu 8,4 s. Intervalli väärtus edastatakse initsialiseerimisfunktsioonile mikrosekundites.

TimerOne'i teek võimaldab kasutada ka taimerit impulsi laiuse modulatsiooni (PWM) signaalide genereerimiseks plaadi kontaktidel 9 ja 10. See võib tunduda liialdusena, sest analoogWrite teeb sama, kuid katkestuste kasutamine võimaldab PWM-signaali täpsemalt juhtida. Eelkõige on seda lähenemist kasutades võimalik positiivse impulsi pikkuse mõõtmine korraldada analoogWrite funktsiooni 0...255 asemel vahemikus 0...1023. Lisaks on analoogWrite’i kasutamisel PWM-signaali impulsi kordussagedus 500 Hz ning TimerOne’i abil saate seda sagedust suurendada või vähendada.

PWM-signaali genereerimiseks TimerOne teegi abil kasutage funktsiooni Timer1.pwm, nagu on näidatud järgmises näites:

// sketch_03_04_pwm

#kaasa

pinMode(9, OUTPUT);

pinMode(10, VÄLJUND);

Timer1.initialize(1000);

Timer1.pwm(9, 512);

Timer1.pwm(10, 255);

Siin valitakse impulsi kordusperioodiks 1000 μs, see tähendab, et PWM signaali sagedus on 1 kHz. Joonisel fig. 3.3 näitab lainekuju kontaktidel 10 ( üles) ja 9 ( põhjas).

Riis. 3.3. 1 kHz impulsi laiusega signaal, mis on genereeritud TimerOne'i abil

Nalja pärast vaatame, mil määral saab PWM signaali sagedust tõsta. Kui perioodi pikkust vähendada 10-ni, peaks PWM-signaali sagedus tõusma 100 kHz-ni. Nende parameetritega saadud lainekujud on näidatud joonisel fig. 3.4.

Vaatamata märkimisväärsetele mööduvatele moonutustele, mis on üsna ootuspärane, jääb positiivsete impulsside pikkus siiski vastavalt 25 ja 50% lähedale.

Riis. 3.4. 100 kHz impulsi laiusega signaal, mis on genereeritud TimerOne'i abil

Lõpuks

Katkestused, mis mõnikord tunduvad keeruliste projektide jaoks ideaalse lahendusena, võivad muuta koodi silumise keeruliseks ega ole alati parim viis keeruliste probleemide lahendamiseks. Enne nendele pühendumist kaaluge hoolikalt võimalikke lahendusi. 14. peatükis vaatleme veel ühte nippi, et ületada raskusi, mis tulenevad Arduino suutmatusest tegeleda korraga rohkem kui ühe ülesandega.

Naaseme katkestuste juurde 5. peatükis, kus vaatleme, kuidas neid saab kasutada Arduino plaadi energiatarbimise vähendamiseks, pannes selle perioodiliselt energiasäästurežiimile, ja 13. peatükis, kus kasutame katkestused, et suurendada digitaalse signaalitöötluse täpsust.

Järgmises peatükis uurime tehnikaid Arduino jõudluse maksimeerimiseks.

Arvutasime välja põhiahela iteratsiooniloenduri ja saime teada, et see ei sobi täpseks ajalugemiseks täiesti - säriaeg ujub ja seda on raske lugeda. Mida teha?

Ilmselgelt vajame mingit välist loendurit, mis tiksuks sõltumata protsessori tööst ja protsessor näeks igal hetkel, mis seal tiksub. Või selleks, et loendur genereeriks üle- või allavoolusündmusi – tõstke lipp või genereerige katkestus. Ja protsenti tunneb selle lõhna ja töötleb seda.

Ja seal on selline loendur, isegi mitte üks - need on välisseadmete taimerid. Neid võib AVR-is olla mitu ja isegi erineva bitisügavusega. ATmega16-l on kolm, ATmega128-l neli. Ja AVR-seeria uutes MK-des võib neid olla veelgi rohkem, ma ei tundnud seda ära.

Pealegi võib taimer olla midagi enamat kui lihtsalt loll loendur. Taimer on üks keerukamaid (alternatiivsete funktsioonide osas) välisseadmeid.

Mida saavad taimerid teha?

  • Tiksub erinevatel kiirustel, lugedes aega
  • Väljastpoolt saabuvate impulsside loendamine (loendurirežiim)
  • Välise kvartsi puuk sagedusel 32768 Hz
  • Genereerige mitut tüüpi PWM-signaale
  • Väljastada katkestusi (pool tosinat erinevat sündmust) ja seada lipud

Erinevatel taimeritel on erinev funktsionaalsus ja erinev bitisügavus. Täpsema teabe saamiseks vaadake andmelehte.

Taimeri linnukese allikas
Taimer/loendur (edaspidi nimetan seda T/C-ks) loeb kas sisseehitatud kellageneraatorist või loendussisendist pärinevaid taktimpulsse.

Vaadake hoolikalt ATmega16 jalgade tihvti, kas näete seal jalgu T1 ja T0?

Need on loendussisendid Timer 0 ja Timer 1. Sobivate seadistustega loendab T/S kas esiserva (serv 0-1) või tagumist serva (serv 1-0). nendele sisenditele saabuvaid impulsse.

Peaasi, et sissetulevate impulsside sagedus ei ületaks protsessori taktsagedust, vastasel juhul pole tal aega impulsside töötlemiseks.

Lisaks on T/C2 võimeline töötama asünkroonses režiimis. See tähendab, et T/S ei loe protsessori taktimpulsse, mitte jalgadele saabuvaid impulsse, vaid omaenda ostsillaatori impulsse, mida toidab eraldi kvarts. Selleks on T/C2-l TOSC1 ja TOSC2 sisendid, millele saab kinnitada kvartsresonaatori.

Miks see üldse vajalik on? Jah, korraldage vähemalt reaalajas kell. Riputasin neile külge kella kvartsi sagedusel 32768 Hz ja lugesin aega - sekundis tuleb 128 ületäitumist (kuna T/C2 on kaheksabitine). Seega on üks ülevool 1/128 sekundist. Veelgi enam, taimer ei peatu ülevoolukatkestuse töötlemise ajal, see jätkab ka loendamist. Nii et kell on imelihtne!

Eeljagaja
Kui taimer loeb impulsse kella generaatorilt või selle sisemiselt, siis saab neid ikkagi läbi eelskaalaja lasta.

See tähendab, et isegi enne loendusregistrisse sisenemist jagatakse impulsi sagedus. Saate jagada 8, 32, 64, 128, 256, 1024-ga. Nii et kui panete T/C2-le kella kvartsi ja lasete selle 128 juures läbi eelskaalaja, tiksub taimer kiirusega üks tikk sekundis.

Mugav! Eelskaalarit on mugav kasutada ka siis, kui on vaja lihtsalt saada suur intervall ja ainsaks tiksumise allikaks on protsessori kella generaator sagedusel 8 MHz, siis tüdinete nende megahertside lugemisest, aga kui see eelskaalajast läbi lasta , 1024 juures, siis on kõik palju rõõmsam.

Kuid siin on üks omapära, fakt on see, et kui käivitada T/S mingi jõhkra eelskaalariga, näiteks 1024 juures, siis esimene linnuke loendusregistrisse ei pruugi tulla peale 1024 impulssi.

Oleneb, mis olekus eelskaalaja oli, mis siis, kui selle sisselülitamise ajaks oli see juba lugenud peaaegu 1024-ni? See tähendab, et kohe tuleb linnuke. Eelskaalaja töötab kogu aeg, olenemata sellest, kas taimer on sisse lülitatud või mitte.

Seetõttu saab ja tuleks eelskaalareid lähtestada. Arvestada tuleb ka asjaoluga, et eelskaalaja on kõikide loendurite jaoks sama, nii et selle lähtestamisel tuleb arvestada asjaoluga, et mõnel teisel taimeril läheb järgmise linnuni aega ja see võib täpselt valesti minna. nii.

Näiteks esimene taimer töötab eelskaalaja viigul 1:64 ja teine ​​​​viigul 1:1024. Teine jõudis eelskaleris peaaegu 1024-ni ja nüüd peaks taimeri linnuke olema, aga siis läksid ja resetitasid eelskaalarit, et esimene taimer täpselt nullist käivitada. Mis juhtub? Täpselt nii, teine ​​jagaja lähtestub kohe 0-le (eelskaalaja on sama, sellel on üks register) ja teine ​​taimer peab soovitud impulsi saamiseks ootama veel 1024 taktitsüklit!

Ja kui lähtestate eelskaalaja esimese taimeri hüvanguks sagedamini kui kord 1024 kellatsükli jooksul, siis teine ​​taimer ei tiksu kunagi ja te lööte peaga vastu lauda, ​​püüdes mõista, miks teie teine ​​taimer ei tööta, kuigi peab.

Eelskaalajate lähtestamiseks kirjutage lihtsalt SFIOR-registrisse PSR10 bitt. PSR10 bitt lähtestatakse automaatselt järgmisel kellatsüklil.

Konto register
Kogu ülalkirjeldatud piina tulemus kogutakse loendusregistrisse TCNTx, kus x on taimeri number. see võib olla kas kaheksa- või kuueteistbitine, sel juhul koosneb see kahest registrist TCNTxH ja TCNTxL – vastavalt kõrgetest ja madalatest baitidest.

Ja siin on konks, kui on vaja panna number kaheksabitisesse registrisse, siis pole probleeme OUT TCNT0, Rx ja pole naelu, siis tuleb kahebaidiste registritega ringi mängida.

Ja kogu asi on selles, et taimer loeb protsessorist sõltumatult, nii et me saame panna esimeseks ühe baidi, see hakkab loendama, seejärel teist ja ümberarvutamine algab teist baiti arvesse võttes.

Kas tunnete end halvasti? Siin! Taimer on täpne seade, nii et selle loendusregistrid tuleb laadida korraga! Aga kuidas? Ja Atmeli insenerid lahendasid probleemi lihtsalt:
Kõrge register (TCNTxH) kirjutatakse TEMP-registris esimesena. See register on puhtalt ametlik ega ole meile kuidagi juurdepääsetav.

Millega me lõpuks jõuame: kirjutame kõrge baidi TEMP-registrisse (meie jaoks on see paganama TCNTxH) ja seejärel kirjutame madala baidi. Sel hetkel sisestatakse meie varem salvestatud väärtus tegelikku TCNTxH-sse. See tähendab, et kaks baiti, kõrge ja madal, kirjutatakse korraga! Tellimust muuta ei saa! Ainus viis

See näeb välja selline:

CLI ; Me keelame katkestusteta! OUT TCNT1H,R16; Kõige olulisem bait kirjutati kõigepealt TEMP OUT TCNT1L,R17; Ja nüüd olen registreerunud nii vanemate kui ka noorte klassidesse! SEI; Katkestuste lubamine

Miks keelata katkestused? Jah, et pärast esimese baidi kirjutamist programm kogemata katkestusteta välja ei torma ja siis keegi meie taimerit vägistab. Siis pole selle registrites see, mida me siia saatsime (või katkestusse), vaid mida kuradit. Nii et proovige hiljem selline viga tabada! Kuid see võib välja tulla kõige ebasobivamal hetkel, kuid te ei saa sellest aru, sest katkestus on peaaegu juhuslik muutuja. Nii et selliste hetkedega tuleb kohe tegeleda.

Kõike loetakse samamoodi, ainult vastupidises järjekorras. Esiteks madal bait (samal ajal kui kõrge surutakse TEMP-i), seejärel kõrge. See tagab, et loeme täpselt seda baiti, mis parasjagu loendusregistris oli, mitte seda, mis jooksis siis, kui me seda baiti loendusregistrist välja valisime.

Kontrollregistrid
Ma ei kirjelda kõiki taimerite funktsioone, vastasel juhul osutub see ülekaalukaks traktaadiks. Parem on rääkida peamisest - loendavast ning kõikvõimalikud PWM-i ja muud generaatorid on teises artiklis. Nii et olge kannatlik või närige andmelehte, see on samuti kasulik.

Seega on põhiregister TCCRx
T/C0 ja T/C2 puhul on need vastavalt TCCR0 ja TCCR2 ning T/C1 puhul TCCR1B

Praegu huvitab meid ainult selle registri kolm esimest bitti:
CSx2.. CSx0, asenda x taimeri numbriga.
Nad vastutavad eelskaalaja ja kella allika seadistamise eest.

Erinevad taimerid on veidi erinevad, seega kirjeldan CS02..CS00 bitte ainult taimeri 0 jaoks

  • 000 - taimer peatus
  • 001 — eelskaalaja on võrdne 1-ga, see tähendab välja lülitatud. taimer loeb kella impulsse
  • 010 - eelskaalaja on 8, taktsagedus jagatakse 8-ga
  • 011 - eelskaler on 64, taktsagedus jagatakse 64-ga
  • 100 - eelskaler on 256, taktsagedus jagatakse 256-ga
  • 101 - eelskaalaja on 1024, taktsagedus jagatakse 1024-ga
  • 110 - taktimpulsid pärinevad T0 kontaktilt üleminekul 1-lt 0-le
  • 111 - taktimpulsid tulevad T0 viigust üleminekul 0-lt 1-le

Katkestab
Igal riistvarasündmusel on katkestus ja taimer pole erand. Niipea kui toimub ülevool või mõni muu uudishimulik sündmus, ilmub kohe katkestus.

Taimerite katkestuste eest vastutavad registrid TIMSK ja TIFR. Ja jahedamatel AVR-idel, nagu ATMega128, on ka ETIFR ja ETIMSK - omamoodi jätk, kuna seal on rohkem taimereid.

TIMSK on maskide register. See tähendab, et selles sisalduvad bitid võimaldavad lokaalselt katkestusi. Kui bitt on seatud, on konkreetne katkestus lubatud. Kui bitt on null, on see katkestus kaetud basseiniga. Vaikimisi on kõik bitid nullid.

Hetkel huvitab meid ainult ülevoolukatkestused. Bitid vastutavad nende eest

  • TOIE0 – luba katkestada taimeri 0 ülevoolul
  • TOIE1 – luba katkestada taimeri 1 ülevoolul
  • TOIE2 – luba katkestada taimer 2 ülevoolu

Muudest funktsioonidest ja taimeri katkestustest räägime hiljem, kui vaatame PWM-i.

TIFR-register on otseselt lipuregister. Kui mõni katkestus käivitatakse, ilmub lipp, mis näitab, et meil on katkestus. Kui programm vektorist lahkub, lähtestatakse see lipp riistvara poolt. Kui katkestused on keelatud, jääb lipp sinna seni, kuni katkestused on lubatud ja programm läheb katkestusele.

Selle vältimiseks saab lipu käsitsi lähtestada. Selleks tuleb TIFR-i registrisse kirjutada 1!

Nüüd lähme persse
Noh, kujundame programmi ümber, et see töötaks taimeriga. Tutvustame programmi taimerit. Tünniorel jääbki nii, las tiksub. Ja lisame teise muutuja, samuti neli baiti:

ORG $ 010 RETI ; (TIMER1 OVF) Timer/Counter1 Overflow .ORG $012 RJMP Timer0_OV ; (TIMER0 OVF) Timer/Counter0 Overflow .ORG $014 RETI ; (SPI,STC) Jadaülekanne lõpetatud

Lisame jaotisesse Katkestused taimeri 0 ülevoolu katkestuste töötleja. Kuna meie tiksumismakro töötab aktiivselt registritega ja rikub lippe, peame esmalt kogu selle asja virna salvestama:

Muuseas, loome veel ühe makro, mis lükkab SREG lipuregistri virna ja teise, mis selle sealt välja toob.

1 2 3 4 5 6 7 8 9 10 11 12 .MACRO PUSHF PUSH R16 SISSE R16,SREG PUSH R16 .ENDM .MACRO POPF POP R16 OUT SREG,R16 POP R16 .ENDM

MACRO PUSHF PUSH R16 SISSE R16,SREG PUSH R16 .ENDM .MACRO POPF POP R16 OUT SREG, R16 POP R16 .ENDM

Kõrvalmõjuna säilib ka R16, pea meeles :)

1 2 3 4 5 6 7 8 9 10 11 12 13 Timer0_OV: PUSHF PUSH R17 PUSH R18 PUSH R19 INCM TCNT POP R19 POP R18 POP R17 POPF RETI

Timer0_OV: PUSHF PUSH R17 PUSH R18 PUSH R19 INCM TCNT POP R19 POP R18 POP R17 POPF RETI

Nüüd lähtestage taimer. Lisage see jaotisesse Internal Hardware Init.

; Sisemine riistvara algseade ======================================= SETB DDRD,4,R16 ; DDRD.4 = 1 SETB DDRD,5,R16 ; DDRD.5 = 1 SETB DDRD,7,R16 ; DDRD.7 = 1 SETB PORTD,6,R16 ; PD6 väljund ülestõstetavale sisendile CLRB DDRD,6,R16 ; Nupu lugemiseks SETB TIMSK,TOIE0,R16 ; Taimeri katkestuse lubamine OUTI TCCR0,1<

Jääb üle vaid meie võrdlusplokk ümber kirjutada ja arv ümber arvutada. Nüüd on kõik lihtne, üks linnuke üks riba. Ilma probleemideta erinevate koodide pikkustega. Ühe sekundi jooksul 8 MHz juures tuleb teha 8 miljonit tikki. Kuueteistkümnendites on see 7A 12 00, arvestades, et alumine bait on TCNT0, siis jääb meie loenduriks 7A 12 ja ka kõrgeim kaks baiti 00 00, siis neid ei pea kontrollima. Pole vaja maskeerida; me lähtestame taimeri hiljem nagunii.

On ainult üks probleem – madal bait, see, mis on taimeris. See märgib iga linnukese ja nõuete täitmist on peaaegu võimatu kontrollida. Sest Vähimgi lahknevus ja võrdlustingimus ilmuvad NoMatchis, aga ära arvata see nii, et selle väärtuse kontroll langeks kokku just selle sammuga... Lihtsam on esimesel katsel juhuslikult nõela heinakuhjast välja tõmmata.

Seega on täpsus antud juhul piiratud – teil peab olema aega, et väärtust kontrollida, enne kui see vahemikust lahkub. Sel juhul on vahemik lihtsuse huvides 255 – madalaima baidi väärtus, mis on taimeris.

Siis on meie teine ​​varustatud 8 000 000 pluss-miinus 256 tsükli täpsusega. Viga pole suur, ainult 0,003%.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 ; Põhiline ================================================== ================ ======== Peamine: SBIS PIND,6 ; Kui nuppu vajutada - üleminek RJMP BT_Push SETB PORTD,5 ; Süütame LED2 CLRB PORTD,4 ; LED1 väljalülitamine Järgmine: LDS R16,TCNT ; Laadige numbrid registritesse LDS R17,TCNT+1 CPI R16.0x12 ; Võrdleme baithaaval. Esimene bait BRCS NoMatch ; Kui see on väiksem, tähendab see, et see ei tabanud. CPI R17.0x7A ; Teine bait BRCS NoMatch ; Kui see on väiksem, tähendab see, et see ei tabanud. ; Kui see sobib, siis teeme Match toimingu: INVB PORTD,7,R16,R17 ; Tagurpidi LED3; Nüüd peame loenduri lähtestama, vastasel juhul põhitsükli sama iteratsiooni ajal; jõuame siia rohkem kui üks kord - taimeril pole aega 255 väärtuseni jõuda; nii et loenduri kahe esimese baidi arv muutub ja tingimus käivitatakse. ; Sellest saab muidugi lisalipuga mööda minna, aga loendurit on lihtsam lähtestada :) CLR R16 ; Vajame null CLI-d; Juurdepääs mitmebaidilisele muutujale; samaaegselt katkestusest ja taustast; teil on vaja aatomi juurdepääsu. Keela katkestused OUTU TCNT0,R16 ; Null taimeri loendusregistrisse STS TCNT, R16 ; Null RAM-i loenduri esimeses baidis STS TCNT+1,R16 ; Null RAM-i loenduri teises baidis STS TCNT+2,R16 ; Null RAM-i loenduri kolmandas baidis STS TCNT+3,R16 ; RAM SEI loenduri esimeses baidis null; Lubame katkestused uuesti. ; Kui ei sobi, siis me seda ei tee :) NoMatch: NOP INCM CCNT ; Tsikliloendur tiksub; Isegi kui seda ei kasutata. JMP Main BT_Push: SETB PORTD,4 ; Valgustage LED1 CLRB PORTD,5 ; Lülitage LED2 RJMP Next välja; Peamine lõpp ================================================= ======================

; Põhiline ================================================== =============== ======== Peamine: SBIS PIND,6 ; Kui nuppu vajutada - üleminek RJMP BT_Push SETB PORTD,5 ; Süütame LED2 CLRB PORTD,4 ; LED1 väljalülitamine Järgmine: LDS R16,TCNT ; Laadige numbrid registritesse LDS R17,TCNT+1 CPI R16.0x12 ; Võrdleme baithaaval. Esimene bait BRCS NoMatch ; Kui see on väiksem, tähendab see, et see ei tabanud. CPI R17.0x7A ; Teine bait BRCS NoMatch ; Kui see on väiksem, tähendab see, et see ei tabanud. ; Kui see sobib, siis teeme Match toimingu: INVB PORTD,7,R16,R17 ; Tagurpidi LED3; Nüüd peame loenduri lähtestama, vastasel juhul põhitsükli sama iteratsiooni ajal; jõuame siia rohkem kui üks kord - taimeril pole aega 255 väärtuseni jõuda; nii et loenduri kahe esimese baidi arv muutub ja tingimus käivitatakse. ; Sellest saab muidugi lisalipuga mööda minna, aga loendurit on lihtsam lähtestada :) CLR R16 ; Vajame null CLI-d; Juurdepääs mitmebaidilisele muutujale; samaaegselt katkestusest ja taustast; teil on vaja aatomi juurdepääsu. Keela katkestused OUTU TCNT0,R16 ; Null taimeri loendusregistrisse STS TCNT, R16 ; Null RAM-i loenduri esimeses baidis STS TCNT+1,R16 ; Null RAM-i loenduri teises baidis STS TCNT+2,R16 ; Null RAM-i loenduri kolmandas baidis STS TCNT+3,R16 ; RAM SEI loenduri esimeses baidis null; Lubame katkestused uuesti. ; Kui ei sobi, siis me seda ei tee :) NoMatch: NOP INCM CCNT ; Tsikliloendur tiksub; Isegi kui seda ei kasutata. JMP Main BT_Push: SETB PORTD,4 ; Valgustage LED1 CLRB PORTD,5 ; Lülitage LED2 RJMP Next välja; Peamine lõpp ================================================= ======================

Nii näeb see välja tegevuses

Ja kui on vaja vilgutada teist dioodi erineva perioodiga, siis võime julgelt programmi panna teise muutuja ja taimeri katkestuste töötlejas saame suurendada kahte muutujat korraga. Nende ükshaaval kontrollimine programmi põhitsüklis.

Saate kinnitamisprotsessi veel veidi optimeerida. Tee see kiiremaks.

Peate lihtsalt muutma konto mitte üles, vaid alla. Need. Laadime arvu muutujasse ja hakkame seda katkestuses vähendama. Ja seal, käitlejas, kontrollime, kas see on null. Kui null, siis määrake mällu lipp. Ja meie taustaprogramm püüab selle lipu kinni ja käivitab toimingu, lähtestades samal ajal säriaja.

Aga mis siis, kui teil on vaja olla täpsem? Noh, on ainult üks võimalus - kasutada sündmuste töötlemist otse katkestuste töötlejas ja kohandada iga kord väärtust TCNT:TCNT0-s, et katkestus toimuks täpselt õigel ajal.

10. õppetund

Taimerid-loendurid. Katkestab

Täna saame teada, mis see on taimerid-loendurid mikrokontrollerites ja miks neid vaja on ning mis on katkestab ja miks neid ka vaja on.

Taimerid-loendurid- need on mikrokontrolleri seadmed või moodulid, mis, nagu nimigi ütleb, loevad pidevalt midagi. Neid loetakse kas teatud väärtuseni või nende bitisügavusega võrdse väärtuseni. Nad loevad pidevalt sama kiirusega, mikrokontrolleri taktsageduse kiirusega, mis on kohandatud sagedusjagajatele, mille me konfigureerime teatud registrites.

Ja need taimerite loendurid loevad pidevalt, kui me need lähtestame.

Taimerid MK-s Atmega8 kolm.

Kaks neist on kaheksabitine taimerid, st need, mis suudavad lugeda maksimaalselt ainult 255-ni. Sellest väärtusest meile ei piisa. Isegi kui me kasutame maksimaalset sagedusjagajat, ei saa me mitte ainult sekundit lugeda, vaid isegi poolt sekundit. Ja meie ülesanne on täpselt selline: lugeda kuni 1 sekund, et kontrollida LED-indikaatori arvu suurenemist. Muidugi võib kasutada ka muutuja suurendamist teatud väärtuseni, aga ma tahaks täiesti riistvaralist arvutust.

Kuid on veel üks taimer - see on täieõiguslik 16-bitine taimer. Ta mitte ainult 16-bitine, kuid sellel on siiski teatud võlusid, mida teistel taimeritel pole. Nende võimalustega tutvume hiljem.

Just seda 16-bitist taimerit me täna uurime ja kasutame. Peale selle, kui olete selle taimeriga tutvunud, ei maksa teile kahe teise töö iseseisvaks uurimiseks midagi, kuna need on palju lihtsamad. Sellegipoolest arvestame tulevikus ka 8-bitiste taimeritega, kuna ühest taimerist ei piisa keerulisemate ülesannete täitmiseks.

Nüüd lühidalt katkestustest.

Katkestab (Katkestused) on mehhanismid, mis katkestavad koodi sõltuvalt teatud tingimustest või teatud keskkonnast, mis dikteerib teatud mikrokontrolleris asuvaid seadmeid, mooduleid ja siine.

Meie Atmega8 kontrolleris on 19 tüüpi katkestusi. Siin on need kõik kontrolleri tehnilise dokumentatsiooni tabelis

Mis tüüpi tingimused võivad olla? Meie puhul näiteks luges taimer teatud väärtuseni või näiteks bait ja muud tingimused saabusid mõnele siinile.

Hetkel töötleme katkestust, mis asub ülaltoodud tabelis positsioonil 7 - TIMER1 COMPA, kutsuti aadressil 0x006.

Nüüd vaatame meie 16-bitist taimerit või TAIMER1.

Siin on selle plokkskeem

Näeme seal registrit TCNTn, milles arv on pidevas muutumises, st kasvab pidevalt. Praktikas on see loendur. See tähendab, et see register salvestab numbri, milleni taimer on loendanud.

Ja registritesse OCRnA Ja OCRnB(tähed n on taimeri number, meie puhul on see 1) - need on registrid, kuhu sisestame numbri, millega TCNTn registris olevat numbrit võrreldakse.

Näiteks sisestasime mingi numbri OCRnA registrisse ja niipea, kui see arv langeb kokku loendusregistris oleva väärtusega, tekib katkestus ja me saame seda töödelda. Katkestustega taimerid on väga sarnased tavalise koodi viivitusega, ainult siis, kui oleme viivituses, ei saa me sel ajal ühtegi koodi käivitada (noh, jällegi piltlikult "meie", tegelikult ALU). Ja kui taimer loeb, käivitatakse sel ajal vaikselt kogu meie programmi kood. Seega võidame tohutult, laskmata kontrolleri tohututel ressurssidel sekundit ega isegi pool sekundit jõude olla. Sel ajal saame hakkama nupuklõpsudega, millega saame hakkama ka taimeris ja palju muud.

Samuti on olemas TCCR register. See register on kontrollregister. Seal on konfigureeritud teatud bitid, mis vastutavad taimeri konfiguratsiooni eest.

Taimeril on ka mitu režiimi, millega samuti veidi hiljem tutvume.

See koosneb kahest poolest, kuna meie kontroller on 8-bitine ja sellel ei saa olla 16-bitisi registreid. Seetõttu salvestatakse registri kõrge osa registri ühes pooles (ja füüsiliselt ühes registris), madal osa aga teises pooles. Võite seda nimetada ka registripaariks, mis koosneb kahest eraldi registrist TCCR1A ja TCCR1B. Number 1 tähendab, et register kuulub taimerile 1.

See TCCR-register vastutab jagaja seadistamise eest, et taimer ei loeks nii kiiresti, samuti vastutab (õigemini selle teatud bitid) teatud režiimi seadistamise eest.

WGM-bitid vastutavad režiimi seadistamise eest

Näeme siin palju erinevaid režiime.

Tavaline- see on tavarežiim, taimer loeb lõpuni.

PWM- See PWM ainult erinevad sordid, see tähendab, et taimer võib mängida rolli impulsi laiuse modulaator. Tutvume selle tehnoloogiaga hilisemates tundides.

CTC- see on juhuse läbi lähtestamine, just see, mida me vajame. Siin võrreldakse TCNT ja OCR registreid. Selliseid režiime on kaks, vajame esimest, teine ​​töötab erineva registriga.

Me ei uuri selles õppetükis kõiki režiimide liike. Kui meil on neid režiime vaja, siis mõtleme selle välja.

No ärgem piinakem end dokumentatsiooniga ja proovime lõpuks mingitesse registritesse midagi sisestada.

Kood, nagu alati, loodi eelmisest projektist. Proteuse jaoks kopeeriti ja nimetati kood ümber ka viimasest õppetunnist ning kontrolleri atribuutides märgiti tee uue püsivara juurde. Nimetame projektid Test07.

Proovime nagu alati koodi kompileerida ja Proteuses käivitada. Kui kõik töötab hästi, hakkame uut koodi lisama.

Lisame veel ühe funktsiooni, kuna õppisime, kuidas funktsioone lisada viimases õppetükis. Funktsiooni kood paigutatakse funktsiooni segchar järele ja põhifunktsiooni ette. Hiljem, kuna kutsume funktsiooni segchar oma uue funktsiooni sees.

Pealegi loome mitte ühe funktsiooni, vaid kaks. Asetame kogu oma taimeri lähtekoodi ühte funktsiooni ja teine ​​funktsioon on taimeri katkestuse töötleja ning sellised funktsioonid on spetsiifilised ja neid ei pea kutsuma. Vajadusel helistavad nad ise sõltuvalt teatud ülaltoodud tingimustest.

Seetõttu kutsume esimest funktsiooni timer_ini

//———————————————

tühinetimer_ini( tühine)

{

}

//———————————————

Eraldagem ka meie funktsioonid, aga ka mõned globaalsete muutujate deklaratsiooniga täielikud plokid funktsiooni prototüüpidega, nende ridade kaupa, mida kahe ees oleva kaldkriipsu olemasolu tõttu kompilaator ei töötle ja hakkab võtke neid kommentaaridena. Nende piiritlemiste tõttu näeme, kus üks funktsioon lõpeb ja teine ​​algab.

Sellel funktsioonil, nagu näeme, pole argumente - ei sisendit ega tagastamist. Kutsume seda funktsiooni kohe funktsioonis main().

allkirjastamatacharbutcount=0,butstate=0;

timer_ini();

Nüüd hakkame seda funktsiooni aeglaselt koodiga täitma.

Alustame taimeri juhtimisregistrist, näiteks TCCR1B. Kasutades oma lemmikoperatsiooni "OR", sisestame registri teatud bitti ühe

tühinetimer_ini( tühine)

TCCR1B|= (1<< WGM12);

Kommentaarist näeme, et töötame režiimibittidega ja nendest määrame ainult WGM12 biti, jättes ülejäänud nullideks. Selle põhjal konfigureerisime selle režiimi:

Taimeril on ka järgmine register: TIMSK. See register vastutab katkestusmaskide eest - Katkestusmask. See register on saadaval kõigi taimerite jaoks, mitte ainult esimese jaoks, see on tavaline. Selles registris määrame biti OCIE1A, mis võimaldab meile vajalikku katkestust TIMER1 COMPA

TCCR1B|= (1<< WGM12); // määrake CTC režiim (lähtestamine juhuslikult)

TIMSK|= (1<< OCIE1A);

Nüüd mängime võrdlusregistrite endaga OCR1A (H ja L). Selleks peate natuke matemaatikat tegema. Registreeri OCR1AH salvestab võrdluseks numbri esiosa ja registri OCR1AL- Noorim.

Kuid enne loendamist kirjutame selle registri mis tahes väärtustega koodi ja seejärel parandame seda, kuna siis lähtestame jagaja ja see osaleb ka vajaliku loendusaja arvutamisel. Ilma jagaja loendab taimer liiga kiiresti.

TIMSK|= (1<< OCIE1A); //seadistab 1. loenduri katkestuse lubamise biti, et see sobiks OCR1A(H ja L)

OCR1AH= 0b10000000;

OCR1AL= 0b00000000;

TCCR1B|= ( ); //määra jagaja.

Me ei paigalda veel ühtegi jagajat, kuna me pole seda veel välja arvutanud. Teeme ära.

Hetkel meie registris OCR1A leitakse arv 0b1000000000000000, mis vastab kümnendarvule 32768.

Meie mikrokontroller töötab, nagu kokku leppisime, sagedusel 8 000 000 Hz.

Jagage 8 000 000 32 768-ga, et saada ligikaudu 244,14. See on sagedus hertsides, millega meie taimer töötab, kui me jagajat ei kasuta. See tähendab, et meie numbrid muutuvad 244 korda sekundis, nii et me ei näe neid isegi. Seetõttu peate kasutama taimeriga sagedusjagurit. Valime jagaja 256-ga. See sobib meile ja siis korrigeerime selle võrdlusnumbri abil täpselt 1 Hz.

Siin on 1 taimeri jagajad:

Olen tabelis esile toonud meile vajaliku jagaja. Näeme, et peame määrama ainult biti CS12.

Kuna meie sagedusjagur on 256, jagame selle jagajaga 8000000, saame 31250 ja see on number, mille peame TCNT-sse sisestama. Meie taimer loeb kuni selle numbrini, et lugeda 1 sekundini. Arv 31250 on binaarses esituses 0b0111101000010010. Paneme selle numbri registripaari ja rakendame ka jagajat

OCR1AH= 0b 01111010 ; //kirjutada võrdluseks registrisse number

OCR1AL= 0b 00010010 ;

TCCR1B|= (1<< CS12 ); //määra jagaja.

Selle funktsiooniga on kõik.

Nüüd on järgmiseks funktsiooniks kokkusattumustaimeri katkestuse töötleja. See on kirjutatud nii

ISR( TIMER1_COMPA_vect)

{

}

Ja selle funktsiooni põhiosa täidetakse ise arvude kokkulangemise korral.

Vajame muutujat. Deklareerime seda globaalselt, faili alguses

#kaasa

//———————————————

allkirjastamatachari;

//———————————————

Sellest lähtuvalt eemaldame funktsiooni main() koodist sama muutuja

intpeamine( tühine)

allkirjastamatachari;

Kommenteerime ka kogu lõpmatu tsükli koodi. Selle rolli täidab nüüd taimer ja ma arvan, et see saab sellega sama hästi hakkama ja isegi paremini, ilma "keegi" segamata.

samal ajal(1)

{

// for(i=0;i<10;i++)

// {

// while (butstate==0)

// {

// kui (!(PINB&0b00000001))

// {

// if(butcount< 5)

// {

//agacount++;

// }

//muu

// {

// i=0;

//butstate=1;

// }

// }

//muu

// {

// if(butcount > 0)

// {

//butcount—;

// }

//muu

// {

//butstate=1;

// }

// }

// }

// segchar(i);

// _delay_ms(500);

//butstate=0;

// }

Nüüd tegelikult käitleja funktsiooni keha. Siin kutsume funktsiooni segchar. Siis suurendame 1 muutuja võrra i. Ja et see ei ületaks ühekohalist numbrit, lähtestame selle sellel tingimusel nullile

ISR( TIMER1_COMPA_vect)

kui( i>9) i=0;

segchar( i);

i++;

Nüüd parandame veidi funktsiooni main() alguses olevat koodi. Port D, mis vastutab segmentide oleku eest, seadistame selle nii, et selle sisselülitamisel indikaator ei sütti, kuna sellel on ühine anood. Seejärel paneme siia globaalsesse muutujasse i numbri 0, lihtsalt järjekorras. Üldiselt on käivitamisel initsialiseerimata muutujad reeglina alati nullid. Kuid me selle ikkagi initsialiseerime. Ja mis kõige tähtsam, et taimeri katkestus töötaks, ei piisa selle lisamisest taimeri lähtestamisele. Samuti peavad üldiselt kõigi katkestuste toimimiseks olema lubatud globaalsed katkestused. Selleks on spetsiaalne funktsioon sei() — määrake katkestus.

Nüüd on kood selline

DDRB= 0x00;

PORTD= 0b 11111111 ;

PORTB= 0b00000001;

i=0;

sei();

samal ajal(1)

Peame faili algusesse lisama ka katkestuse teegi faili

#kaasa

#kaasa

#kaasa

Samuti ei vaja me veel nupu jaoks muutujaid, kuna me ei tööta selle nupuga täna. Kommenteerime neid

intpeamine( tühine)

//signed char butcount=0, butstate=0;

timer_ini();

Paneme oma koodi kokku ja kontrollime selle toimivust esmalt Proteuses. Kui kõik töötab hästi, siis kontrollime seda ka vooluringis

Kõik töötab meie jaoks. Suurepärane!

Selline sai stopper. Kuid kuna meil pole isegi kvartsresonaatorit, ei saa seda stopperit täpseks nimetada.

Sellest hoolimata õppisime täna palju. Õppisime katkestustest, õppisime ka nende töötlemist, õppisime töötama taimeritega, konfigureerima mitmeid uusi mikrokontrolleri registreid, enne seda töötasime ainult pordiregistritega. Samuti oleme tänu sellele kõigele oluliselt kergendanud oma mikrokontrolleri aritmeetika-loogilist seadet.

Vaata VIDEOÕPETUST

Postituse vaatamisi: 17 258