Monesti ohjelman suoritusta pitää pystyä ohjaamaan useampaan eri suuntaan. Tätä tarkoitusta varten löytyy switch-case rakenne. Esimerkiksi jos ohjelma on osa automaattista robotin ohjausjärjestelmää, täytyy sen osata ohjata robotti eteen, taakse, vasemmalle ja oikealle. Tieto siitä, mihin suuntaan robotin pitää mennä tulee tietenkin antureilta, jotka kertovat onko esteitä havaittu. Tässä ei kuitenkaan keskitytä robottien ohjaukseen enempää tällä kertaa, vaan mennään asiassa eteenpäin.
Switch-case rakenteessa suoritusta ohjaa yleensä muuttujan arvo, mitä tarkistetaan switch -lauseen sulkujen sisällä. Alla olevaan esimerkkikoodiin on jälleen tehty uusi ohjelma Launchpadin ledien vilkuttamiseksi.
Esimerkki 1: Switch-case valintarakenne
// aliohjelman esittely
void Viive(unsigned int aika);
void main(void)
{
signed char valintaMuuttuja = 0; // huomaa tällä kertaa signed char!
WDTCTL = WDTPW + WDTHOLD; // vahtikoiran pysäytys
P1DIR |= BIT0 + BIT6; // asetetaan ledipinnit outputiksi
P1OUT &= ~(BIT6 + BIT0); // asetetaan ledipinnit nollaan ettei ledit pala
while(1) // pääohjelman pääsilmukka
{
while(P1IN & BIT3); // odotellaan napin painallusta
Viive(15000); // lisätään viivettä kytkinvärähtelyn suodattamiseksi
switch(valintaMuuttuja)
{
case 0:
// sytytetään punainen LED
P1OUT |= BIT0;
break;
case 1:
// sytytetään vihreä LED
P1OUT |= BIT6;
break;
case 2:
// sammutetaan punainen LED
P1OUT &= BIT0;
break;
case 3:
// sammutetaan vihreä LED
P1OUT &= ~BIT6;
// 'nollataan' rakenteen toiminta
valintaMuuttuja = -1;
break;
default:
// default lohko suoritetaan silloin, kun
// mikään muuttujan arvo ei ole mikään edellä
// olevista vaihtoehdoista. Tässä koodissa tänne ei
// koskaan kuitenkaan päästä
break;
}
// kasvatetaan muuttujaa, jolloin seuraavalla kerralla
// suoritetaan seuraava case numero.
valintaMuuttuja++;
}
}
void Viive(unsigned int aika)
{
unsigned int x=0;
for(x=0;x<aika;x++);
}
Ohjelman kulku
Ohjelman varsinainen toiminta alkaa jälleen alustusten jälkeen pääsilmukassa. Koodissa yksinkertaisesti odotetaan while -silmukassa niin kauan, kunnes käyttäjä on painanut nappia. Kun napin painallus huomataan, ohjelma poistuu nappia tarkistavasta while -silmukasta.
Seuraavaksi suoritetaan switch-case rakenne valintaMuuttujan arvon mukaisesti. Switch -sulkujen sisällä on arvo, minkä mukaan valinta tehdään. Koska valintaMuuttuja alustettiin ohjelman alussa 0:aan, eikä sitä vielä missään ole muokattu, ohjautuu suoritus case 0: -lohkon sisään. Case -lohkojen toiminta on kuvattu kommenteissa, joten en niitä tähän selosta enää uudelleen. Default suoritetaan silloin, kun mitään muuta vaihtoehtoa ei ole suoritettavissa, eli jos muuttujan valintaMuuttuja arvo olisi vaikkapa 10, niin default -lohko tulisi suoritettavaksi (ellei case 10: lohkoa olisi kirjoitettu). Lopuksi kasvatetaan valintaMuuttujan arvoa ja aloitetaan pääsilmukka alusta napin painallusta odottaen.
Jokaisen case -lohkon suoritus päättyy break; -sanaan. Tämä käsky lopettaa suorituksen ja switch -sulkujen sisältä poistutaan välittömästi. Break; -sana on tärkeää olla aina case -lohkojen välillä, muutoin ohjelma voi suorittaa useamman case -lohkon kerralla.
Voit jälleen kokeilla muokata koodia ja tutkia miten ohjelma käyttäytyy eri tilanteissa. Kokeile jättää esimerkiksi viive aliohjelma kutsumatta tai käytä pienempää tai suurempaa viiveen arvoa. Huomaat varsin nopeasti miksi kytkinvärähtelyä pitää suodattaa ohjelmassa.
Switch case tilakoneena
Edellä oleva esimerkkiohjelma toimii kuten tilakone. Tilakoneessa ohjelma vaihtaa toimintoa tai tapaansa käyttäytyä erilaisen syötteen/tapahtuman tai "ärsykkeen" mukaan. Tässä tapauksessa syöte on napin painallus ja toiminto on ledien tilan muutos. Syötteitä/tapahtumia kutsutaan englanninkielisin termein event -nimellä ja ohjelman muuttunutta toimintoa kutsutaan usein action -termillä. Muitakin nimityksiä varmaankin on, mutta tärkeintä on ymmärtää tilakoneen toiminta.
Tilakone voi vaikuttaa yksinkertaiselta ja niin se onkin silloin, kun ohjelmakin on yksinkertainen (kuten esimerkissämme). Suuremmissa ohjelmakokonaisuuksissa tilakoneilla voi olla kymmeniä ellei satoja erilaisia tiloja ja lisäksi aliohjelmilla omat tilakoneensa. Vielä kun sekaan heitetään kourallinen luettavia syötteitä ja erilaisia ohjauksia, ei ohjelmoijan työ helpotu yhtään. Hyvän tilakoneen suunnittelu onkin yksi haastavimmista tehtävistä ohjelmoitaessa.
Tilakoneen rakentaminen aloitetaan kartoittamalla kaikki mahdolliset tilat, joissa ohjelma tulee viettämään aikaa. Sitten listataan kaikki mahdolliset syötteet jotka tilakone voi saada. Sellaisia syötteitä ei tarvitse huomioida, joiden takia tilaa ei tarvitse vaihtaa. Lopuksi määritellään mitä tapahtuu kun tila vaihtuu ja mitä tehdään kun tila on vaihdettu.
Switch-case rakennetta on "helppo" hyödyntää tilakoneena, koska jokaisen case -lohkon voi ajatella olevan eri tila. Tilan vaihtaminen perustuu switch -sulkujen sisällä olevan muuttujan arvoon ja jokaisen tapahtuman (event) tulisi vaikuttaa tähän muuttujaan. Muuttuja voidaan siten muuttaa joko tilakoneen sisällä tai jollakin ulkoisella menetelmällä, kuten keskeytyksellä (keskeytyksistä lisää myöhemmin).
Jos tilakoneessa olisi esimerkiksi kuusi mahdollista eri tilaa, niin case -lohkoja tulisi silloin kuusi. Tämän lisäksi on suotavaa myös kirjoittaa default -lohko, koska virheitä tuppaa sattumaan ja ne on hyvä saada kiinni. Tilakoneen voi tehdä myös if else -rakenteella tai pelkästään if -lauseita apuna käyttäen. Tällöin kuitenkin suoritusaikaa menee ns. hukkaan, koska if -lauseiden ehdot tarkistetaan ohjelmassa aina (switch-case rutiinissa voidaan hypätä suoraan ohjelman eri kohtaan).
Seuraavassa osassa käsitelläänkin sitten globaaleita muuttujia ja vakioarvoja sekä vakiomuuttujia.