27. MSP430G2231 SPI-väylä

Tässä osassa perehdytään siihen, miten SPI-väylä otetaan käyttöön esimerkiksi MSP430G2231 mikrokontrollerilla ja kuinka tietoa saadaan lähetettyä. Esimerkissä ei kuitenkaan tarvita orjalaitetta, sillä MISO ja MOSI linjat kytketään mikrokontrollerilla ristiin. Tällöin saadaan luettua sama tieto mikä tuli lähetettyä ja saadaan testattua tekemämme ohjelma varsin helposti.

Aikaisemmin on ollut puhetta sarjamuotoisista väylistä, esimerkiksi sarjaportista jota käsiteltiin osissa 19 ja 20. Näissä kappaleissa perehdyttiin siihen, kuinka mikrokontrollerin sarjaportti saadaan lähettämään tietokoneelle dataa. Sarjaportista ja sarjaväylästä voi muistin virkistykseksi vilkaista sarjaliikenne -sivua, missä aiheeseen pureudutaan syvällisemmin ja SPI-väylästä yleisesti on kirjoitettu enemmän SPI-väylä sivulla. Alle on kuitenkin kirjoitettu muutamia perusasioita SPI-väylästä.

SPI-väylä

Kuvan lähde wikipedia.

SPI-väylän nimitys tulee englannin kielen sanoista Serial Peripheral Interface tai löysästi suomennettuna sarjamuotoinen oheislaiteväylä ja se on Motorolan nimeämä tiedonsiirtostandardi.

SPI-väylä on synkronoitu sarjaväylä, missä tieto liikkuu bitteinä kahden tai useamman laitteen välillä. Synkronoitu väylä tarkoittaa tässä tapauksessa sitä, että bittien ajoitukset on synkronoituja, eli väylän toimintaan liittyy olennaisesti kellosignaali, joka "tikittää" lähettävän laitteen tahdissa. SPI-väylä on synkronisuuden lisäksi kaksisuuntainen (engl. full duplex) eli lähettävä laite yhtäaikaa lukee ja kirjoittaa tietoa väylälle.

SPI-väylän käyttämiseen tarvitaan vähintään kolme signaalia, jotka ovat: kellopulssi ja luku- että kirjoitussignaalit. Näiden kolmen signaalin lisäksi kuitenkin käytetään vielä kommunikoitavan piirin valintaan yhtä signaalia, joka on "orjan valinta" -signaali SS.

Virallisemmat nimitykset luetelluista signaaleista on listattu alle:

SPI-väylän signaalit (suluissa vaihtoehtoiset nimeämistavat):
- SCLK (SCK,CLK) = Serial Clock = Väylän kellosignaali
- MOSI (SDI,DI,SI)= Master Output, Slave Input = Isäntälaitteen datalinja ulos ja orjalaitteen vastaanottolinja sisään.
- MISO (SDO,DO,SO)= Master Input, Slave Output = Isäntälaitteen lukulinja sisään ja orjalaitteen lähetyslinja ulos.
- SS (nCS,CS,nSS,STE,CE) = Slave Select = 0-aktiivinen orjalaitteen valintasignaali (yksilöllinen kullekin laitteelle).

Väylällä voi kommunikoida vain yksi isäntälaite kerrallaan, mutta tietoa voidaan välittää samalla kertaa yhdelle tai useammalle orjalaitteelle. Isäntälaite lisäksi määrittelee, mille laitteelle ja millä nopeudella se minäkin ajanhetkenä kommunikoi (orjalaitteen valintaan käytetään SS-signaalia). Mikäli väylällä on vain yksi orjalaite, voidaan SS-signaali kytkeä kiinteästi laitteiston maahan, jolloin orjalaitteen luku- ja kirjoitusrekisterit ovat koko ajan aktiivisia. Tämä ei kuitenkaan ole pakollista.

Tarvittavat osat

Esimerkkien totettamiseen tarvitset seuraavia osia:

Esimerkki 1:

  • Hyppylanka

Esimerkki 2:

  • 5 kpl Johtimia
  • 2 x Launchpad + MSP430G2231 mikrokontrolleri (myös MSP430G2211 käy)

MSP430G2231 USI-moduuli

USI on MSP430G2x31 ja MSP430G2x32 mikrokontrollereista (ja muistakin) löytyvä lisälaite/moduuli, minkä avulla sarjamuotoinen kommunikointi tapahtuu. USI tulee englannin kielen termistä Universal Serial Interface ja tämä moduuli soveltuu käytettäväksi SPI-, I2C ja UART -väylien kanssa.

