9. Globaalit muuttujat

Alunperin julkaistu: 11.2.2017

Viimeksi muokattu: tiistai 19.12.2017

Ohjelmoitaessa tarvitaan usein myös globaaleita muuttujia. Tällainen muuttuja ei ole mitenkään poikkeava tavanomaisesta muuttujasta, joita aiemmin on tässä oppaassa käsitelty, ainoastaan sen näkyvyys on laajempi kuin tavallisten muuttujien. Näkyvyydellä tarkoitetaan sitä, miten ohjelma voi jotain muuttujaa käyttää suorituksen aikana. Alla on esimerkki kuinka globaali muuttuja määritellään.

#include <msp430g2231.h>
// globaali muuttuja. Määrittely aina alussa. 
char globaali = 0;

void main(void)
{
      // watchdog pysäytys jätetty tarkoituksella pois koska tätä ohjelmaa ei ole tarkoitus ajaa.
     char paikallinen = 0; // paikallinen muuttuja, määrittely aina funktion sisällä.

     while(1)          
     {
          // ohjelmakoodia 
     }
}

Tavallinen funktion sisällä esitelty muuttuja, kuten esimerkkiohjelmissamme on tähän mennessä tehty, säilyttää arvonsa vain kyseisen funktion sisällä. Globaali muuttuja taas säilyttää arvonsa niin kauan, kunnes se pyyhitään tai sähkö katkaistaan (tai tapahtuu jotain muuta enemmän tai vähemmän tarkoituksellista).

Globaaleita muuttujia tarvitaan usein mm. keskeytyskäsittelijöissä ja funktioiden välisissä vuorovaikutuksissa. Globaalia muuttujaa kannattaa hyödyntää varsin harkitulla tavalla ainakin Launchpadilla, sillä MSP430G2211/MSP430G2231 mikrokontrollereiden muistit ovat rajalliset ja jokainen byte muistin käytössä merkitsee. Globaalit muuttujat ovat kuitenkin usein välttämättömiä, joten niiden käyttäminen ei ole huono asia.

Vakiot

Vakioilla tarkoitetaan sellaisia arvoja, jotka ovat pysyviä eivätkä muutu, eli ovat vakioita nimensä mukaisesti. Seuraavassa esitellään kuinka vakioita voidaan C-kielessä määritellä ja kuinka voit itse luoda jonkin halutun vakion.

#include <msp430g2231.h>

void main(void)
{
     // watchdog pysäytys jätetty tarkoituksella pois koska tätä ohjelmaa ei ole tarkoitus ajaa.
     const char kiintea_8bit_numero = 152; // vakioarvoon asetettu muuttuja 
     char muuttuva_8bit_numero = 200; // muuttuja jonka alkuarvoksi on asetettu 200
     while(1)
     {
          // tästä tulee käännösvirhe, vakiota ei voi muuttaa kun se on asetettu const sanalla    
          kiintea_8bit_numero = 100;
          // tästä ei tule virhettä, sillä ei-vakio muuttujaa voidaan muokata 
          muuttuva_8bit_numero = 7;
     }
}

Usein vakioarvoja käytetään myös siten, että ne nimetään kääntäjälle varatun sanan #define avulla. Tämä onkin mielestäni parempi tapa nimetä vakioarvot, sillä jos muutostarve koodiin tulee, niin yhdellä rivillä saadaan muutos aikaiseksi ilman koodin täydellistä läpikäyntiä. Selventävä esimerkki alla.

#include <msp430g2231.h>

// määritellään kääntäjälle, että koodirivi, missä esiintyy VAKIOLUKU korvataan numerolla 152 #define VAKIOLUKU 152 // huomaa ettei tässä ole puolipistettä! 

void main(void)
{
     // watchdog pysäytys jätetty tarkoituksella pois
     const char kiintea_8bit_numero = VAKIOLUKU; // vakioarvoon asetettu muuttuja
     char muuttuva_8bit_numero = 200; // muuttuja jonka alkuarvoksi on asetettu 200
     while(1)
     {
          // koodia 
     }
}

Jos ylläoleva VAKIOLUKU esiintyisi numeerisena arvona (eli 152) useassa eri paikassa, niin käsin korjaaminen esimerkiksi arvoon 33 olisi työlästä. Kun VAKIOLUKU on määritetty kääntäjälle symboliksi yhdellä rivillä, niin luvun muuttaminen on helpompaa ja nopeampaa.

Huom! Vakioita määritettäessä #define -sanan avulla ei puolipistettä rivin loppuun tule.

Makrot

Makrot ovat pieniä pätkiä kääntäjälle tarkoitettua koodia, joilla kääntäjä saadaan tekemään asioita ohjelmoijan puolesta. Ne ovat monesti hyvin hyödyllisiä ja auttavat/nopeuttavat koodaamista, sekä oikein käytettyinä tuovat koodiin kaivattua selkeyttä.

Kääntäjälle voidaan määritellä makroja #define -sanan avulla. Esimerkiksi, jos haluamme aiemmin käytetyt ledin vilkutukset hoitaa makrojen avulla, voidaan edellisen osan esimerkkikoodiin kirjoittaa:

#include <msp430g2231.h>

// makro punaisen ledin päälle laittamiseksi
#define LED_PUN_ON (P1OUT |= BIT0)
// makro punaisen ledin sammuttamiseksi 
#define LED_PUN_OFF (P1OUT &= ~BIT0)
// makro vihreän ledin päälle laittamiseksi 
#define LED_VIH_ON (P1OUT |= BIT6)
// makro vihreän ledin sammuttamiseksi 
#define LED_VIH_OFF (P1OUT &= ~BIT6)

// 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 
     LED_PUN_OFF; // asetetaan ledit nollaan ettei ledit pala 
     LED_VIH_OFF;          

     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 makron avulla 
               LED_PUN_ON;
               break;
          case 1:
               // sytytetään vihreä 
               LED LED_VIH_ON;
               break;
          case 2:
               // sammutetaan punainen LED 
               LED_PUN_OFF;
               break;
          case 3:
               // sammutetaan vihreä LED 
               LED_VIH_OFF;
               // '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++);
}

Huomaat varmaankin, että koodin ymmärrettävyys paranee kun ledien ohjaukset on kirjoitettu selkeämmin makrojen avulla. Käytännössä tässä tapahtuu niin, että kääntäjä asettaa esimerkiksi LED_VIH_OFF -merkkijonon tilalle koodin P1OUT &= ~BIT6. Puolipiste täytyy tässä tapauksessa muistaa, sillä sitä ei voi #define -riville kirjoittaa.

Tämä riittää makroista, vakiomuuttujista ja globaaleista muuttujista tällä kertaa. Seuraavassa osassa tutustutaan taulukoihin ja leikitään muuttujilla hieman eri tavalla.