1. Johdanto

Alunperin julkaistu: 11.2.2017

Viimeksi muokattu: lauantai 11.2.2017

Koska C-kieli on standardi ohjelmointikieli, ei ole oikeastaan väliä missä ympäristössä ohjelmointia opettelee. Samat opitut asiat pätevät niin tietokoneelle ohjelmoitaessa, kuin mikrokontrollerille ohjelmia tehdessä. Ohjelmoinnin periaatteet ovat siis samat, olipa kieli mikä hyvänsä. Voitkin soveltaa opittua hyvin monessa eri asiassa, mutta kannattaa muistaa kuitenkin, että mikroprosessori ei ole tietokone ja on oltava tarkkana siitä, mitä siihen lähdekoodiin itse asiassa kirjoittaa.

Ohjelmoida voi monella eri kielellä ja monella eri tavalla, eikä C-kieli suinkaan ole ainoa millä homma hoituu. Sanottakoon vielä, että webbisivujen tekeminen html-tageja hyödyntämällä ei ole ohjelmointia!

Tervaa ja höyheniä sille joka niin väittää cool

Tässä oppaassa keskitytään vain C-kieleen ja MSP430 mikrokontrollereihin, etenkin Launchpadiin. Opas on yritetty kirjoittaa siten, että lukija ei tarvitse kovin syvällistä ymmärrystä ohjelmoinnista, mutta perusasioita olisi hyvä tietää. Toisaalta olen yrittänyt selvittää asiat mahdollisimman yksinkertaisesti niin, että asiat tulevat tutuksi oppaan aikana vaikka ymmärrys olisikin hieman vajaavaista.

Oppaassa puhutaan myös sekalaisesti ja vaihtelevin väliajoin mikrokontrollerista tai mikro-ohjaimesta tai MCU:sta. Käytettiinpä siis mitä sanaa tahansa, niin ne tarkoittavat samaa asiaa.

Alkusanat

Aluksi oli maa ja taivas, sitten tulivat ihmiset. Ihmisten jälkeen tulivat tietokoneet ja maan päälle tarvittiin ihmisiä, jotka osaisivat puhua tietokoneiden kanssa. Niin syntyivät ohjelmoijat. (Lopuksi syntyi terminaattori).

No niin, aivan noinhan se ei mennyt mutta idea on sama. Tietokonekieli, eli C-kieli on laadittu sen vuoksi, että ihmiset tosiaan pystyisivät kertomaan tietokoneelle tai mikroprosessorille mitä sen tulisi tehdä. Ilman ohjeita tietokoneet eivätkä liioin mikroprosessoritkaan tee yhtään mitään. Tämä saattaa joskus unohtua kokeneeltakin ohjelmoijalta, sillä ihmisillä on tapana tehdä olettamuksia jonkin laitteen/koodin toiminnasta. Täytynee tässä välissä mainita, että C-kielen luoja taikka isä on nimeltään Dennis Ritchie, R.I.P.

Alla olevissa kappaleissa käyn lyhyesti läpi ohjelmointiprosessin vaiheet. Tämän läpikäynnin tarkoituksena on antaa lukijalle käsitys siitä, millaisia vaiheita mikrokontrollerin ohjelmoinnissa on. Lisäksi pyrin selventämään sitä, miksi ylipäätään käytetään C-kieltä mikroprosessorin ohjelmoimisessa.

Ongelmasta C-kieliseksi ratkaisuksi

Syy siihen, miksi ohjelmointikieltä ylipäätään tarvitaan on tämä: meillä on ongelm(i)a! Tietokoneet luotiin ihmisten apuvälineiksi ratkomaan monia erilaisia ongelmia, joita voidaan laskemalla ratkoa. Mikrokontrollerit on keksitty myös periaatteessa tähän tarkoitukseen, mutta ne keskittyvät nykypäivänä enemmänkin jonkin pienemmän ongelman ratkaisemiseksi. Esimerkiksi kuinka säätää termostaattia, tuulettimen pyörimisnopeutta, ohjata jotain lisälaitetta, muuntaa käyttäjän kosketus tietokoneen käskyksi tai näyttää LCD-näytöltä, että tulostimesta on muste loppu tms.

