Avtor: Mag. Vladimir Mitrović
2017_256_40
V preteklem delu smo spoznali ZS-042 modul in pojasnili koncept ter vsebino programske knjižnice DS3231$SE.sub. Če jo pridružimo k našim BascomAVR programom bodo ukazi iz knjižnice omogočili enostavno delo z integriranim vezjem DS3231 bodisi da ga uporabimo kot del ZS-042 modula ali v nekem drugem vezju. Sledijo primeri, ki bodo pokazali kako pravilno uporabiti ukaze iz te knjižnice in kako povezati ZS-042 modul z Mini in MegaPinom.
Program DS3231_1.bas
Program je napisan za vezje po shemi na sliki 4. To je pravzaprav poenostavljena shema razvojnega sistema MiniPin, zato se vezje lahko realizira z njegovo pomočjo, s pomočjo razvojnega sistema MegaPin ali na neki drugi platformi, ki ima dostopne ustrezne priključke mikrokontrolerja. ZS-042 modul se veže na istoimenske priključke na levi strani slike 4; neizkoriščene priključke modula (32K in SQW) pustimo nepovezane.
Analizirali bomo samo dele programa, ki uporabljajo RTC; za popolno razumevanje dela je potrebno pogledati celoten program.
Na začetku programa je potrebno vključiti programsko knjižnico:
$include "DS3231$SE.sub"
S tem so v programu definirane vse DS3231$ spremenljivke in dostopni postanejo vsi ukazi iz knjižnice. Sestavni del programa bodo postali samo tisti ukazi, ki jih bomo uporabljali. Opomba: DS3231 knjižnica uporablja novi način definiranja podprograma (Config Submode = New), zato je s tem potrebno računati, v kolikor se v programu uporabljajo podprogrami iz nekaterih drugih knjižnic ali lastni podprogrami.
Sledi konfiguracija displeja,
Config Lcdpin = Pin ...
vhodnih pinov (Pind.0 – Pind.4), katerim so dodeljena alternativna imena v skladu s shemo(Pind.0 = Tp1, Pind.1 = Tp2, itd.)
Tp1 Alias Pind.0 Config Tp1 = Input Portd.0 = 1 ...
in I2C vodila, preko katere mikrokontroler komunicira z RTC-jem:
Config I2cdelay = 5 Config Sda = Portd.4 'SDA Config Scl = Portd.5 'SCL I2cinit
Glede na moje izkušnje navedeni I2cdelay omogoča največjo hitrost I2C komunikacije (okoli 75 kHz), v primeru ko se uporablja softverska emulacija in 8 MHz takt.
Komunikacijo z RTC-jem pričnemo z njegovo konfiguracijo:
Ds3231$write_control &B00000000
V tem primeru so vsi biti kontrolnega registra postavljeni v stanje “0”. S tem je zagotovljeno, da RTC dela na način ki nam ustreza:
oscilator bo nadaljeval z delom tudi takrat, ko RTC ostane na rezervnem baterijskem napajanju,
RTC generira pravokotni signal frekvence 1 Hz na SQW priključku (v tem primeru tega signala ne bomo uporabili),
izključene so vse prekinitve vezane za pojav alarma.
Četudi to s konfiguracijskim ukazom ni izrecno konfigurirano, bodo časovni ukazi iz programske knjižnice predpostavljali zapis in prikaz časa v 24-urnem formatu, datumski ukazi predpostavljajo, da se datum nahaja v 21. stoletju.
Po konfiguraciji RTC nadaljuje z delom na nastavljeni način in bo delal tako, dokler je spojen na osnovno (Vcc) ali rezervno baterijsko (Vbat) napajanje. Iz programa moramo samo periodično prebrati njegove časovne in datumske registre in jih nato na prikladen način izpisati na LCD-ju:
Do Ds3231$read Gosub Disp_time Gosub Disp_date ... Waitms 100 Loop
Kot smo pokazali v predhodnem nadaljevanju, Ds3231$read prenaša vsebino časovnih in datumskih registrov iz RTC-ja v ustrezne globalne spremenljivke. Zdaj so te vrednosti dostopne drugim ukazom znotraj programa. V našem primeru v zgornji vrstici displeja prikazujemo trenutni čas:
Disp_time: Home U Lcd_hh_mm_ss Return
v spodnji vrstici pa datum in dan v tednu:
Disp_date: Home L Lcd_dd_mm_20yy Lcd " " Lcd_dan_slo Return
Za prikaz na displeju so uporabljeni ukazi Lcd_hh_mm_ss, Lcd_dd_mm_20yy in Lcd_dan_slo iz programske knjižnice DS3231$SE.sub.
Opomba: integrirano vezje DS3231 si dneve v tednu zapomni kot številke v razponu od 1 do 7, zato so na isti način zapisani tudi v globalni spremenljivki DS3231$day. DS3231 nima vpisanega algoritma izračuna dneva v tednu iz datuma, ampak samo inkrementira dan v tednu kadar ura preskoči iz 24h na 00h. Ukazi za prikaz dneva v tednu iz DS3231.sub knjižnice v tem primeru Lcd_dan_slo, pretvarjajo številčno oznako dneva v tednu v trištevilčno kratico imena, s predpostavko, da je prvi dan v tednu ponedeljek.
Kako to izgleda resnici prikazuje fotografija na sliki 5. Da bi bil prikaz časa in datuma točen, bo te podatke potrebno najprej vpisati v RTC DS3231. To delamo v podprogramih T2, T3 i T4 z ustreznimi tipkami Tp1, Tp2 i Tp3. Program znotraj Do…Loop zanke redno preverja stanje teh tipk, katerih namen je sledeč:
Če tipka Tp1 ni pritisnjena,
- s tipko Tp2 nastavljamo ure,
- s tipko Tp3 nastavljamo minute,
- tipko Tp4 nastavljamo dneve v tednu.
Če je tipka Tp1 pritisnjena,
- s tipko Tp2 nastavljamo dneve v mesecu,
- s tipko Tp3 nastavljamo mesece in
- s tipko Tp4 nastavljamo leta.
Vsak pritisk na neko tipko pomeni povečanje ustrezne vrednosti za 1. Vsaka sprememba minut hkrati resetira sekunde. Program preverja vpisane vrednosti in ne dovoljuje vnos izven dopustnega obsega. Na primeru ur (dopustni obseg je 00 – 23 v BCD formatu) to izgleda tako:
... Incr_bcd Ds3231$hour If Ds3231$hour > &H23 Then Ds3231$hour = &H00 End If Ds3231$write_hour ...
Za vsako spremembo datuma je potrebno bolj zahtevno preverjanje številke dneva v mesecu, odvisno od tekočega meseca in tega, ali je leto prestopno ali ne. Preverjanje se odvija v podprogramu Check_dte, ki se ne nahaja v programski knjižnici, ampak je sestavni del programa:
... Incr_bcd Ds3231$dte Gosub Check_dte Ds3231$write_date ...
Podprograma Check_dte tukaj ne bomo analizirali, poglejte ga v programu. Potrebno je samo vedeti, da je spremembo datuma potrebno vpisovati po vrsti leto, mesec, dan; v nasprotnem primeru Check_dte ne bo delal korektno. Bodite pozorni na to, da je po vsaki spremembi vrednosti neke globalne spremenljivke potrebno vpisati to novo vrednost v ustrezen register RTC-ja (Ds3231$write_hour, Ds3231$write_date).
Program DS3231_2.bas
Program DS3231_2.bas je napisan za vezje po shemi na sliki 6, ki se od vezja s slike 4 razlikuje samo po tem, da je sedaj izkoriščen tudi priključek SQW RTC-ja. Vezje se lahko realizira s pomočjo razvojnih sistemov MiniPin in MegaPin ali na neki drugi platformi ki ima pristopne ustrezne priključke mikrokontrolerja. Neizkoriščeni priključek 32K ZS-042 modula je nepovezan.
S povezovanjem priključka SQW z mikrokontrolerjem smo načelno dobili možnost uporabe alarmnega vezja RTC-ja, ker RTC javlja alarmni dogodek s postavljanjem SQW pina v stanje logične “0”. Za to je potrebno v RTC vpisati želena časovna alarma, postaviti A1IE in A2IE bita v kontrolnem registru in konfigurirati SQW pin kot izhodni pin alarmnega vezja s postavljanjem INTCN bita kontrolnega registra.
Zdaj se ne bomo ukvarjali z alarmnim vezjem RTC-ja, smo pa to omenili zgolj informativno. Program DS3231_2.bas, ki ga analiziramo, je funkcionalno zelo podoben prejšnjemu programu DS3231_1.bas. Njegov osnovni namen je prikaz časa in datuma, dodane pa so mu nekatere nove funkcionalnosti, ki jih bomo zdaj podrobno pogledali. Ker program ne uporablja alarmnega vezja RTC-ja, je kontrolni register konfiguriran na isti način kot prej:
Ds3231$write_control &B00000000
Tako je alarmno vezje RTC-ja onemogočeno, na SQW pinu pa se pojavlja pravokotni signal frekvence 1 Hz. Ta signal bomo izkoristili zato, da bi zmanjšali pogostost komunikacije mikrokontrolerja z RTC-jem na enkrat v sekundi. Pin, na katerega je spojen SQW signal, je konfiguriran kot vhodni:
Sqw Alias Pind.6 Config Sqw = Input Portd.6 = 0
Stanje tega pina se stalno preverja v Do…Loop zanki, ki sedaj vsebuje samo Debounce ukaze:
Do Debounce Sqw , 0 , New_second , Sub Debounce Tp2 , 0 , T2 , Sub Debounce Tp3 , 0 , T3 , Sub Debounce Tp4 , 0 , T4 , Sub Loop
Tipke Tp2, Tp3 in Tp4 imajo isto funkcijo kot v prvem primeru in njim pridruženi podprogrami so zelo podobni predhodnim. Branje časovnih in datumskih registrov RTC-ja in njihov prikaz na displeju so sedaj premeščeni v podprogram New_second, ki se izvršuje ko SQW pin spremeni stanje iz “1” u “0”, torej, enkrat v sekundi:
New_second: Ds3231$read Gosub Check_holiday_slo Gosub Check_dst Gosub Disp_time Gosub Disp_date Return
Opazili bomo dva podprograma, Check_holiday_slo in Check_dst. Check_dst avtomatsko popravlja prehod iz zimskega v letno računanje časa in obratno. Te korekcije niso rešene v integriranem vezju DS3231 in sem jih zato moral implementirati programsko. Prehod iz zimskega v letno računanje časa se dogaja v 2 urah (Ds3231$hour = &H02) zadnjo nedeljo (Ds3231$day = 7) v tretjem mesecu (Ds3231$mth = &H03). Ta zadnja nedelja nastopa po 24. dnevu tretjega meseca (Ds3231$dte >= &H25), zato je samo potrebno počakati, da se pokrijejo vsi pogoji in izvršiti premik za 1 uro vnaprej:
Check_dst: If Ds3231$day = 7 Then If Ds3231$mth = &H03 Then If Ds3231$dte >= &H25 Then If Ds3231$hour = &H02 Then Ds3231$hour = &H03 Ds3231$write_hour End If End If End If ...
Prehod iz letnega v zimsko računanje časa se dogaja zadnjo nedeljo (Ds3231$day = 7) desetega meseca (Ds3231$mth = &H10), ki nastopa po 24. dnevu v mesecu (Ds3231$dte >= &H25). To je programsko najbolj zahteven podvig: vračamo se eno uro nazaj, zato je potrebno vgraditi zaščito, da se skok nazaj ne bi v nedogled ponavljal. Zaščito smo dosegli z zastavico Flag_s. Najprej bomo počakali prvo uro (Ds3231$hour = &H01) v dnevu, v katerem nastopa sprememba in postavili zastavico (Flag_s = 1):
If Ds3231$mth = &H10 Then If Ds3231$dte >= &H25 Then If Ds3231$hour = &H01 Then Flag_s = 1 End If
Zdaj čakamo tretjo uro (Ds3231$hour = &H03) in opravljamo korekcijo časa samo, če je zastavica postavljena:
If Ds3231$hour = &H03 Then If Flag_s = 1 Then Ds3231$hour = &H02 Flag_s = 0 Ds3231$write_hour End If End If End If End If End If Return
Sočasno brišemo zastavico zato, da se proces ne bi ponavljal. Opazili boste še, kako smo vsako korekcijo z ukazom Ds3231$write_hour morali vpisati v ustrezen register RTC-ja.
Da bi se te časovne korekcije pravilno odvile, je nujno da je program v mikrokontrolerju aktiven v trenutku, ko je potrebno prestaviti računanje časa z zimskega na poletno in celo uro pred prehodom z letnega na zimsko računanje. Če RTC v teh kritičnih obdobjih ni povezan z mikrokontrolerjem, se avtomatska korekcija ne bo dogodila in jo bo potrebno opraviti ročno.
Druga nadgradnja programa je preverjanje državnih praznikov. To sem potreboval v enem projektu, ko je program moral med delovnimi dnevi delovati na en način, med vikendom drugače in spet drugače med prazniki. Ker gre za zanimivo programsko rešitev, sem se jo odločil vgraditi v ta program.
Preverjanje dneva vikenda je enostavna: v sobotu je Ds3231$day = 6, v nedeljo Ds3231$day = 7. Preverjanje državnih praznikov je precej bolj zahtevno; to rutino preverbe sem namestil v podprogram Check_holiday_slo. Če ugotovi, da gre za državni praznik, bo podprogram postavil zastavico Flag_h. V glavnem delu programa lahko to informacijo izkoristimo, kakor želimo. V našem primeru, če gre za državni praznik, bo podprogram za izpis datuma v zgornji vrstici displeja dodatno izpisal oznako DP:
Disp_date: Locate 1 , 13 If Flag_h = 1 Then Lcd "DP" Else Lcd " " End If Home L Lcd_dd_mm_yyyy Lcd " " Lcd_dan_slo Return
Zdaj pa poglejmo, kako v podprogramu Check_holiday_slo preverjamo, če gre za praznik ali ne. Obstajata vrsti državnih praznikov: tisti, ki dogajajo vsako leto na isti datum in tisti, pri katerih je datum »premičen«. Datumi “fiksnih” praznikov v Sloveniji so vpisani v tabelo Dp_slo:
Dp_slo: 'Državni prazniki SLO Data &H01 , &H01 'Novo leto Data &H02 , &H01 'Novo leto Data &H08 , &H02 'Prešernov dan Data &H27 , &H04 'Dan upora Data &H01 , &H05 'Praznik dela Data &H02 , &H05 'Praznik dela Data &H25 , &H06 'Dan državnosti Data &H15 , &H08 'Marijino vnebovzetje Data &H31 , &H10 'Dan reformacije Data &H01 , &H11 'Dan spomina na mrtve Data &H25 , &H12 'Božič Data &H26 , &H12 'Dan samostojnosti Data &H00 , &H00
Podatke iz tabele beremo enega za drugim v pomožno spremenljivko vrste word z imenom Hol_dtemth, primerjamo s tekočim datumom iz RTC-ja in, če sta se obe spremenljivki pokrili, postavljamo Flag_h:
Flag_h = 0 Restore Dp_slo Do Read Hol_dtemth Gosub Check_holiday_date Loop Until Hol_dtemth = &H0000 ... Check_holiday_date: If Hol_dtemth = Ds3231$date Then Flag_h = 1 End If Return
Program vsebuje tudi tabelo datumov fiksnih državnih praznikov v Hrvaški, Dp_hr. Na isti način se lahko preverjajo tudi prazniki na Hrvaškem. Po potrebi se naredi podobna tabela za neko drugo državo ali za nek drug namen. Če delate svojo tabelo, jo obvezno zaključite z lažnim datumom 00.00 (Data &H00 , &H00); v nasprotnem bo podprogram preverjanja nadaljeval z uporabo datumov izven tabele.
Med premične praznike v Sloveniji spadajo tudi Velika noč, Velikonočni ponedeljek in Binkošti. Čeprav se na internetu lahko poiščejo kalkulatorji za izračun datuma Velike noči za vsako leto posebej, sem se odločil za drugačen pristop in v tabelo vpisal datume Velike noči do leta 2050:
Uskrs: 'Velika noc Data &H16 , &H04 '2017 Data &H01 , &H04 '2018 Data &H21 , &H04 '2019 ... ... Data &H05 , &H04 '2048 Data &H18 , &H04 '2049 Data &H10 , &H04 '2050
Z malo truda je tabelo možno podaljšati za še več let. V programu se tekoče leto preračunava v indeks za iskanje v tabeli:
Hol_pom = Makedec(ds3231$yr) Hol_pom = Hol_pom - 17
S tem računom leto 2017 (Ds3231$yr = 17h) dobi indeks 0 in tako po vrsti do leta 2050 (Ds3231$yr = 50h), pri katerem je indeks 33. Podprogram najprej preverja, ali je indeks v dovoljenem razponu, nato odčitava datum Velike noči iz tabele in preverja, ali je enak tekočem datumu:
If Hol_pom < 34 Then Hol_dtemth = Lookup(hol_pom , Uskrs) Gosub Check_holiday_date
Datum drugega dne Velike noči dobimo tako, da datum iz tabele povečamo za 1, datum za Binkošti pa tako, da datum Velike noči povečamo za 49. Pri tem moramo upoštevati, da so datumi zapisani v BCD formatu in paziti na dolžine posameznih mesecev; postopek izračuna poglejte v programu. (Na Hrvaškem je državni praznik Telovo, ki pride 60 dni po Veliki noči. Ta datum lahko izračunamo na podoben način, kot smo v slovenski verziji računali datum Binkošti). Vsak dobljen datum primerjamo s tekočim datumom s pomočjo podprograma Check_holiday_date ki bo, če je katera koli od teh primerjav pozitivna, postavil Flag-h. To bo glavnemu programu signaliziralo, da je aktualni datum državni praznik. Kot smo predhodno rekli, v programu DS3231_2.bas to informacijo uporabljamo za prikaz oznake DP na displeju.
Program DS3231_3.bas
Program DS3231_3.bas je napisan za mikrokontroler ATmega328P, ki ga lahko naložimo v razvojne sisteme MiniPin II ali MegaPin. Program je funkcionalno identičen programu DS3231_2.bas, zato tega dela ne bomo analizirali; posvetili se bomo samo posebnostim, ki jih prinaša ATmega328P. Čeprav je ATmega328P večji in zmogljivejši mikrokontroler kot ATtiny4313, žal nima razpoložljivih vseh pinov porta B. Če bi torej želeli uporabiti LCD adapter iz naše ponudbe, moramo LCD “premestiti” na port D. Zato so tipke Tp1 – Tp4 premeščene na pine 0 – 3 porta B, SQW, SDA in SCL pa na pine 3, 4 in 5 porta C. Vse te spremembe so prikazane na shemi na sliki 7, v programu so tudi prikladno zamenjani konfiguracijski ukazi posameznih pinov.
Za razliko od ATtiny4313, ima ATmega328P hardversko rešeno I2C (TWI) komunikacijo, kjer sta SDA in SCL liniji povezani na pine 4 in 5 porta C. Zato so ravno te pini konfigurirani kot I2C priključki. Da bomo lahko uporabljali TWI, je v programu potrebno uporabiti alternativno knjižnico i2c_twi.lbx. Tako je možno doseči večje hitrosti komunikacije. V našem primeru uporabljamo 400 kHz:
'use TWI instead SW emulated I2C: $lib "i2c_twi.lbx" Config Twi = 400000
Ostanek programa je identičen programu DS3231_2.bas.
V teh primerih smo pokrili osnovne funkcije RTC-ja DS3231 in ilustrirali uporabo ukazov iz knjižnice DS3231$SE.sub. Delo z alarmi bomo pustili za neko drugo priložnost, čaka nas še veliko dela…
Opomba: DS3231$SE.sub knjižnico in vse navedene programske primere v tem članku lahko brezplačno dobite v uredništvu revije Svet elektronike.
www.svet-el.si
Mitrovic_Barduino2