Raspberry Pi

37) Vlastní ovladač pro RTC obvod BQ32000 v Pythonu

 

V článku č. 14 jsem zprovozňoval modul hodin reálného času RTC s obvodem PCF8563.  Tehdy jsem ještě neměl z programováním moc zkušeností, tak jsem využíval už hotové ovladače. Nyní už se v komunikaci I2C vyznám trochu lépe, a tak jsem si napsal vlastní ovladač v Pythonu pro RTC obvod BQ32000 od Texas Instruments.  Tento obvod jsem si zvolil hlavně z toho důvodu, že je možné jeho přesnost naladit softwérově. Navíc jsem ho sehnal zdarma jako vzorek od výše uvedené firmy - Thank you  T.I.

Jeho katalogový list je tady: http://www.ti.com/lit/gpn/bq32000

Schéma zapojení:

 

Celý ovládací program je tady:

Dal by se napsat i úsporněji, ale já jsem se snažil spíše o to, aby byl lépe pochopitelný. 

Když program trochu upravíte, měl by být použitelný i pro jiné RTC obvody, které jsou řízeny přes I2C.

Některé části tu vysvětlím podrobněji:


1) Předávání parametrů přes příkazovou řádku.

Program má v sobě několik funkcí, které lze vybírat pomocí parametrů předávaných přes příkazovou řádku. 

Konkrétní příklad vypadá třeba takto:

sudo python /home/pi/bq32000.py -pi2rtc

Parametr je v tomto případě řetězec "-pi2rtc" (je to příkaz pro uložení systémového času z RasPi do obvodu RTC).

 

Jiný příklad je tady:

sudo python /home/pi/bq32000.py -f1

Tady je parametrem řetězec "-f1" (tímto parametrem se na výstupní pin obvodu RTC pošle obdélníkový signál s frekvencí 1Hz).

 

Speciálním případem pro předávání parametrů je nastavování kalibrační konstanty pro přesné seřízení času. V tom případě se předávají parametry dva:

sudo python /home/pi/bq32000.py -k 12

Parametr č.1 je řetězec "-k" a parametr č.2 je také řetězec "12".

Pomocí pythonu je možné tyto předávané parametry ukládat do proměnných a dále s nimi pracovat.
Pokud je parametr očekávaný, ale nebyl předaný, skončil by program s chybovým hlášením. Proto je vhodné se na tento případ připravit a tuto případnou chybu odchytit. K tomu se používá konstrukce příkazů "try:" a "except:"

Část programu, která se stará o předávání a vyhodnocování parametrů pak vypadá takto - ke každému parametru (akci) je přiřazen nějaký podprogram:

.
.
.
import sys 
.
.
.
.

# vyhodnoceni predavaneho parametru pri spousteni programu
try:                      # odchytavani chyby, ktera by vznikla pri chybejicim parametru
  akce = sys.argv[1]      # parametr spousteneho programu do promenne "akce"
except:                   # kdyz parametr chybi, je nahrazen prazdnym retezcem
  akce=""

akce = akce.lower()       # prevod parametru na mala pismena

if (akce == "-i"):        # rozeskoky pri ruznych akcich
  rtc_info()
elif (akce == "-pi2rtc"):
  rtc_zapis()
elif (akce == "-rtc2pi"):
  rtc_info(True)
elif (akce == "-ntp2pi"):
  ntp()
elif (akce == "-reg"):
  rtc_registry()
elif (akce == "-f1"):
  frekvence(1)
elif (akce == "-f512"):
  frekvence(512)
elif (akce == "-out1"):
  vystup(True)
elif (akce == "-out0"):
  vystup(False)
elif (akce == "-stop"):
  zastavit(1)
elif (akce == "-start"):
  zastavit(0)
elif (akce == "-k"):
  try:                             # odchytavani chyby, ktera by vznikla pri chybejicim parametru
    konstanta = int(sys.argv[2])   # hodnota zadane kalibracni konstanty do promenne "konstanta"
  except:                          # kdyz hodnota chybi, nebo je to text, nastaví se konstanta na 0
    konstanta = 0
  naladit(konstanta)