Kaiken toiminnallisuuden lisäksi, sovelluksen pitäisi olla rakenteeltaan usein yksinkertainen ja vähän tilaa vievä. Mikrokontrollerit ovatkin tässä lyömätön apuväline. Edellä mainittuihin yksinkertaisiin tehtäviin olisi tietokoneen käyttäminen sama asia, kuin ampuisi kärpästä tykillä - siis aivan ylimitoitettua. On siis tärkeää tiedostaa ongelma ja sen ratkaisemiseen käytettävät menetelmät, jotta asiat voidaan tehdä tehokkaasti ja resursseja (taloudellisia, fyysisiä sekä hermoja) säästäen.

Kun mikrokontrolleri on valittu tehtävän suorittamiseen, niin käytännössä sen ohjelmointi suoritetaan C-kielellä (poikkeuksia tietenkin on). Vaikka ohjelmointikieli ei olisikaan C, niin C:n oppimisesta on silti hyötyä - aina.

C-kielestä kääntäjälle

C-kieli on korkean tason (high level) ohjelmointikieli. Tämä tarkoittaa sitä, että sillä on tarkka "kielioppi" ja asioiden merkitykset on määritelty aika tarkasti (nojoo, tästä voi olla montaa mieltä ;). Korkean tason kieltä voidaan kutsua myös lausekieleksi. Korkean tason kielille tai lausekielille on yhteistä se, että ne ovat laitteistosta ja alustasta riippumattomia. Voit siis kirjoittaa C-kielisen ohjelman vaikkapa notepadilla, mutta jos haluat että se toimii mikrokontrollerilla, tarvitset siihen kääntäjää.

Kääntäjä toimii kuten tulkki kahden erikielisen ihmisen kommunikoidessa: se kääntää toisen puhuman kielen toiseksi kieleksi, jolloin yhteisymmärrys löydetään. Teknisesti sanottuna: käännetään korkean tason käskyt matalan tason laitekohtaiseksi koodiksi (binääriksi). C-kielen opettelua voisikin löysästi verrata uuden "oikean" kielen opetteluun, vaikka muistettavia sanoja onkin paljon vähemmän.

Kääntäjästä kontrollerille

Koska jokaisella eri prosessorityypillä on oma käskykantansa, tarvitaan erilaisia työkaluja ja kääntäjiä prosessoreiden käskyttämiseksi. Kun kyseiset työkalut editoreineen on integroitu samaan ohjelmistoon, käytetään siitä nimitystä IDE (Integrated Development Environment). Kun IDE asennettaan tietokoneelle, siihen samalla asentuu kääntäjä ja muu prosessorikohtainen lähdekoodi, joiden avulla ohjelmia voidaan kääntää oikealle alustalle sopivaksi.

Kehitysympäristöissä on yleensä koodieditori, mihin ohjelman lähdekoodi luodaan. Kääntäjällä luotu lähdekoodi käännetään prosessorin ymmärtämään muotoon eli biteiksi. Mikäli ohjelma menee käännöksestä läpi ilman virheitä, ohjelma voidaan ladata eli "flashata" mikrokontrollerille. Usein kuulee myös puhuttavan ohjelman poltosta. Ohjelma tallentuu mikrokontrollerin flash-muistiin pysyvästi ja mikrokontrolleri suorittaa ohjelmaa sieltä.

Kontrollerilta testaajalle

Jokainen joka tekee ohjelmaa jollekin alustalle, haluaa yleensä olla varma siitä, että ohjelma myös toimii alustalla oikein. Harrastusprojekteissa sekä koodaaja, että testaaja on sama henkilö, joten voisi olettaa että virheet saa nopeasti poistettua koodista. Näin ei kuitenkaan ole, sillä ohjelman tekijä on usein sokea omille virheilleen. Testaukseen kuluu siis aikaa ja virheet saattavat jäädä pimentoon pitkiksikin ajoiksi.

Virheenjäljityksestä käytetään yleisesti termiä debuggaus. Debuggaaminen onnistuu Launchpadilla helposti ja siihen paneudutaan heti ensimmäisessä esimerkkiohjelmassa myöhemmin tässä oppaassa.

