28. MSP430G2231 I2C-väylä

Tässä osassa tutustutaan I2C-väylän käyttöönottoon ja luetaan lämpötilatietoa TMP101-anturilta. Mikro-ohjain toimii väylässä isäntänä (master) ja väylässä on kiinni yksi orjalaite (slave). TMP101 anturi ohjataan antamaan hälytys, kun asetettu lämpötila-arvo ylittyy. Kun hälytys aktivoituu niin sammutetaan Launchpadin vihreä LED, mutta normaalitilassa sen annetaan loistaa iloisesti vihreänä. Eli lyhyesti sanottuna rakennetaan lämpötilavahti.

I2C-väylän käyttäminen MSP430G2231:n USI-modulin avulla on monimutkaisempaa kuin SPI-väylän käytössä ja rekisteriasetuksia joutuu tekemään enemmän. Esimerkki on pyritty sen vuoksi kirjoittamaan mahdollisimman ymmärrettäväksi ja sovellus tekemään sellaiseksi, että se on helppo ottaa käyttöön muissa MSP430G2 sarjan mikrokontrollereissa - tai no jos ei ihan niks naks mene, niin hieman muokkaamalla ainakin!

Ennen kuin siirrytään itse asiaan, on hyvä mainita muutamalla sanalla käytettävästä väylästä ja anturista.

I2C-väylä

Koska olen kirjoittanut I2C-väylästä sulautettu elektroniikka osaan jo valmiiksi juttua, niin en ala niitä tähän uudestaan yhtä laajasti kirjoittamaan. Sen sijaan olen ottanut esille muutaman olennaisen asian, jotka on mielestäni hyvä käydä läpi ennen kuin siirrytään esimerkkikoodin ja laitteiston pariin. Tarkemmin I2C-väylästä voit lukea sivulta sarjaliikenne - I2C-väylä.

I2C-laitteet ovat suosittuja helpon liittämisen ansiosta ja suhteellisen yksinkertaisen protokollan vuoksi, vaikka protokollan yksinkertaisuudesta voidaan toki olla montaa mieltä. Myös väylän fyysinen kytkentä on hyvin yksinkertainen, koska tarvitaan vain kaksi johdinta ja kaksi ylösvetovastusta.

I2C

Väylä perustuu kahteen signaaliin, joista toinen vie kellopulssia ja toinen dataa orjalaitteelle. Signaalien nimet ovat vastaavasti SCK tai SCL ja SDA tai DATA, tai jotain tähän suuntaan olevaa. Logiikkajännite voi olla 3.3V tai 5V, vaikka on mahdollista että muitakin jännitteitä käytetään, nämä ovat ne yleisimmät.

Orjalaite on mikä tahansa laite, joka ei lähetä kellopulssia kyseisenä ajanhetkenä, jolloin tietoa siirretään. Isäntälaite on väylää hallitseva laite ja hoitaa kellotuksen ja datan lukemisen sekä kirjoituksen. Orjalaitteet erotellaan toisistaan osoitteiden avulla ja osoite voi olla 7- tai 10-bittinen, yleensä se on kuitenkin 7-bittinen.

Lämpötila-anturi TMP101

TMP101 on SOT23-6 paketissa oleva I2C-väyläinen lämpötila-anturi. Anturin sisällä on 12-bittinen AD-muunnin, millä saavutetaan jopa 0,0625 Celsius asteen tarkkuus. Anturi on I2C-väylän lisäksi myös SMBus yhteensopiva, mikä on tyypillistä I2C-väylän laitteille. Anturi voidaan ohjelmoida antamaan hälytys (ALERT) kun lämpötila ylittää tietyn raja-arvon.

TMP101 anturi

Kyseisen piirin datasivulla mainitaan, että laitteen 7-bittinen osoite on 0x90, mikäli laitteen ADD0 -pinni on kytketty maahan. Mikäli ADD0 on jätetty kellumaan (eli ei ole kytketty mihinkään), niin laitteen osoite on 0x91. Laitteen osoite on 0x92 mikäli ADD0-pinni on jätetty kellumaan. Alla oleva taulukko on peräisin datalehdestä, missä osoitteet mainitaan.

TMP101 osoite