else:                              # pri jakemkoli jinem parametru se zobrazi napoveda
  print "Pripustne parametry:"
  print "... -i        INFO - Jen zobrazi cas v RasPi a v RTC"
  print "... -pi2rtc   Zapise cas z RasPi do RTC"
  print "... -rtc2pi   Zapise cas z RTC do RasPi"
  print "... -ntp2pi   Restartuje NTP - seridi cas v RasPi podle internetu"
  print "... -reg      Vypise aktualni stav vsech registru"
  print "... -f1       Nastavi IRQ generator na 1Hz"
  print "... -f512     Nastavi IRQ generator na 512Hz"
  print "... -out0     Nastavi IRQ na '0'"
  print "... -out1     Nastavi IRQ na '1'"
  print "... -stop     Zastavi hodiny"
  print "... -start    Spusti zastavene hodiny"
  print "... -k xnn    Nastavi kalibracni konstantu na hodnotu 'xnn' (-31 az +31)"
  print "                    x je znamenko ('-' zrychluje RTC; '+' zpomaluje RTC)"
  print "-------------"
  print "Priklady:"
  print "  sudo python /home/pi/bq32000 -k -15  .......... zmena konstanty na -15"
  print "  sudo python /home/pi/bq32000 -k +3   ............ zmena konstanty na 3"
  print "  sudo python /home/pi/bq32000 -f512   ....... vystup IRQ rozkmita 512Hz"
  


 


2) Čtení a vyhodnocování registrů v RTC obvodu

Další část programu převádí přečtené číslo z jednoho z registrů na srozumitelný údaj o čase.
Jako příklad je tady zjištění sekund z nultého registru.
Podle katalogového listu obsahuje nultý registr 3 údaje:

 - V nejvyšším (7.) bitu je informace o tom, jestli RTC obvod běží, nebo jestli je zastavený.
 - V bitech 6, 5 a 4 je údaj o desítkách sekund.
 - V nejnižších 4 bitech je údaj o jednotkách sekund.

 

Část programu, která tento registr vyhodnocuje, je tady:

.
.
.
data =  bus.read_i2c_block_data(adresa,0x00) # okamzite nacteni vsech registru z obvodu

sek1  = data[0] & 0b00001111      # spodni 4 bity v nultem registru jsou jednotky sekund
sek10 = data[0] & 0b01110000      # horni 3 bity v nultem registru jsou desitky sekund
sek10 = sek10 >> 4                # posun desitek sekund o 4 pozice vpravo
sekundy = (sek10 * 10) + sek1
if (data[0] > 0b01111111):        # kdyz je nejvyssi bit nastaven na "1", RTC je zastavene
  print "RTC obvod je v rezimu STOP!"
.
.
.

Při těchto operacích se využívá funkce logického bitového součinu dvou čísel.
Jedno z těch čísel je načtená hodnota z registru, a druhé číslo je bitová maska. Pro větší přehlednost jsem tu masku zapisoval ve dvojkové soustavě.
Platí, že bity, které jsou v masce nulové, budou nulové i ve výsledku. Naproti tomu bity, které jsou v masce jedničkové, se z registru překopírují do výsledku. Tímto způsobem je možné pracovat jen z několika bity z registru a ostatní bity ignorovat.

Pro příklad: 
Když je třeba získat údaj o jednotkách sekund, který se nachází na nejnižších 4 bitech nultého registru, nastaví se maska na 0b00001111.
Když pak registr bude obsahovat například binární hodnotu 0b01010110 (to je 86 desítkově), bude výsledkem číslo 0b00000110.
Což znamená šestka na místě jednotek sekund.

Stop

10_sek 1_sek
Registr 0 1 0 1 0 1 1 0 = 86 desítkově
Maska 0 0 0 0 1 1 1 1  
Bitový součin 0 0 0 0 0 1 1 0 = 6 desítkově

 

Ze stejného čísla z registru je třeba získat i údaj o desítkách sekund.
V tom případě se maska nastaví na  0b01110000.
Po jejím logickém součinu s hodnotou v registru 0b01010110 je výsledkem  číslo: 0b01010000.
Toto číslo je ještě třeba posunout o 4 místa vpravo, aby se nejnižší pozice dostala na nultý bit.
Takže výsledkem je 5 desítek sekund.

