16. MSP430G2231 mikrokontrollerin kellot

Julkaistu: sunnuntai 12.2.2017

Tietokoneiden ja mikrokontrollereiden CPU:t toimivat aina jollakin kellotaajuudella. Tietokoneiden maailmassa käyttäjä harvoin muokkaa suorituskykyyn vaikuttavaa CPU:n kellotaajuutta, ellei harrasta kellottamista. Mikrokontrollereilla kuitenkin kellojen muokkaus on enemmän sääntö kuin poikkeus, sillä usein mikrokontrolleriin liitetyt laitteet toimivat jollakin tietyllä omalla nopeudellaan.

Mikrokontrollerin suorituskykyyn vaikuttaa siis se, miten nopeasti CPU:n kello tikittää. Nopealla kellolla mikrokontrolleri kykenee tekemään monta eri suoritusta sekunnin aikana, kun taas hitaalla kellolla suorituskyky on vaatimattomampi. Se miten nopeasti mikrokontrollerin tarvitsee toimia, riippuu tietenkin sovelluksesta.

Esimerkiksi ajan mittaaminen kahden tapahtuman väliltä, kuten vaikka kytkimen painallusten väliltä, ei välttämättä vaadi kovin suurta kellotaajuutta jos vaadittu mittaustarkkuus on esimerkiksi 10 millisekuntia. Jos signaalin muutos on nopeampi, esimerkiksi moottorin kierroslukuja mitattaessa, täytyy mikrokontrollerin kyetä mittaamaan signaalin muutosta paljon nopeammin. Näistä ei kuitenkaan tämän enempää, sillä näitä asioita tullaan myöhemmissä osissa käsittelemään tarkemmin.

Kuten sanottua, kellosignaalit ovat olennaisia signaaleita mikrokontrollerin toiminnassa. Kellomoduulin avulla saadaan mikrokontrolleri tekemään asioita hyvin nopeasti ja tehokkaasti, mikä myös tarkoittaa että virtaa kuluu 'paljon'. Vastaavasti mikrokontrolleri voidaan asettaa hyvin vähävirtaiseen tilaan, mutta kellosignaali on silloin hidas ja suorituskyky vähäinen.

Kellosignaaleja voidaan tuottaa monella eri tavalla, mutta käytännössä - varsinkin mikrokontrollerien maailmassa - on vain kaksi tapaa:

  • Sisäisesti oskillaattorilla (DCO/VLO)
  • Ulkoisesti kiteellä, resonaattorilla tai oskillaattorilla tai muulla kellosignaalia tuottavalla kytkennällä

Mainituista tavoista molemmat, siis sisäiset ja ulkoiset signaalit, ovat jo tähän mennessä tulleet vastaan, mutta syvennytään niihin vielä kuitenkin paremmin.

MSP430 kellot

Kellomoduulin olennaisimpana osana on ohjattavat rekisterit, mutta ennen kuin perehdytään niihin, tutustutaan tarkemmin kaikkiin mahdollisiin kelloihin, joita kellomoduuli tarjoaa. Kellomoduulia kutsutaan käyttömanuaalissa muuten nimellä 'Basic Clock Module+'. Kellomoduuliin liittyy kaksi aihealuetta joita tässä käsitellään ja nämä ovat kellotulot sekä kellosignaalit.

Kellotulot ovat käytännössä niitä tuloja, mistä kukin kello saa 'tikityksensä' ja kellosignaalit ovat mikrokontrollerin sisäisiä linjoja, mistä eri laitteet (SPI/I2C/UART/ADC/DAC jne...) ottavat oman 'tikityksensä' eli kellonsa. Näin ollen mikrokontrollerin eri moduuleita voidaan käyttää eri kellonopeuksilla, jotka otetaan kellosignaaleista.

Kellotulot

MSP430 G2 sarjan (Value Line) mikrokontrollereilla on vähintään kolme ja enintään neljä eri kellotuloa valittavissa. Kellojen määrä riippuu mikrokontrollerista, mutta jos tarkastellaan MSP430G2231 mikrokontrollerin kelloja, niin käytössä on neljä kellotuloa. Nämä ovat LFXT1CLK, DCOCLK, VLOCLK ja ulkoinen kellosignaali.