Ohjelman perusrakenne ja ohjelman kulku

Ennen kuin paneudutaan tarkemmin muuttujiin ja C-kielen syntaksiin eli kielioppiin ja siihen, miten mikrokontrolleri saadaan tekemään asioita, täytyy meidän ensin selvittää ohjelman periaatteellinen toiminta. Siis se, miten tietokone- tai mikrokontrolleriohjelma periaatteessa toimii?

Muuttujien maailma

Ohjelmassa usein tai itse asiassa lähes aina toiminta perustuu tietoon, joka voi muuttua ja tietoon jota pitää hallita. Tietojen käsittelemiseksi tarvitaan siis ohjelmaa, joka osaa käsitellä muuttuvaa tietoa tarkoitukseen sopivalla tavalla. Yksittäistä tietoa, joka muuttuu sanotaan tietenkin muuttujaksi. Ohjelma suorittaa toimintojaan myös tietyin ehdoin ja ohjelman kulkua säädellään erilaisin tavoin. Ohjelman on esimerkiksi osattava päättää, kääntääkö se ohjausta oikealle vai vasemmalle, jos suoritettavan ohjelman tehtävänä olisi vaikkapa robotin ohjaaminen.

Mikrokontrollerin toiminta

Mikrokontrollerin (ja CPU:n) toiminta perustuu erilaisiin rekistereihin, joiden arvoja muuttamalla saadaan ohjelma ja siten mikrokontrolleri tekemään erilaisia asioita. Mikrokontrolleriin on yleensä integroitu enemmän tai vähemmän erilaisia lisälaitteita, joiden avulla voidaan ulkomaailmaan ja tarvittaessa käyttäjälle kommunikoida. Lisäksi mikrokontrolleri kykenee saamaan tietoa ulkomaailmasta sen yleiskäyttöisten tulopinnien avulla sekä esimerkiksi AD-muuntimien avulla. Mikrokontrollerilla onkin yleensä vaihteleva määrä inputteja sekä outputteja, eli suomeksi tuloja ja lähtöjä. Mikrokontrolleri ei kuitenkaan tee itsestään mitään ilman ohjelmoijaa.

Esimerkkiongelma ja sen ratkaisu

Otetaanpa hyvinkin yksinkertainen asia kuten LEDin ohjaus (yllätys yllätys), etteivät asiat menisi heti kovin mutkikkaaksi. Ajatellaan, että halutaan ohjata LEDiä käyttäjän napin painalluksella niin, että LED palaa kun nappi on pohjassa.

Ongelman ratkaisemiseksi on olemassa monta eri vaihtoehtoa, mutta asioiden yksinkertaisuuden vuoksi oletetaan että halutaan käyttää ns. pollaavaa menetelmää (POLL, engl. kysely). Kyseessä on menetelmä, missä ohjelma tarkistaa jatkuvasti onko käyttäjä painanut nappia ja jos on, niin LED ohjataan toiseen tilaan eli päälle tai pois riippuen missä asennossa se on.

Ongelma ratkaistiin niin sanotusti Top-Down menetelmällä. Tässä ongelma LEDin sytytys ratkaistiin seuraavasti:

  • Lue nappi
  • Ohjaa LED

Top-Down menetelmän eri osat voidaan vielä jakaa pienempiin kokonaisuuksiin, esimerkiksi:

  • Lue nappi
    - onko nappi pohjassa? --> odota irti päästöä
    - nappi on päästetty irti --> suorita LEDin ohjaus
  • Ohjaa LED
    - onko LED päällä? --> sammuta
    - onko LED sammuksissa --> sytytä

Top-Down menetelmässä siis aloitetaan keskittymällä suurempiin osa-alueisiin, joita myöhemmin tarkennetaan. Tämä voi joillekin ohjelmoijille olla yleinen tapa tehdä asiat, sillä ohjelman rakenne määräytyy tällä tavalla mietittynä lähes itsestään.

Ongelman olisi voinut ratkaista myös Bottom-Up menetelmällä, eli olisi aluksi luotu apuvälineet toiminnon toteuttamiseksi. Tässä tapauksessa tämä tarkoittaisi aluksi esimerkiksi funktioiden luomista ongelmien ratkaisua varten.

