2014 m. lapkričio 26 d., trečiadienis

AVR ir asemblerio iššūkis I

KAS ČIA BUS?
Jeigu trumpai, susidūriau su šiokia tokia problema.
Problemos priešistorė paprasta: įstojau mokytis į KTU ištęstines studijas. Ir pirmam semestre atsirado toks modulis, kaip mokomoji praktika. Viskas ten tvarkoj, realiai tas modulis - įvadas į elektronikos pasaulį, tačiau tas įvadas gavosi kiek panašus į šaltą dušą, ypač tiems, kurie mažiau susidūrę su elektronika. Konkrečiau - gavome užduotį parašyti programą AVR mikrovaldikliui. Paprastutę, trumpą programėlę, pamirksinančią šviesos diodus (yra ir bonusas - ekstra užduotis su PWM - impulso pločio moduliacija, bet šitą kiek vėliau). Viskas būtų kaip ir tvarkoj, jei viso to nereikėtų padaryti asembleriu. Na taip, leido C kalba, tačiau turiu įtarimą, kad asemblerio dar prireiks, tai galvoju, jog verta pasižiūrinėt, kol dar mokslai labai nespaudžia. O va analogišką asemblerį paskutinį kartą čiupinėjau prieš maždaug 15 metų, o gal ir daugiau - buvo gūdūs laikai, kai personalinį kompiuterį turėjo tik išrinktieji :) Aš, kaip paprastas paauglys, turėjau ZX SPECTRUM kompiuterį, ir jis buvo programuojamas arba BASIC''u, arba Z80 asembleriu. Bet keliomis nedidelėmis programėlėmis mano pažintis su šiuo daiktu ir baigėsi.


Vadinasi taip. Su sąlyga, kad mano grupiokams asembleris dar tamsesnis miškas nei man, bandysiu jiems pagelbėti parašydamas tokį atseit įvadą, kurio, jeigu sakyti tiesą, dar pats nežinau - pradėjau skaityti knygas apie asemblerį. Gal ir dar kam pravers.
Rašliavos pradžioje dar bandysiu nupasakot, kas tie mikrovaldikliai, kaip su jais bendraut. Čia esančiuose kituose straipsniuose bus analogiškos informacijos, kurios stengsiuosi nedubliuot, bet man gali ir nepavykt, tai gal niekas nepyks.

