5. Näppäimen/kytkimen luku

Alunperin julkaistu: 11.2.2017

Viimeksi muokattu: torstai 11.6.2020

Launchpadissa painonappi S2 on kytketty ylösvetovastuksella alla olevan kuvan mukaisella tavalla.

Kun nappia painetaan, porttipinni P1.3 (nasta 5) kytkeytyy maatasoon ja mikrokontrolleri tulkitsee sen nollana "0". Tällainen kytkentätapa on yleinen ja esimerkiksi napin pohjaan jääminen voidaan havaita helposti.

Ylösvetovastus 47kΩ rajoittaa virran kulkua silloin, kun kytkintä painetaan. Vastus on erittäin tärkeä, sillä ilman vastusta kytkimen painallus aiheuttaa oikosulun piirissä ja kaikki virta kulkeutuu kytkimen läpi. Jos käytössä olisi suuremmat virrat, niin kytkin voisi jopa hitsautua pohjaan välittömästi ja ennen pitkää savu nousisi muualtakin kuin kytkimestä.

Esimerkkiohjelma 1: Näppäinpainalluksen lukeminen

Mikrokontrolleri voi toimia sellaisessa ympäristössä, missä sen tarvitsee lukea käyttäjän syötteitä esimerkiksi näppäinten avulla. Esimerkiksi oven avausmoottorin ohjaus kytkintä painamalla.

Muokataan edellisellä sivulla kirjoitettua esimerkkiä siten, että sytytetään vihreä LED silloin kun näppäin on painettu pohjaan. Jos nappia taas ei ole painettu, niin sytytetään puolestaan punainen LED. Ohjelman koodi alla:

#include <msp430g2231.h>

void main(void)
{
       char nappi=0; // tehdään uusi muuttuja napin lukua varten       
   
       WDTCTL = WDTPW + WDTHOLD; // Pysäytetään vahtikoira 
       P1DIR |= BIT0 + BIT6; // asetetaan ledipinnit outputiksi 
       P1OUT &= ~(BIT6 + BIT0); // asetetaan ledipinnit nollaan ettei ledit pala  
        
       while(1) // pääohjelman pääsilmukka          
       {
              // joka kierroksen alussa luetaan nappi
              nappi = P1IN; // luetaan P1IN rekisteri muuttujaan talteen
              if( ! (BIT3 & nappi) ) // vertaillaan onko juuri BIT3 nollassa vai ei
              {
                     // sytytetään vihreä led 
                     P1OUT |= BIT6;
                     // ja sammutetaan punainen led
                     P1OUT &= ~BIT0;
              }
              else 
              {
                     // sytytetään punainen led 
                     P1OUT |= BIT0;
                     // ja sammutetaan vihreä led 
                     P1OUT &= ~BIT6;
              }
       }
}

Note 1: Rekisterien arvot voidaan lukea aina samalla tavalla muuttujaan talteen, olipa kyseessä P1IN rekisteri tai mikä tahansa muu rekisteri (paitsi CPU:n statusrekisteri).
Note 2: Ohjelmakoodia voi lyhentää byten verran kirjoittamalla if-sulkujen sisään (BIT3 & P1IN) ja jättämällä muuttujan nappi kokonaan pois.

Ohjelman kulku

Kun pääohjelma käynnistyy, niin alustetaan nappi-muuttuja arvoon 0. Seuraavaksi pysäytetään vahtikoira ja pääsilmukan ensimmäisellä suoritettavalla rivillä luetaan nappi-muuttujaan P1IN rekisterin arvo. Launchpadilla kytkin S2 on kytketty porttipinniin P1.3 (kuten kuvassa), joten mielenkiinnon kohteena on vain kyseinen bitti. Jos nappi on pohjassa, niin neljäs bitti nappi-muuttujassa on nolla tilassa (muista että BIT3 oli määritetty 0x8 = 0000 1000 <- neljäs bitti). Napin ollessa pohjassa sytytetään vihreä LED ja kun se ei ole pohjassa asetetaan punainen LED palamaan.

Vertailulausekkeen if() toiminta

Jos olet lukenut osan 4, niin if-lauseeseen asti kaikki lienee selvää. If-lauseessa verrataan nappi-muuttujaan tallennettua tietoa. Huutomerkki tarkoittaa NOT-operaatiota eli ei tosia.

Tässä tapauksessa tarvitaan NOT-operaatiota, koska vertailemme onko nappi-muuttujan neljäs bitti (eli kytkin) "1" vai "0" tilassa. Koska "0" tila on aktiivinen tila napille, niin NOT operaatiolla saadaan suoritus siirtymään if-lohkon sisään.

BIT3 ja nappi-muuttujan kesken tehdään AND-operaatio, jolla saadaan selville onko kyseinen kytkimen bitti "1" tilassa. Mikäli näin on eli kytkintä ei ole painettu, saa if-lauseen ehto arvon tosi ja else-lohko suoritetaan koska käytimme huutomerkkiä (NOT) ehdon edessä. Mikäli huutomerkkiä ei ole, niin ohjelma toimii päinvastoin eli vihreä LED palaa silloin kun nappia ei paineta ja punainen kun nappia painetaan.

Sulkeet kertovat kääntäjälle missä järjestyksessä toiminnot suoritetaan ja ilman riittävää määrää sulkeita (tässä 2 kpl) ohjelma ei toimisi oikein.