Kellotulojen pinneihin voidaan kytkeä resonaattori, kellokide tai jokin muu oskillaattori, jonka taajuus on pienempi kuin 16 MHz. Kellotulo voi olla myös mikrokontrollerin sisäinen oskillaattori. Seuraavassa esitellään käytettävissä olevat kellotulot.

LFXT1CLK, Low Frequency XT1 Clock

Tämä kello on tarkoitettu käytettäväksi ulkoisella kellokiteellä, joka tulee launchpadin mukana. Kellokide tulee kytkeä mikrokontrollerin XIN ja XOUT -pinneihin, jolloin tämän kellomoduulin kellosignaaliksi tulee ACLK (Auxiliary Clock). ACLK kello voidaan kytkeä pois päältä Status rekisteristä bitillä OSCOFF. Tähän tuloon on liitetty jo aiemmin Launchpadin mukana tullut 32768 Hertsin kellokide.

DCOCLK, Digitally Controlled Oscillator Clock

Nimensä mukaisesti tämä kello on digitaalisesti ohjattu oskillaattori. Mikrokontrolleri ohjaa tätä kelloa sisäisesti eikä käyttäjän tarvitse tämän toimintaan puuttua. Tätä kelloa voidaan kuitenkin kalibroida, jos niin halutaan tehdä. Kalibroitu DCOCLK antaa muutaman prosentin tarkkuudella oikean taajuuden. DCOCLK:in kellotaajuutta voidaan muuttaa, mikä vaikuttaa olennaisesti suorituskykyyn ja virrankulutukseen. Mikrokontrollerin CPU toimii DCOCLK:in taajuudella ja taajuus on käynnistyshetkellä noin 1,1 MHz ilman kalibrointia taikka muita säätöjä. DCOCLK voidaan kytkeä pois päältä, jolloin prosessori siirtyy lepomoodiin. MSP430G2231 mikrokontrollerin suurin datalehdessä luvattu kellotaajuus on 16 MHz ja pienin taajuus millä kontrolleri vielä kykenee toimimaan on VLO:n kellotaajuus (n. 12 kHz).

VLOCLK, Very Low Frequency Oscillator Clock

Tämä sisäinen kello mahdollistaa mikrokontrollerin toiminnan n. 12 kHz:n nopeudella. Tämä tarkoittaa tietenkin todella vähäistä virrankulutusta ja tätä voi käyttää esimerkiksi ns. sleepmode -tilassa, kun mikrokontrollerin ei tarvitse tehdä mitään tai suorittaa vain pieniä tarkistuksia. Toiminnaltaan tämä on kuitenkin epävakaampi ja herkempi lämpötilamuutoksille, joten esimerkiksi ajan ottamiseen tai muihin aikakriittisiin sovelluksiin tätä ei suositella.

Ulkoinen kellosignaali porttipinniin P1.0 (ACLK)

Mikrokontrolleria voidaan myös kellottaa ulkoisesti vaikkapa signaaligeneraattorista. Tällöin kellosignaali ohjataan P1.0 pinniin, joka on MSP430G2231 mikrokontrollerilla nasta numero 2. Tällöin 'sisäänottokello' on LFXT1CLK:n tapaan myös ACLK (Auxilary Clock), jolloin vapautuu käyttöön XIN ja XOUT -pinnit. Tällainen käyttötapaus voisi olla hyödyllinen silloin, kun kahta tai useampaa mikrokontrolleria haluttaisiin kellottaa samalla kellolla samasta kellolinjasta (esimerkiksi synkroninen SPI/I2C -väylä). Mikrokontrollerin CPU:ta ei porttipinnillä voi kuitenkaan kellottaa, vaan sen kello on otettava muualta.

Kellosignaalit