Tarkkasilmäinen lukija huomaa, että osoite ei itse asiassa kuitenkaan ole 0x90, 0x91 tai 0x92, koska käytössä on vain 7 bittiä, joten osoitteet oikeasti tulkittuna olisivat 0x48, 0x49 ja4A. Koska I2C-väylässä osoite ja R/W-bitti lähetetään samassa 8-bitin rimpsussa, tarvitsee osoitetta siirtää yhden bitin verran vasemmalle, jolloin osoitteeksi muodostuu mainitut 0x90, 0x91 ja 0x92. Osoitteen siirron jälkeen lähetettävään tavuun lisätään RW-bitti "luodulle" tyhjälle paikalle ja käytännössä tämä tarkoittaa sitä että osoitteeseen lisätään numero 1 luettaessa laitteelta.

Tarvittavat osat

Esimerkkien toteuttamiseen tarvitset seuraavat osat:

  • TMP101 anturi (tai jokin muu I2C-väyläinen laite)
  • Launchpad + MSP430G2231 MCU (myös MSP430G2211 käy)
  • 6 kpl hyppylankoja
  • Koekytkentäalusta​ (riippuu kuitenkin käytettävästä laitteesta)

Osalistasta huomataan, että I2C-väylän vaatimat ylösvetovastukset puuttuvat. Nämä voi toki lisätä erillisinä vastuksina, mutta tämän osan esimerkissä on koitettu osien määrä minimoimaan ja sen vuoksi käytetään mikrokontrollerin sisäisiä ylösvetovastuksia.

Ennen kytkennän rakentamista ja ohjelmointia, käydään läpi USI-moduulin toimintaa I2C-käytössä.

MSP430G2231 USI I2C-modessa

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 I2C-väylän käytössä seuraavia ominaisuuksia:

  • 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 moni-isäntätilassa (Multi-master, Arbitration lost)
  • Ohjelmoitava kellosignaali

Edellisessä osassa käytiin läpi USI-laitteen rekistereitä SPI-väylän käyttämisessä ja tässä esimerkissä voidaan joitain jo läpi käytyjä asioita hyödyntää. Luonnollisesti uuttakin asiaa tulee, onhan käytössä sentään eri väylä.

Alla mainitut asiat pätevät kaikille MSP430G2-sarjan mikro-ohjaimille, joiden sisältä USI-moduuli löytyy.

USI rekisterin asetukset

Seuraavassa käydään läpi USI-rekisteriin tehtävät asetukset, jotka mahdollistavat väylän toiminnan I2C-moodissa.

Alla olevat jutut ovat aika tekniikkapainotteisia, joten jos kaikki ei heti kerralla mene jakeluun, niin ei kannata turhautua. Esimerkin avulla todennäköisesti käsiteltävät asiat ja väylän perustoiminta käy parhaiten selville.

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. MSP430G2231 mikrokontrollerin I2C-master väyläpinnit ovat P1.6 ja P1.7, joten rekisteristä asetetaan seuraavat bitit:

SDA = P1.6 (USIPE6)
SCL = P1.7 (USIPE7)

Datan siirron salliminen väylälle tehdään USIOE -bitillä, joka luonnollisesti asetetaan päälle kun väylää halutaan käyttää, mutta alustuksessa sitä ei vielä tarvitse asettaa.

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

Lisäksi USILSB -bitti tulee nollata ennen I2C:n käyttöä.

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

USIPEx - datalinjojen valinta
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

Tämän rekisterin USII2C -bitillä saadaan asetettua I2C-väylä aktiiviseksi. Rekisteristä tulee nollata kuitenkin USICKPH-bitti.

USISTTIE ja USIIE -bitit asetetaan päälle, koska väylän tilan muutoksista halutaan keskeytykset sallita. USISTTIE on START-tilan keskeytyksen sallinta ja USIIE-bitti on keskeytysten sallinta lähetetyille biteille. USIAL-bitti liittyy multi-master toimintaan ja sitä ei tässä esimerkissä tarvita.

Lisäksi tämä rekisteri pitää sisällään USI-moduulin keskeytyslippuja, jotka aktivoituvat I2C-väylän eri vaiheissa. USISTTIFG-bitti asettuu silloin, kun START-tila on havaittu. USISTP-bitti asettuu puolestaan STOP-tilan kohdalla.

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

USICKPH - kellosignaalin vaihe (toimintatilan/moodin valinta).
USI2C - I2C moodin valintabitti.
USISTTIE - START-tilan keskeytyksen sallinta.
USIIE - keskeytysten sallinta lähetetyille biteille.
USISTTIFG - START-tilan havaintolippu.
USISTP - STOP-tilan havaintolippu.
USIIFG - keskeytyslippu, joka asettuu kun väylällä on havaittu tilan muutos.

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ä. Koska käytetään I2C-väylää, tämän bitin tulee olla asetettuna.  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 I2C-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
USICKPL - kellosignaalin polariteetin valinta

