Analoginis įvadas Avietei

Posted: 2013-09-16 in Darbeliai
Žymos:, , ,

Kaip žinia, Raspberry Pi kompiuteriukas nors ir labai universalus, bet turi ir šiokių tokių trūkumų. Tarkim, jame nėra analoginės įvesties, t.y. analoginio-skaitmeninio keitiklio (ADC — analogue-digital converter). Tad jei kyla poreikis nuskaityti kokių nors analoginių daviklių reikšmes, su Aviete tai taip paprastai neišdegs. Reikia prie kompiuteriuko prikabinti kokį nors aparatą, kuris tą sugebėtų padaryti. Tarkim, mikrovaldiklį su ADC arba specializuotą mikroschemą.

Man užėjo noras paknebinėti MCP3208 mikroschemą. Šis konkretus modelis yra šešiolikos kojų tarakonas ir turi aštuonis ADC kanalus, kurie yra 12 bitų skiriamosios gebos. Yra ir MCP30xx šeima, kurių skiriamoji geba yra 10 bitų. Be to, du paskutiniai modelio skaičiukai nusako ir kanalų kiekį. Yra ir dviejų kanalų keitikliukai, jei reikia.

Šie keitikliai valdomi per SPI magistralę. Tai — dar viena magistralė įvairiam duomenų apsimainymui tarp visokių valdiklių, mikroschemų ir panašiai. Ji spartesnė, nei I2C, bet ir laidų daugiau reikia. I2C magistralėje aparatai turi savo adresus, o SPI įrenginiai yra įjungiami atskiru elektrodu CS (chip select). Kai jiems tas elektrodas užtrumpinamas į žemę, tai aparatai susipranta, kad čia jau juos dabar „apklausinės“. Dar yra duomenų įvestis, išvesties ir taktinio dažnio elektrodai. Su „žeme“ — iš viso penki laidai. Šiaip SPI dar vadinama „keturių laidų“ magistrale (na, „žemės“ niekas neskaičiuoja paprastai).

Raspberry Pi be kitokios periferijos yra ir aparatūrinė SPI magistralė bei Raspbian OS skirtas Linux branduolio modulis. Python taip pat yra sukurtos bibliotekos, tik dar kol kas vystymo stadijoje, tad jas reikia įsidiegti iš atskiros GIT saugyklos. O ir pavyzdukų internete, kaip terliotis su MCP3xxx mikroschemom, yra nemažai. Tiesa, visi jie paremti vienu ir tuo pačiu pavyzdžiu. Aš juo irgi pasinaudojau (o kaipgi kitaip).

Kad galėtume dirbti su aparatūrine SPI magistrale, reikia įjungti branduolio modulį spi-dev. Perkrovus Avietę /dev direktorijoje turi atsirasti pora naujų divaisų:

pi@Uoga ~ $ ls -l /dev/spidev0.*
crw------- 1 pi pi 153, 0 Sau 1 1970 /dev/spidev0.0
crw------- 1 pi pi 153, 1 Sau 1 1970 /dev/spidev0.1

Aš jiems iš karto padariau chown savo „pi“ paskyrai, kad nereikėtų visų programulkių iš po root leidinėti. Ir dar tą chown įsidėjau į /etc/rc.local skriptą, kad kas kartą Avietei persikrovus reikalo nereikėtų kartoti.

Dar reikia parsisiųsti, susikompiliuoti ir įsidiegti py-spidev paketuką. Na, pirmiausia tai pasikopijuojam iš GIT kodą, tada įeinam vidun, tada kompiliuojam, o po to ir įdiegiam. Dariau šitą reikalą virtualioje Python aplinkoje (apie tai bus kitą kartą šiek tiek):

git clone git://github.com/doceme/py-spidev
cd py-spidev
make
make install