USI muodostuu 8- tai 16 -bittisestä siirtorekisteristä, mutta väylälle voi kirjoittaa esimerkiksi 7 tai 13 bittiä jos niin haluaa tehdä. USI-moduuli helpottaa koodin kirjoittamista SPI- ja I2C-väylän käyttämiseksi ja se tarjoaa myös keskeytyksiä ohjelmoijan käyttöön.

USI moduuli tukee seuraavia ominaisuuksia:
- 3-wire SPI
- I2C
- Muuttuva datapituus
- Slave/Orja toiminnallisuus LPM4-tilassa (osassa 21 on kerrottu LPM-tiloista)
- MSB tai LSB ensin, valittavissa ohjelmallisesti
- START ja STOP tilan tunnistus I2C-moodissa automaattisella kellokontrollilla
- I2C väylän vikatilan tunnistus isäntätilassa (Arbitration lost)
- Ohjelmoitava kellosignaali
- Kellon vaiheen ja polariteetin määritys ohjelmallisesti

I2C-väylästä tulee juttua joskus myöhemmin, joten nyt ei kannata siitä välittää, vaikka se on tekstissä mainittu. Tässä osassa keskitytään vain SPI-väylään.

USI-moduulin rekisterit

USI-moduulin rekisterit säätävät, käytetäänkö laitetta SPI vai I2C väyläisenä ja mitkä ovat väylän asetukset. Mikäli USI-moduulia halutaan käyttää UART-väylän korvikkeen, täytyy se asettaa SPI väyläiseksi. Tällöin standardeja baudinopeuksia voi olla kuitenkin hieman hankala asettaa ilman sopivaa kellokidettä, mutta ei tästä nyt tämän asian yhteydessä enempää.

MSP430G2231 mikrokontrollerilla on mahdollista säätää USI-moduulin kuutta rekisteriä, jotka ovat USICTL0 (ohjausrekisteri 0), USICTL1 (ohjausrekisteri 1), USICKCTL (kellosignaalin ohjausrekisteri), USICNT (laskurirekisteri), USISRL (LSB-siirtorekisteri) ja USISRH (MSB-siirtorekisteri).

Jokaiseen rekisteriin voidaan kirjoittaa ja niistä voidaan lukea arvoja. Yksi tärkeä asia on kuitenkin muistettava, ennen kuin säätöjä USI-moduulin rekistereihin tehdään ja se on moduulin resetointi. Moduuli täytyy siis asettaa reset-tilaan USICTL0 rekisterin USISWRST -bitillä, ennen kuin muutoksia asetuksiin tehdään.

Alle on lueteltu lisää, mitä nämä edellä mainitut rekisterit pitävät sisällään ja kaikki olennaiset rekisteribitit SPI:n kannalta on otettu tarkastelun alle.

USICTL0

7 6 5 4 3 2 1 0
USIPE7 USIPE6 USIPE5 USILSB USIMST USIGE USIOE USISWRST

USICTL0 rekisterissä ylimmäisinä ovat bitit USIPE7 - USIPE5, joilla säädetään mitä porttipinnejä mikrokontrollerilta käytetään. Jos käytössä on MSP430G2231 (kuten esimerkissä), niin tämän rekisterin kaikki USIPEx -bitit täytyy aktivoida. Tällöin MSP430G2231 mikrokontrollerin SPI-väyläpinnit ovat seuraavat:

SCLK = P1.5 (USIPE5)
MOSI = P1.6 (USIPE6)
MISO = P1.7 (USIPE7)

Pinnimääritysten lisäksi USICTL0 rekisteriin asetetaan tieto siitä, lähetetäänkö LSB vai MSB bitti ensin. LSB-bitti lähetetään ensimmäisenä väylälle silloin, kun USILSB -bitti on aktivoituna tästä rekisteristä. Datan siirron salliminen väylälle tehdään USIOE -bitillä, joka luonnollisesti asetetaan päälle kun väylää halutaan käyttää.

Edellisten lisäksi USICTL0 rekisteristä valitaan onko laite SPI-väylässä isäntä- vai orjalaite. Tämä tehdään bitillä USIMST ja kun kyseinen bitti on asetettu, niin laite toimii SPI-isäntälaitteena (Master).