Esimerkiksi funktiot olisi voitu kirjoittaa seuraavalla tavalla (älä välitä vielä tässä vaiheessa vielä sanoista void, char, unsigned jne...):

- Funktio LEDin ohjaamiseen:  void LedOnOff(unsigned char OnOff);
- Funktio napin tutkimiseen:   unsigned char NapinTila();
- Funktio LEDin tutkimiseen:   unsigned char LedinTila();

Luotuja funktioita voitaisiin nyt käyttää halutun toiminnon aikaansaamiseksi. Bottom-Up menetelmä on hyödyllinen silloin, kun ei tiedä tarkkaan ohjelman kulkua, mutta tietää kuitenkin mitä ohjelman pitää ohjata tai ratkaista suurinpiirtein. Tällöin projekti voidaan aloittaa, vaikka koko ohjelman määrittely ja tarkempi toiminnallisuus olisi vielä kesken. (Pssst. Jos et ymmärtänyt yllä olevista sanoista mitään, älä huoli, palaamme niihin pian).

Kuten huomaat, voi ongelmaan perehtyä erilaisilla keinoilla. Tämä onkin mukava asia ohjelmoinnissa, sillä asiat voidaan ratkaista hyvin monella eri tavalla ja tästä syystä ohjelmat ovat yleensä tekijänsä näköisiä - niin hyvässä kuin huonossa.

Kaavioiden käyttö

Tehtävästä ohjelmasta voidaan luoda kaavio, joka kertoo ohjelman toiminnasta graafisin elementein. Kaaviosta nähdään ohjelman eteneminen vaihe vaiheelta sekä tarpeelliset muuttujat. Kaavion hyödyllisyys on siinä, että siitä voidaan helposti nähdä ohjelman epäloogisuudet. Kaavio ei kuitenkaan ole läheskään aina edellytys ohjelman tekemiselle ja se voidaan toki jättää työvaiheista poiskin. Määrittele siis itse, tarvitsetko kaavioita vai et.

Alle olen kuitenkin selittänyt kuinka esimerkkiongelma ratkaistaan vuokaavion ja tilakaavion avulla. Nämä kaksi lienevät yleisimmin tunnettuja use-case -kaavion lisäksi, mutta tämän kolmannen kaavion selityksen olen jättänyt tarkoituksellisesti pois. Näistä löytyy kyllä Googlella lisää tietoa varsin kattavasti.

Vuokaavio

Vuokaavion tarkoitus on kertoa ohjelmoijalle sovelluksen kulkureitti, jolloin ohjelmoija pystyy rakentamaan koodin siihen muotoon, että se noudattaa haluttua rakennetta. Vuokaavio siis määrittelee ohjelman rakenteen ja kulun (vuon) suhteellisen tarkasti, mutta funktioiden sisäiseen toimintaan se ei välttämättä aina ota kantaa (eikä se ole aina tarkoituskaan).

Kun edellä mainitusta LEDin ohjauksesta, esimerkkiongelmasta, piirretään vuokaavio, saadaan siitä alla olevan kuvan mukainen kaavio (jälleen jos menee yli hilseen niin ole kärsivällinen ja lue vain eteenpäin, asiat kyllä aukenevat mitä pidemmälle pääset):

Vuokaaviosta voit huomata termin while(1), joka tarkoittaa silmukkaa, missä nappia tarkastetaan koko ajan (eli pollataan). Lisäksi while(1) on kirjoitettu salmiakkikuvioisen laatikon sisään. Ylimmässä salmiakkikuviossa, missä while(1):kin on, tarkistetaan onko ehto 1 totta. Koska 1 on totta aina, niin ohjelman kulku ohjautuu toiseen salmiakkikuvioon, missä tarkistetaan onko nappi pohjassa vai ei. Näin ollen LEDin tila vaihdetaan sen mukaan, onko nappi pohjassa vai ei.

