DomovDownloadBASCOM programiBascom-AVR knjižnice za Arduino module (2)

Bascom-AVR knjižnice za Arduino module (2)

eestecAvtor: 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

Slika 4: Shema mikrokontrolerskega vezja za katerega je napisan 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
Slika 5: Fotografija ure realizirane z razvojnim sistemom MiniPin

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

Slika 6: Shema mikrokontrolerskega vezja za katero je napisan 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

Slika 7: Shema mikrokontrolerskega vezja za katerega je napisan 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

Bascom-AVR knjižnice za Arduino module (1)