Mikrokontrollerin CPU voi käyttää ainoastaan LFXT1CLK, VLOCLK, XT2CLK (jos piiriltä löytyy) tai DCOCLK kellotuloja toimintaansa. Sen sijaan mikrokontrollerin eri moduuleiden/laitteiden, kuten SPI/I2C/UART/ADC/DAC kellot otetaan kellosignaaleilta, joita mikrokontrollerissa on tarjolla kolmea erilaista. Seuraavassa tarkemmin kellosignaaleista:

  • ACLK: Auxiliary Clock. ACLK voidaan valita silloin, kun kellokide on kytketty mikrokontrolleriin. Vaihtoehtoisesti tätä kellosignaalia voidaan käyttää myös sisäisellä VLOCLK:lla. ACLK:n kellotuloksi voidaan siis valita LFXT1CLK tai VLOCLK. ACLK voi toimia kellosignaalina mikrokontrollerin eri laitteille (sisäisille) ja sitä voidaan jakaa 1,2,4 ja 8:lla.
  • MCLK: Master Clock. MCLK:n kellotuloksi voidaan valita LFXT1CLK, VLOCLK, XT2CLK tai DCOCLK. MCLK toimii kellosignaalina mikrokontrollerin CPU:lle ja muulle sisäiselle järjestelmälle. Tämäkin kellosignaali on jaettavissa 1,2,4 ja 8:lla.
  • SMCLK: Sub-Main Clock (Sub-system Main Clock): SMCLK:n kellotuloksi voidaan valita samat kuin MCLK:lla ja se on myös jaettavissa 1,2,4 ja 8:lla. SMCLK voidaan valita kellosignaaliksi mikrokontrollerin eri laitteille, mutta ei CPU:lle.

Kuten huomataan, on käytössä monipuolinen valikoima eri kellosignaaleita joita käyttäjällä on mahdollisuus hyödyntää. Lyhyesti yhteenvetona yllä olevista voidaan sanoa, että CPU toimii aina MCLK:n taajuudella ja sisäiset laitteet (peripherals) MCLK, SMCLK tai ACLK taajuudella.

Kun kellomoduulin signaalit ja tulot on selvillä, on aika perehtyä rekistereihin ja moduulin asetuksiin, seuraavassa siis niistä.

MSP430G2231 DCO:n rekisterit

Yksi tärkeimmistä rekistereistä, joita kannattaa oppia käyttämään on ehdottomasti kellotaajuuksien asetuksiin liittyvät rekisterit. CPU:n kellotaajuuden asettaminen on yksi tärkeimmistä tehtävistä mikrokontrollerin ohjelmassa, joten se on syytä olla hallinnassa. Vaikka asia on tärkeä ja saattaa kuulostaa pelottavan vaikealta, ei se oikeastaan sitä ole, kun asian vain hoksaa. Lähdetäänpä tarkastelemaan asiaa siitä hetkestä, kun mikrokontrollerille laitetaan virrat päälle.

Kun MSP430G2231 mikrokontrolleri käynnistyy, niin kellomoduulin rekisterien DCOCTL ja BCSCTL1 arvot ovat oletuksena asetettu niin, että CPU:n kellotaajuus on noin 1,1 MHz. Tällöin myös MCLK ja SMCLK toimivat DCO:n taajuudella ja ACLK ottaa automaattisesti käyttöönsä LFXT1CLK-tulon (eli XIN/XOUT pinnit).

DCOCTL on DCO:n (Digitally Controller Oscillator) ohjausrekisteri ja BCSCTL1 on kellomoduulin (Basic Clock System) ohjausrekisteri. Näiden rekisterien arvoja muuttamalla voidaan vaikuttaa DCO:n kellotaajuuteen ja kun vaikutetaan DCO:n kellotaajuuteen vaikutetaan automaattisesti CPU:n kellotaajuuteen. Kuulostaa monimutkaiselta, mutta koitetaan selventää tätä asiaa.

Alla oleva kuva, joka on otettu käyttöoppaasta (kannattaa ottaa se NYT esille), näyttää kuinka DCO ja RSEL -bitit korreloivat keskenään DCO:n taajuudessa:

Kuvasta voidaan huomata, että valikoima käytettävissä olevista taajuuksista asettuu kukin hieman toistensa kanssa päällekkäin. Tämä tarkoittaa siis sitä, että siinä missä RSEL7 arvoalue loppuu, niin RSEL8 arvoalue alkaa jne.

Kuulostaa edelleen hankalalta, joten ilmaistaan asia näin. Ajattele, että DCO-bittien arvo on porras, joka nostaa CPU:n taajuutta tietyn askeleen. RSEL-bitit puolestaan määrittelevät kerroksen, missä askel portaalle otetaan. Näin ollen ylemmällä kerroksella taajuus on suurempi ja alemmalla kerroksella taajuus on pienempi. Tämä tarkoittaa sitä, että DCOCTL-rekisteriin kirjoitettava DCOx arvo nostaa CPU:n kellotaajuutta maltillisemmin kun taas BCSCTL1 -rekisteriin kirjoitettavien RSEL-bittien asettaminen vaikuttaa kellotaajuuteen radikaalimmin.