Mano dideliausias noras buvo pasidaryt lauko termometrą. Kadangi jau registruoju kambario temperatūrą su vienu Dallas davikliu, tai panorau pažiūrėti ir temperatūrą lauke. Kol kas daugiau dalasiukų neturiu, siuntinys dar neatkeliavo, bet turiu iš anksčiau termistorių. O termistoriai yra analoginiai įtaisai, apie juos jau kartą rašiau ir bandžiau nuskaitinėti temperatūrą su Arduino. Tas pats galioja ir čia, su MCP3xxx keitikliu. Į žemę įjungiamas 10 kΩ rezistorius, o į maitinimo įtampą – termistorius, taip sudarant įtampos daliklį, kurio pokyčius ir registruoja analoginis keitiklis. Beje, labai smagu, kad MCP3xxx gali darbuotis ir su 3,3 V įtampa – nereikia loginio keitiklio su Raspberry Pi.

Schemą sujungiau greitai:

Prie Raspberry Pi prijungtas analoginis-skaitmeninis keitiklis MCP3208 | Darau, blė

O paskui pradėjau ir programuojamuosius darbelius. ADC grąžinamos reikšmės pavertimą temperatūra pasiėmiau iš Arduino pavyzdžio, tik perrašiau su Python. Čia padariau vieną klaidą: pamiršau reikšmės 1024 konstantą (10 bitų keitikliui) pakeisti į 4096 – 12 bitų atitikmenį. Bet po kelių pabandymų pamačiau, kad temperatūra skaičiuojama teisingai… na, tik jei ne per dažnai matuoju, gal kas pusę sekundės. Kitu atveju pabyra logaritmo skaičiavimo klaidos, kažkodėl gaunama neigiama varža… Kažkokia nesąmonė.

Galų gale, sugalvojau pažiūrėti į to MCP3208 datašytą. Be abejo, tą reikėjo iš karto padaryti, bet kur jau ten 🙂 Ir radau nerealybę… Suprantate, kodą kopijavau nuo vieno veikėjo, kuris naudojo 10 bitų keitiklį, MCP3008. Na, ir siunčiama užklausa atitinkama. Pasirodo, kad MCP30xx ir MCP32xx užklausos skiriasi. Iš siunčiamų trijų baitų nurodyti start ir compare bei kanalo numerio bitukai pas MCP32xx pasislinkę per dvi pozicijas kairėn… Palyginkite, kairėje MCP32xx, o dešinėje — MCP30xx (sputelėkite paveiksliuką, pasididins):

mcp3208 ir mcp3008 kontrolinių bitų sekų palyginimas pagal datašytus (datasheet) | Darau, blė

Štai čia bitukų užklausa Pythone 10 bitų keitikliui:

r = spi.xfer2([1, 8 + adcnum << 4, 0])

Pirmojo baito dešinysis bitukas yra start, toliau yra compare ir ADC kanalo numeriukas, pastumti iki antrojo baito galo. Na, o paskutinis baitas niekam neįdomus. Ką reikia padaryti? Start pastumti per dvi pozicijas kairėn, compare pernešti į pirmąjį baitą, taip pat pernešti kanalo numeriuko vieną bitą irgi į pirmąjį baitą. Kanalai nurodomi trijų bitų skaičiumi. Atitinkamai pastumti likusius du kanalo numerio bitukus iki antrojo baito galo. Trečiąjį vėlgi palikti tuščią. Pakeitimas toks:

r = spi.xfer2([6+(adcnum>>2), adcnum << 6, 0])

Viskas, po šito pakeitimo keitiklis pradėjo spjaudytis gražiomis dvylikos bitukų reikšmėmis. Keitiklis, beje, atgal irgi atsiunčia tris baitukus, kurių reikia dalį ignoruoti ir atsifiltruoti 10 arba 12 bitų skaičių. Aš pačioje pradžioje irgi atsifiltravau 12 bitukų, nors užklausą siunčiau 10-ies… O tai reiškia, kad du bitukai buvo nenuspėjami ir kartais vietoj nuliukų ten įšokdavo koks vienetukas ir subliaudavo man visus reikalus.