USICTL0 rekisterin tärkeimmät bitit SPI-väylän käytössä ovat siis:

USIPEx - datalinjojen valinta
USILSB - LSB vai MSB ensin (1 = LSB ensin)
USIMST - Master vai Slave (1 = Master)
USIOE - dataväylän tiedonsiirron sallinta (1 = salli)
USISWRST - USI-moduulin resetointibitti (1 = reset)

USICTL1

7 6 5 4 3 2 1 0
USICKPH USII2C USISTTIE USIIE USIAL USISTP USISTTIFG USIIFG

USICTL1 rekisterissä on SPI-väylän kannalta olennaisia bittejä vähemmän kuin USICTL0 -rekisterissä ja tätä rekisteriä tarvitsee "nyplätä" enemmän I2C-väylän kanssa. Muutama tärkeä bitti on kuitenkin syytä mainita.

Tämän rekisterin USICKPH -bitillä säädetään SPI-väylän kellosignaalin vaihetta (phase). Tämän bitin asettamisella saadaan asetettua SPI-väylä joko moodiin 2 tai 4 .

Lisäksi tämä rekisteri pitää sisällään USI-moduulin keskeytyslipun USIIFG, joka asettuu "1" -tilaan silloin, kun kaikki bitit SPI-väylällä on lähetetty.

USICTL1 rekisterin tärkeimmät bitit SPI-väylän käytössä ovat siis:

USICKPH - kellosignaalin vaihe (toimintatilan/moodin valinta)
USIIFG - keskeytyslippu, joka asettuu kun kaikki bitit on lähetetty

USICKCTL

7 6 5 4 3 2 1 0
USIDIVx USISSELx USICKPL USISWCLK

USICKCTL rekisterin biteillä vaikutetaan USI-moduulin kellosignaaliin. Moduulia voidaan kellottaa muutamasta eri signaalilähteestä valitsemalla bitit USISSEL_0 - USISSEL_7 tai ohjelmallisesti "käsin" USISWCLK -bitillä. Tämän rekisterin avulla valitaan myös kellosignaalin polariteetti, eli onko kellosignaali "idle"-tilassa "1" vai "0" USICKPL -bitillä. Kellosignaalia voidaan myös jakaa kahdeksalla eri jakajan arvolla USIDIV_0 - USIDIV_7, jolloin USI-moduuli on mahdollista saada toimimaan hyvin monella eri kellotaajuudella.

USICKCTL rekisterin tärkeimmät bitit SPI-väylän käytössä ovat siis:

USIDIV_x - kellosignaalin jakajan arvo (1,2,4,8,16,32,64,128)
USISSEL_x - kellosignaalin valinta
USISWCLK - kellosignaalin manuaalinen kellotus
USICKPL - kellosignaalin polariteetin valinta

USICNT

7 6 5 4 3 2 1 0
USISCLREL USI16B USIIFG USICNTx

USICNT rekisterin biteillä vaikutetaan siihen, montako bittiä väylälle kirjoitetaan ja käytetäänkö 8- vai 16-bitin tiedonsiirtoa. Kun väylää halutaan käyttää 16-bittisenä tai minä tahansa yli kahdeksan bittisenä, niin USI16B-bitti asetetaan päälle. USIIFGCC-bitillä vaikutetaan siihen, aiheutuuko USIIFG-lipun nollaus automaattisesti kun USICNT-rekisteriä päivitetään. Kun SPI-väylälle halutaan kirjoittaa, niin USICNTx -bitteihin päivitetään tieto siitä, kuinka monta bittiä siirtorekistereistä USISRL/USISRH siirtyy väylälle.

USISRL ja USISRH

7 6 5 4 3 2 1 0
USISRx

USISRL ja USISRH ovat käytännössä siirtorekistereitä, joista tieto väylälle siirretään ja mihin se sieltä myös luetaan. Ennen SPI-väylän siirron käynnistämistä täytyy näihin rekistereihin kirjoittaa se arvo, joka halutaan väylälle lähettää.

SPI-väylän käyttöönotto MSP430G2231 mikrokontrollerilla on kuitenkin kaikista esitellyistä rekistereistä huolimatta sangen helppoa, ja näitä kaikkia esiteltyjä asioita ei tarvitse ulkoa opetella. Riittää että ymmärtää edes suurinpiirtein, mitä rekisteriasetukset vaikuttavat SPI:n toimintaan.

