5. PWM-ohjaus

Julkaistu: sunnuntai 12.2.2017

Edellisessä osassa kytkettiin Arduinolle LDR-vastus ja potentiometri, joita käytettiin ledin sytyttämiseen ja sammuttamiseen. Tehtiin siis yksinkertainen ON/OFF-ohjauskoodi. Samaa kytkentää hyväksikäyttäen, tosin vain hieman sitä muuttamalla, saadaan aikaiseksi himmennettävä led jota voidaan ohjata potentiometrillä. Mutta aluksi aiheesta PWM-ohjaus, mikä se oikein on?

PWM-ohjaus

PWM-ohjaus on yksinkertaisesti pulssisuhteen muutoksella tehtävää ohjausta, jolloin ohjaus menee vuorotellen päälle ja pois tietyllä taajuudella - kyseessä on siis digitaalinen on/off-ohjaussignaali. Pulssin taajuuden arvoa merkitään Hz ja puhutaan hertseistä. Hertsi tarkoittaa sitä, kuinka monesti signaali käy nollassa ja ykkösessä. Esimerkiksi 10 hertsiä tarkoittaa, että signaali menee nollaan ja ykköseen kymmenen kertaa sekunnin aikana.

Pulssisuhde puolestaan tarkoittaa sitä suhdetta, miten pitkään signaali on "1"-tilassa ja miten pitkään "0"-tilassa. Usein tätä suhdetta ilmaistaan prosenteilla, sillä suhdeluvut ovat ehkä helpoiten esitettävissä nimenomaan prosenteilla. Alla olevassa kuvassa on esitetty 80, 50 ja 20 prosentin pulssisuhteet.

PWM-ohjaus

Periaate PWM-ohjauksessa on siis yksinkertainen. Kun pulssisuhde on suurimmillaan niin kuormaan ohjataan eniten tehoa. Pulssisuhde on suurimmillaan tietenkin 100 %:ssa, jolloin ohjaus on siis täysin päällä eli "1"-tilassa. Pienimmillään ohjaus on vastaavasti 0 %:ssa, jolloin ohjaus on täysin pois päältä eli "0"-tilassa.

Arduinon PWM-lähdöt

Arduinossa PWM-lähdöt, joita on 6 kappaletta, on merkitty piirilevyn pintaan aaltoviivalla (~) pinninumeron viereen:

Aikaisemmassa kytkennässä ledin ohjaus oli kytkettynä pinniin numero 8. Nyt kun PWM-ohjausta halutaan käyttää, täytyy led kytkeä pinniin 9, joka on merkitty PWM-ohjauspinniksi.

PWM-ohjauksen koodi

Arduinossa PWM-ohjauksen tekeminen on yhtä helppoa kuin ledin sytytys tai kytkimen tai analogiainputin lukeminen.

PWM-ohjauksen toteuttamiseen käytetään analogWrite() -metodia, joka tuottaa halutun PWM-pulssisuhteen. Arduinossa PWM-taajuus on noin 490 hertsiä, eli pulssi käy nollassa ja ykkösessä 490 kertaa sekunnissa. Pinneissä 5 ja 6 on hieman suurempi taajuus, noin 980 hertsiä. Nämä johtuvat Arduinon ohjelmointiympäristön rakenteesta ja niihin ei ihan helposti voi vaikuttaa, siksi emme niitä sen enempää murehdikaan.

PWM-metodille annetaan kaksi parametria eli syötettä ja ne ovat haluttu ohjauspinni ja arvo väliltä 0 - 255. Alta nähdään edellisestä esimerkistä muokattu koodi kokonaisuudessaan, missä lediä ohjataan PWM:llä mikäli käyttäjä painaa painonappia pohjassa:

void setup() {
  // LED on lähtönä pinnissä 9
  pinMode(9,OUTPUT);
  // Alustetaan kytkin tuloksi
  pinMode(7,INPUT);
}
void loop() {
     /* Sytytetään LED PWM:llä jos käyttäjä pitää painokytkintä
        pohjassa ja pyörittää potentiometriä.
        LED syttyy myös jos potentiometri on yli puolen välin
        LED syttyy myös jos LDR-vastuksen eteen tulee varjo.
     */
     int analogLDR = analogRead(0);
     int analogPot = analogRead(1);
     bool digitalButton = digitalRead(7);
     
     if(digitalButton == HIGH) // Painonapin tutkiminen
     {
       analogWrite(9,analogPot / 4);
     }
     else if (analogLDR > 500) // LDR:n tutkiminen
     {
       digitalWrite(9,HIGH);
     }
     else // muussa tapauksessa ledin sammutus
     {
       digitalWrite(9,LOW);
     }
}