Šiame reikale truputuką kvepia sąmokslais. MCP32xx datašyte nė puse žodelio nėra užsiminta, kad šis keitiklis palaiko 10 bitų režimą. Tai gal pasiuntus 12 bitų komandą ir MCP30xx taip pat padarytų 12 bitų? Gal tik tiek ir tėra — skirtingi truputuką datašytai ir numeriukas ant mikroschemos? Gaila, neturiu MCP30xx, o norėtųsi pabandyti… gal kas iš skaitytojų turi ir pabandys?

Taigi viską susitvarkiau ir pradėjo mano keitiklis nuskaitinėti tris termistorius: vieną lauke šešėliuotą, kitą atvirą ir trečią kambaryje. Programėlė testavimui gavosi tokia:

# -*- coding: utf-8 -*-
'''
Created on 2013-09-04

@author: Darau, blė
'''

from __future__ import division
import time
import spidev
from math import log

'''
Funkcijikė susišnekėjimui su MCP32xx. Užkomentuota
eilutė skirta MCP30xx, bet MCP32xx ji perveda į
10 bitų režimą. „adcout“ irgi užkomentuota: skirta
10 bitų reikšmės „nufiltravimui“.
''' 
def readadc(adcnum):
    spi = spidev.SpiDev()
    spi.open(0, 0)
    if adcnum > 7 or adcnum < 0:
        return -1
    #r = spi.xfer2([1, 8 + adcnum << 4, 0])     r = spi.xfer2([6+(adcnum>>2), adcnum << 6, 0])
    #adcout = ((r[1] & 3) << 8) + r[2]
    adcout = ((r[1] & 15) << 8) + r[2]
    return adcout

'''
Funkcijikė vidurkinimui — reikšmės nuskaitomos kelis kartus.
MCP32xx mikroschema irgi analoginį palyginimą atlieka tris
kartus „iš vidaus“, bet čia papildomai.
'''
def readMeanAdc(adcnum, repeat=1):
    v = 0
    for i in range(0, repeat):
        time.sleep(0.05)
        v += readadc(adcnum)
    return v/repeat

'''
O čia gi funkcijikė ADC reikšmės pavertimui temperatūra.
'''
def thermistor(RawADC):
    # Rezistoriaus reikšmė omais
    pad = float(10000)

    #rez = ((1024 * pad / RawADC) - pad)
    rez = ((4096 * pad / RawADC) - pad)
    # Čia šiaip optimizacija pagal Arduino bendruomenės kodą,
    # kad daug kartų logaritmo nereikėtų skaičiuoti.
    t = log(rez)
    t = 1 / (0.001129148 + (0.000234125 * t) + (0.0000000876741 * t * t * t))
    # Formulė Kelvinams, reikia versti Celsijais
    t = t - 273.15;                      
    return t

# Test plest!
value = readMeanAdc(0, 5)
print 'Reikšmė: ', value, ', temperatūra: ', thermistor(value)
value = readMeanAdc(1, 5)
print 'Reikšmė: ', value, ', temperatūra: ', thermistor(value)
value = readMeanAdc(2, 5)
print 'Reikšmė: ', value, ', temperatūra: ', thermistor(value)

Va, galite imtie ir bandytiez.

Parašykite komentarą

Įveskite savo duomenis žemiau arba prisijunkite per socialinį tinklą:

WordPress.com Logo

Jūs komentuojate naudodamiesi savo WordPress.com paskyra. Atsijungti /  Pakeisti )

Google photo

Jūs komentuojate naudodamiesi savo Google paskyra. Atsijungti /  Pakeisti )

Twitter picture

Jūs komentuojate naudodamiesi savo Twitter paskyra. Atsijungti /  Pakeisti )

Facebook photo

Jūs komentuojate naudodamiesi savo Facebook paskyra. Atsijungti /  Pakeisti )

Connecting to %s

Brukalų kiekiui sumažinti šis tinklalapis naudoja Akismet. Sužinokite, kaip apdorojami Jūsų komentarų duomenys.