Esimerkki 1: SPI-väylän käyttäminen

Seuraavassa on tehty yksinkertainen esimerkkikoodi, missä SPI-väylälle kirjoitettu tieto tulostetaan näkyville sarjaportin avulla. Ohjelmassa lähetetään tieto itselle ns. loop-back -menetelmällä, joka käytännössä tarkoittaa lähetys- ja vastaanottonastojen yhdistämistä toisiinsa johtimella. Alla olevassa kuvassa nähdään käytetty loop-back johdin, joka kytketään pinnien P1.7 (SDI tai MISO) ja P1.6 (SDO tai MOSI) väliin.

SCLK-nastaa ei tarvitse kytkeä, sillä mikrokontrollerin kellosignaali on automaattisesti synkroninen tällaisessa kytkennässä (eikä sitä kyllä voisikaan kytkeä mihinkään tässä tapauksessa).

Ohjelmakoodi on jaettu omiin kansioihinsa ja projektin päätasolla nähdään vain main.c tiedoston koodi. USI-kansiossa on USI-moduulin alustukseen ja SPI-väylän käyttämiseen tarvittavat InitUSI() ja SPI_SendByte(...) -funktiot.

Launchpad-kansiossa on launchpadin käyttämiseen tarkoitettu launchpad.c ja .h-tiedostot sekä UART-koodit, joiden avulla sarjaporttiin voidaan kirjoittaa. UART.h -tiedostosta on lisäksi konfiguroitavissa haluaako käyttää lähetystä vai vastaanottoa vai molempia.

Alla olevassa koodin pätkässä on näytetty vain pääohjelman koodi, mutta loput koodista voit tutkimista varten ladata alla olevasta linkistä:

Lataa tämän esimerkin CCSv5 projekti tästä.

int main(void)
{
      unsigned char byte = 'A';
      WDTCTL = WDTPW | WDTHOLD;

      InitLaunchpad();
      TimerA_UART_pinInit();
      TimerA_UART_init();

      InitUSI();

      while(1)
      {
            if(SWITCH_PRESSED)
            {
                  LED_GREEN_ON;
                  TimerA_UART_tx(SPI_SendByte(byte++));
            }
            else
            {
                   LED_RED_ON;
            }
            TimerA_UART_print("Ready! ");
            TimerA_Delay(1);
      }
}

Ohjelman toiminta

Ohjelma tulostaa virtuaalisarjaporttiin sekunnin välein tekstin "Ready!" ja tutkii onko nappia S2 painettu. Mikäli nappi on tutkimisen hetkellä pohjassa, niin kirjain lähetetään SPI-väylälle. Koska olemme kytkeneet loop-back johtimen RX ja TX -nastan väliin (tai siis MISO ja MOSI väliin), niin mikro-ohjain vastaanottaa lähetetyn kirjaimen ja tulostaa vastaanotetun kirjaimen sarjaporttiin.

Ohjelman toimintaa voi seurata sarjaportista, kun avaa terminaaliohjelmaan oikean COM-yhteyden auki. Alla olevassa kuvassa ohjelma on ollut kaksi sekuntia päällä, kunnes nappi on laitettu pohjaan neljäksi sekunniksi. Tuona aikana on lähetetty kirjaimet A,B,C ja D SPI-väylälle sekä vastaanotettu ne sieltä ja tulostettu sen jälkeen sarjaporttiin.

Voit testata ohjelman toimintaa irroittamalla loop-back johtimen, jolloin huomaat ettei SPI-väylältä vastaanoteta mitään tai korkeintaan tyhjiä ruutuja. Heti kun kytket johtimen takaisin ja painat nappia uudestaan, alkaa SPI-vastaanottokin toimia ja lähetetyt kirjaimet näkyvät ruudulla.

Koska tämmöisessä yhden laitteen mielipuolisessa kommunikointiasetelmassa ei ole oikeastaan mitään jännittävää, niin tehdään aiheesta seuraavaksi toinen esimerkki.

Esimerkki 2: SPI-väylä kahden Launchpadin välillä

SPI-väylä on, kuten sanottu yleensä vähintään kahden laitteen välinen kommunikointiväylä. Tämän vuoksi parempi esimerkki saadaan toteutettua kahdella eri laitteella, esimerkiksi kahdella Launchpadilla.

Sovelluksen toiminnan kuvaus