MSP430x2xx USI: I2C master-lähetys

Kun laite toimii isäntälaitteena ja lähettää tietoa, data ladataan ensin USISRL-rekisteriin ja lähtö sallitaan USIOE-bitillä USICTL0-rekisterissä. Tämän jälkeen USICNT-rekisteriin kirjoitetaan luku 8 jonka jälkeen MCU aloittaa START-tilan generoimisen ja orjan osoitteen kirjoittamisen väylälle.

9:säs bitti, eli ACK-bitti luetaan asettamalla USIOE-bitti nollaan ja lataamalla USICNT-rekisteriin luku 1. Kun kellopulssi on väylälle tullut, voidaan ACK-bitti tutkia USISRL-rekisterin alimmasta bitistä. USI-moduulia käytettäessä ACK-bitti täytyy siis tutkia ohjelmallisesti keskeytysten avulla.

MSP430x2xx USI: I2C master-vastaanotto

Kun laite toimii isäntälaitteena ja tietoa luetaan väylältä. USIOE-bitti asetetaan nollaan USICTL0-rekisteristä. Tämän jälkeen USI-moduuli asetetaan valmiiksi vastaanottamaan 8-bittiä dataa kirjoittamalla USICNT rekisteriin luku 8. Tämä nollaa USIIFG-lipun ja tarvittavat kellopulssit luodaan väylälle isäntälaitteen toimesta.

9:säs bitti, eli ACK-bitti generoidaan masterin toimesta väylälle kun data on luettu. Tämä tapahtuu siten, että USISRH-rekisteriin ladataan kuittausbitti 0, USIOE-bitti asetetaan 1-tilaan ja USICNT-rekisteriin ladataan luku 1. Kun ACK-bitin vaatima kellopulssi on generoitu väylälle ja ACK bitti on lähetetty, niin USI-moduulin USIIFG-lippu asettuu ja seuraava data voidaan lukea mikäli tarpeellista.

I2C-lämpötila-anturin kytkentä Launchpadille

Ennen ohjelmaan paneutumista on hyvä näyttää laitteiston fyysinen kytkentä, joka löytyykin alta.

​TMP101 anturi kytketään mikrokontrollerille sen nastoista 1,2,3, 4 ja 6. Tiedonsiirtojohdinten lisäksi tarvitaan luonnollisesti käyttösähkö ja maajännitteet, sekä ALERT-pinni. Mikrokontrollerilta kytketään porttipinni P1.4 launchpadin vihreälle ledille ja samalla poistetaan vihreän ledin jumpperi.

TMP101 anturi on SOT23-6 koteloinen, joten olen sille rakennellut alla olevan kuvan mukaisen adapterilevyn, mikä mahdollistaa sen asentamisen koekytkentäalustalle taikka hyppylankojen päähän kuten kuvissa on tehty.

SOT23-6-adapterilevy

SOT23-6-adapterilevy

Huom. Yllä olevassa kuvassa ALERT-pinniä eikä vihreää lediä ole vielä kytketty.

Esimerkki 1: I2C-väyläisen lämpötila-anturin luku

Seuraavassa on esitelty lyhkäinen esimerkkikoodi, joka on rakenneltu TI:n sivuilta löytyvän I2C-esimerkkikoodin pohjalta. Koodi on pyritty kirjoittamaan niin, että se olisi helposti uudelleen käytettävissä ja että käyttäjän ei tarvitsisi juurikaan tietää käytetyistä rekistereistä ja komennoista.

Ohjelman tarkoituksena on tutkia onko laite väylällä ja ilmoittaa siitä sarjaporttiin Launchpadin avulla. Ohjelma alustaa lämpötila-anturin ja asettaa raja-arvot hälytyslämpötilalle. Kun hälytyslämpötila anturilla ylittyy, asettuu porttibitti P1.3 nollaan (ALERT-pinni on kytketty tähän) ja sarjaporttiin tulostetaan sekunnin välein teksti "ALARM!" kunnes lämpötilahälytyksen alaraja on suurempi kuin anturin lämpötila.

Ohjelmassa on muutamia erikoisuuksia ja uusia komentoja, joita ei tähän mennessä ole tullut vastaan, mutta tarkastellaan niitä lisää hieman myöhemmin.

