SpaceX je za sodobna vesoljska plovila Starship v razvoju priredil krmilno elektroniko in programje svojega »delovnega konja«, rakete Falcon 9, a šele v peto se mu je posrečil njegov pristanek z višine 10 km. Podobno velja za domačo elektroniko. Kako hitro in enostavno razvijati? Kako testirati in dopolnjevati ugnezdeno programsko opremo? Kaj narediti, da bo zanesljivo delovalo tudi, ko gre kaj narobe? Kdaj začeti vsakodnevno uporabljati?
Avtor: dr. Simon Vavpotič
https://sites.google.com/site/pcusbprojects
2021-298-40
Arduino razvojno okolje je primerno za programiranje skoraj vseh procesorsko zmogljivejših mikrokontrolerjev. V preteklem nadaljevanju smo spoznali chipKIT razširitev za Arduino razvojno okolje in se nato lotili pogostih napak v ugnezdeni programski opremi pri gradnji uporabniških vmesnikov, še posebej, kako pravilno sprogramiramo ugnezdeno programsko kodo za nadzor mikro tipk, pa tudi en digitalni priključek hkrati uporabimo za prižiganje in ugašanje LED-ice ter zajemanje stanja mikro tipke. Videli smo tudi, kako preko ene digitalne (mikro) tipke podamo več kot en bit informacije. Naučili smo se pravilno meriti čas s sistemsko uro in na koncu zgradili digitalno preklopno uro z upravljanjem prek Wi-Fi, ki ne potrebuje 32,768 kHz oscilatorja, ampak zadovoljivo natančno meri čas kar s pomočjo glavnega oscilatorja in sistemskega števca ESP8266 modula.
Tokrat se lotimo bomo preverili še alternativno možnosti izdelave tipkovnice, ki jo povežemo z mikrokontrolerjem le preko enega analognega vhoda. Nato se lotimo pogostih napak pri pisanju ugnezdene programske opreme, poleg tega pa še: na kaj moramo paziti pri krmiljenju grafičnega prikazovalnika? Kako popraviti zamaknjen izpis? Kako izdelati generator znakov? Kako risati grafične elemente? Kako hitro lahko prenašamo podatke? Kako prilagodimo programsko kodo za I2C prikazovalnik za SPI prikazovalnik in obratno? Ali lahko prikazovalnik z vodilom SPI premenimo v prikazovalnik z vodilom I2C? Zanimalo nas bo tudi, kako zagotovimo stabilno komunikacijo med dvema Arduino napravama, če za prenos podatkov pri uporabimo diagnostični zaporedni RS232 vmesnik. Kako ločimo komunikacijo naprav od diagnostičnih sporočil?
Ta vsebina je samo za naročnike
Kako zajemati podatke preko analognega vhoda?
Sodobni mikrokontrolerji imajo vgrajenih po več A/D pretvornikov, ki jih lahko s pridom uporabimo tudi za zajem signalov s tipkovnice. Tipkovnico lahko izdelamo iz uporov in mikrostikal tako, da deluje po principu A/D pretvornika, tako da je vrednost izhodne napetosti tipkovnice odvisna do pritisnjenih tipk. Resda moramo za branje tipk žrtvovati del zmogljivosti A/D pretvornika, a prihranimo lahko veliko digitalnih vhodov. Vendar moramo paziti, da za referenčno napetost A/D pretvornika izberemo napajalno napetost mikrokontrolerja, saj so spremembe vrednosti napetosti proporcionalne pritisnjeni kombinaciji tipk v razponu od napajalne napetosti mikrokontrolerja proti 0 V. Več kot pritisnemo tipk, nižja je izhodna napetost.
Na sliki 2 prikazani primer je za tipkovnico z 8 tipkami, pri kateri je mogoče prek enega vhoda zaznati vse kombinacije osmih tipk. Če dovoljeno število hkrati pritisnjenih tipk omejimo, je kombinacij napetosti še bistveno več in lahko priklopimo dodatne tipke. Vezje najlažje izdelamo tako, da namesto enakih 1k-ohmskih uporov, vežemo vsako tipko prek lastnega upora z unikatno vrednostjo. Vendar pri tem ni zagotovila, da bomo ob pritisku več tipk hkrati vsakokrat dobili drugačno vrednost napetosti.
Po drugi strani, je analogna tipkovnica zelo občutljiva na neželena nihanja napetosti in zahteva dobro glajenje napajalne napetosti. Vendar, če namesto osmih tipk na isti vhod priklopimo le 3 ali 4 tipke, lahko njihove analogne vrednosti precej lažje ločimo. Občutljivost na neželene napetostne motnje lahko zmanjšamo s pametno zasnovo algoritma za branje vrednosti tipk, podobno kot smo to naredili v preteklem nadaljevanju. Pri branju vrednosti z A/D pretvornika lahko podaljšamo čas pretvorbe in obenem povečamo ločljivost A/D pretvorbe. Ker je tipkanje počasno opravilo, lahko z A/D pretvornika pri vsakem preverjanju stanja tipkovnice zajamemo po več vrednosti, izmed katerih izločimo morebitne anomalije, ali pa namesto tega določimo časovni interval (npr. 100 ms), v katerem mora A/D pretvornik izmeriti približno enake vrednosti napetosti, da lahko predpostavimo, da je izmerjeno stanje tipkovnice pravilno. Kot vidimo, je zanesljivo branje stanja analogne tipkovnice podobno branju stanja posameznih digitalnih tipk, le da moramo ločevati tudi med več napetostnimi nivoji.
Prenos podatkov po koaksialnem kablu namesto po zraku
Nekatere izvedbe Wi-Fi modulov, kot so ESP8266, ESP32 in WINC1510, imajo miniaturni antenski priključek, prek katerega jih lahko povežemo z anteno ali pa kar medsebojno, če prej poskrbimo za skupno maso. Z žico povezana modula razumljivo zato zaznata odlična medsebojna signala, obenem pa tudi signale bližnjih modulov, ki oddajajo prek anten. Skratka, ustrezen kabel zagotavlja bistveno boljšo povezavo kot prenos po zraku, kar pa je najpomembneje, zadeva deluje. Vsekakor pa boste za neposredno povezavo dveh Wi-Fi modulov, ki oddajata na frekvencah okoli 2,4 GHz, potrebovali tudi ustrezen koaksialni kabel, ki je bistveno tanjši kot tisti za FM radijsko področje. Kratek košček tovrstnega kabla z ustreznima priključnima konektorjema za Wi-Fi modul in anteno, prilagajo tudi proizvajalci Wi-Fi anten. Če imamo dva taka kabla, lahko priključka za anteni odrežemo, preostanka kablov pa povežemo skupaj, tako da dobimo kabel z dvema priključkoma za Wi-Fi modul. Pri tem je pomembno, da je spoj čim manjši in da ga na koncu električno izoliramo in ovijemo s koncema oklopne žice iz obeh koncev kablov. Tako dobimo skoraj idealen kabel za povezavo dveh Wi-Fi modulov. Pri daljšem kablu je treba biti pri izbiri njegove dolžine previden, saj lahko pride med napravami, ki se napajajo prek različnih neozemljenih ali slabo ozemljenih napajalnikov tudi do razlike električnih potencialov med Wi-Fi moduloma.
No, kakorkoli, sam sem preizkusil modula, ki sta bila precej blizu skupaj, vendar povezani kar z 10 cm koškom žice za 220 V, saj ustreznega koaksialnega kabla nisem imel pri roki. Slabljenje okoli -6 dB jasno kaže, da je signal veliko veliko močnejši kot pri najbolj kakovostnem prenosu preko zraka. Z ustreznim koaksialnim kablom bi vsekakor dobil boljše rezultate, verjetno pa bi obenem zajel manj okoliških Wi-Fi postaj, kar je v strnjenih blokovskih naseljih več kot ugodno.
Povejmo še, da je mogoče z ustreznim koaksialnim kablom povezati tudi Wi-Fi module brez vtičnic, tako, da njihove PCB antene razklenemo in na vsakega od modulov prispajkamo koaksialni kabel. Pomembno je le, da pri tem masi povežemo skupaj, prav tako pa tudi antenska priključka. Podrobnosti si oglejte na sliki. Nekateri moduli imajo PCB anteno, obenem pa še priključek za zunanjo anteno. Če želimo uporabiti priključek za zunanjo anteno, moramo miniaturni 0-ohmski upor prespajkati v drug položaj. To je nemalokrat težko, vendar lahko izgubljeni ali uničeni upor brez skrbi nadomestimo s koščkom žice ali kepico spajke, saj so kontakti zelo blizu skupaj. Če niste dovolj spretni, lahko namesto PCB anteno predelate v antenska priključka in neposredno prispajkan antenski koaksialni kabel.
Kako zamenjati prednastavljeno SPI funkcionalno enoto?
Začeli smo z nekaj zamislimi za nove projekte, zdaj se lahko lotimo še nekaterih pogostih napak pri pisanju ugnezdene programske kode. Arduino programske knjižnice različnih proizvajalcev mikrokontrolerjev imajo za določene funkcionalnosti lahko ugnezdene prednastavljene vrednosti. Denimo, Espressif Systems-ova podporna knjižnica za podporo delovanja ESP32, ima ugnezdeno konstanto SPI, ki določa, katero SPI funkcionalno enoto za serijsko komunikacijo bomo uporabili, če v klicih funkcij tega podatka eksplicitno ne navedemo. Pri ESP32, ki ima tri takšne funkcijske enote, je to VSPI. Kadar želimo uporabiti drugo enoto, moramo spremeniti kodo programske knjižnice, tako da na koncu programske datoteke SPI.cpp (datoteko najdemo v mapi: <sistemski_disk>:Users<windows_uporabniško_ime>AppDataLocalArduino15packagesesp32hardwareesp32<različica>librariesSPIsrcSPI.cpp) definiramo SPIClass SPI(HSPI), s čemer to naredimo za vse projekte. Alternativna možnost je, da definicijo SPIClass SPI(VSPI) zakomentiramo z »//«, kar pomeni, da bomo morali v lastnih aplikacijah vsakokrat sami izdelati razred SPI, preden ga bomo lahko začeli uporabljati SPI funkcije. Več kot očitno je, da so pisci Arduino knjižnice mislili predvsem na manj izkušene programerje, zato so definicijo razreda SPI vnesli kar v programsko knjižnico, kar pa morda ni najbolje niti s stališča racionalne porabe pomnilnika ESP32, ko SPI komunikacije ne potrebujemo. Več lahko preberete na spletni strani sites.google.com/site/pcusbprojects v članku o gradnji spletnega radija s priključkom na ožičeni Ethernet.
Alternativa opisanemu postopku je tudi definicija SPI razreda (oz. SPIClassa) z drugo oznako, denimo SPIClass hSPI(HSPI), vendar tu nemalokrat naletimo na težave, če želimo uporabiti programske knjižnice višjega nivoja, ki uporabljajo prednastavljeno SPI funkcionalno enoto, še posebej, če zanje nimamo izvorne kode.
Kako prilagoditi ugnezdeno programsko kodo za grafični prikazovalnik?
Segmentni in tekstovni prikazovalniki se počasi umikajo grafičnim tudi pri enostavnih domačih projektih. Grafični prikazovalniki dajejo omogočajo načrtovalcu ugnezdene programske opreme veliko svobode in skoraj neomejene možnosti pri izbire znakovnih naborov. Veliko jih temelji na SH1306 mikrokontrolerju, ki je zelo podoben starejšemu SH1106 mikrokontrolerju. Oba lahko z nadzornim mikrokontrolerjem ali računalnikom komunicirata prek SPI ali I2C vodila. K sreči ista Arduino programska knjižnica podpira tudi starejši krmilnik, težava pa je v obstoječi programski kodi spletnega radia, ki je prilagojena za uporabo SH1306. Zato se slika zamakne. K sreči lahko to popravimo v zagonski programski kodi tako, da v nabor registrov vnesemo popravljene začetne vrednosti.
Nekoliko več dela je, če želimo originalno Arduino programsko kodo v knjižnici SSD1306.h za komunikacijo prikazovalnika prek vodila I2C predelati za komunikacijo prek vodila SPI. V ta namen moramo spremeniti metodi SSD1306::SSD1306 in SSD1306::display. Poglejmo, kako to naredimo pri SSD1306::SSD1306. Ker spletni radio iste SPI priključke uporablja za različne naprave, moramo pred vsako SPI komunikacijo pridobiti SPI vodilo za izvedbo ene nedeljive transakcije. To naredimo z ukazom SPI.beginTransaction, rezervira SPI vodilo vse do izvedbe ukaza SPI.endTransaction. Vmes lahko komuniciramo s prikazovalnikom.
SSD1306::SSD1306(uint8_t sda,uint8_t scl){ esp_err_t espRc; ssdbuf =(page_struct*)malloc(8*sizeof(page_struct) ); SPI.beginTransaction(SSD1306_SPI); pinMode(ini_block.tft_cs_pin,OUTPUT); pinMode(ini_block.tft_dc_pin,OUTPUT); digitalWrite(ini_block.tft_cs_pin,LOW); // display select digitalWrite(ini_block.tft_dc_pin,LOW); // command font = SSD1306font ; delay(100); // wait 100 ms for display init SPI.write(OLED_CONTROL_BYTE_CMD_STREAM); SPI.write(OLED_CMD_SET_CHARGE_PUMP); SPI.write(0x14); // Enable charge pump SPI.write(0x4); // lower column address= 4 – SH1106 SPI.write(0x10); // highter clumn address =0 – SH1106 SPI.write(OLED_CMD_SET_CONTRAST); SPI.write(255); – SH1106 SPI.write(OLED_CMD_DISPLAY_ON); digitalWrite(ini_block.tft_cs_pin,HIGH); // display deselect SPI.endTransaction(); clear(); // Clear the display }
Inicializaciji, pri kateri na začetku kreiramo tudi pomnilniško polje za vmesno shranjevanje slike zaslon, sledi uporaba prikazovalnika. Metoda SSD1306::display ob vsakem klicu osveži vsebino zaslona iz pomnilniškega polja za vmesno shranjevanje. Zato je poleg prikazane inicializacijske metode edina, ki uporablja SPI vodilo. Ostale funkcije lahko pri spremembi načina komunikacije s prikazovalnikom ostanejo nespremenjene. Omenimo še, da je pomembna razlika med uporabo I2C komunikacije in SPI komunikacije pri SH1306 tudi ta, da pri SPI komunikaciji v prikazovalnik samo pišemo podatke, pri I2C komunikacijo pa jih lahko tudi beremo. Je pa I2C komunikacija pri isti hitrosti prenosa podatkov nekoliko počasnejša. Ker uporabljamo vmesno shranjevanje zaslonske slike, z I2C komunikacijo ne bi kaj dosti pridobili.
Omenimo še, zakaj se splača uporabljati vmesno shranjevanje zaslonske slike. Mikrokontroler dostopa do pomnilnika prikazovalnika bistveno počasneje kot do svojega delovnega pomnilnika, ne glede na to ali uporabljamo I2C ali SPI vodilo. Vmesno shranjevanje se splača organizirati tako, da ima vsaka pomnilniška banka svojo zastavico pisanja, ki se postavi, ko se spremeni njegova vsebina. SH1306 organizira zaslonski pomnilnik v 8 blokov po 136 bajtov. Zato lahko metoda SSD1306::display vsakokrat osveži le spremenjene, kar v praksi pogosto pomeni veliko manj prenosa podatkov, kot če bi vsakokrat ponovno zapisali vse pomnilniške bloke, ali jih prej celo prebrali, spremenili njihovo vsebino in jih nato zapisali nazaj.
Je grafični generator alfa numeričnih znakov težko narediti?
Ko smo že pri prikazovalnikih, je prav, da omenimo še, kako najenostavneje implementiramo generator znakov. Ker uporabljamo vmesno shranjevanje zaslonske slike, lahko generator implementiramo izključno kot funkcijo nad pomnilniškim poljem za vmesno shranjevanje zaslonske slike in ga uporabljamo za poljuben prikazovalnik z enako organizacijo zaslona.
Poglejmo:
void SSD1306::print(char c){ if(( c>=' ')&&(c<='~')){ // Check the legal range memcpy ( &ssdbuf[ychar].page[xchar], // Copy bytes for 1 character &font[(c-32)*SSD1306FONTWIDTH], SSD1306FONTWIDTH ); ssdbuf[ychar].dirty=true; } else if(c=='n'){ // New line? xchar = dsp_getvisiblewidth(); // Yes, force next line } xchar+=SSD1306FONTWIDTH; // Move x cursor if(xchar>(dsp_getvisiblewidth()-SSD1306FONTWIDTH)){// End of line? xchar=0; // Yes, mimic CR ychar=(ychar+1)&7; // and LF } }
Funkcija generira en znak na prikazovalniku, pri katerem vsak pomnilniški blok s 136 bajti predstavlja 8 slikovnih vrstic tako, da prvi bajt vsebuje bite vrstic od 0 do 7 v stolpcu 0, drugi bajt bite vrstic 0 do 7 v stolpcu 1 itn. Naslednji blok je sestavljen enako kot prvi blok 0, le da se začne na zaslonu 8 slikovnih pod njim. Vse omenjeno vidimo tudi pri zgornji implementaciji generatorja znakov poljubne širine in višine 8 bitov. Če bi hotel višje znake, bi morali pri njihovem izrisu kombinirati bite različnih blokov, kar je procesorsko zamudno. Zato povejmo še, da so prikazovalniki s procesorji SH1106 in SN1306 najprimernejši za izris 8 pik visokih znakov. Obenem lahko slike znakov hranimo v enotnem pomnilniškem polju, razporejene po ASCII abecedi. Tako lahko iz ASCII kode znaka vselej poiščemo tudi njegovo sliko, tako da kodo preprosto pomnožimo s širino znaka v pikah na prikazovalniku. Prostor med znaki lahko opredelimo kot del slike znaka, ali pa ga samodejno zapolnimo s praznim bajtom in na ta način prihranimo pomnilnik mikrokontrolerja.
Kot vidimo, generatorja znakov ni težko narediti, precej več časa pa bi porabili za risanje znakov različnih črkovnih naborov. K sreči lahko bitne slike različnih črkovnih naborov poiščemo tudi v spletu, vsekakor pa jih najdete tudi v datoteki SSD1306.h, ki je priložena implementaciji spletnega radija na spletni strani sites.google.com/site/pcusbprojects.
Kako filtrirati podatke na Arduino programsko/diagnostični RS232 vratih?
Arduino zagonski nalagalnik in programske knjižnice ugnezdene programske opreme nudijo programersko/diagnostično podporo prek enih od RS232 vrat mikrokontrolerja. Obenem je eden ali več od digitalnih vhodov na ohišju mikrokontrolerskega čipa namenjenih izbiri načina zagona v enem od načinov za programiranje ali enem od načinov normalnega delovanja.
Serijski priključek lahko uporabimo tudi za komuniciranje z drugimi mikrokontrolerji ali napravami s TTL RS232 povezljivostjo. Vendar moramo pri tem ločevati med morebitnimi sistemskimi sporočili in aplikacijskimi sporočili oddajnega mikrokontrolerja.
Oddajni mikrokontroler sprogramiramo tako, da vsem aplikacijskim sporočilom doda značilno predpono, ki je v sistemskih sporočili ni. Tako sprejemni mikrokontroler obdela le sporočila s predpono, ostala sporočila pa lahko zavrže, ali le posreduje uporabniku. Slednje je smiselno pri napravah z dvema Wi-Fi moduloma, od katerih eden brezžično komunicira z glavnim računalnikom, drugi pa je v pripravljenosti in hkrati opravlja druge naloge.
Ukaz serial.print(”#__#c1”) preko serijskih vrat za programiranje in diagnostiko odda krmilno kodo »c1« z uporabo predpone »#__#«. V sprejemnem mikrokontrolerju moramo zato implementirati programski filter, ki kot krmilne kode upošteva le znakovne nize, ki se začnejo z »#__#«. Pri tem moramo upoštevati, da znake prek RS232 zajemamo zaporedno, pri čemer so kot krmilne kode zanimivi samo znakovni nizi, daljši od štirih znakov, kakršna je dolžina prikazane pripone. Vsekakor lahko izberemo tudi drugo predpono, pomembno je predvsem, da ni predolga, saj tako po nepotrebnem zaseda pasovno širino. Pomembno je tudi, da niza znakov, kot ga ima predpona, ni sistemskih sporočilih Arduino programskih knjižnic
Je mogoče izklopiti Arduino sistemska sporočila?
Z ustrezno nastavitvijo konstant programskih knjižnic naj bi bilo mogoče izklopiti obveščanje prek RS232, vendar se v praksi izkaže, da nekateri programerji pri tem (vsaj pri ESP32 modulih) niso bili dosledni, ali pa niso upoštevali priporočil. Kakorkoli, ko že mislimo, da smo obveščanje izklopili, tu in tam še vedno prileti kako sporočilo, na kar mora biti sprejemni mikrokontroler pripravljen, saj lahko v nasprotnem primeru v sprejetem sporočilu po naključju razbere tudi krmilne kode in izvede napačne akcije. Še posebej pozorni moramo biti v primerih, ki lahko iz enega Wi-Fi modula zaženemo drug, zmogljivejši Wi-Fi modul z večjo porabo energije. Med zagonom Wi-Fi modula se zažene njegov zagonski nalagalnik, ki v vsakem primeru izpiše uvodne obvestila. Slednjih se lahko znebimo, če začnemo podatke sprejemati s časno zakasnitvijo, vendar s tem ne izključimo možnosti nehotenega ponovnega zagona oddajnega mikrokontrolerja, če gre v njem kaj narobe, še posebej, če v programski kodi uporabljamo t.i. strojnega psa čuvaja (angl. hardware watchdog).
Sprejemanja nehotenih sporočil prek diagnostičnega RS232 se zanesljivo znebimo le, če pred vsako želeno sporočilo dodamo predpono, ki je v sistemskih sporočilih ni. Uporaba različnih predpon hkrati omogoča ločevanje sporočil v skupine, ki jih obdelamo v različnih podprogramih. Denimo, lahko ločimo sporočila za krmiljenje pogonskih motorjev robota in sporočila za upravljanje nastavitev njegove kamere.
Kako prek RS232 prenašati datoteke?
Medtem, ko je v Arduino razvojnem okolju sorazmerno dobro poskrbljeno za shranjevanje in branje datotek na in iz SD kartic ali Flash RAM-a, je programskih knjižnic za implementacijo prenosa podatkovnih datotek prek RS232 povezave sorazmerno malo in še te imajo zaradi uporabe različnih, predvsem arhaičnih modemskih komunikacijskih protokolov, precej presežnega prenosa podatkov.
Če se implementacije algoritma lotimo sami, moramo upoštevati predvsem dvoje: RS232 je asinhroni komunikacijski protokol, zato morata biti pri hitrem prenosu velikih količin podatkov uri oddajne in sprejemne enote RS232 še posebej dobro usklajeni.
To lahko preverimo pri programiranju nove ugnezdene programske opreme, če postopno zvišujemo hitrost prenosa programske kode in podatkov iz osebnega računalnika v Arduino modul. Če pri tem presežemo najvišjo zmogljivost sprejemnega RS232 modula, bomo morda res prenesli nekaj začetnih blokov podatkov, kasneje pa bo sinhronizacija padla in programiranja ne bomo uspešno zaključili. Je pa res, da je prenos podatkov tem zanesljivejši, tem bližje skupaj sta oddajno in sprejemi modul.
Drugi problem je detekcija napak pri prenosu podatkov in ponavljanje prenosa podatkovnih blokov z napakami in morebitno odpravo manjših bitnih napak. Za to funkcionalnost potrebujemo algoritem za sprotno izračunavanje CRCC kod podatkovnih blokov. Obenem je pomembno tudi, da pravilno določimo njihovo dolžino.
Prenos poljubne datoteke prek RS232 torej poteka v več fazah: najprej vsebino datoteke razdelimo na podatkovne bloke praviloma enake dolžine, razen zadnjega, ki je navadno krajši, če se deljenje na bloke ne izide na celo število. Prvi podatkovni blok lahko prihranimo za opisne podatke datoteke, saj tako sprejemni računalnik že takoj ve, kako veliko datoteko bo moral poustvariti na podatkovnem mediju, kamor prenašamo podatke prek RS232. Vsak blok podatkov mora obenem imeti na koncu pripeto tudi CRCC kodo, na osnovi katere sprejemni računalnik preveri pravilnost prenesenih podatkov. Omenimo še, da lahko knjižnice za izračun CRCC na osnovi različnih polinomov poiščemo v spletu.
Kako sinhronizirati RS232 prenos podatkov?
Čeprav RS232 protokol za sinhronizacijo ur oddajnika in sprejemnika uporablja enega ali dva stop bita, je idealno če uri sprejemnika in oddajnika že v osnovi tečeta kar se da usklajeno in zato ne potrebujeta niti dveh stop bitov. Hitrosti ur in s tem prenosa podatkov lahko do neke mere prilagajamo programsko. Je pa to odvisno tudi od dolžine števcev frekvenčnih delilnikov, s katerimi posamezni mikrokontrolerji ustvarijo interni taktni signal za RS232 funkcijsko enoto. Če hočemo v praksi zmanjšati napako pri prenosu podatkov, ni pomembno, ali prenašamo podatke s 921600 ali s standardnimi 921584 baudi, pomembno je, da imata oddajni in sprejemni mikrokontroler kar se da enako nastavljeni hitrosti ur. Dostikrat je pri enostavnejših 8-bitnih mikrokontrolerjih s 16-bitnimi in 8-bitnimi števniki težko ujeti standardizirano hitrost prenosa podatkov, lahko pa namesto tega pred prenosom izvedemo sinhronizacijo. Slednja odpravi tudi morebitne težave z netočnostjo kristalnih oscilatorjev, ki poganjata oddajni in sprejemni mikrokontroler.
Pred sinhronizacijo je seveda nujno zagotoviti prenos osnovnih podatkov, denimo okvirne želene hitrosti prenosa. Vendar tega lahko izvedemo pri nižjih prenosnih hitrostih, denimo 115200 baudih, kjer manjše razlike v prenosnih hitrostih še ne pomenijo prekinitve komunikacije, oziroma se lahko RS232 funkcijski enoti oddajnega in sprejemnega mikrokontrolerja sorazmerno enostavno samodejno usklajujeta, četudi ena oddajan podatke pri dejanskih 115200 baudih, druga pa jih sprejema pri 115384 baudih, ali obratno. Taka razlika v urah je pri nizkih hitrostih prenosa podatkov dopustna, saj sinhronizacija s stop biti sledi po vsakem prenesenem bajtu.
Se da uporabiti SPI ali I2C namesto RS232?
SPI in I2C sta sinhrona protokola za serijski prenos podatkov, zato moramo poleg podatkovnega signala prenašati tudi taktni signal. Pri SPI imamo, podobno kot pri RS232, dvosmeren prenos podatkov, zato poleg SDI (andl. serial data in, ki nadomešča signal RxD pri RS232 prenosu) in SDO (angl. serial data out, ki nadomešča TxD pri RS232 prenosu) potrebujemo še taktni signal (CLK). Nasprotno, se pri I2C zadovoljimo že z enim dvosmernim podatkovnim kanalom in taktnim signalom. Zato ne potrebujemo dodatne podatkovne povezave. Slaba lastnost I2C je predvsem, da lahko naenkrat prenašamo podatke samo v eno smer. Ampak to pri hitrem prenosu datotek niti ni velik problem, saj naenkrat že tako ali tako prenašamo samo eno datoteko.
Zato preostane edino še vprašanje, ali lahko med delovanje Arduino modula spremenim način delovanja priključkov RxD in TxD tako, da postanega del I2C ali SPI vmesnika. Že omenjeni primer z internetnim radijem na primer, pokaže da lahko s pomočjo ustreznega dodeljevanja vodila s pomočjo nedeljivih programskih transakcij, iste priključke mikrokontrolerja enkrat uporabljamo kot SPI vodilo, drugič pa kot I2C vodilo. Vendar je primer izdelan za ESP32 module, za druge pa moramo preveriti, ali se da implementirati tak način delovanja. No, vsekakor se da, če omenjene protokole implementiramo programsko, vendar je preštevanje, razstavljanje in sestavljanje bitov v programski kodi potratno in procesorsko zahtevno opravilo. Zato seveda mislimo na možnost strojne implementacije.
Nekaterih novejši mikrokontrolerji različnih proizvajalcev imajo USART enote, ki združujejo strojne podpore za RS232, I2C, SPI in druge protokole. Pomembno pa je, da sistemska programska oprema dopušča zamenjavo načina delovanja USART med izvajanjem. Vsekakor pa moramo pred spremembo načina delovanja diagnostičnega RS232 preusmeriti tok sistemskih sporočil, kot smo že omenili. Če pa ne, lahko le upamo, da ima USART funkcijska enota za asinhrono oddajanje in sprejemanje podatkov prek RS232 druge registre kot za sinhrone prenose prek I2C in SPI. Le tako je vseeno, če »neposlušne« sistemske funkcije še naprej pišejo v te registre.
Nekateri mikrokontrolerji imajo za asinhroni prenos podatkov in sinhroni prenos podatkov ločene funkcijske enote. Pri teh je pomembno, da si delijo zunanje priključke mikrokontrolerja, saj lahko tako s pomočjo v mikrokontroler vgrajenega multiplekserja (kot pri nekaterih PIC32) preprosto preusmerimo tok podatkov k drugi funkcijski enoti.
Če se vprašamo še, ali se splača uporabljati sinhroni prenos podatkov namesto asinhronega, je odgovor pritrdilen, saj pri sinhronih protokolih prenašamo tudi taktni signal in zato ni potrebno usklajevati ur oddajnika in sprejemnika. Zato je lahko prenos podatkov tudi do 100-trat hitrejši. Vsekakor pa je zamenjava komunikacijskega protokola potrebna tako v oddajniku kot v prejemniku. Je pa res, da tako vsestranskih mostov med RS232 in USB ne najdemo. Zato se moramo sami potruditi do rešitve. Prvi korak je preverjanje zmogljivosti in razpoložljivosti mikrokontrolerjev ter združljivosti s CDC podporo operacijskih sistemov. Vsaj za slednje lahko rečemo, da ni problem, saj je za USB CDC gonilnik precej vseeno, kako podatki pridejo vanj. Po drugi strani, omogoča USB 2.0 hitrosti prenosa podatkov do več 100 MB/s. No, mi se bomo za enkrat zadovoljili z nekaj MB/s, kolikor lahko dosežemo z PIC32MX, za več pa potrebujemo PIC32MZ, ki podpira »high speed« in ne samo »full speed«. Več o tem projektu v prihodnjem nadaljevanju.
Kaj je t.i. brow out napetost in kako jo nastavimo v Arduino razvojnem okolju?
Mikrokontrolerji potrebujejo za normalno delovanje dovolj visoko napajalno napetost. Če za napajanje uporabljamo baterijski vir, se napetost postopno zniža do praga normalnega delovanja. Zato moramo takrat znižati obremenitev baterijskih virov, pomembno pa je, da čim dlje ohranimo delovanje jedrnega mikrokontrolerja. Kakorkoli, tudi določene funkcionalne enote jedrnega mikrokontrolerja začno pri prenizki napajalni napetosti odpovedovati. Zato je pomembno, da določimo prag napajalne napetosti, pri kateri izvedemo varno zaustavitev mikrokontrolerja. To je še posebej pomembno, če za shranjevanje podatkov uporabljamo SD kartice, saj moramo pred zaustavitvijo varno zapreti vse odprte datoteke, tako da ni potrebno naknadno popravljanje datotečnega sistema z osebnim računalnikom.
Denimo, brown out napetost pri ESP32 vklapljamo in nastavljamo za vse Arduino projekte v datoteki sdkconfig.h (<sistemski pogon>:Users<uporabniško ime>AppDataLocalArduino15 packagesesp32hardwareesp32<različica>toolssdk), ki je del Arduino podpore. Prednastavljene vrednosti so naslednje:
CONFIG_BROWNOUT_DET=y CONFIG_BROWNOUT_DET_LVL_SEL_0=y CONFIG_BROWNOUT_DET_LVL_SEL_1= CONFIG_BROWNOUT_DET_LVL_SEL_2= CONFIG_BROWNOUT_DET_LVL_SEL_3= CONFIG_BROWNOUT_DET_LVL_SEL_4= CONFIG_BROWNOUT_DET_LVL_SEL_5= CONFIG_BROWNOUT_DET_LVL_SEL_6= CONFIG_BROWNOUT_DET_LVL_SEL_7= CONFIG_BROWNOUT_DET_LVL=0
Kot vidimo, s črko »y« vklopimo detekcijo in eno od ponujenih opcij, s katerimi določimo prag detekcije.
Če brown out (porjavitvene) napetosti ne nastavimo, tvegamo nezanesljivo in eratično izvajanje programske kode v ESP32.
Prihodnjič
Tokrat smo odgovorili na kar nekaj pomembnih vprašanj. V naslednjem nadaljevanju se lotimo praktičnih projektov, v katerih bom uporabili pridobljeno znanje.
Nekatere funkcionalnosti bomo preizkusili tudi na raziskovalnem robotu.