Seuraavassa esimerkissä toinen laitteista toimii isäntälaitteena (master) ja toinen orjalaitteena (slave). Laitteet kytketään toisiinsa niin, että MOSI (SDO) ja MISO (SDI) nastat asetetaan ristikytkentään ja kellosignaali SCLK kytketään yhteen. Tämän lisäksi otetaan käyttöön neljäs CS-nasta, jolla signaloidaan orjalaitteelle, että isäntälaite aloittaa datan lähetyksen.

Orjalaite on vähävirtaisessa tilassa ja reagoi ainoastaan vastaanottaessaan oikean komentoviestin. Oikeaan komentoviestiin kuitataan isäntälaitteelle "OK!" ilman lainausmerkkejä. Mikäli laite ei tunnista komentoa, laite ei vastaa ollenkaan. Isäntälaite tulostaa lähetetyn komennon ja vastaanotetun kuittauksen sarjaporttiin. Mikäli isäntälaite ei saanut kuittausta komentoon, sytytetään punainen LED virheen merkiksi.

Isäntälaite tarkastelee näppäimen painallusta sekunnin välein ja tulostaa "Master Ready!" sarjaporttiin tietokoneelle.

Kommunikointiprotokolla

Laitteiden välille rakennetaan AT-komentoja muistuttava kommunikointiprotokolla orjalaitteen punaisen ledin ohjaamiseksi SPI-väylän kautta. Taulukkoon on koottu kommunikointiprotokollan tiedot (M = master, S = slave):

Komento Suunta Toiminto/Kuvaus Kuittaus (S -> M)
LED1=ON + enter M -> S Sytyttää laitteen LED1:n OK!

Kun kommunikointiprotokolla on selvillä, voidaan esitellä isäntälaitteen koodi.

Isäntälaitteen toiminta

Isäntälaitteen koodi on periaatteeltaan aika samanlainen kuin esimerkissä 1, mutta rivimäärällisesti suurempi ja logiikaltaan hieman monimutkaisempi.

Isäntälaite tarkkailee painonappia, joka Launchpadilla on kytkin S2. Kun painonappia on painettu, isäntälaite lähettää SPI_Write(..) -funktiolla komennon "LED1=ON!" (+ null-merkki) väylälle. Tämän jälkeen odotellaan hieman ja siirrytään lukufunktioon SPI_Read(..). Lukufunktio toimii itse asiassa SPI-isäntälaitteessa yleensä aina niin, että väylälle kirjoitetaan nollaa. Tällöin ainoastaan kellosignaali naksuttaa ja orjalaite siirtää bitit MISO-linjalle (eli  isäntälaitteen SDI-linjaan).

Luetut tavut tarkistetaan strcmp() -funktiolla, joka vertailee kahta merkkijonoa keskenään. Mikäli orjalaitteen kuittausviesti oli oikein, kirjoitetaan sarjaporttiin "Command OK!" -viesti. Jos kuittaus puolestaan oli väärin tai sitä ei ollut ollenkaan, niin punainen LED sytytetään Launchpadilta ja sarjaporttiin lähetetään viesti "Error".

Alla on pääohjelman koodi suomeksi kommentoituna ja joiltain osin lyhennettynä. Kokonaisuudessaan koodit löytyvät alempaa, kappaleesta Ohjelmien lähdekoodit zip-paketista.