Vuokaaviossa salmiakkikuviolla ilmaistaan vuon (eli ohjelman kulun) ohjausta jonkin muuttujan tai ehdon perusteella. Suorakulmaisella kuviolla ilmaistaan puolestaan tekemistä, joka ei ole ehdollista toimintaa - tässä tapauksessa siis LEDin tilan vaihtaminen.

Ehdoista, silmukoista ja muuttujien tarkistuksesta kerrotaan tarkemmin tuonnempana, joten älä liikaa takerru yksityiskohtiin jos asiat tuntuvat vielä hämäriltä.

Tilakaavio

Tilakaavion tarkoitus on hieman erilainen kuin vuokaavion. Tilakaaviolla tarkastellaan enemmänkin sitä, missä tilassa ohjelma kulloinkin on ja mitkä ovat edellytykset ohjelman tilan vaihtamiseksi. Tilakaaviota käytetään ehkä eniten silloin, kun ohjelman toiminta edellyttää jonkinlaisen tilakoneen luomista. Tilakone on yksinkertaisesti eri tavalla toteutettu ohjelmarakenne, missä ohjelman suoritus pysyy yhdessä tilassa niin kauan, kunnes jokin ehto täyttyy tai jokin muuttuja saa tietyn ennalta määrätyn arvon. Toiminta kuitenkin käyttäjälle näyttää edelleen samalta. Edellä mainitusta LEDin ohjauksesta tulisi siten alla olevan kuvan mukainen tilakaavio.

Voidaan huomata, että vaikka ohjelman suoritus olisi tässä tapauksessa samankaltainen kuin aiemmin, niin kaavio näyttää erilaiselta. Kun napin painallus havaitaan, vaihdetaan ohjelman tila toiseksi ja sytytetään tai sammutetaan LED, jonka jälkeen vaihdetaan takaisin odottavaan tilaan.

Tilakaaviossa on tärkeää tietää ja havaita ne tilanteet, jolloin tila pitää vaihtaa. Ohjelman eri tilat kirjoitetaan ympyröiden sisään ja niistä piirretään nuolet tilan vaihtojen mukaisesti. Tilakaavio ei määrittele ohjelman rakennetta yhtä tarkasti kuin vuokaavio, joten monesti tilakaavio on kätevämpi hahmotella ennen vuokaaviota, mikäli ohjelman toteutus vaatii tilakonetta (usein näin on).

C:n kirjoitussäännöt ja varatut sanat

Jokaisella kielellä on omat sääntönsä, kuinka sitä kirjoitetaan. Kielioppi, eli syntaksi on tärkeää tietää ja ymmärtää, jotta ohjelmointi ylipäätään onnistuu. Jos olet joskus lukenut tekstin "syntax error", niin se on yksinkertaisesti tietokoneen ilmoittama asia että "Hei kuules, kirjoitit nyt komennon väärin.".

Ohjelmoijan tulee osata ajatella loogisesti, mutta yhtä olennaista on tietää miten ohjelmaa käytännössä kirjoitetaan. Jos lähdekoodissa on syntaksivirheitä (eli kielioppivirheitä) niin meidän onneksemme kääntäjä varoittaa niistä, eikä ohjelmaa voi suorittaa tai ladata mikrokontrollerille ennen kuin virheet on korjattu. Tämä estää suurimman osan perusvirheistä. Mutta vaikka nykypäivänä on käytössä edistyneet ohjelmointityökalut, on säännöt silti hyvä tuntea.

Olipa kyseessä C-kieli tai joku muu ohjelmointikieli tai jopa skriptikieli (javascript, php), niin kaikille niille on yhteistä ainakin muuttujat, silmukkarakenteet, ehtolauseet ja funktiot. Se, miten ne on kullekin kielelle toteutettu, riippuu luonnollisesti kielestä. Seuraavissa osissa kerrotaan lisää varsinaisista ohjelmarakenteista, mutta ensin tarkastellaan C:n kirjoitussääntöjä ja varattuja sanoja.

C-kielisen ohjelman ulkoasu on tarkkaan määritelty. Sen tulee noudattaa aina kirjoitussääntöjä, joten pidemmittä puheitta menemme näihin sääntöihin. Toki on totta, että C-kieltä voi kyllä kirjoittaa haluamallaan tavalla, mutta tarkoitan tässä kirjoitussäännöillä nyt ihan perusasioita, eniten syntaksia.