Ohjelmakoodi on jaettu omiin kansioihinsa ja projektin päätasolla nähdään vain main.c tiedoston koodi. USI-kansiossa on USI-moduulin alustukseen ja I2C-väylän käyttämiseen tarvittavat funktiot. Usi.h -tiedostossa voidaan asettaa COMMUNICATION määrityksellä käytetäänkö SPI:tä, joka on tuttu edellisestä osasta vai I2C:tä. USI.h tiedoston säätämistä ei kuitenkaan tarvita tässä, sillä ohjelma kutsuu vain TMP101-kansiosta löytyviä funktioita ja määritykset sekä projektiasetukset ovat valmiiksi oikein zip-paketissa.

Logiikka-analysaattorikuva

Lataa CCSv5 esimerkkiprojekti tästä. (koodi päivitetty 20.10.2013)

Ohjelman toiminta

Kun ohjelma käynnistyy, tarvittavat moduulit, MCU:n pinnit ja TMP101 anturin asetukset alustetaan:

    // Init launchpad LEDS and S2
    InitGPIO();

    // Init timer for uart and delay
    TimerA_UART_pinInit();
    TimerA_UART_init();

    // Init USI for I2C Master
    InitUSI();

    // Init TMP101
    InitTMP101();

    // Tell user that we are ready
    TimerA_UART_print( (char *) MasterReadyText);

Tulostettavien merkkien tallentaminen erilliseen muistiin

Tulostusfunktion riviltä voidaan huomata, että enää tulostus ei olekaan heittomerkkien sisällä kuten esimerkiksi SPI-väylän tapauksessa, missä tulostettiin funktiolla näin:

TimerA_UART_print( "Ready! " );

I2C:n tapauksessa kiinteitä tekstejä on haluttu siirtää MSP430-mikrokontrollereista löytyvän INFO-muistialueen sekaan, jolloin jää enemmän tilaa varsinaiselle hyötykoodille. Tekstimerkit vaativat kukin yhden tavun, joten on ihan järkevää siirtää ne sellaiseen paikkaan missä ne ovat eniten hyödyksi.

INFO-muistialue on alue, joka on tarkoitettu muuttumattoman tiedon tallennuspaikaksi. Code Composer Studiossa sinne voidaan merkkejä tallentaa suoraan ohjelman polton yhteydessä avainsanalla #pragma yhdessä DATA_SECTION() -funktion kanssa, eli näin:

#pragma DATA_SECTION(MasterReadyText, ".infoC")          // Tiedon tallennus INFO-muistiin
const char MasterReadyText[] = "Master Ready! ";

MSP430-mikrokontrollereilla on 4 kappaletta INFO-muistialueita ja näitä voidaan yllä olevassa koodissa käyttää muuttamalla ".infoC" -merkintää. (Käytettävissä on 256 tavua muistia ja nämä alueet on jaettu neljään yhtäsuureen osaan A - D. A-osassa on kalibrointitietoja ja sen käyttäminen vaatii erityistoimenpiteitä kun taas B,C,D muistialueet on vapaasti käytettävissä.)

Kun merkit tallennetaan const char avainsanalla, ei niitä voida enää muokata ohjelmassa. Tämä ei tosin ole tarpeellistakaan. Jos joudut kuitenkin muuttamaan tekstejä koodiin, varmista että projektiasetuksissa on asetettu täplä kohtaan "Erase main and information memory", kuva alla:

Projektiasetukset

Älä kuitenkaan valitse "Erase main, information and protected information memory" sillä tällöin menetetään 1MHz:n kalibrointiarvo MCU:lta.

Orjalaitteen tarkistus

Alustusten jälkeen ohjelma siirtyy tarkistamaan, onko orjalaite paikalla. Mikäli orjalaite puuttuu, punainen LED sytytetään ja odotetaan sekunti, jonka jälkeen tarkistetaan uudelleen:

// Check if slave exists, if not then do not proceed and turn on the error led
while(CheckForSlaveExists(TMP101_SLAVE_ADDRESS) == 0)
{
    TimerA_UART_print( (char *) SlaveNotFoundText);
    TimerA_Delay(1);
    LED_RED_ON;
}
TimerA_UART_print((char *) SlaveFoundText);

Mikäli osoite on oikein ja laite on väylällä, ohjelma etenee asettamaan lämpötila-anturin hälytysrajan ja ALERT-pinnin polariteetin:

// Set Alarm values for the ALARM-pin
SetHighTempLimit( (30 / RESOLUTION_CALCULATION_VALUE) );
// 30 degrees of Celsius limit, 30 = 480 (12-bit resolution)

SetLowTempLimit( (28 / RESOLUTION_CALCULATION_VALUE) );
// 28 degrees of Celsius limit, 28 = 448 (12-bit resolution)

// Set ALERT pin polarity "0" to simulate the button press
SetAlertPinPolarity(0);