void main(void)
{
     unsigned char receiveBuffer[10] = {0};
     unsigned char wait = 1;
     // ....
     // alustukset
     // ...
     // ...
     while(1)
     {
          // funktio kirjoittaa sarjaporttiin "orja valmis"
          TimerA_UART_print("Master Ready! ");

          // tarkastetaan onko nappia painettu
          if(SWITCH_PRESSED)
          {
               // tulostetaan toiminto mikä aiotaan tehdä
               TimerA_UART_print("Set LED1 on ");
               // lähetetään komento SPI-väylälle
               SPI_Write((unsigned char *)"LED1=ON!");
               // odotetaan hieman, että orjalaite kerkeää käsittelemään tiedon
               __delay_cycles(50);
               // Luetaan vastaanottobufferiin orjalaitteen kuittaus (lopetus ! -merkkiin)
               SPI_Read(receiveBuffer);
               
               // vertaillaan onko vastaanotettu kuittaus oikein
               if( strcmp("OK!", (const char *)receiveBuffer) == 0)
               {
                     // komento onnistui, ilmoitetaan sarjaporttiin siitä
                     TimerA_UART_print("Command OK! ");
                     // odotetaan vain yksi sekunti
                     wait = 1;
                     // sammutetaan error led
                     LED_RED_OFF;
               }
               else
               {
                     // sytytetään isäntälaitteen punainen led virheen merkiksi
                     LED_RED_ON;
                     // Tulostetaan vastaanotettu tieto ja virhe-teksti
                     TimerA_UART_print((char *) &receiveBuffer[0]);
                     TimerA_UART_print(" Error ");
                     // odotetaan hieman pidempään virheen sattuessa
                     wait = 3;
               }
               // tyhjennetään vastaanottobufferi väärinkäsityksien välttämiseksi
               memset((void*) receiveBuffer, 0, sizeof(receiveBuffer));
          {
          TimerA_Delay(wait);
     }
}

Orjalaitteen toiminta

Orjalaitteen koodi on vastaanoton ja lähetyksen kannalta erilainen kuin isäntälaitteen koodi, minkä vuoksi orjalaitteelle on tarvittu kokonaan oma projekti koodin tekemistä varten. Voit tarkastella orjalaitteen koodin toimintaa linkitetystä lähdekoodista ja kommenteista (englanniksi), mutta selostetaan se varmuuden vuoksi lyhyesti.

Orjalaite odottaa siis viestiä SPI-väylältä. Viestin odotus aloitetaan kutsumalla funktiota SPI_WaitData(...). Parametreina tälle funktiolle annetaan bufferi mihin data tallennetaan, ASCII-merkki mikä kertoo että viesti loppuu ja maksimipituus mikä voidaan lukea. Kun viesti vastaanotetaan, funktiosta poistutaan ja välittömästi lähetetään isäntälaitteelle OK! kuittaus komennon vastaanottamisesta.

Lopuksi verrataan strncmp() -funktiolla vastasiko lähetys oikeaa komentoa vai ei ja sytytetään punainen LED 4 sekunnin ajaksi, mikäli oikea komento vastaanotettiin. Alta nähdään orjalaitteen lyhennetty pääohjelman lähdekoodi. Jälleen kerran pinnan alla tapahtuu enemmän kuin mitä pääohjelma antaa ymmärtää. Konepellin alle kannattaa kuitenkin kurkistella ja kokeilla kuinka homma onnistuu, kun ohjelmaa muokkaa mieleisekseen.

Alla on pääohjelman koodi suomeksi kommentoituna ja joiltain osin lyhennettynä. Kokonaisuudessaan koodit löytyvät alempaa, kappaleesta Ohjelmien lähdekoodit zip-paketista.

unsigned char rxBuffer[10];   // bufferi

void main(void)
{
     // ....
     // alustukset
     // ...
     // ...
     while(1)
     {
          // funktio kirjoittaa sarjaporttiin "orja valmis"
          TimerA_UART_print("Slave Ready! ");
          
          // Datan odotus aloitetaan tästä
          SPI_WaitData(rxBuffer,'!',sizeof(rxBuffer));
          
          // Lähetetään vahvistus komennon vastaanottamisesta
          // SPI_SendOK();    // lähettää vain "OK!" 
          SPI_SlaveSend("OK!",3);   // voidaan lähettää mitä halutaan
          
          // verrataan komentoa oliko se protokollan mukainen
          if(strncmp("LED1=ON!",(const char *) rxBuffer, sizeof("LED1=ON!") - 1) == 0)
          {
               // oikea komento
               LED_RED_ON;
               TimerA_UART_print("Command received! ");
          {
          else
          {
                // tuntematon komento
               TimerA_UART_print("Unknown command! ");
          {  
          TimerA_Delay(4);
          LED_RED_OFF;
     }
}

Ohjelmien lähdekoodit

Muutama sananen lienee paikallaan näiden esimerkkien lähdekoodeista.

Ensinnäkin molemmissa ohjelmissa, sekä isäntä- että orjalaitteissa on asiat tehty hieman eri tavalla esimerkiksi merkkijonojen vertailuissa. Tämän lisäksi isäntälaitteen ohjelma ei jää odottamaan mitään tiettyä tapahtumaa, vaan suorittaa koodinsa ilman ns. "jumiluuppeja". Orjalaitteessa puolestaan jumiluupit ovat mahdollisia, esimerkiksi jos isäntälaite yrittää lähettää liian nopeasti uutta komentoa (isännän launchpadin nappia S2 painetaan ennenkuin orjan punainen LED on sammunut).

Koodien erilaisuus on tehty tavallaan tarkoituksella, jotta koodeista saisi mahdollisimman paljon uusia asioita kaivettua esille.

Esimerkiksi merkkijonojen vertailu tehdään kahdella eri C-standardin funktiolla strcmp ja strncmp, joilla on oikeastaan erona vain se, että toiselle annetaan merkkijonon pituus (jolloin merkkijonon ei tarvitse päättyä 0-merkkiin [null]) ja toiselle annetaan vain merkkijonot joita verrataan (jolloin bufferissa olevan merkkijonon tulee päättyä 0:aan).

Molempien lähdekoodien USI.c tiedostot ovat yksilölliset ja siinä olisikin hyvää harjoitusta yhdistää molemmat tiedostot yhdeksi ja määritellä esimerkiksi definellä onko kyseessä orja- vai isäntälaite (wink, wink).

Ohjelmien lähdekoodit voit tarkastelua varten ladata alta. En kuitenkaan suosittele näitä mihinkään turvakriittisiin tai varmatoimisiin laitteisiin, sillä nämä on tuotettu aika kiireessä ja niissä olisi varmasti paranneltavaa. Harrastustoimissa nämä koodit riittänevät kuitenkin aika varmasti.

Lataa isäntälaitteen SPI-väylän CCSv5 projekti tästä.

Lataa orjalaitteen SPI-väylän CCSv5 projekti tästä.

SPI-väylän analysointi ja havainnollistaminen

Todistaaksemme ja varmistaaksemme väylän oikean toiminnan, voimme tarkastella sitä vielä logiikka-analysaattorin avulla.

Alla olevassa kuvassa näkyy luettu data linjalta, kun isäntälaite kirjoittaa väylälle rimpsun "LED1=ON!" (ilman lainausmerkkejä). Logiikka-analysaattorin ohjelma osaa tunnistaa lähetetyt merkit oikein, mutta zoomauksen vuoksi olen käsin lisännyt tulkinnan kuvan ylälaitaan. Kuten kuvasta voidaan nähdä, niin jokaisen lähetetyn tavun välissä CS-linjaa käytetään "1"-tilassa. Koska orjalaite aloittaa tiedon vastaanoton CS-linjan laskevasta reunasta, täytyy CS-linjaa tällä tavalla "naksutella" ylös ja alas. CS-linja mahdollistaa myös laitteen olemisen LPM3-tilassa, sillä MSP430 mikrokontrolleri voi tästä tilasta herätä porttinastan keskeytyksestä (muutoin täytyisi käyttää LPM0-tilaa).

Yhden komennon (8 tavua) lähettämiseen kuluu tässä tapauksessa noin 943 mikrosekuntia aikaa. Vastaavasti yhden tavun lähettämiseen kuluu aikaa noin 73 mikrosekuntia. Yhden bitin lähettämiseen kuluu aikaa puolestaan tasan 1 mikrosekunti, koska SPI-väylää kellottavan isäntälaitteen USI-moduulin kellotaajuutena on sama kuin SMCLK eli 1 MHz, joka on kalibroitu laitteen sisällä. Teoreettinen lähetysnopeus tällä asetuksella on 1 Mbps (megabitti sekunnissa), mutta käytännössä tähän nopeuteen ei koskaan päästä, johtuen muista ohjelman viiveistä (ellei kasvateta kellosignaalin nopeutta). Alla olevassa kuvassa nähdään kyseinen 1 MHz:n kellosignaali SCL-linjalla ja MOSI (tai isäntälaitteen SDO) linja kun kirjain 'L' on lähetetty.

Analysaattorilla voidaan todeta, että väylä toimii kuten on suunniteltukin ja orjalaitteen vastaus näkyy samalla tavalla kuin isäntälaitteen lähetyskin, tällöin ei tietenkään MOSI-linjassa liiku dataa.

Sarjaporttitulosteet ovat ohjelmien suoritusten aikana seuraavat, kun isäntälaitteen Launchpadilta nappia S2 painetaan:

Ohjelmat on nyt testattu ja todettu toimiviksi (ainakin näiltä osin), joten voinemme siirtyä asiassa eteenpäin. Seuraavassa jaksossa tutustutaan aiemmin mainittuun I2C-väylään, missä riittää pureskeltavaa siinäkin (oikeastaan enemmän kuin SPI:ssä).