15. Moottorin ohjaus PWM:llä

Tässä osassa perehdytään PWM-ohjauksen saloihin ja sitä varten tarvitaan jokin laite mitä ohjataan. Näin ollen romujeni seasta löysin pienen DC-moottorin joka toimii ainakin 5 voltin (USB) jännitteellä. Mikäli sinulla ei moottoria ole käytössäsi, voit käyttää myös lediä ja ohjata sen kirkkautta.

Ennen kuin mennään itse sovellukseen ja kytkentään, voisin muutaman sanan mainita PWM-ohjauksesta.

Pulse Width Modulation eli PWM-ohjaus

PWM-ohjaus on yksinkertaisesti pulssisuhteen muutoksella tehtävää ohjausta, jolloin ohjaus menee vuorotellen päälle ja pois tietyllä taajuudella. 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 aina helpoiten esitettävissä nimenomaan prosenteilla. Alla olevassa kuvassa on esitetty 80 %, 50 %:n ja 20 %:n pulssisuhteet.

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.

PWM-ohjausta voi tehdä joko sovelluksella (SW/FW = SoftWare/FirmWare) tai rautatasolla eli elektroniikalla (HW=HardWare). Luonnollisesti HW-tason ohjaus on nopeampaa, sillä silloin mikrokontrollerin CPU:n ei tarvitse signaalia erikseen ohjata tai operoida. Ohjelmallisesti (SW/FW) tehty PWM-ohjaus luonnollisesti vaatii CPU:n työskentelyä ja näin ollen kuluttaa ohjelman suoritusaikaa, mutta pienillä ohjelmilla tällä ei liene käytännön merkitystä. Toisaalta ohjelmallisesti tehty PWM vaatii tekijältään enemmän taitoa ja koodirivejäkin se vie enemmälti. Tässä osassa keskitytään vain HW:lla tehtyyn PWM-ohjaukseen, mihin kuitenkin tarvitaan hieman sitä softaa 😉

PWM ohjauksen käyttö eri sovelluksissa

PWM-ohjaus on hyvin yleinen tapa säätää kuormassa kulutettavaa tehoa, erilaisia voimakkuuksia tai pyörimisnopeuksia. Hiemankaan älykkäämmissä moottorinohjauksissa PWM-ohjaus on lähes poikkeuksetta mukana. LEDejä voidaan ohjata kirkkaammaksi tai himmeämmäksi PWM-ohjauksella (esim. kannettavien näyttöjen taustavalo).

Kuten varmaan hoksasitkin, niin 3,6 voltin jännitetasolla ja mikrokontrollerin suoralla ohjauksella (eli ei mitään kytkentöjä välissä) ei voi ohjata esimerkiksi 12 voltin tuuletinta. Tai no ainahan voi yrittää, mutta tuuletin ei pyöri, koska mikrokontrolleri ei kykene syöttämään riittävää määrää tehoa tuulettimelle. Mikrokontrolleri ei siis välttämättä ole kytketty suoraan ohjattavaan kuormaan, vaan välissä on jokin puskuri tai vahvistin, joka kytkee kuormaan tarvittavan määrän virtaa ja jännitettä. Tällainen väli-kytkentä myös suojaa mikrokontrollerin I/O:ta ja on helppo myöskin testata erikseen.

PWM-ohjauskytkentä

Jotta mikrokontrolleri saadaan oikeasti ohjaamaan jotain kuormaa taikka moottoria, niin sille täytyy tehdä kytkentä joka mahdollistaa ohjauksen. Tällainen kytkentä on mahdollista tehdä monella eri tavalla, mutta tässä on turvauduttu yksinkertaiseen transistorikytkentään.

Launchpadilla on mahdollista ohjata pieniä kuormia, mutta moottori ei olekaan mikään pieni kuorma. Tämän vuoksi käytetään USB:stä saatavaa 5 volttia, joka otetaan launchpadin kautta pisteistä TP3 (GND) ja TP1 (USB +5 V). Alla olevassa kuvassa näkyy kuinka omistamassani launchpadissa on näihin pisteisiin juotettu johtimet juuri tällaista käyttöä varten. Näistä pisteistä saadaan siis suoraan USB-portista tuleva sähkö käyttöön.

 