Lämpötilarajat on asetettu 30 ja 28 asteeseen sen vuoksi, että ohjelman toimintaa on helppo testata lämmittämällä anturia esimerkiksi sormella.

Vakiossa RESOLUTION_CALCULATION_VALUE on käytetyn resoluution arvo, jonka avulla voidaan laskea lämpötilarajat. Kun anturissa käytetään 12-bitin muunnosta, niin yhden bitin muutos vastaa 0,0625:n asteen lämpötilan muutosta. Jakamalla haluttu lämpötila tällä luvulla saadaan raja-arvo ylä- tai alapäähän joka voidaan asettaa.

Kun ohjelma on käynnissä ja kaikki on kunnossa, niin sarjaporttiin tulostuu tekstit "Master Ready!", "Slave Found!" (mikäli laiteosoite oli oikein) ja "I'm Fine!". Teksti  "I'm Fine!" tulostuu niin kauan kun lämpötila pysyy alle hälytysrajan, kuten kuvassa alla.

Logiikka-analysaattorikuva

Kun anturia lämmitetään ja asetettu lämpötilaraja ylittyy, alkaa sarjaporttiin tulostua teksti "ALARM!" ja vihreä LED menee pois päältä.

Logiikka-analysaattorikuva

Alla vielä ohjelman pääsilmukan koodi, missä on tarkoituksella jätetty kommentin sisälle TMP101-kirjaston kutsuntafunktio GetTemperatureTMP101() sekä muut koodit:

while(1)
{
    /* Commented out on purpose.

    // Read the temperature
    temperature = GetTemperatureTMP101();

    // Compare temperature
    if(temperature > (30 / RESOLUTION_CALCULATION_VALUE)) // 26 degrees of Celsius
    {
        TimerA_UART_print("Temp Over 30 ");
    }
    else
    {
        TimerA_UART_print("Temp Under 30 ");
    }

    */

    // If ALERT-pin is active, ALARM! text is printed to UART
    if(SWITCH_PRESSED)
    {
        TimerA_UART_print((char *) AlarmText);
        P1OUT &= ~BIT4;
    }
    else
    {
        TimerA_UART_print((char *) OkText);
        P1OUT |= BIT4;
    }
    // Sleep one second
    TimerA_Delay(wait);
}

Väylän toiminnan analysointi

Toiminnan varmentamiseksi olen käyttänyt logiikka-analysaattoria TMP101-kirjaston toiminnan tarkasteluun.

Alla olevassa kuvassa on otettu kaappaus tietoliikenteestä, kun kutsutaan USI-I2C -kirjaston funktiota CheckForSlaveExists() ja annetaan parametrinä orjan osoite (7 bittiä siirrettynä vasemmalle yhdellä):

Logiikka-analysaattorikuva

Kuvasta voidaan huomata, että väylä toimii kuten pitääkin ja orja vastaa ACK-bitillä (vaaleanpunainen palikka) osoitteeseensa.

Esimerkiksi TLOW -rekisterin asetuksesta on alla otettu lisää pari kuvakaappausta.

Kuvissa olen jakanut TLOW -rekisterin asetuksen kahteen osaan, jotta kuva ei venyisi kovin pitkäksi. Kuvista siis nähdään, kun I2C-isäntälaite aloittaa viestiliikenteen START-tilalla. Tämän jälkeen kirjoitetaan väylälle orjan osoite ja sen jälkeen rekisteri, mihin kirjoitusoperaatio kohdistetaan ja lopuksi kyseisen rekisterin arvo.

Tässä tapauksessa kirjoitetaan luku 1E0 joka vastaa desimaalilukua 480. Tämä on juurikin sama luku joka on koodissa rivillä, missä kutsutaan SetHighTempLimit() -funktiota:

SetHighTempLimit((30 / RESOLUTION_CALCULATION_VALUE));

Koska TMP101 anturin AD-muuntimen resoluutio alustettiin InitTMP101() -funktiossa 12-bittiseksi, niin RESOLUTION_CALCULATION_VALUE arvona on 0,0625 astetta per muuntimen muutos tai "tiksi". Tällöin 30 jaettuna 0,0625:llä antaa arvon 480, joka voidaan kirjoittaa TLOW -rekisteriin.

Logiikka-analysaattorikuva

Logiikka-analysaattorikuva

Ohjelma on nyt testattu ja todettu toimivaksi. Bugeja varmaan löytyy, joten jos käytät koodeja jossakin projektissa, muista että tämä ei ole täydellinen tuotos.