Mikrokontroleris – tai ne kas kita, kaip plačios(arba specializuotos) paskirties programuojama ir mikroprocesorių turinti schema,
telpanti į vieną DIP (angl. dual inline package), TQFP (angl. thin quad flat pack) ar kitokį įpakavimą. Pats mikrokontroleris gali būti programuojamas (t.y. įrašoma
sukompiliuota programa) vieną arba tūkstančius kartų. Įrašyta programa yra komandų rinkinys, kurios po vieną yra vykdomos mikroprocesoriaus kiekvieno darbo takto (angl. clock) metu. Svarbiausios charakteristikos, kuriomis pasižymi šie įrenginiai
– tam tikras Flash (max programos dydžio), EEPROM ir SRAM atminties
kiekis, toliau - fiksuotas operuojamų žodžio bitų skaičius 8-32bit, maitinimo įtampa (pvz. 3.3-5V), veikimo dažnis kHz ar MHz, I/O išvadų(angl. pins) skaičius ir kt. Taip pat reikia pastebėti, jog 8 bitų mikroprocesorius gali apdoroti 32 bitų instrukcijas/operacijas, tačiau tam prireiks apie 4 kartus daugiau laiko nei specializuotam. Tokia pilna CPU sistema dar kitaip vadinama SoC (angl. system
on chip) arba tiesiog vienluste sistema (angl. embedded system). Liaudiškai mikrokontrolerį priimta trumpinti tiesiog kaip „uc“, čia u – kaip
graikų „mikro“ raidė. Nors tokių lustų gamybos kaštai labai dideli (kadangi
reikia ypač tikslių mikro/nanometrinių technologijų), tačiau dėl didelių gamybos
ir pardavimo (jų gaminama milijonais vienetų) mastų, patys paprasčiausi ir
dažniausiai naudojami mikrokontroleriai yra pakankamai pigūs 5-25Lt
(priklausomai nuo pateikiamų funkcijų ir atminties kiekio). Geriausiai žinomi
ir plačiausiai naudojami mėgėjiški norvegų/amerikiečių kompanijos "Atmel" ATmega ir JAV
kompanijos Microchip Technology PIC
mikrokontroleriai. Abiejų kompanijų gaminių nesunku įsigyti artimiausioje
elektronikos prekių parduotuvėje (www.lemona.lt,
www.rcl.lt, www.evita.lt
ir pan.). Šiam straipsniui pasirinkau Atmel ATmega16 mikrokontrolerį dėl savo
populiarumo ir daugelio pavyzdžių kiekio.
Norint pradėti programuoti mikrokontrolerius
reikia pirmiausia bent šiek tiek būti susipažinus su diskretine elektronika (varžos, kondesatoriai ir pan.) bei C/C++ programavimo kalba,
todėl tikiuosi, jog skaitytojui su ja jau teko susipažinti ir pasipraktikuoti. Patys
mikrokontroleriai yra programuojami tiesiogiai jungiant juos prie kompiuterio per COM9/LPT/USB portus ir per specialų programatorių (kitais atvejais netgi ir tiesiogiai). Tokiu būdu šiandien lengviausia programuoti šiuos lustus - programa parašoma kompiuteriu kokia nors programavimo kalba, toliau sutransliuojama į mikroprocesoriaus suprantamų komandų seką (t.y. dvejetainio/šešioliktainio(hex) kodo pavidalą) ir įrašoma į Flash atmintį.
Programatorius – tai prietaisas, paverčiantis iš/į
PC ateinančius signalus į mikrokontroleriui suprantamus signalus. Taip pat jis tiesiog gali suvienodinti skirtingų jungčių įtampų lygius su mikrokontrolerio naudojama įtampa (pvz. COM9 -15...+15V su TTL 0-5V įtampa). Programatorius nėra privalomas ir būtinas siekiant programuoti mikrokontrolerius, kai kurie lustai palaiko ir tiesioginį programavimą per LPT ir pan. Interneto platybėse yra nemažai pavyzdžių, kaip tai padaryti, jeigu po ranka kompiuteris su tokia jungtimi. Tačiau mano atveju pasitaikė tik COM portas kompiuteryje, tad teko pasipraktikuoti šiek tiek daugiau. Norintiems šiuolaikiškesnio - USB pavyzdžio, taip pat yra ir USBasp programatorius Atmega programavimui. Kad ir kokį variantą benaudotumėte, principai išlieka tie patys.
Taigi, naudojant COM, reikės pirmiausia susikonstruoti programatorių, kad galėtume užprogramuoti patį mikrokontrolerį. Internete galima rasti labai daug įvairių programatorių, vienas iš paprastesnių ir dirbančių per COM atrodo taip:
Taigi, naudojant COM, reikės pirmiausia susikonstruoti programatorių, kad galėtume užprogramuoti patį mikrokontrolerį. Internete galima rasti labai daug įvairių programatorių, vienas iš paprastesnių ir dirbančių per COM atrodo taip:
Diagramoje raudona spalva apibrėžta universalaus
programatoriaus schema (tinkanti programuoti ir kitas Atmega versijas), o pilka spalva
parodyta Atmel ATmega mikroschema ir jos prijungimas prie programatoriaus. Prie
skirtingų elementų parašiau tokius parametrus, kuriuos man pavyko gauti ar rasti,
tačiau galima naudoti ir kitus panašaus nominalo komponentus. Pats
programatorius turi COM jungtį jungiamą į PC ir prie jos prijungtą MAX232
mikroschemą, reikalingą suvienodinti COM įtampų lygius su Atmega TTL 0-5V
lygiais. Taip pat tranzistorių (reset signalo inversijai) ir porą keraminių bei
vieną elektrolitinį(+) kondensatorių įtampai, paduodamai į Atmega, išlyginti/stabilizuoti. Elektrolitinis ar keramikinis kondensatorius - nėra skirtumo. Taip pat siūlau atkreipti dėmesį į tai,
jog programatoriui, kaip ir daugliui kitų, reikalingas atskiras 5V maitinimas!
Nusprendžiau atskirai sulituoti programatorių, kad būtų galima lengvai vėliau dar kartą jį panaudoti, o bandomą ATmega mikrokontrolerį tiesiog lituoti kitoje plokštėje. Jei nenorite atskirai daryti programatoriaus, tai skaitykite toliau, nes pateikiama ir schema, kurioje parodytas pilnas programatoriaus ir Atmega prijungimas.
Nusprendžiau atskirai sulituoti programatorių, kad būtų galima lengvai vėliau dar kartą jį panaudoti, o bandomą ATmega mikrokontrolerį tiesiog lituoti kitoje plokštėje. Jei nenorite atskirai daryti programatoriaus, tai skaitykite toliau, nes pateikiama ir schema, kurioje parodytas pilnas programatoriaus ir Atmega prijungimas.
Programuojant mikrokontrolerius iš pradžių
gali būti tikrai daug kas neaišku, todėl dar siūlau atsisiųsti oficialius max232 ir atmega16
aprašus (datasheets), kuriuose be visų funkcijų aprašymų dar pateikta ir tiksli
išvadų schema, registrų reikšmės ir t.t. Sėkmingai pasigaminus programatorių toliau galėsime atlikti jo prijungimą prie Atmegos. Pilna jungimo schema su išvadų numeriais pavaizduota žemiau.
Naujoje schemoje programatorius sujungtas tiesiai
su Atmega mikrokontroleriu, tokia schema manau yra žymiai aiškesnė, tačiau ne
tokia universali, jei realizuojame viską vienoje spausdintinėje plokštėje (angl. PCB).
Prie mikrokontrolerio testavimui ir vaizdumui taip pat prijungti 8 LED diodai,
pagal kuriuos bus pradžioje lengva matyti visus kontroliuojamo registro 8 bitus.
Mikrokontrolerio širdis – taktinis generatorius (t.y. kvarcinis rezonatorius), su Atmega16 galima naudoti 4-16MHz dažnio kvarcą. Aš panaudojau turėtą 12MHz, tačiau šį komponentą su šalia esančiais kondensatoriais pradžioje galima ir praleisti, nes jis nebūtinas. Atmega jau tiesiai iš gamyklos turi sukonfigūruotą vidinį 1 MHz taktinį generatorių, kurio pirmiems bandymams gali visiškai pakakti. Norint prijungti didesnio dažnio generatorių, reikia teisingai nustatyti "fuse" konfigūracinius bitus, esančius Atmega mikroprocesoriuje. Tai daroma atskirai Fuse bitų programavimo metu ir apie tai šiame straipsnyje nešnekėsiu.
Mikrokontrolerio širdis – taktinis generatorius (t.y. kvarcinis rezonatorius), su Atmega16 galima naudoti 4-16MHz dažnio kvarcą. Aš panaudojau turėtą 12MHz, tačiau šį komponentą su šalia esančiais kondensatoriais pradžioje galima ir praleisti, nes jis nebūtinas. Atmega jau tiesiai iš gamyklos turi sukonfigūruotą vidinį 1 MHz taktinį generatorių, kurio pirmiems bandymams gali visiškai pakakti. Norint prijungti didesnio dažnio generatorių, reikia teisingai nustatyti "fuse" konfigūracinius bitus, esančius Atmega mikroprocesoriuje. Tai daroma atskirai Fuse bitų programavimo metu ir apie tai šiame straipsnyje nešnekėsiu.
Straipsnio pabaigoje pateikiu ir PCB
plokščių takelių vaizdus bei Sprint Layout programa sukurtą .lay schemą, jei susiruošite pasigaminti
ėsdintą spausdintinę plokštę. ĮSPĖJIMAS: pcb takelių schemos detaliai
netikrintos ir neišbandytos, nors daryta pagal tą
pačią schemą - naudokite atsargiai.
Kaip man pavyko pagaminti schemas – toliau
nuotraukose. Visiškai išvengti orinio montažo nepavyko, tačiau svarbiausia, jog
schema veiktų. Montažinės plokštės tinka greitam paeksperimentavimui, tačiau turi ir trūkumų - ilgai kaitinant gali atsilupti varis, lengvai įmanoma užtrumpinti gretimus elementus, nėra kompaktiškos.
Baigus visus litavimo darbus lieka prijungti viską
prie PC, įjungti išorinį maitinimą ir pabandyti suprogramuoti demo programą.
Mikrokontroleriai dažniausiai programuojami C kalba (galima ir asembleriu pagal pageidavimą), toliau kodas transliuojamas ir kompiliuojamas į mašininį kodą (.hex failai), gautą hex failą pateikiame PonyProg ar AVR Burn-O-Mat programai, kuri persiunčia (įrašo) tą dvejetainį kodą į mikrokontrolerio Flash atmintį. Naudojant C kalbą gauname pilną prietaiso valdymą, tiesioginį priėjimą prie atminties ir aukštesnį abstrakcijos lygį nei programuojant asembleriu. Tačiau čia irgi lengva padaryti klaidų. Kartais programuojama asembleriu dėl kodo spartos/efektyvumo ir programos dydžio kompaktiškumo sukompiliavus (mikrokontroleryje Flash atmintis visgi ne begalinė!), tačiau tai vertėtų daryti tik įvertinus patirties, visus efektyvumo ir laiko kaštus. Šiais laikais kompiliatoriai labai gerai optimizuoja kodą, tad be didelių pastangų gauname efektyvų mašininį kodą.
Programavimui patariu naudoti Atmel kompanijos
siūlomą IDE programavimo aplinką AvrStudio,
o hex kodo įrašymui į mikrokontrolerį jau minėtas – AVR
Burn-O-Mat su AVRDUDE arba PonyProg programas. Atsidarius AVR Studio pirmiausia sukuriame naują projektą ir
sukonfigūruojame, jog galėtume kompiliuoti C kodą ATmega16 mikrokontroleriui. Tam
pasirenkame AVR GCC kompiliatorių ir įrašome projekto vardą, C kodo failo pavadinimą
ir judame toliau.
Atmega modelio pasirinkimas - jei programą reikės
derinti pradžioje, tada geriausia pasirinkti "AVR Simulator". Būtina teisingai
nustatyti, jog kompiliuojame kodą būtent pasirinktam Atmega16 mikrokontroleriui,
to nepadarius – programa gali nekorektiškai veikti arba iš viso neveikti! Skirtingi Atmega modeliai turi skirtingas instrukcijas, registrus ir pan., todėl to pamiršti nevalia!
Dar prieš rašant kodą šiek tiek teorijos. Jei
pažvelgsime į oficialią Atmega16 išvadų schemą, pamatysime išvadus pažymėtus
PAx, PBx, PCx, PDx su skirtingais indeksais ir pan. Jie skirti duomenų įvedimui
arba išvedimui. Kai kurie išvadai yra naudojami specialiai valdymui/komunikacijai MISO, MOSI arba atlieka antrą funkciją ir pan. Išvadų kryptis IN/OUT gali būti dinamiškai
keičiama – taigi Atmega, gali per tą patį išvadą siųsti informaciją, o kitu (ne
tuo pačiu) momentu per tą patį ir gauti. Informacijos kryptis gali būti keičiama programiškai. Tai nustatoma keičiant
reikšmes specialiuose registruose DDRA, DDRB, DDRC, DDRD. Atitinkamai kiekvienam
išvesčių tipui A-D. Informacijos nuskaitymas/išvedimas atliekamas
skaitant/įrašant atitinkamo registro PORTA, PORTB, PORTC ar PORTD reikšmę programos kode. Visi
šie registrai yra 8 bitų ilgio ir kode jau yra apibrėžti per specialias pridedamas AVR
bibliotekas. Telieka jomis teisingai pasinaudoti. Neatsitiktinai tuos aštuonis
LED diodus prijungėme būtent prie PD0-PD7 išvadų, nes kaip tik toliau pavyzdžiuose valdysime DDRD
ir PORTD registrus. PD0-PD7 išvadai kaip tik ir sudaro vieną baitą susidedantį iš 8 bitų - t.y. visą registrą.
Primenu, jog vienas bitas yra loginė reikšmė, kuri yra 0 (išjungta) arba 1 (įjungta), to atitikmuo atmega išvaduose yra atitinkama įtampa. Jei bitas = 0,
tai išvade turime 0V, jei bitas = 1, tai išvade bus +5V, manau pakankamai
paprasta. Taigi, pasiaiškinę principus, atsidariusiame AVR Studio projekto kodo lange rašome pirmąjį kodą. Paprasčiausią vienos lemputės įžiebimą. Kad lemputė užsidegtų, turime jai paduoti įtampą, šiuo atveju +5V. Tai atitinka vieno bito reikšmę programiškai pakeisti iš 0 į 1 valdomame registre.
Pirmos programos kodas:
Kodo pradžioje visada prisijungiame standartines
AVR bibliotekas (angl. headers), jose apibrėžti registrai ir kitos reikšmės. Visas kontrolerio
kodas rašomas main() metode. Įprasta, jei teko susidurti su konsolinių programų rašymu-programuoti. Pradžioje DDRD
nustatome 255 arba 0xFF reikšmę, tai atitinka visus 8 bitus nustatytus į 1. Tokių
būdu nurodome, jog visi PD0-PD7 išvadai bus naudojami duomenų išvedimui. Norint juos nustatyti įvedimui, turėtume reikšmę pakeisti į 0 arba 0x00. Su PORTD =
0x01 įjungiame pirmą LED lemputę (t.y. pirmas 8 bitų registro bitas nustatomas į 1, kiti bitai lieka 0). Taip į PD0 išvadą paduodame +5V įtampą. Toliau begaliniame while cikle leidžiame
mikrokontroleriui nieko nedaryti, bet neuždaryti/neužbaigti vykdomos programos. Čia cikle vykdoma
asemblerio NOP (angl. no operation) komanda, kuri leidžia procesoriui praleisti taktą nieko nedarant. Galėtume šioje vietoje nerašyti asemblerio komandos, tačiau tai apsaugo nuo pernelyg gero kompiliatoriaus optimizavimo. Kompiliatoriai labai gerai moka optimizuoti kodą, jei kodas nieko nedaro naudingo (kaip mūsų atveju - suka begalinį tuščią ciklą), net gali jį išmesti iš viso. NOP komanda yra vienas iš būdų kaip priversti procesorių neužbaigti programos vykdymo.
Toliau sukompiliuojame kodą (F7), įsitikiname, jog AVR
studio nepraneša jokių kritinių klaidų. Jei viskas tvarkoj – gausime
sukompiliuotą hex kodo failą (projekto direktorijoje), jo toliau reiks įrašyti programą į
mikrokontrolerio atmintį. Pasileidžiame AVR
Burn-O-Mat ir sukonfigūruojame: nurodome kelius, pasirenkame COM prievadą ir
programatoriaus tipą į „siprog“. Jei tokio programatoriaus nėra, tai atsidarome
avrdude.conf failą (jį galima rasti ten, kur instaliuotas AVRDude) ir pridedame žemiau esančias eilutes bei paleidžiame iš
naujo AVR Burn-O-Mat.
programmer
id = "siprog";
desc = "Lancos SI-Prog";
type = serbb;
reset = ~3;
sck = 7;
mosi = 4;
miso = 8;
;
Toliau pasirinkę .hex failą įrašome jį į Flash
atmintį spausdami Write.
Jei viskas sėkmingai ir programatorius randamas, tai programa įrašoma į mikrokontrolerio Flash atmintį.
Sėkmingas įrašymas |
Jei rodoma klaida, kad programatorius nerastas
arba nėra ryšio su juo, tai teks patikrinti, ar viskas teisingai sukonfigūruota,
nustatytas reikiamas COM portas, sulituota ir nepadaryta klaidų montuojant
komponentus. Šiame etape viena iš dažniausių problemų būna ryšys su
programatoriumi. Na, o jei viskas gerai, tai pirmoji LED lemputė turėtų
įsižiebti ir degti iškart vos tik programa sėkmingai įrašoma.
Dažniausia klaida - nėra ryšio su programatoriumi ar ATmega |
Dabar pabandysime parašyti programą, su mirksinčia
lempute. Principas nesudėtingas – įjungti lemputę, palaukti trumpą laiką, po to išjungti lemputę ir vėl palaukti. Visa tai kartoti be galo daug kartų. Tai
galima realizuoti while cikle, tik reikia papildomai parašyti laukimo funkciją,
kad būtų įmanoma laukti reikiamą laiko tarpą.
Tokiose realaus laiko sistemose laiką skaičiuoti tiksliai
yra gana sudėtinga, kadangi taktinio generatoriaus dažnis nėra visiškai
stabilus ir idealus - jis svyruoja šiek tiek dėl temperatūros kaitos ir pan. Nors kvarciniai rezonatoriai pasižymi ypač tiksliu dažnio išlaikymu, neretai priimama, kad mikroprocesorius veikia būtent tuo dažniu, kuris rašomas ant kvarco ir į svyravimus nekreipiama dėmesio. Kitaip tariant, priimama sąlyga, kad jei taktinis generatorius dirba 1 MHz dažniu, tai jis ir atlieka 1000000 operacijų per sekundę. Kadangi paprastai operacijai/instrukcijai atlikti reikia tik vieno takto (kaip Atmega‘oje), tai
vienai tokiai operacijai atlikti reikės 1/1000000 = 1 us laiko. Pasikeitus dažniui galima atitinkamai perskaičiuoti reikšmę. Iš to jau galime pakankamai gerai paskaičiuoti laiką - tarkime, norėdami palaukti 1ms, turėtume atlikti 1000 elementarių operacijų. Tokia matematinė operacija kaip skaitliuko padidinimas/sumažinimas (sudėtis/atimtis) gali būti traktuojamas kaip viena paprasta operacija. Dabar, jeigu šią operaciją pakartosime 1000 kartų, tai CPU bus užimtas 1000 taktų ir tai apytiksliai užtruks apie 1 milisekundę. Taigi manau dabar viskas pakankamai aišku.
Šiuose mano pateikiamuose pavyzdžiuose priimta,
jog mikrokontroleris naudoja vidinį Atmega 1 MHz taktinio dažnio generatorių. Jei naudosite
išorinį kvarcinį rezonatorių ar kitą generatorių, tai atitinkamai programos vykdymas pagreitės apie
4x – jei 4MHz, 12x – jei naudojamas 12 MHz kvarcas ir pan., tada užlaikymo
funkciją teks pakoreguoti. Kaip ir minėjau anksčiau, naudojant išorinį kvarcą ar generatorių teks pakeisti atitinkamus Atmega
„fuse“ bitus. Jeigu naudojamas vidinis Atmega generatorius, tai nieko papildomai keisti nereikia.
Naujoje programoje viską darome panašiai kaip ir
prieš tai, tik dabar vykdymas perkeliamas į „amžiną“ while ciklą ir išvedamos
reikšmės į išvadus keičiamos dinamiškai laike. Laukimo funkcijoje priimta, kad skaitliuko
pamažinimas vienetu + NOP yra ta elementari operacija atliekama apytiksliai
per 1 us, todėl galima pakankamai gerai skaičiuoti laiką, tą patį patvirtina ir
praktika. Įrašius programą į mikrokontrolerio atmintį, gausime kas sekundę mirksintį LED.
Reziumė
Panagrinėjome paprasčiausius programų pavyzdžius
– palieku skaitytojui pabandyti kitus scenarijus su užuominom: įjungti/išjungti keletą LED lempučių
vienu metu (t.y. įrašyti kitą reikšmę į PORTD), padaryti bėgančią grandinėlę (įjungti
lemputes paeiliui – panaudojus numerio skaitliuką ir bitų operacijas), palaipsniui (pulsuojančiai) valdyti lemputes (panaudoti PWM – pulse width modulation), t.y. kai uždegimo/užgesinimo trukmės ilgis keičiasi dinamiškai laike.
Visus projektų dokumentus ir sukompiliuotas
programas galite rasti čia.
Jei viską pavyko atkartoti, tai galime pasidžiaugti - įvykdėme tą nelengvą pradžią į mikrokontrolerių
programavimą. Žinau, jog nėra ji tokia lengva ir aiški, tačiau pasigaminus
veikiantį programatorių galima toliau praktikuotis, pradėti
taikyti išmoktas žinias vis rimtesniuose projektuose, valdyti kitus prietaisus,
rinkti sensorinę informaciją, pasigaminti mažai įtampos naudojančius prietaisus
(atmega „L“ pažymėti modeliai) ir t.t. Su mikrokontroleriais atsiveria
plačiausios elektronikos durys. Išmokus dirbti su vienu Atmega modeliu, nesunku bus panaudoti ir kitą. Įgudus siūlau netgi išbandyti kitus, pvz. PIC
mikrokontrolerius, 8-32bitų ARM procesorius ar rimtesnes Xilinx bei Altera FPGA matricų sistemas, kuriose programuojama VHDL ar Verilog kalbomis ir patys galite susikurti savo mikroprocesorių. Sėkmės!
Sveikas,
AtsakytiPanaikintinorečiau truputėlio pagalbos su Atmega 8. Pakeitus fuse bitus atmegos į lfuse:D7, o hfuse:D9, nebeišeina nuskaityti atmegos. Papildomai mėginau pajungti 4 ir 12 mhz kvarcinius rezonatorius, tačiau tai nedavė jokios naudos, vis tiek neina nuskaityti mikrokontrolerio. Ar tai gali būti del fuse bitų, o gal tiesiog sugebėjau sugadinti atmegą?
Sveikas,
PanaikintiGreičiausiai, kad dėl fuse bitų. Reikšmės lfuse:D7, o hfuse:D9 duoda clock select bitus tokius: CKSEL3..0=0111 (galima pasitikrinti su AVR Burn-O-Mat programa). Oficialiame atmega8 datasheet "Clock Sources" skyriuje Table 2. parašyta, jog 0111 reikšmė papuola į "External RC Oscillator" diapazoną. Taigi kvarcinis rezonatorius šiuo atveju netiks - atmegą atgaivinti galima pajungus prie XTAL1 ir GND pin'ų 3-8MHz RC osciliatorių - t.y. varžą ir kondensatorių. Schemą ir detalesnę informaciją patarčiau žiūrėt minėtame datasheet dokumente - "External RC Oscillator" skyriuje viskas paaiškinta bei kokių nominalų komponentų reikia.
Kaip ir sakei, pajungus atitinkamą varžą bei kondensatorių atmega atgijo. Ačiū už greitą atsakymą
AtsakytiPanaikintiSveiki,
AtsakytiPanaikintiMan reikalingas zmogus galintis suprojentuoti PCB plokste pagal esama pvz. Taip pat nuklonuoti arba sukurti nauja koda mikrokontroleriui.
Sveiki,
PanaikintiŠiais klausimais galite susisiekti e-paštu: be.lietaus [slyva] gmail.com
Labas,
AtsakytiPanaikintiGal programuoji su VHDL? reiktų šiek tiek pagalbos.