Input / Output¶
Osaamistavoitteet: I/O-pinnien ohjelmallinen käyttäminen eri tarkoituksiin sulautetussa laitteessa.
Input / Output, eli tuttavallisesti I/O (suom. siirräntä) tarkoittaa informaation siirtämistä tietokoneen komponenttien ja oheislaitteiden välillä. Inputit, ovat komponentin vastaanottamia syötteitä / signaaleja / dataa, kuten näppäimen painallus tai viesti oheislaitteelta. Outputit komponenttien lähettämiä signaaleja tai informaatiota, kuten ohjauskomento oheislaitteelle.
Sulautetun laitteen mikrokontrolleriin itseensä sekä piirikortille on yleensä integroituna valmiita fyysisiä komponentteja ja niiden hallintaan liittyvää logiikkaa I/O:ta varten. Alla listattuna tyypillisimpiä komponentteja, joista osaa tulemme käyttämään kurssilla.
- Painonappeja, liukukytkin, kosketusnäyttö (ts. laitteen "näppäimistö")
- LEDejä
- Ajastinpiiri (engl. Timer)
- Analogia-digitaalimuunnin (AD-muunnin, ADC)
- PWM-piiri (Pulssinleveysmodulaatio, engl. Pulse Width Modulation)
- Sarjaliikennepiirejä, kuten UART, SPI ja I2C
- Haihtumatonta muisti kuten EPROMia
- USB- ja/tai Ethernet-ohjainpiirejä
- LCD-Näyttö
Oheiskomponentit ovat joko digitaalisia tai analogisia. Digitaaliset komponentit ovat usein monimutkaisempia, niissä on erilaisia väyliä ja niiden kanssa pelataan siis bittiarvojen (ts. sovittujen jännitetasojen) avulla.
Analogiset komponentit antavat jännitearvoja, jotka voivat olla mitä vain maatason ja käyttöjännitteen väliltä (esim. 0-5V). Esimerkkinä monet yksinkertaiset anturit ilmaisevat mittausarvonsa analogisena jännitteenä. Tietokoneelle tietysti analogiset signaalit on ensin muunnettava digitaaliksi eli numeroarvoiksi, jonka jälkeen niitä voidaan käsitellä numeraalisesti. Tätä toimintoa varten mikrokontrollereissa on valmiiksi integroituna analogi-digitaalimuuntimia, jotka tuottavat jännitteestä (pyöristettyä) numeroarvoa sovitussa tarkkuudessa, esimerkiksi 8-bitin lukualueella (0-255). Nyt ohjelmoijan tehtäväksi jää sitten muuntaa AD-muuntimen antama lukuarvo vastaamaan fyyisen maailman mitattavan suureen arvoa, jonka jälkeen sitä voidaan käsitellä ohjelmassa.
Esimerkkeja tällaisista antureista ovat erilaiset lämpötila- / ilmanpaine- / valaistusvoimakkuus-anturit ja myös mikrofoni. Jos oletetaan lämpötila-anturin mittausalueeksi 0-100C, tällöin ohjelmoijan pitäisi laatia muunnosfunktio AD-muuntimen arvosta (0-255) lämpötilaan (0-100C). Sitten voitaisiin kertoa käyttäjälle laitteen näytöllä, että huoneen lämpötila on esimerkiksi +21C.
SensorTag:ssa integroidut anturit ovat pääasiassa digitaalisia (tästä lisää myöhemmin), mutta analogisia komponentteja ovat juurikin LED:it joiden kirkkautta voimme säätää PWM:n avulla.
Pinni¶
Okei, katsotaanpa perusteita aluksi. Pinni (myös nasta, engl. pin) tarkoittaa mikropiirin / komponentin fyysistä jalkaa tai liittimen piikkiä. Pinnien / nastojen tarkoitus on komponentin sähköinen ja mekaaninen liittäminen piirilevylle. Piirin jokaisella jalalla / nastalla / pinnillä on tietty käyttötarkoitus. Pinnijärjestys kuvaa mikä tai mitkä ovat pinnien käyttötarkoitukset. Kuten huomataan, joskus yhdellä pinnillä on useitakin eri käyttötarkoituksia riippuen laitteen toiminnallisuuksista. Täten laitteen toiminta on rajoitettua niin että voimme käyttää vain jotain näistä toiminnallisuuksista kerrallaan.
Alla esimerkin vuoksi Intel 4004:n (maailman ensimmäinen kaupallinen mikroprosessori vuodelta 1971) pinnien kuvaus.
Pinnit ja bitit¶
Ja taas hieman kertausta aiemmasta Bittoperaatiot-luentomateriaalista. Muistetaan yleinen periaate on, että ohjelmakoodissa jokaista oheislaitteen ja nyt myös mikrokontrollerin pinniä vastaa yksi bitti, jonka käpistelyyn meillä on C-kielessä ne kuuluisat bittioperaatiot.
- Asettamalla tämä bitti joko loogiseen nollaan tai ykköseen, fyysisen pinnin jännitetaso muuttuu ja näin ohjelmallisesti ohjataan pinniä ja siihen kytkettyä komponenttia / oheislaitetta.
- Vastaavasti luetaan fyysisen pinnin tila (eli jännite) ja sitä vastaava bitti asettuu vastaavaan loogiseen arvoon.
- Loogista ykköstä (engl. High) yleensä vastaa käyttöjännite (tyypillisesti 3.3V tai 5V) tai jännite sovituissa rajoissa. Usein 2.0-2.7V suuremmat arvot tulkitaan ykköseksi.
- Loogista nollaa (engl. Low) vastaa maataso 0V, tai yleensä 0.7V pienempi jännite.
Pinneille annetaan ohjelmoijan (ja elektroniikkasuunnittelijan) työtä helpottamaan niiden toimintaan liittyvä looginen nimi, esimerkiksi
RESET
tai IO_16
. Nämä nimet ovat yleensä yhdenmukaisia oheislaitekirjastojen nimien kanssa, eli nimeä vastaa vakio, jonka avulle teemme bittioperaatiot kyseiselle bitille. Esimerkki. Resetoidaan laite nollaamalla RESET-bitti laitteen ohjausrekisteristä. (Yleensä ohjelmasta ei voi näin suoraan resetoida laitetta, mutta saatiinpahan raflaava esimerkki..)
ohjausrekisteri = ohjausrekisteri & ~(1 << RESET);
Osa pinneistä on määritelty yleiskäyttöön (engl. General Purpose I/O, GPIO) ja näiden pinnien käyttötarkoituksen voi ohjelmoija vapaasti määritellä, tietysti sen mukaan mitä oheislaitteita niihin kytketään. Loogisesti mikrokontrollereissa GPIO-pinnit on joskus rytmitelty I/O-porteiksi, niin että esimerkiksi 8 pinniä tulkitaan loogisesti yhdeksi portiksi. Tämä on kätevää, jos laite tarvitsee useita I/O-linjoja, niin niitä voidaan käsitellä loogisesti yhtenä yksikkönä ja ohjata käsitellä kätevästi binäärilukuina. SensorTag ajattelee kuitenkin kaikki I/O-pinnejä yksittäin, ellei ohjelmoija itse määrittele loogiset portit haluamilleen pinneille. Tätä ei harjoitustyössä tarvita.
Muistiinkuvattu I/O¶
Muistiinkuvattu I/O:han tarkoitti sitä, että varaamme laitteen keskusmuistista muistipaikkoja (laite)rekistereiksi, jotka on kytketty oheislaitteen pinneihin, ja niiden bittejä käpistelemällä ohjelmassa muuttujien kautta vuorovaikutamme oheislaitteen kanssa. Näiden muistipaikkojen voidaan ajatella muodostavan oheislaitteen vaatimat data-, osoite- ja ohjausväylät, niiltäosin kun moiselle on siinä laitteessa tarvetta:
- Nyt hyödynnetään osoitinmuuttujia, alustamalla ne osoittamaan oheislaitteelle varattuihin muistipaikkoihin. Näiden muistipaikkojen osoitteet sovitaan laitteen suunnitteluvaiheessa ja kun oheislaitteelle tehdään kirjasto, niin ne asetetaan siellä kohdilleen valmiiksi.
- Voimme koodista ohjata oheislaitetta käsittelemällä muistipaikoissa niitä vastaavia bittejä. Sekä asettaa ohjausbittejä että lukea laitteen tila ohjausbiteistä.
- Osana ohjausväylää voi myös olla keskeytyspinni, jonka lähettämä signaali (eli keskeytys) siepataan ohjelmassamme ja siihen reagoidaan halutulla tavalla. No, keskeytyksistä lisää ihan hetken päästä..
- Vastaavasti monimutkaisemmassa laitteessa dataväyläksi valitaan muistipaikka/paikkoja, jotka kytketään oheislaitteen dataväylään (ts. pinneihin) ja näihin saadaan laitteelle välitettyä informaatiota binäärilukuina.
- Joissain monimutkaisemmissa oheislaitteissa, kuten näytöissä, voi olla vielä erillinen osoiteväylä, jonka muistipaikat ovatkin laitteen sisäisiä muistipaikkoja. Jännä.
Rekistereitähän on näinollen kolmea tyyppiä: osoite-, ohjaus- ja datarekisterit. Oheislaite voi tarjota kaikkiin näihin tyyppeihin useita rekistereitä, mikä vain kombinaatio näistä kolmesta tyypistä laitteen toteutuksesta riippuen on mahdollinen. Sensortagissa esimerkiksi jonkun monimutkaisemman oheislaitteen käyttämiseen voimme tarvita kymmeniä rekistereitä. On siis varsin helpottavaa käyttää oheislaitteen kanssa pelailuun valmiita kirjastoja!
Jokaisesta näistä rekisteristä meidän täytyy siis tietää:
- Yleinen tarkoitus, toiminnallisuudet ja miten niitä käytetään.
- Muistiosoitteet joissa rekisterit ovat, ja tarjoaako kääntäjäympäristö näitä vastaavat vakiot ja (osoitin)muuttujat valmiiksi.
- Jokaisen rekisterin yksittäisten bittien käyttötarkoitus.
Esimerkkinä SensorTag:n yksi rekisterikuvaus. Tämä oheislaitteen (patterin jännitettä valvova oheispiiri) datarekisteri pitää sisällään laitteen patterin jänniteen. Varsin hyödyllinen rekisteri siis!
Kuvasta nähdään, että rekisterin koko on 32 bittiä (
31-0
, kuvassa keltainen kenttä). Huomataan, että bitit 31-11
ovat varattuja laitteen sisäiseen käyttöön, mutta muissa biteissä on meitä kiinnostavaa tietoa:- Bitit
10-8
pitävät sisällään patterijännitteen kokonaisosan - Bitit
7-0
patterijännitteen desimaaliosan, koodattuna tietyllä tavalla.
Nyt joskun haluaisimme tietää patterin jännitteen, kysyisimme asiaa tältä muistiinkuvatulta datarekisterilta ja tekisimme sen arvolle muunnoksen vaikkapa liukuluvuksi. SensorTagin RTOS tarjoaa suoraan rekisterin arvon lukemiseen ja kirjoittamiseen
HWREG
-makron. uint32_t patterin_jannite = HWREG(AON_BATMON_BASE + AON_BATMON_O_BAT);
Tässä nyt rekisterin muistiosoitteen saamme laskemalla yhteen patterijännitteen valvontaan tarkoitetun muistialueen alkuosoitteen (
AON_BATMON_BASE
) ja rekisterin osoitteen muistialueella (AON_BATMON_O_BAT
). Datakirja¶
Komponentin tai mikrokontrollerien manuaali, eli datakirja, selittää yksityiskohtaisen pilkuntarkasti kaiken sen sisäisen sekä jokaisen integroidun piirin toiminnan. Datakirja toimii referenssinä laitteistoa ohjelmoitaessa.
Datakirja on tyypillisesti englanninkielinen, vahvasti ammattisanastoon perustuva ja satoja ellei tuhansia sivuja pitkä monimutkaisemmissa laitteissa. Ihan 8-bittisen Arduinon mikrokontrollerinkin datakirja on vajaat 300 sivua pitkä! Eli kun ajatellaan kuinka yksinkertaista pinnien ohjaus on Arduinoilla, sielläkin on takana kymmeniä sivuja datakirjan materiaalia, josta ohjelmoijan ei välttämättä tarvitse tietää mitään. Monimutkaisilla laitteilla voi olla datakirjojen lisäksi manuaaleja, esimerkiksi SensorTag Technical Reference Manual. Tässä käsikirjassa on 1743 sivua!
Onneksi, sen sijaan että kahlaisimme läpi satoja sivuja datakirjoja, kehitysympäristöihin löytyy valmiiksi kirjastoja, funktioita, makroja ja vakioita, joissa on toteutettu valmiiksi oheislaitteiden ohjausta ja korkeamman tason toimintoja varten.
Kurkistus SensorTagiin¶
Kuvassa lohkokaavio SensorTagin mikrokontrollerin (TI Simplelink CC2650 Wireless MCU) toiminnallisuuksista. Kuten nähdään, CC2650 on laitteena varsin monipuolinen ja -mutkainen. Siihen on jopa integroitu kaksi erillistä ARM Cortex-tuoteperheen ydintä, joilla on kummallakin omaa ohjelma- ja RAM-muistia. Tehokkaampaa (Main CPU, Cortex M3) käytetään käyttäjän ohjelmien suorittamiseen ja toinen (Rf Core, Cortex M0) on varattu pelkästään langattoman tiedonsiirtoon. SensorTag on suunniteltu siten, että molempia ytimiä ohjelmoidaan erikseen toisistaan riippumatta. Tästä on myös etua sikäli että laitteeseen voidaan vaihtaa kokonaan toinen tiedonsiirron protokollapino (langattoman tiedonsiirron teknologia) ilman että muutoksella on vaikutusta käyttäjän ohjelmaan.
Alla SensorTag:n mikrokontrollerin (TI CC2560) pinnijärjestyksen kuvaus. Pinneille annetaan ohjelmoijan (ja elektroniikkasuunnittelijan) työtä helpottamaan niiden toimintaan liittyvä looginen nimi, esimerkiksi
RESET_N
tai DIO_16
. Osa pinneistä on määritelty yleiskäyttöön (engl. General Purpose I/O, GPIO) ja näiden pinnien käyttötarkoituksen voi ohjelmoija vapaasti määritellä, tietysti sen mukaan mitä oheislaitteita niihin on kytketty. SensorTagissa laitteen suunnitteluvaiheessa osa GPIO-pinneistä on varattu oheislaitteiden liittämiseen mikrokontrolleriin ja tämä tietysti määrää niiden käyttötarkoituksen.
Sensortagissa RTOS:n kirjasto tarjoavat meille nämä vakiot ja valmiita funktiokutsuja käyttää pinnejä ohjelmallisesti.
Alla esimerkinomaisesti SensorTagin I/O-pinneille annettuja vakioita otsikkotiedostoissa. Pinnin loogista nimeä käytämme koodissa suoraan otsikkotiedostoissa annettujen vakioiden kautta. Voisimme siis käyttää vakiota
IOID_18
osoittamaan laitteen fyysiseen pinniin DIO_18 ja sitten bittioperaatioilla ohjelmasta muuttaa sitä vastaavan bitin loogista arvoa. Kätevää!#define IOID_18 0x00000012 // IO Id 18
#define IOID_19 0x00000013 // IO Id 19
#define IOID_20 0x00000014 // IO Id 20
I/O-pinnien käyttö¶
Seuraavaksi perehdymme I/O-pinnien käyttämiseen SensorTagissa koodiesimerkin avulla. Käytössä olevat SensorTag-spesifiset vakiot löytyvät kätevästi ohjelmistoprojektiimme automaattisesti ilmestyvästä otsikkotiedostoista Board.h ja CC2650STK.h.
Allaoleva esimerkkimme, kaikessa kauneudessaan käyttää toista SensorTagin kahdesta painonappia on/off-kytkimenä yhdelle laitteen ledeistä. Eli meidän täytyy tässä määritellä kaksi pinniä ohjelman käyttöön: painonappia vastaava pinni ja lediä vastaava pinni.
Käytämme valmista kääntäjäympäristön tarjoamaa
Pin
-kirjastoa. Koska SensorTag:ssa tietyt I/O-pinnit ovat valmiiksi kytkettynä painonappeihin ja ledeihin, saamme niiden määritykset mukaan koodiin otsikkotiedostolla PINCC26XX.h
. Painonapin käyttöönottamiseksi ohjelmassa täytyy tehdä neljä asiaa- Esitellään RTOS:n globaalit muuttujat, joilla painonappi alustetaan toimimaan ja sitä voidaan käsitellä.
- Alla
buttonHandle
,buttonState
ja taulukkobuttonConfig[]
- Alustetaan fyysit painonapit halutusti.
- Alla taulukkoon
buttonConfig[]
- Laaditaan napinpainalluksen käsittelijäfunktio.
- Alla keskeytysrutiin käsittelijäfunktio
buttonFxn
- ja tietysti
main
-funktiossa ottaa nappeja vastaavat I/O-pinnit kirjastofunktioiden avulla käyttöön ohjelmassa.
Hox! SensorTagissa on kaksi painonappia ja kaksi lediä. Nämä määrittelyt tulee tehdä jokaiselle pinnille erikseen. Eli jos haluamme kaikki käyttöön, kirjoitamme koodiin neljä esittelyä ja käyttöönottoa.
Tämä esimerkkiohjelma siis, joka kerta kun nappia painetaan, suorittaa funktion
buttonFxn
, jossa lediä vastaavan pinnin tila vaihtuu, joka siis ohjaa laitteen lediä päälle / pois päältä. Esimerkki on purettu osiin alla. #include <ti/drivers/PIN.h>
#include <ti/drivers/pin/PINCC26XX.h>
...
// RTOS:n globaalit muuttujat pinnien käyttöön
static PIN_Handle buttonHandle;
static PIN_State buttonState;
static PIN_Handle ledHandle;
static PIN_State ledState;
// Pinnien alustukset, molemmille pinneille oma konfiguraatio
// Vakio BOARD_BUTTON_0 vastaa toista painonappia
PIN_Config buttonConfig[] = {
Board_BUTTON0 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
PIN_TERMINATE // Asetustaulukko lopetetaan aina tällä vakiolla
};
// Vakio Board_LED0 vastaa toista lediä
{{{
PIN_Config ledConfig[] = {
Board_LED0 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
PIN_TERMINATE // Asetustaulukko lopetetaan aina tällä vakiolla
};
// Napinpainalluksen keskeytyksen käsittelijäfunktio
void buttonFxn(PIN_Handle handle, PIN_Id pinId) {
// Vaihdetaan led-pinnin tilaa negaatiolla
uint_t pinValue = PIN_getOutputValue( Board_LED0 );
pinValue = !pinValue;
PIN_setOutputValue( ledHandle, Board_LED0, pinValue );
}
int main(void) {
Board_initGeneral();
// Otetaan pinnit käyttöön ohjelmassa
buttonHandle = PIN_open(&buttonState, buttonConfig);
if(!buttonHandle) {
System_abort("Error initializing button pins\n");
}
ledHandle = PIN_open(&ledState, ledConfig);
if(!ledHandle) {
System_abort("Error initializing LED pins\n");
}
// Asetetaan painonappi-pinnille keskeytyksen käsittelijäksi
// funktio buttonFxn
if (PIN_registerIntCb(buttonHandle, &buttonFxn) != 0) {
System_abort("Error registering button callback function");
}
BIOS_start();
return (0);
}
Puretaanpas ohjelmaesimerkki osiin.
RTOS:n muuttujat pinnien käyttöön¶
Ensin esittelemme joukon muuttujia per käyttämämme pinni, joita RTOS tarvitsee. Jälleen tarvitaan kahvat pinnille, joka esitellään muuttujalla
Pin_Handle
. Toinen RTOS:n tarvitseman muuttuja on pinnin tila, joka esitellään muuttujalla Pin_State
. Näitä muuttujia emme omassa koodissa tarvitse, mutta pidetään nyt RTOS tyytyväisenä. // RTOS:n muuttujat pinnien käyttöön
static PIN_Handle buttonHandle;
static PIN_State buttonState;
static PIN_Handle ledHandle;
static PIN_State ledState;
Tässä esitellään muuttujat kahdelle pinnille toinen painonappi ja toinen ledi. Kumpaankin pinniä varten tarvitsemme yo. kaksi muuttujaa, eli button-pinnille
buttonHandle
, buttonState
ja vastaavasti nämä myös ledille. Pala kakkua. Pinnien alustus¶
Seuraavaksi alustamme jokaisen pinnin joko sisääntuloksi (input) tai ulostuloksi (output) sen omaan asetustaulukkoon. Kaikki tässä käytetyt vakiot ja niiden tarkoitukset löytyvät Pin-kirjaston dokumentaatiosta, mutta ao. määrityksillä pärjäämme kurssilla.
PIN_Config buttonConfig[] = {
Board_BUTTON0 | PIN_INPUT_EN | PIN_PULLUP | PIN_IRQ_NEGEDGE,
PIN_TERMINATE
};
PIN_Config ledConfig[] = {
Board_LED0 | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW | PIN_PUSHPULL | PIN_DRVSTR_MAX,
PIN_TERMINATE
};
Hoksataan että muuttujat
buttonConfig
ja ledConfig
ovat kahden alkion taulukkoja. Ensimmäisessä alkiossa teemme neljän vakion TAI-operaation ja toisen alkion arvo on vakio PIN_TERMINATE
.Ensimmäisessä alkiossa vakioiden arvot luetaan seuraavasti. Ensimmäinen vakio (asetusbitti) on SensorTagin painonappia / lediä vastaava tunnite, eli joko painonapeille
Board_BUTTON0
tai Board_BUTTON1
ja ledeille Board_LED0
ja Board_LED1
. Toinen vakio on pinnin käyttötarkoitus, eli käsitelläänkö sitä sisääntulona (engl. input) vai ulostulona (engl. output). Input-pinnistä luemme sen tilan (esimerkiksi nappia painettu / ei painettu) ja output-pinnille asetamme sen tilan (esimerkiksi ledi päälle / pois). Kaiken tämän selityksen luettuamme asetamme siis esimerkissä painonapin sisääntuloksi ja ledi-pinnin ulostulo.
- Sisääntuloksi pinni asetetaan vakiolla
PIN_INPUT_EN
- Ulostuloksi asetus vakiolla
PIN_GPIO_OUTPUT_EN
.
Kolmas asetusbitti kertoo mihin tilaan alustamme pinnin:
- Vakio
PIN_GPIO_LOW
asettaa pinnin jännitteen maatasoon (0V, GND), joten tässä koodissa ledi ei ole päällä. - Vakio
PIN_GPIO_HIGH
asettaisi jännitteen käyttöjännitteeseen (3.3V, VCC) eli ledi päälle. - Lisäksi RTOS tarjoaa mahdollisuuden määritellä muitakin pinnien sähköiseen toimintaan liittyviä asetusparametreja, eli tässä
PIN_PULLUP
,PIN_PUSHPULL
jaPIN_DRVSTR_MAX
. Mutta kuten lupasimme, niin kurssilla ei tarvitse elektroniikkaa ihmeemmin osata.
Lisäksi huomataan painonappien asetuksissa vakio
PIN_IRQ_NEGEDGE
, jolla asetamme pinnin tuottamaan ohjelmassa keskeytyksen aina kun sen tila muuttuu laskevalla reunalla. Ts. kun nappi painetaan alas niin sen jännite tippuu maatasoon nollaksi. Tai kun nappi vapautetaan, sen jännite nousee käyttöjännitteseen (nouseva reuna) ja siitä seuraava keskeytys saataisiin kiinni vakiolla PIN_IRQ_POSEDGE
. Pinnikeskeytyksistä lisää hetken päästä seuraavassa materiaalissa. Alustustaulukko loppuu aina vakioon
PIN_TERMINATE
. Pinnikeskeytyksen käsittelijäfunktio¶
Sisääntuloksi merkityille pinneille tarvitsemme (yleensä) käsittelijäfunktion, eli siis sen toiminnallisuuden mikä suoritetaan kun nappia painetaan alas ja tästä aiheutuu keskeytys. Tätä varten ohjelmassamme on toteutettu käsittelijäfunktio
buttonFxn
. void buttonFxn(PIN_Handle handle, PIN_Id pinId) {
// Vaihdetaan led-pinnin tilaa negaatiolla
uint_t pinValue = PIN_getOutputValue( Board_LED0 );
pinValue = !pinValue;
PIN_setOutputValue( ledHandle, Board_LED0, pinValue );
}
Funktio toimii seuraavasti. Ensin luemme ledi-pinnin tilan (päällä "1" / pois päältä "0") funktiolla
PIN_getOutputValue
muuttujaan pinvalue
. Tämä on yleinen funktio tarvitsee argumentiksi pinniä vastaavan vakion Board_LED0
. Sitten arvolle tehdään negaatio, eli täällä meidän reaalimaailmassa vaihdetaan ledin tilan päälle / pois. Uusi tila asetetaan sitten ledin tilaksi asetusfunktiolla PIN_setOutputValue
. Pinnit mukaan ohjelmaan¶
Seuraavaksi menemme
main
-funktioon. Pinnit varataan ohjelmamme käyttöön Pin_open
-funktiokutsulla, jonka parametreiksi tulee äsken esittelemämme RTOS:n omat pinnimuuttujat ja alustukset. // Ledi käyttöön ohjelmassa
ledHandle = PIN_open( &ledState, ledConfig );
if(!ledHandle) {
System_abort("Error initializing LED pin\n");
}
// Painonappi käyttöön ohjelmassa
buttonHandle = PIN_open(&buttonState, buttonConfig);
if(!buttonHandle) {
System_abort("Error initializing button pin\n");
}
// Painonapille keskeytyksen käsittellijä
if (PIN_registerIntCb(buttonHandle, &buttonFxn) != 0) {
System_abort("Error registering button callback function");
}
RTOS käsittelee taskit ja keskeytyksien käsittelijäfunktiot toisiaan vastaavina toiminnallisuuksina. Keskeytyksistä lisää hetken päästä, mutta nyt kun painonapin tila muuttuu (laskevalla reunalla, koska vakio
PIN_IRQ_NEGEDGE
) aiheutuu siitä keskeytys. Funktiolla PIN_registerIntCb
asetamme sitten sen funktion joka keskeytyksen seurauksena suoritetaan, ts. on sen käsitteljä. Tässä ohjelmassa siis funktio buttonFxn
on tämän keskeytyksen käsittelijä. Hox! Olisimme tietenkin voineet toteuttaa saman painonapin tilan tarkistuksen superloop-hommina niin, että ikuisessa silmukassa kyseltäisiin joka iteraatiolla painonapin pinnin tilaa, ja jos se olisi muuttunut, tehtäisiin jotain. Taas näitä superloop-hommia, mutta kuten näemme homma hoituu helpommin käsittelijällä.
Lopuksi¶
Muistiinkuvatun I/O:n lisäksi toinen mahdollisuus olisi porttikuvattu I/O, jossa rekistereitä käsitellään erillisten in- ja out-käskyjen avulla. Noh, tätä mekanismia SensorTag ei käytä.
Mutta hei.. materiaalin perusteella osaat jo tehdä sulautetun ohjelman, joka vilkuttaa laitteen lediä kun se hoksaa käyttäjän napin painalluksen! Olisko kakkukahvien paikka?
Anna palautetta
Kommentteja materiaalista?