Kuten saattaa huomata, niin koodi on lähes samanlainen. Yksi else if -lohko on poistettu ja painonapin tutkintalohkon sisään on kirjoitettu käyttämämme uusi metodi analogWrite(). Tälle metodille annetaan tieto mikä PWM-ohjauslähtö on kyseessä (eli nasta 9) ja kuinka paljon PWM:ää tälle syötetään. AnalogWrite hyväksyy vain arvot 0 - 255, joten potentiometrin luettua arvoa jaetaan neljällä. Jako neljällä on samalla arvon skaalaus hieman alaspäin niin, että kun analogRead palauttaa arvon 1023 niin arvoksi tulee 255,75 joka pyöristyy arvoon 255 (tietokoneet pyöristävät joskus hieman eri tavoin kun matemaatikot).

Toisin sanoen kun potentiometri on toisessa ääripäässä niin ledin kirkkaus on täysillä. Toisessa laidassa puolestaan led ei loista ollenkaan. Aika yksinkertaista, eikö?

Arduinon muuttujat

Sitten hieman teoriaa muuttujista, kuten edellisessä osassa lupasin. Sivuihin perehtynyt huomaa, että samoja asioita on käsitelty myös MSP430 Launchpad oppaassa, mutta sieltäpä onkin hyvä lainata asioita tänne :). Eli siis, mikä se muuttuja nyt oikein on, kun niitä on tässäkin oppaassa jo käytetty?

Muuttujista... ja vähän tietotyypeistäkin

Muuttuja on jonkin tiedon tallentaja ja tämä tallennettu tieto voi ohjelman suorituksen aikana muuttua, siksi siis nimi muuttuja (engl. variable).

Muuttujaa käytetään varastoimaan tietoa väliaikaisesti ohjelman suorituksen aikana. Esimerkiksi lämpötilaa mittaavan sovelluksen muistissa on lämpötila muuttuja, mihin mittauksen aikana otettu arvo tallentuu. Muuttujia käytetäänkin ohjelmoinnissa aina ja siksi on tärkeää ymmärtää mikä muuttuja on ja miten sitä käytetään.

Pelkästään tieto siitä, että meillä on muuttuja käytössään ei riitä tietokoneelle, sen täytyy tietää myös mitä käytössä olevalla tiedolla tarkoitetaan. Arduinon kääntäjän pitää tietää onko kyseessä kokonaisluku, desimaaliluku vai esimerkiksi kirjain. Tämän tiedon perusteella muuttujia voidaan käsitellä kullekin tietotyypille ominaisella tavalla.

Tietotyypillä tarkoitetaan esimerkiksi yllä olevassa koodissa potentiometrin arvoa tallentavalle muuttujalle annettua tyyppiä int. Tämä sana määrittää minkälaista arvoa muuttuja pitää sisällään. Koska tietokoneet ja mikroprosessorit eivät ainakaan vielä ole kovin fiksuja, täytyy niiden ohjelmoinnissa kertoa, että "tämä muuttuja tässä pitää sisällään numeerista arvoa joka on tietyllä valillä (vaikkapa 0 - 100)". Tosin tietokoneessa/mikroprosessorissa muuttujien numeerinen arvo on pienimmillään yleensä 0 - 255 välillä (puhutaan tällöin tavusta engl. byte).

Käytännössä tietotyyppi siis kertoo mikroprosessorille tai tietokoneelle kuinka paljon tilaa muistista varataan ja mitä muuttujalla on tarkoitus tehdä.