Stop 10_sek 1_sek
Registr 0 1 0 1 0 1 1 0 = 86 desítkově
Maska 0 1 1 1 0 0 0 0  
Bitový součin 0 1 0 1 0 0 0 0  
Po posunu o 4 místa  >> 0 0 0 0 0 1 0 1 = 5 desítkově

Nakonec se k počtu desítek sekund přičte ještě hodnota jednotek sekund. Tento součet pak udává celkový počet sekund v RTC obvodu - v příkladu je to 56 sekund.

Poslední informace z nultého registru se nachází v nejvyšším bitu. I tady je možné použít stejný princip s maskou, jako při zjišťování počtu sekund. V tomto případě je ale jednodušší testovat údaj z registru na to, jestli je větší, než 127 (binárně je to 0b01111111).
Když je totiž nejvyšší bit v "1", je číslo v registru minimálně 128. 

V předchozím přikladu je číslo v registru 86. To je menší, než 128. To tedy znamená, že "STOP_bit" je v nule - RTC tedy běží.


3) Ukládání hodnot do RTC

Opačným případem je ukládání aktuálního času do registru v  RTC obvodu.

.
.
.
datcas = datetime.datetime.utcnow()   # datcas bude obsahovat aktualni datum a cas v UTC (GMT)

sek10 = int(datcas.second / 10)                                 # cele desitky sekund
sek1 = datcas.second - (10 * sek10)                             # jednotky sekund
stopbit = 0          # stopbit nastavit na normalni beh (pri 128 by se RTC zastavil)
bus.write_byte_data(adresa , 0 ,stopbit + (sek10 << 4) + sek1)  # ulozit do registru 0
.
.
.

To je trochu jednodušší. Nejdříve se zjistí aktuální čas. 
Je zvykem do RTC obvodu ukládat čas převedený do UTC - Univerzální (světový) čas. Proto je použita funkce utcnow().

Z času se zjistí aktuální počet sekund - například 56 (jako v minulém příkladu).
Tento údaj se rozdělí na desítky sekund a sekundy: 5 a 6 
Protože je nevhodné, aby po seřízení času v RTC byl tento obvod v zastaveném stavu, musí se ještě vynulovat "STOP_bit".
(Ta proměnná "stopbit" je tam sice zbytečná, ale myslím, že pro pochopení skládání výsledného čísla je to názornější.)

Údaj v desítkách sekund je třeba bitově posunout ještě o 4 místa vlevo.
To se provede buď operátorem << 4, nebo jednoduchým vynásobením číslem 16.
Nakonec se takto upravený údaj s desítkami sekund sečte s údajem o jednotkách sekund, případně se k tomu všemu ještě přičte STOP_bit.
Výsledný součet se pak zapíše do příslušného registru v RTC obvodu.


4) Příkaz pro nastavení času v RasPi

Další důležitou operací je nastavení systémového času.
Nepodařilo se mi zjistit, jestli je možné přímo z Pythonu měnit čas v RasPi. Pomohl jsem si tedy oklikou.
Python dokáže spouštět příkazy BASHe, tak se stačilo podívat, jak se v BASHi nastavuje čas a tento příkaz potom v Pythonu vykonat:

.
.
import os
.
.
.
prikaz="sudo date -u " + (str(mesice).rjust(2,"0") +
                          str(dny).rjust(2,"0") +
                          str(hodiny).rjust(2,"0") + 
                          str(minuty).rjust(2,"0") + 
                          str(roky) + "." + 
                          str(sekundy).rjust(2,"0"))
os.system (prikaz)
.
.
.

Protože je v RTC uložený datum a čas v UTC, musí se i při nastavování času v RasPi použít za příkazem date parametr -u pro univerzální čas.
V tomto příkladu je navíc ukázáno rozdělení dlouhé a nepřehledné řádky na několik jednodušších řádek.
Funkce rjust doplní před řetězec nuly tak, aby výsledek měl požadovanou délku (v tomto případě 2 znaky). 


5) SFR - Registr pro speciální funkce