MIKROVALDIKLIAI?
Dabar labai daug visur girdėti apie mikrovaldiklius (mikrokontrolerius, uC), o ypač apie AVR šeimos. AVR šeimos mikrovaldikliai, gaminami ATMEL, apima labai platų tokio tipo įrenginių spektrą: nuo paprasčiausių 8 bitų valdiklių DIP8 korpuse iki monstrų 32 bitų mikrovaldiklių ar netgi ARM architektūros mikroprocesorių. Aš kol kas tiek aukštai šokti neplanuoju, tai apsiribosiu paprasčiausiais 8 bitų įrenginiais. Tačiau teneapgauna žodis "paprasčiausiais" :) Netgi pats paprasčiausias mažytis mikrovaldiklis gali atlikti nemažas užduotis ir visia nemažu greičiu - dažniausiai iki 16 milijonų instrukcijų per sekundę.
Jei grubiai, mikrovaldiklis - tai tokia mikroschema, kuri sudaryta iš įvairių atminčių (flash, eeprom, ram), prievadų: tiek loginių, kaip kad veikimo ar nustatymų registrai, tiek fizinių (elementariausi, kuriuos ir dabar teks panaudot - I/O prievadai (Input/Output), taip pat per tuos pačius prievadus pakeičiant jų paskirtį programiškai - papildomomis galimybėmis pasižymintys, kaip kad ADC/DAC (Analog-digital converter, Digital-analog converter), PWM, taimeriai, pertraukimo įėjimai ir kt.). Šiuos fizinius prievadus reikėtų įsivaizduoti kaip mikroschemos kojas (iš tikro tai taip ir yra :) ). Keikviena mikroschemos koja turi savo paskirtį, kuri aprašyta mikrovaldiklio aprašyme (datasheet, pateikiamas gamntojo, galima parsisųsti iš atmel.com). Trečias mikrovaldiklio sudedamasis elementas - centrinis procesorius (CPU) su aritmetine logine dalimi, kuris rūpinasi programos vykdymu ir bendravimu su išoriniu pasauliu per prievadus.
O viskas vyksta taip: specialus failas , kuris paruošiamas kompiliatoriaus(kompiliatorius - speciali programa, kuri programos kodą, t.y. tekstą, kurio mikrovaldiklio procesorius nesupranta,  pagal tam tikras taisykles verčia į šešioliktainius skaičiukus, kuriuos mikrovaldiklio procesorius supranta),  įrašomas į mikrovaldiklio flash atmintį (failo plėtinys *.HEX).  CPU nuo flash atminties pirmojo adreso (yra išimčių) pradeda skaityti tą failą, kuris yra ne kas kita, kaip instrukcijų rinkinys procesoriui. Perskaitęs ir įvykdęs duotą užduotį pirmoje eilutėje procesorius "ima" sekančią eilutę ir taip toliau. Žinoma, tam skirtomis instrukcijomis šią eigą galima pakeisti, tarkim, nurodyti, kad programa vyktu "amžinu ciklu", t.y. visą laiką "suktųsi" ta pati programa, nuo pabaigos vėl šokdama į priekį, lyg pasaka be galo. Arba galima padaryti sąlyginį programos šakojimą, pavyzdžiui, programa laukianti mygtuko paspaudimo: parašomas amžinas ciklas, kurio metu žiūrima, ar mygtukas paspaustas. Jeigu mygtukas nepaspaustas - CPU grįžta atgal ir vėl tikriną mygtuką. Jeigu nuspaustas - CPU peršoka į kitos, programoje nurodytos eilutės skaitymą ir vykdymą.
Pavyzdžiui įkelsiu vieno iš paprastesnių mikrovaldiklių, ATtiny2313, išvadų aprašymą.

Štai šio mikrovaldiklio aprašymas - viso tik 226 puslapiai :). Tačiau gera naujiena yra ta, kad programuojant asembleriu to dokumento užtenka visoms užduotims, t.y. tame dokuente surašytos visos galimos asemblerio instrukcijos.
Painiavos gali sukelti prie kiekvienos kojos keletas aprašymų, tačiau iš tikro painiavos čia nedaug: kiekvienas mikroschemas išvadas daro tai, kas nurodoma programoje, t.y. jei liepsite, pavyzdžiui, 2-ajai mikroschemos kojai mirksinti diodą, ji tą ir darys. Lygiai taip pat galima nurodyti per tą koją priimti duomenis iš UART sąsajos (tas pats visiems žinomas COM portas kompiuteryje, tik šiek tiek kitokiais įtampų lygiais) (RXD - receive data), tačiau diodo pamirksinti jau nebepavyks.Savaime aišku, 10 koja - maitinimo minusas, 20 - maitinimo pliusas (maitinami šie įrenginiai standartiškai 5V įtampa, tačiau yra ir kitokių galimybių bei modifikacijų).
Žvelgiant į išvadus į akis krenta PB0...7 bei PD0...PD6. Šie išvadai šiai užduočiai (tiek įsijaučiau rašyt, kad užmiršau užduotį) yra svarbiausi. Mikrovaldikliuose paprasta skaitmeninė įvestis ar išvestis (loginis 1 = 5 v., loginis 0 = 0 v.) yra vykdoma mikroschemos kojomis, kurios priskirtos prievadams PORT A, PORT B, PORT C, PORT D - jų kiekis priklauso nuo mikrovaldiklio modelio. Šie prievadai yra 8 bitų, t.y. vienas prievadas valdo 8 kojas, kurias skirtingu metu galima išjungti arba įjungti. Pažymėtina, kad mikrovaldiklyje esantis 8 bitų prievadas nebūtinai turi realiai valdyti visas aštuonias kojas. Taigi, ATtiny2313 atveju turime tris prievadus: PA0-PA2 (PORT A, trys bitai = trys atskirai valdomi signalai), PB0-BB7 (PORT B, aštuoni bitai = pilnas prievadas) bei PD0-PD6 (PORT D, septyni bitai, nepilnas prievadas). Reiktų atkreipti dėmesį, kad numeracija yra nuo 0, taigi 8-asis PORTB bitas yra PB7. Viso šiame mikrovaldiklyje yra 3+8+7=18 bitų, kuriuos galima valdyti programiškai įjungiant arba išjungiant. Paprasčiausias pavyzdys - 18 šviesos diodų (LED), junginėjamų atskirai.
Į kitus išvadus šiam kartui nekreipsiu dėmesio - pirmam kartui užteks ir diodų.