Esimerkki kellojen asetuksesta

Kun teoriaa on taas jonkun verran tullut esille, voidaan tutkia ja kokeilla miten saadaan käytännössä jokin haluttu kellotaajuus tehtyä. Tällä kertaa emme tarvitse ulkopuolista kytkentää, vaan voimme tyytyä Launchpadilta löytyviin ledeihin.

Tässä esimerkissä asetetaan mikrokontrollerin CPU:n kellotaajuus kuuteen eri kellotaajuuteen. Esimerkissä hyödynnetään mikrokontrollerille tallennettuja kalibrointiarvoja, jotka on tallennettu INFOA flash-muistialueeseen (Flash-muistin lukemista ei tarvitse säikähtää, se hoituu tässä esimerkissä automaattisesti). Tässä hyödynnetään lisäksi edellisissä esimerkeissä esille tullutta timeria viiveiden tekemiseen. Seuraavassa siis ohjelman koodi.

#include <msp430g2231.h> 

unsigned int ms=0; // globaali muuttuja joka pitää millisekunnit tallessa 
void ms_viive(unsigned int odotusAika); // aliohjelman esittely 

void main(void)
{
     unsigned int i=0, hyppy=1, cpuLaskuri=0;
     WDTCTL = WDTPW + WDTHOLD; // watchdog pysäytys 
     P1DIR |= BIT0+BIT6; // LED1 ja LED2 lähdöksi 
     P1OUT &= ~(BIT0+BIT6); // LED1 ja LED2 pois päältä 

     BCSCTL3 = XCAP_3; // asetetaan 12,5 pF:n sisäiset kondensaattorit aktiiviseksi
     CCTL0 = CCIE; // sallitaan vertailijan keskeytys 
     ​// asetetaan jakaja 0, laskentamoodi vertaileva ja ACLK-kello. 
     TACTL |= ID_0 + TASSEL_1;  
     CCR0 = 33; // ladataan vertailurekisterin arvo 1 millisekunnin keskeytystä varten
     
     __enable_interrupt(); // sallitaan globaalit keskeytykset

     while(1)
     {
          switch(hyppy)
          {
               case 1: // Kalibroitu 1 Mhz:n arvo
                    BCSCTL1 = CALBC1_1MHZ; // Asetetaan esiohjelmoidut kalibrointiarvot 
                    DCOCTL = CALDCO_1MHZ;
                    break;
               case 2: // Kalibroimaton ~2,3 MHz 
                    BCSCTL1 = 8; // Asetetaan RSEL = 8, DCO = 3, MOD = 0 
                    DCOCTL = DCO1 + DCO2;
                    break;
               case 3: // Kalibroimaton ~4,25 MHz 
                    BCSCTL1 = 11; // Asetetaan RSEL = 11, DCO = 3, MOD = 0 
                    DCOCTL = DCO1 + DCO2;
                    break;
               case 4: // Kalibroimaton ~7,8 MHz 
                    BCSCTL1 = 13; // Asetetaan RSEL = 13, DCO = 3, MOD = 0 
                    DCOCTL = DCO1 + DCO2;
                    break;
               case 5: // Kalibroimaton ~12 MHz (8,6 - 13,9 MHz)
                    BCSCTL1 = 14; // Asetetaan RSEL = 14, DCO = 3, MOD = 0
                    DCOCTL = DCO1 + DCO2;
                    break;
               case 6: // Kalibroimaton ~15,25 MHz
                    BCSCTL1 = 15; // Asetetaan RSEL = 15, DCO = 3, MOD = 0
                    DCOCTL = DCO1 + DCO2;
                    hyppy = 0;
                    break;
               default:
                    break;
     }
     cpuLaskuri = 0;
     for(i=0;i<20;i++) // Vihreän ledin vilkutus 20 kertaa 
     {
          P1OUT |= BIT6; // vihreä led päälle 
          for(cpuLaskuri = 0; cpuLaskuri < 60000; cpuLaskuri++); // huomaa puolipiste
          P1OUT &= ~BIT6; // vihreä led pois päältä 
          for(cpuLaskuri = 0; cpuLaskuri < 60000; cpuLaskuri++);
     }
     hyppy++; // aiheuttaa etenemisen switch-case lauseessa
     s_viive(1000); // 1 sekunti viivettä 
     }
}