Jednou ze zvláštností obvodu BQ32000 je ovládání výstupního pinu IRQ.
Ve většině ostatních RTC obvodů stačí jednoduše do nějakého registru uložit číslo a výstup se přepne do "1", "0", nebo začne kmitat nějakou frekvencí .

Tento obvod je však neobvyklý tím, že pokud chcete výstup rozkmitat, musíte si nejdřív "odemknout" speciální registr (SFR).
K odemknutí jsou třeba dva klíče (kódy), které se postupně zapisují do registrů 0x20 a 0x21. Hned potom se získá přístup do SFR v registru 0x22. Zapsáním "1" nebo "0" do tohoto SFR registru se nastavuje výstupní frekvence pinu IRQ buď 512Hz, nebo 1Hz.

Oba klíče jsou uvedeny v katalogovém listu.
Do registru se 0x20 se musí uložit kód 0x5E a do registru 0x21 se vkládá kód 0xC7.
Protože mezi ukládáním klíčů do registru nesmí dojít k přerušení datového toku v komunikaci I2C, je třeba tyto registry plnit trochu jiným způsobem (sekvenčně):

.
.
.
if (freq == 1):
  klice=[0x5E,0xC7,0x01]                 # frekvenci generatoru nastavit na 1Hz
  bus.write_i2c_block_data(adresa,0x20,klice)
  print "vystup IRQ kmita frekvenci 1Hz"
else:
  klice=[0x5E,0xC7,0x00]                 # frekvenci generatoru nastavit na 512Hz
  bus.write_i2c_block_data(adresa,0x20,klice)
  print "vystup IRQ kmita frekvenci 512Hz"
.
.
.

Sice to nikde není uvedeno, ale domnívám se, že existují i jiné, tajné klíče, které otevírají přístup k dalším funkcím.
Proč by se jinak tak složitě nastavoval jeden parametr, který je u jiných RTC obvodů běžně přístupný?
Ty "tajné" funkce mohou být třeba přístup do volného paměťového prostoru, nebo nastavování jiných frekvencí na IRQ výstupu.
To jsou ale jen moje spekulace. Třeba tam opravdu žádné další tajné funkce nejsou.


6) Kalibrace krystalu

Další funkce, kterou u jiných RTC obvodů většinou nenajdete, je možnost softwérově dokalibrovat frekvenci oscilátoru.
Pomocí čísla zapsaného do registru 0x07 se každou minutu může přidat nebo ubrat několik impulzů oscilátoru podle tabulky v katalogovém listu.

Zápis a čtení konstanty je podobné, jako zápis a čtení údaje o sekundách, které jsem popisoval výše. Číselná konstanta je uložena v bitech 0 až 5, takže může nabývat desítkové hodnoty 0 až 31. K tomu je ještě v bitu č.6 uloženo znaménko, které udává zrychlování, nebo zpomalování RTC obvodu.

 


7) Výpis registrů

Do programu jsem přidal ještě funkci rtc_registry(), která přehledným způsobem vypíše aktuální stav všech registrů v obvodu s jejich rozdělením na jednotlivé bloky.


Tento výpis byl získán v neděli 2.6.2013 v 17:43:19 SELČ (...to je 15:43:19 UT).
LEDka na výstupu IRQ blikala frekvencí 1Hz.
Kalibrační časová konstanta byla nastavena na číslo +3 (to je zpomalení o 6 pulzů oscilátoru za minutu).

 


8) Seřízení času podle internetu.

Při pokusech se mi několikrát stalo, že jsem si rozladil čas v RasPi a pak jsem ho musel složitě přes příkaz date v terminálu nastavovat zpátky.

RasPi si sice nastavuje čas automaticky podle internetu při spouštění, ale restart trval ještě déle, než ruční vypsání všech parametrů v tom příkazu date.

Pak jsem ale zjistil, že se dá čas seřídit podle internetu i bez restartu celého RasPi. Stačí v terminálu zadat:

sudo /etc/init.d/ntp restart

Předpokládá to samozřejmě, že je RasPi k internetu připojeno.


 

 

 


úvodní strana webu AstroMiK.org

poslední úprava stránky 2.6.2013