Kirjaimet, numerot ja merkit

Ohjelmaa kirjoitetaan kuten mitä tahansa dokumenttia, mutta sallittuja kirjaimia ovat vain aakkoset a-z isoina tai pieninä. Lisäksi numerot (0-9), välilyönnit, väliviivat ja alaviivat, tabulaattorit ja rivinvaihdot ovat sallittuja. Koska koodissa on usein asioita, jotka tarvitsevat kommentointia ja selittelyä, niin niillekin on luotu omat erikoismerkkinsä.

Erikoismerkit ovat sallittuja tietyin rajoituksin, esimerkiksi seuraaville merkeille on varattu oma käyttötarkoituksensa, joten niitä ei voi käyttää miten sattuu. Alle olen luonut taulukon, missä kyseiset merkit käydään tarkoituksineen läpi. Tässä tulee vastaan joukko uusia termejä ja asioita, mutta älä taaskaan takerru liikaa näihin. Oppaan jatko-osissa nämä tullaan selvittämään kyllä.

Erikoismerkit Merkitys
( ) Käytetään funktioiden yhteydessä ja tyypityksessä.
{ } Käytetään ohjelmalohkojen eristämisessä toisistaan.
[ ] Käytetään taulukoiden käsittelyssä.
// Käytetään koodissa yhden rivin kommentissa.
/* */ Käytetään koodissa kun kommentoidaan useampi rivi.
# Kääntäjälle varatun sanan aloitusmerkki.
; Käytetään kun riville kirjoitettu käsky päätetään. Käytännössä jokaisen C-rivin pitää päättyä tähän merkkiin (paitsi kääntäjälle varatut rivit).

Sulkeet ja muut erikoismerkit tulevat kyllä tutuksi ohjelmointiin perehtyessä, mutta muutaman sanan haluan kertoa kommenteista.

Kun haluat selittää jonkin muuttujan tai funktion toimintaa lähdekoodissa, käytät siihen kommentointia. Tämä on hyvin suositeltavaa, koska koodia saatetaan myöhemmin tarvita, eikä tehtyjen ohjelmien suoritusta välttämättä heti muista. Kommentointi auttaa tässä asiassa, kun lähdekoodiin on kirjoitettu mitä siinä tehdään. Kommentointi on myös hyvän ohjelmoijan sekä hyvien ohjelmointitapojen tunnusmerkki, joten kirjoita koodiin edes lyhyitä kommentteja.

Kommentteja voit lisätä lähdekoodiin seuraavasti:

// yksirivinen kommentti alkaa kahdella kauttaviivalla

/*
* Useampirivinen kommentti alkaa kauttaviivalla ja tähtimerkillä ja päättyy samoilla merkeillä, mutta toisinpäin aseteltuna. Näiden merkkien väliin voit lisätä mitä tahansa merkkejä haluat. Myös ääkkösiä, linkkejä yms.
*/

Varatut sanat

C-kielessä joukko varattuja sanoja, joita ei saa käyttää muussa yhteydessä kuin mihin ne on tarkoitettu. Alla on lista kaikista C-kielen varatuista sanoista.

auto double int struct
break else long switch
case enum register typedef
char extern return union
const float short unsigned
continue for signed void
default goto sizeof volatile
do if static while

Jos varattua sanaa yrittää käyttää väärässä paikassa, kääntäjä yleensä kyllä herjaa siitä ja antaa virheen tai varoituksen. Varattuja sanoja pitää myös aina käyttää juuri samalla tavalla kirjoitettuna. INT ja int ovat C-kielessä kaksi eri asiaa kääntäjälle, joten isojen ja pienten kirjainten kanssa pitää olla tarkkana.

Varattujen sanojen syntaksi on yleisesti ottaen seuraava:

<varattu sana>   <muuttuja> TAI <funktio>

Esim.

float janniteArvo;
signed int PalautaJanniteArvo();

Seuraavassa osassa käydään tarkemmin läpi muuttujia ja tietotyyppejä ja mihin varatut sanat läheisemmin liittyvät.