11001110001101011 ???
Aštuonių bitų prievado valdymas vykdomas arba vienu skaičiumi, arba naudojant bitų postūmį. Kol kas, pradžiai, apsiribosiu valdymu skaičiumi, kuris programos vykdymo metu įrašomas į norimą prievadą, tarkim, PORT B. Skaičius gi gali svyruoti nuo 0 (visi 8 prievado bitai = 0V., t.y. išjungti) iki 255 (visi bitai = 5v., t.y. įjungti).Gali kilti klausimas, kaip vienu skaičiumi galima valdyti 8-ias mikroschemos kojas? Va čia į sceną ateina dvejetanė skaičiavimo sistema, kur ir pasimato visas grožis. Tarkime, mums reikia užgesinti visus diodus. Kaip jau minėjau, skaičius, tam reikalingas yra nulis. Dvejetainėje sistemoje jį galima užrašyti ir taip: 0b00000000 . Čia yra 8 bitų dvejetainis skaičius, kurio kiekvienas skaičiukas atitinka vieną prievado bitą (Simbolis "0b" rašomas tam, kad atsiskirtų, kokia skaičiavimo sistema naudojama). Jeigu, pavyzdžiui, reikia įjungti kas antrą bitą prievade - į PORT B rašysime 0b10101010, kas atitinka dešimtainį skaičių 170. Du įjungti, du išjungti - 0b11001100, kas dešimtainėje sistemoje yra 204. Ir taip toliau. Visi prievadai ir visi registrai valdomi tokiu pat būdu, tačiau išmokus daugiau gyvenimą sau galima palengvinti nerašant kiekvienąkart visos reikšmės į PORT B, o tik keičiant tam tikrą bitą. Kitas dalykas - kartais galima nežinoti, koks buvo bitas, įjungtas ar išjungtas, todėl visus bitus nurodyti pavojinga. Dar vienas niuansas - to paties prievado dalį bitų galima nurodyti kaip įėjimus, dalį - kaip išėjimus, tokiu atveju visų bitų rašymas į PORT B įneša dar daugiau painiavos.

IŠ VIRTUALAUS APRAŠYMO Į REALŲ LAIDŲ PASAULĮ
Norint, kad mikrovaldiklis veiktų (veiktų apskritai, t.y. tiesiog būtų įjungtas) reikia poros detalių ir maitinimo šaltinio. Pati paprasčiausia būtent šio mikrovaldiklio schema atrodo šitaip:

Tiesą sakant, šią shemą dar galima supaprastinti, nuo 4 ir 5 kojos atjungus kvarcinį rezonatorių bei kondensatorius - jie reikalingi, jeigu vidinio generatoriaus tikslumo neužtenka, arba jeigu reikia didesnio veikimo greičio.
Nuo 1-osios kojos pajungti 10 k rezistorius ir 100n kondensatorius "nuresetina" naujai įjungtą mikrovaldiklį. Kojos 17/MOSI, 18/MISO, 19/SCK bei 1/RESET naudojamos programatoriui prie mikrovaldiklio prijungti, todėl naudoti šių kojų kitiems tikslams nerekomenduoju, bent kol kas. Programatoriaus schemą rasite kituose straipsniuose šiame puslapyje (čia ir čia).
Prie 11-osios kojos per rezistorių prijungtas šviesos diodas, ko mano užduočiai ir reikės. Vėliau bus schema, reikalinga užduočiai atlikti, dabar gi užteks tokios.
Maitinimo šaltinio reikia 5V. Galima arba darytis pačiam su, pvz., LM7805 mikroschema (paieškojus internete galima rasti daug pavyzdžių), arba naudoti kokį nors esamą. Turėtų tikti NOKIA įkroviklis (naujųjų telefonų), tik būtinai reikia pažiūrėti, ar ten 5 V (kad nebūčiau paskui aš kaltas dėl sudeginto mikrovaldiklio ar įkroviklio). Žinoma, prijungimas galimas tik arba padarius kažkokį lizdą, arba nukirpus laidus ir prijungus tiesiai. Spręsti patiems :) Be to, visai sėkmingai galima maitinti 4xAA įkraunamais akumuliatoriais - jų bendra įtampa bus 4.8 V (pažiūrėkite mikroschemos aprašymą, ten parašyta, kokiomis įtampomis galima maitinti).
Visą schemą lituoti nuo pat pradžių nerekomenduoju. Visiškai universalią schemą pasidaryti užims daug laiko, o ir tokios reikalingumas - su klaustuku. O lituoti mažas schemas su pora ar trejetu diodu irgi neekonomiška. Todėl šiuo atveju rekomenduočiau naudoti maketinę plokštę - kaina nedidelė, o patogumas milžiniškas: sukaišioji detales be litavimų, o norint perdaryti tereikia ištraukti detalę ir įkišti kitur. Štai tokios plokštės pavyzdys.
Tokią minimalią schemą sujungus ir įjungus maitinimą mikrovaldiklis veiks, tačiau neturėdamas jokios programos, kurią galėtų vykdyti, jis tyliai ramiai ir gyvens. Iki prasmingesnio jo gyvenimo trūksta dviejų dalykų: programos, parašytos ir sukompiliuotos į *.HEX failą (tai padarys programinė įranga), ir programatoriaus. Jau minėjau, kad programatoriaus schemą šiame puslapyje rasite, ir instrukcijas, kaip juo naudotis, taip pat, bent jau minimalias.

O MINKŠTOJI VISO TO DALIS, ARBA SOFTAS PROGRAMAVIMUI?
Pradžiai noriu už išsireiškimą "minkštoji dalis" padėkoti komunikavimo pagrindų dėstytojai, tuo tarpu aš bandysiu įvardinti, ko reikės užduočiai atlikti.
Aš asmeniškai naudoju AVR Studio, paties ATMEL produktą programavimui. Juo galima rašyti programas AVR mikrovaldikliams tiek C, tiek ASSEMBLER kalba, kas man dabar ir aktualu. Taip pat su ta programa galima simuliuoti mikrovaldiklio veikimą, stebint registrų ir prievadų būsenas, bei galima programuoti su specialiu programatoriumi. Tačiau kadangi aš to programatoriaus neturiu, šios funkcijos nenaudosiu. Parašius programą AVR Studio aplinkoje ją galima sukompiliuoti ir projekto kataloge rasti failą, su plėtiniu *.HEX, kurį, kaip žinia, mes ir turime įrašyti į mikrovaldiklį (iš tikro mums jį užteks atsineštį pas dėstytoją, bet primigtynai rekomenduoju patiems pabandyti pasidaryti veikiantį prototipą).
Sekantis žingsnis - šio failo įrašymas į mikrovaldiklį. Tam reikalui naudojama programa priklauso nuo naudojamo programatoriaus. Kol turėjau programatorių, veikiantį per LPT ar COM  kompiuterio sąsajas - naudojausi PonyProg2000, kai pasidariau USBTiny programatorių - teko naudotis AVRdude. Pirmoji - grafinė programa, antroji - valdoma komabdine eilute, bet visai smagi.
Po programos, kad ir paprastutės parašymo, jos sukompiliavimo ir įrašymo su programatoriumi į mikrovaldiklį jis jau kažką darys, o ne vartos elektrą veikdamas dykai.