void ms_viive(unsigned int odotusAika)
{
     ms=0; // nollataa millisekuntilaskuri
     TACTL |= MC_1; // käynnistetään laskuri 
     P1OUT |= BIT0; // sytytetään punainen LED
     // odotetaan että millisekuntilaskuriin
     // tulee suurempi arvo kuin odotusAika -muuttujaan 
     while( ms < odotusAika); // kulutetaan CPU-aikaa 
     TACTL &= ~MC_3; // pysäytetään laskuri, nollataan varmuuden vuoksi kaikki bitit 
     P1OUT &= ~BIT0; // sammutetaan punainen LED 
}

// huomaa että tässä tapauksessa keskeytysvektori on TIMERA0
#pragma vector = TIMERA0_VECTOR  
__interrupt void TimerA0(void )
{
     ms++; // millisekunnin välein tapahtuva keskeytys 
}

Ohjelman kulku ja alustukset

Kun ohjelma käynnistyy, suoritetaan ensin alustukset. Nämä ovat jo tuttua kauraa ja alustuksista ihmetystä voinee herättää CCR0 rekisterin arvo. Kyseiseen rekisteriin ladataan luku 33, koska tällä luvulla saadaan tuotettua yhden millisekunnin timer-keskeytys. Luku saadaan kun jaetaan XT1-pinneihin kytketyn kellokiteen taajuus 1000:lla ja pyöristetään lähimpään tasalukuun.

Jokaisessa switch-lauseen case-lohkossa asetetaan Basic Clock System Control -rekisteriin (BCSCTL1) taajuusalue ja DCO Control -rekisteriin (DCOCTL) DCOCLK taajuus. Ainoastaan 1 MHz:n kellotaajuus on kalibroitu ja nämä kalibroidut arvot on mikrokontrollerin valmistustehtaalla esiohjelmoitu INFOA-muistialueeseen. Kalibroidulla taajuudella saadaan ainakin noin 2-3 %:n tarkkuus DCOCLK-kellosignaalille.

Ihmetystä voi herättää se, mistä arvot 8,11,13,14 ja 15 (RSEL) on otettu valintarakenteen muihin lohkoihin, kun ladataan rekisteriin BCSCTL1 eri arvoja.

Mikrokontrollerin taajuutta muutetaan manipuloimalla Basic Clock Module+ -rekisterien DCOCTL ja BCSCTL1 arvoja. DCOCTL-rekisteriin asetetaan ikäänkuin DCO:n perustaajuus, jonka tasoa muutetaan BCSCTL1 rekisteriin ladattavilla RSEL-biteillä.

BCSCTL1 -rekisteriin ladattavat RSEL-arvot määrittävät sen, mikä on DCO:n taajuusalue (Range). Nämä tiedot on saatu mikrokontrollerin datalehdestä, mistä on otettu kuvakaappaus DCO ja RSEL -arvojen taulukosta alle (sivulta. 23):

Kun kellotaajuus kasvaa, niin CPU:n suorituskyky (tai sekunnissa suoritettavien käskyjen määrä) kasvaa myös ja tämä voidaan huomata vihreän ledin vilkutuksen nopeutumisena. Ohjelma sytyttää punaisen ledin sekunnin ajaksi, kun kellotaajuutta kasvatetaan. Kun kellotaajuus on lähes suurin mahdollinen (16 MHz), niin kellotaajuus asetetaan takaisin 1 MHz:iin. Ohjelman suoritus on selvyyden vuoksi myös kuvattu alla olevalle videolle:

Tämä riittänee kelloista tällä kertaa. Uskon, että tässä on pureskeltavaa hetkeksi aikaa. Seuraavan osan aiheena on AD-muuntimen käyttö ja kuinka esimerkiksi potentiometrin arvoa voidaan lukea muuttujaan talteen ja miten jännite saadaan laskettua. Tämä toimii jatkossa edellytyksenä mm. moottorinohjaus sovellukselle, missä moottorin pyörimisnopeutta säädetään suoraan suhteessa AD-muuntimen arvoon.