Lyhyt esittely funktioihin

Aliohjelmaa, joka alla tullaan esittelemään voidaan sanoa myös funktioksi. Funktio on ohjelman palanen, joka tekee tietyn toiminnon ja jonka jälkeen ohjelma palaa suorittamaan funktiokutsun jälkeistä toimintoa. Funktioita käytetään kun jotakin suoritusta voidaan toistaa ohjelman eri vaiheissa.

Funktioiden tarkoituksena ohjelmoinnissa on helpottaa kokonaisuuden ymmärtämistä ja suorittaa pieniä rutiineja aina silloin, kun ohjelman niitä tarvitsee suorittaa. Hyvin kirjoitettu funktio on uudelleen käytettävissä ja sitä voidaan käyttää myös muussa ympäristössä (esimerkiksi PC-ohjelmoinnissa) tarpeen tullen (pieniä muutoksia voi toki joutua tekemään).

Funktio ei siis ole sen ihmeellisempi juttu kuin muuttujakaan, vaikka sillä on tiettyjä rajoituksia kuinka sitä C-kielessä voidaan käyttää ja toiminta on sinällään erilaista kuin muuttujalla. Tästä tullaan kertomaan jatkossa lisää, mutta lyhyt esittely oli mielestäni paikallaan kun opasta eteenpäin kahlataan.

Esimerkkiohjelma 2: Ledin sytytys ja sammutus viivettä apuna käyttäen

Joskus on tarvetta sellaiselle toiminnalle, että ennen porttipinnin asetusta pitää ohjelman odottaa hetken aikaa. Tällainen käyttötapaus voisi tulla vastaan edellä mainitussa oven lukon ohjauksessa, missä solenoidille tai muulle sähkömoottorille annetaan aikaa avata ovi, kunnes sytytetään oven avaamisesta kertova vihreä merkkivalo.

Ajan antamiseksi on käytettävä viivettä. Seuraavaksi kirjoitetaan lyhyt viive aliohjelma, missä kulutetaan aikaa jotta ohjelman suoritus ei etenisi niin nopeasti. Alla oleva koodi lisätään pääohjelmamoduulin alle.

// Aliohjelma Viive() joka kuluttaa CPU:n suoritusaikaa. Ns. tuhlaava rutiini.
 void Viive(unsigned int aika)
 {
 unsigned int x=0; // luodaan uusi muuttuja
 // asetetaan x=0, vertaillaan onko x pienempi kuin aika muuttuja
 // jos ei ole niin kasvatetaan muuttujaa x yhdellä.
 for(x=0;x<aika;x++)
 {
 // ei tehdä mitään
 // pyöritään vain silmukassa
 }
 }

Koodia kutsutaan pääohjelmasta Viive(<aika>); käskyllä. Kokonaisuudessaan pääohjelman koodi alla:

#include <msp430g2231.h>

 // aliohjelmat täytyy esitellä koodissa

 void Viive(unsigned int aika);

void main(void)
 {
 char nappi=0; // tehdään uusi muuttuja napin lukua varten
 WDTCTL = WDTPW + WDTHOLD; // Pysäytetään vahtikoira
 P1DIR |= BIT0 + BIT6; // asetetaan ledipinnit outputiksi
 P1OUT &= ~(BIT6 + BIT0); // asetetaan ledipinnit nollaan ettei ledit pala

while(1) // pääohjelman pääsilmukka
 {
 // joka kierroksen alussa luetaan nappi
 nappi = P1IN; // luetaan P1IN rekisteri muuttujaan talteen
 if( ! (BIT3 & nappi) ) // vertaillaan onko juuri BIT3 nollassa vai ei
 {
 // Oven avausfunktion kutsu voisi olla tässä.
 // Aliohjelman kutsu kun nappia on painettu
 Viive(50000);
 // sytytetään vihreä led
 P1OUT |= BIT6;
 // ja sammutetaan punainen led
 P1OUT &= ~BIT0;
 // Aliohjelman (funktion) kutsu
 Viive(50000);
 }
 else
 {
 // sytytetään punainen led
 P1OUT |= BIT0;
 // ja sammutetaan vihreä led
 P1OUT &= ~BIT6;
 }
 }
 }

Viive aliohjelma ei ole kovin järkevä käytännössä, sillä kyseisessä rutiinissa tuhlataan vain CPU:n laskenta-aikaa. Järkevämpi olisi tehdä tämä esimerkiksi ajastimella (Timer), mutta tässä esimerkissä ei vielä tähän paneuduta.

Kuten esimerkistä 5 voidaan huomata, niin ohjelma sytyttää punaisen ledin hieman viiveellä ja sammuttaa sen hetken päästä. Aliohjelman kutsuminen tapahtuu koodissa yksinkertaisesti kirjoittamalla sen aliohjelman nimi, jota halutaan kutsua. Aliohjelmalle välitettävää tietoa kutsutaan parametriksi ja tässä tapauksessa välitettiin parametri viive, millä kerrottiin aliohjelmalle kuinka monta kertaa sen täytyy tyhjää silmukkaa pyörittää. Steppaamalla (F5 tai F6) koodia huomaat, kuinka ohjelman suoritus etenee. Kokeile myös vaihtaa viiveen arvoa (muista että suurin luku on 216-1, tämän saat laskea itse jos et muista.) Voit myös miettiä, miten saisit viivettä pidennettyä.