ASEMBLERIS?
Kaip jau minėjau, AVR Studio aplinkoje galima programuoti C arba asemblerio kalbomis. Su C man daugmaž aišku, tačiau dėl asemblerio...
Programavimo kalbos skirstomos į aukšto ir žemo lygio programavimo kalboms. Jei grubiai - kuo aukštesnio lygio programavimo kalba - tuo ji suprantamesnė žmogui, tuo mažiau labai griežtos sintaksės ir panašiai. Šiose kalbose parašęs vieną operatorių jau gali tikėtis kažkokio rezultato. Tačiau gero niekada nebūna daug :) Kuo aukštesnio lygio kalba, tuo ji veiks lėčiau, nes visą programą turi "suvirškinti" kompiliatorius, kuris gauna užduoti žmonėms suprantamą kalbą paversti kalba, kurią supranta procesorius. Tas "suvirškintas" produktas ir veikia lėčiau, nes didelę dalį visų veiksmų kompiliatorius sugeneruoja pagal vienokias ar kitokias taisykles, tuo pačiu įtraukdamas ir komandas, kurių galima išvengti. Taip pat nukenčia naudojama vieta, kas mikrovaldikliuose yra ypač skaudi tema, nes, pavyzdžiui, ATtiny2313 mikrovaldiklyje tėra 2kB vietos programai, o kompiliatorius susikelia daug bibliotekų, iš kurių panaudoti gali būti tik kelios funkcijos.
Tuo tarpu kuo programavimo kalba žemesnio lygio, tuo ji suprantamesnė procesoriui, tuo komipliatoriui mažiau darbo, nes žmogus turi viską numatyti ir daryti pats. Įgudus galima iki minimumo sumažinti programos apimtis ir atlikimo laiką, tačiau rašyti ir galvoti reikia daugiau. Va toks ir yra tas asembleris. Jis turi baigtinį skaičių komandų, kurios tiesiogiai verčiamos į mašininį kodą - šešioliktainių skaičių rinkinį - tą jau aptartą *.HEX failą. Taip, programos C kalba irgi galų gale pasiekia tokią pabaigą (HEX), tačiau vietos užima daugiau.
Man asmeniškai priimtinesnė C kalba, tačiau viskas gali pasikeisti, be to, žinoti reikia ir tą, ir aną.

UŽDUOTIS
Gautoji užduotis visiškai paprasta, jei yra tekę bent kiek mirksinti diodukus su mikrovaldikliais: turi būti prijungti trys šviesos diodai prie mikrovaldiklio išvadų (Red, Green, Blue), kuriuos reikia keisti periodiškai, vėliau parodyti visas įmanomas spalvų kombinacijas bei kad bet kuris diodas sumirksėtų penkis kartus.
Geresniam veikimo suvokimui pravartu nusibraižyti veikimo algoritmą, t.y. funkcinę schemą. Pabandau:


Pabandysiu paaiškint, ką čia pribraižiau. Čia programos, kuri pakaitomis uždega tris skirtingus šviesos diodus R, G, B.
Kairėje pusėje - pagrindinė programa, kuri pradedama vykdyti nuo mikrovaldiklio nunulinimo (reset). Sekantis žingsnis - programos inicializacija. Tai gali apimti daug dalykų, tačiau šiuo atveju - nurodomas mikrovaldiklio tipas, kad kompiliatorius žinotų, su kokiu įrenginiu dirbama; registrams, kurie bus naudojami, suteikiami vardai, kad būtų lengviau prisiminti; nurodomi, kokie prievadai bus naudojami, jiems suteikiami lengvai įsimenami pavadinimai, nurodoma prievadų kryptis (įėjimas ar išėjimas, šiuo atveju - išėjimas) bei pradinės reikšmės, kurios dauguma atvejų turėtų būti nuliai (t.y. jei LED jungti prie paskutinių trijų bitų, tik įsijungus mikrovaldikliui neturi šviesti nei vienas diodas, 0b00000000).
Sekantis žingsnis - uždegamas vienas diodas, RED, kiti užgesinami. Taigi, dvejetainiu kodu galėtų atrodyti šitaip: 0b00000100.
Sekantis žingsnis - uždelsimas. Kadangi mikrovaldiklis dirba nemažu greičiu, nenaudojant uždelsimo šviesos diodai pakeis spalvas kas vieną mikrosekundę, jeigu mikrovaldiklis dirba standartiniu 1 MHz dažniu, tokiu atveju žmogaus akis matys tris degančius šviesos diodus. Tam reikia parašyti paprogramę pauzei, kurią ir matote dešinėje. Skaičius 100 čia tik pavyzdys, nes realiai reikėtų tūkstančių, kad pamatyti efektą.  Paprogramės veikimas paprastas: nustatomas kintamasis, šiuo atveju X=100, tada jis sumažinamas vienetu (X=X-1) ir tikrinama sąlyga: ar X jau sumažintas iki nulio? Jeigu ne, X mažinamas dar kartą, jeigu taip - grįžtama į pagrindinę programą. Kiekvienas žingsnis naudoja procesoriaus laiką, todėl jeigu ciklas reliame mikrovaldiklyje "apsisuks" 100.000 kartų, uždelsimas jau bus pastebimas :)
Sekantys žingsniai analogiški: uždegamas vis kitas šviesos diodas, užgesinami nereikalingi. Programos pabaigoje, po uždelsimo turi būti nuoroda grįžti į žingsnį, kuriame uždegamas raudomas šviesos diodas. Tokiu būdu sudarysime amžiną ciklą, kuris paieliui junginės prijungtus LED.
Analogiškai reikėtų sudarinėti ir kitų užduoties dalių schemas.

SCHEMA
Realiam tokios programos atvaizdavimui vėlgi rekomenduoju susidėlioti tikrą schemutę (jos kaina tikrai neviršys 20 lt). Ji turėtų atrodyti maždaug taip:

Rezistoriai, kurie jungiami nuosekliai diodams, reikalingi sumažinti srovę - mikrovaldiklio išvadai gali atiduoti berods iki 30 mA srovės, todėl per rezistorius galima jungti tiesiogiai. Norint pajungti ką nors, reikalaujančio daugiau galingumo, reiktų naudoti tranzistoriaus raktus. Šiuo atveju aš naudoju 1K rezistorius.
Turint tokią schemą telieka pasidaryti patį paprasčiausią programatorių (jeigu Jums pasisekė ir kompiuteryje yra LPT arba COM jungtis), arba kiek sudėtingesnį, bet tikrai pakankamai paprastą, USBTiny. Žinoma, galima stebėti kaip programa veikia naudojant AVR Studio simuliatorių, bet matyti realius mirksėjimus yra nepalyginamai maloniau :)
Kituose straipsniuose yra nuorodos į AVR Studio ir šiokios tokios instrukcijos, ko dar reikia. O šį straipsnį šiandien baigiu, nes matau, kad klaidos didėja geometrine progresija, eisiu miegot :) Tebūna ši dalis susipažinimui. Antrą dalį parašysiu, tikiuosi, greit :) Ten jau bandysim programuot.

Beje, atsakomybės apribojimas :) Neatsakau už Jūsų bet kokias bėdas darant ir bandant čia pateiktas schemas bei vadovaujantis čia esančia informacija. Būkit protingi ir atsargūs.

Ir dar: jei prirašiau neteisybės ar šiaip kokių vėjų - duokit žinot, išmoksiu ir parašysiu geriau :) Atsiprašau už žioplas klaidas dar irgi.


Komentarų nėra:

Rašyti komentarą