Transistorina tässä kytkennässä toimii ns. Darlington-transistori joka on tyypiltään NPN ja sen malli on BDX53C. Tämä transistori koostuu sisäisesti kahdesta "tavallisesta" transistorista, jolloin tämä komponentti voi ohjata suuriakin virtoja suuren virtavahvistuksen ansiosta. Kytkentäkaavio moottorinohjaukselle löytyy alta.

Kytkennässä on siis mikrokontrollerin porttipinni kytkettynä suoraan NPN-transistorin kannalle. Diodi on moottorin rinnalle kytkettynä sen vuoksi, ettei moottorin tuottamat transientit vahingoita ohjaavaa transistoria. Diodin ja moottorin rinnankytkennästä löytyy myös kondensaattori, joka suodattaa moottorin tuottamia häiriöitä, tämä ei tosin ole tarpeellinen toiminnan kannalta, mutta lähinnä lisäsin sen "makuasioiden" vuoksi.

Tämä kytkentä ei kuitenkaan ole 100% turvallinen mikrokontrollerin kannalta, koska jos transistorin kollektori ja kanta oikosulkeutuu (esimerkiksi liiallisen kuorman tai muun syyn vuoksi) niin mikrokontrollerin pinniin P1.2 tulee liian suuri jännite. Lisäksi tässä tapauksessa jos mikrokontrolleri yrittää ohjata pinniä P1.2 "0"-tilaan, niin 5 voltin syöttösähkö oikosulkeutuu maahan joka voi mahdollisesti hajottaa sekä mikrokontrollerin pinnin sekä virtalähteen. MUTTA, tämä on harvinaista ja tuskin tulee tapahtumaan jos tekee asiat oikein joten tätä on turha pelätä.

Pieneen moottoriin on testailuja varten helppo juottaa jopa pintaliitos diodi, mistä on kuva alla. Ensin väänsin pintaliitosdiodin jalkoja niin, että ne yltävät moottorin napoihin jonka jälkeen lisäsin kondensaattorin ~100 nF ja juotin komponentit paikoilleen. Diodiksi kelpaa mikä tahansa diodi, tässä on käytetty schottcky-tyyppistä, jonka kynnysjännite on 0,3 volttia.

Seuraavassa kuvassa on esitetty moottorinohjauskytkentä hieman graafisemmassa muodossa ja sen alla on valokuva oikeasta kytkennästä.

Kun rautapuoli on esitelty ja käyttövalmiina, on aika tehdä ohjelma. Seuraavassa siis siitä.

PWM Esimerkki: 100 hertsin PWM, 20% pulssisuhde

Edellisen osan perusteella osaamme käyttää timeria niin, että voimme tehdä sillä haluamamme taajuuden. Nyt siis haluamme 100 hertsin taajuuden ja käytämme siihen Launchpadille asennettua kidettä. Tehdäänpä siis hieman laskentaa:

Laskurin kello on taajuudeltaan 32768 hertsiä ja tarvitsemamme CCR0 rekisterin arvo on tuntematon X. Haluamamme taajuus on 100 hertsiä, jolloin:

CCR0 = FKELLO / FPWM 
CCR0 = 32768 / 100 Hz = 327,68

Emme kuitenkaan voi ladata CCR0 rekisteriin saamaamme arvoa tuollaisenaan, joten joudumme hieman pyöristämään arvoa sopivaan tasalukuun. Pyöristämme arvon siten 327:ään tavallisten pyöristyssääntöjen mukaisesti. CCR0 rekisteri siis määrittää millä taajuudella ohjaussignaali asettuu "1" -tilaan.

Tässä ei tietenkään ole vielä kaikki. Seuraavaksi täytyy laskea pulssisuhde, joka muodostetaan myös rekisterin arvolla, mutta tällä kertaa rekisteri jonka arvoa lasketaan on CCR1. Tämä rekisteri määrittää milloin signaali puolestaan asettuu "0"-tilaan. Pulssisuhteen (Duty Cycle) kaava menee siis näin:

D = CCR1 / (CCR0 * PWMpulssisuhde)

Tästä haluamme kuitenkin saada sellaisen arvon, että pulssisuhde on 20%, joten kaavaa muokkaamalla saadaan:

CCR1 = (PWMpulssisuhde) * CCR0

Mistä lasketaan, että:

CCR1 = 0,2 * CCR0
CCR1 = 0,2 * 327
CCR1 = 65,4
CCR1 = 65

Kun "vaativat" matemaattiset toimitukset on saatu tehtyä, niin voimme kirjoittaa ohjelman joka alustaa ja käynnistää suunnittelemamme PWM-ohjauksen.

#include <msp430g2231.h>

void main(void)
{
     WDTCTL = WDTPW + WDTHOLD; // watchdog pysäytys 
     P1DIR |= BIT1 + BIT2; // P1.1 = TA0.0, P1.2 = TA0.1 
     P1SEL |= BIT1 + BIT2; // asetetaan porttipinnien toimintatila timerin ohjaamaksi 
     // Kellokiteen asetus: 
     BCSCTL3 = XCAP_3; // asetetaan 12,5 pF:n sisäiset kondensaattorit aktiiviseksi 

     // Timerin asetus: 
     CCTL0 = OUTMOD_4; // asetetaan moodi toggle 
     CCTL1 = OUTMOD_7; // asetetaan moodi Reset/Set, huomaa että rekisteri on CCTL1
     // asetetaan jakaja 0, laskentamoodi vertaileva ja ACLK-kello.
     TACTL |= ID_0 + TASSEL_1;  
     CCR0 = 327; // ladataan 100 hertsin arvo 
     CCR1 = 65; // ladataan pulssisuhteen arvo 

     TACTL |= MC_1; // käynnistetään timer

     while(1)
     {
          // ei tehdä mitään 
     }
}

Ohjelman kulku ja alustukset

Aivan aluksi totuttuun tapaan alustetaan tarvittavat porttipinnit ja pysäytetään watchdog-kytkentä. Kelloksi alustetaan edellisestä osasta tuttu kide ja lopuksi alustetaan timerin rekisterit.

Uusia asioita, joita edellisissä osissa ei vielä ole käyty läpi on CCTLx-rekisteriin asetettavat moodiarvot. Nämä rekisterit vaikuttavat siihen, kuinka porttipinnit P1.1 ja P1.2 käyttäytyy kun ne ovat timerin ohjauksessa. Tässä asetetaan varsinaiselle PWM-ohjaukselle moodi 'Reset/Set', joka vuoroin nollaa ja asettaa porttibitin "1"-tilaan. CCTL0-rekisteriin asetetaan moodi 'Toggle', joka myös asettaa porttibitin nollaan ja "1"-tilaan.

CCTL0-rekisteri ohjaa porttipinniä P1.1, joka on kytkettynä sisäisesti timerin TA0.0 lähtöön ja sen "naksutustaajuuteen" vaikuttaa nimenomaan rekisteri CCR0. Vastaavasti CCTL1-rekisteri ohjaa porttipinniä P1.2, joka on kytkettynä sisäisesti timerin TA0.1 lähtöön ja sen "naksutustaajuuteen" vaikuttaa nimenomaan rekisteri CCR1.

PWM-taajuus saadaan aikaiseksi siis CCR0-rekisterillä, ja pulssisuhde muodostetaan CCR1-rekisterillä. Voit ajatella asiaa niin, että kun laskuri on saavuttanut saman arvon kuin mikä on rekisterissä CCR0, niin se ikään kuin käynnistää toisen sisäisen laskurin, joka laskee arvoon joka vastaa CCR1-rekisteriä. Alla oleva kuva havainnollistaa kuinka ohjelma pinnejä ohjaa.

Ohjelman toiminta

Kun timer käynnistyy, niin moottori alkaa pyöriä ja pwm-ohjauksemme toimii. Ohjelmassa ei tapahdu alustusten jälkeen mitään ja PWM-ohjaus tulee täysin itsenäisesti MSP430G2231:n timer lohkon ohjaamana. Alla on kannettavalla skoopilla otettu kuva porttipinnista P1.2, joka todistaa että ohjauksemme toimii kuten on suunniteltu.

Jos muutat CCR1-rekisterin arvoa, saat esille erilaisia pulssisuhteita. CCR0-rekisteriä muuttamalla puolestaan voit vaikuttaa ohjaustaajuuteen. Edellisiä osia hyödyntämällä saat lisäksi tehtyä vaikkapa kytkimellä ohjautuvan moottorinohjaus PWM:n.