Arduinolla on pitkä lista erilaisia tietotyyppejä, joita voidaan käyttää. Alle on lisätty lista tietotyypeistä, joita klikkaamalla pääsee Arduinon englannin kielisille sivuille tutkimaan mitä kukin tarkoittaa, mutta olen kuitenkin lyhyen suomennoksen/selityksen näille antanut:

  • void = tarkoittaa tyhjää, olematonta. Void ei voi pitää sisällään mitään eikä palauta mitään.
  • boolean = looginen arvo, joka voi olla vain true tai false, eli tosi tai epätosi. Tosi tarkoittaa että jokin asia on arvossa 1, kun taas epätosi on arvossa 0.
  • char = pitää sisällään tiedon kirjaimesta. Esimerkiksi kirjain 'A' tarkoittaa jotain tiettyä numeroa (65), joka nyt satutaan vain tulkitsemaan kirjaimeksi a. Kuitenkin Arduinossa tätä tietotyyppiä käytetään nimenomaan varastoimaan tietoa jostakin kirjaimesta. Tämä tietotyyppi voi tallentaa numeerista arvoa väliltä -127 ... +127 (ainoastaan positiivisia tai negatiivisia kokonaislukuja).
  • unsigned char = hieman sama kuin char, mutta tämä voi tallentaa numeerista tietoa välillä 0 ... 255 (ainoastaan positiivisia kokonaislukuja).
  • byte = tarkoittaa tavua. Yksi tavu pitää sisällään numeerisen tiedon joka voi olla mitä tahansa välillä 0 ... 255 (ainoastaan positiivisia kokonaislukuja). Byte on myöskin mikroprosessorin pienin käsittelemä kokonaisluku.
  • int = tarkoittaa yleensä numeroarvoa (kokonaislukua) joka on välillä -32767 ... +32767. Tämä tietotyyppi koostuukin kahdesta bytestä/tavusta (ainoastaan positiivisia tai negatiivisia kokonaislukuja).
  • unsigned int = tarkoittaa numeroarvoa (kokonaislukua), joka on välillä 0 ... 65535 (ainoastaan positiivisia kokonaislukuja). Tämä myöskin koostuu kahdesta bytestä/tavusta.
  • word = täysin sama kuin unsigned int.
  • long = tämä tietotyyppi on kuin kaksi yhdistettyä wordia tai unsigned int:iä. Pitää sisällään neljän byten/tavun mittaisen kokonaisluvun välillä -2,147,483,647 ... +2,147,483,647.
  • unsigned long = periaatteessa sama kuin long, mutta pitää sisällään vain positiivisia kokonaislukuja nollasta 4 294 967 295.
  • short = sama kuin int.
  • float = käytetään, kun halutaan tallentaa liukuluku, eli luku missä on desimaalipiste (esimerkiksi 3,141). Liukulukuja tarvitaankin usein erilaisten arvojen käsittelyssä, sillä kokonaislukujen resoluutio (tarkkuus) ei riitä. Arduino varaa yhdelle liukuluvulle 4 tavua/byteä muistia ja tyypillisesti liukuluvun tarkkuus on noin 6-7 desimaalia.
  • double = tarkoittaa tuplatarkkuuden liukulukua, mutta Arduinolla tämä on kuitenkin sama kuin float, paitsi Arduino Due:lla, missä double varaa muistista neljän tavun sijaan kahdeksan tavua.
  • string = kirjaimista muodostuva taulukkoa. Tämän tyyppiseen muuttujaan voidaan tallentaa jokin rimpsu, joka koostuu chareista, esimerkiksi (string url[ ] = "www.hutasu.net";).
  • String = merkkijono-objekti. String tyyppisille muuttujille voidaan tehdä kaikenlaisia operaatioita monipuolisemmin ja helpommin kuin string-taulukolle.

Tietotyyppien käytöstä ei kannata tässä vaiheessa vielä huolestua, mutta jos olet tehnyt samat koodit tähän asti mitä minäkin, niin olet jo käyttänyt tietotyyppejä varsin sujuvasti. Se ei ole sen ihmeellisempää kuin muuttujalle nimen antaminen.

Lopuksi

Tässä tämänkertainen selostus. Jos teksti on liian vaikeasti ymmärrettävää niin aina voit Googlettaa tarkennusta tai ottaa yhteyttä info-sivujen kautta. Toisaalta lukeminen uudestaan ja muiden sivustojen etsiminen ja tiedon opiskelu sieltä voi olla myös hyödyllistä.

Seuraavassa osassa otetaan puuhalaatikosta esille 7-segmenttinäyttö ja ohjataan näytölle numeroita, sekä puhutaan hieman operaattoreista.

Seuraava osa: 7-segmenttinäyttö.