Raspberry Pi

30) Časomíra

V článku č. 20 jsem pomocí raspíčka vytvořil jednoduchou časomíru, která měřila a zaznamenávala délku kmitu kyvadla.
Tady se ještě jednou k měření času vrátím.

V článku s kyvadlem byl program trochu nepřehledný (nepočítalo se každé přerušení světelného paprsku, ale pouze přerušení světla v případě zhoupnutí kyvadla zleva doprava).

Připravil jsem trochu jednodušší časomíru se dvěma tlačítky (START a STOP).

Schéma je tady:

 

 

Program v pythonu pro jednoduché měření času:  * na konci článku je úprava programu s využitím pollingu 


#!/usr/bin/env python

# JEDNODUCHA  CASOMIRA


import time
import RPi.GPIO as GPIO

# startovaci pin  
pin1=15  # cislo pinu 15 na konektoru odpovida GPIO22
# stopovaci pin  
pin0=16  # cislo pinu 16 na konektoru odpovida GPIO23

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(pin1, GPIO.IN)
GPIO.setup(pin0, GPIO.IN)

#cekani na preklopeni startovaciho pinu do stavu "0"
while (GPIO.input(pin1) == 1): 
  time.sleep(0.001)


starttime = time.time()      # aktualni cas v sekundach od roku 1970
print "Cas na startu : " , starttime



#cekani na preklopeni stopovaciho pinu do stavu "0"
while (GPIO.input(pin0) == 1): 
  time.sleep(0.001)


stoptime = time.time()      # aktualni cas v sekundach od roku 1970
print "Cas v cili : " , stoptime


rozdilsek = stoptime - starttime 
print "Celkovy cas = " , rozdilsek , " sek."


Ruční spínání tlačítek je velice závislé na rychlosti reakce obsluhy, takže přesnost měření času je v tomto případě velice špatná.

Proto jsem vytvořil vytvořil kolejnici, která je mírně sklopená a ve které jezdí ocelová kulička.
Kolejnice má na svých koncích dvě fotočidla, na která svítí lasery. Tato fotočidla nahrazují tlačítka z prvního schématu.
Když kulička projde skrz první paprsek, časomíra se spustí. Když kulička přeruší druhý paprsek, časomíra se zastaví.
Jako fotočidlo jsem použil to, co jsem doma našel.
Na startu je nějaká neoznačená fotodioda, v cíli je fotoodpor. 
Pro vyhodnocení přerušení paprsku jsem musel ještě signály z čidel zesílit a upravit. Na to jsem použil dvojitý komparátor, u kterého jsem pomocí trimrů nastavil překlápěcí úrovně.

Pomocí tohoto zařízení bych měl získat časový normál. Kulička by v kolejnici měla projíždět pokaždé stejnou rychlostí, takže čas pohybu mezi startem a cílem by měl být pokaždé stejný. Ve skutečnosti záleží i na počáteční rychlosti kuličky a různých drobných nepřesnostech na dráze, které kuličku náhodně zpomalují.

Schéma vstupních fotočástí je tady:

 

Jak se ukázalo, přesnost měření je dána hlavně rychlostí čtení stavu dvou vstupů na GPIO konektoru.
Čím rychlejší jsou smyčky pro čtení GPIO, tím je přesnost vyšší. Avšak při zvětšování rychlosti dochází také k většímu vytížení procesoru.
Navíc hrozí, že začne RasPi vykonávat nějaký jiný proces, který bude mít vyšší prioritu, a kvůli tomu se některá ze čtecích smyček na chvíli zastaví.
Další nevýhodou může být nutnost přerušit paprsek alespoň na tak dlouho, dokud trvá jedna čtecí smyčka. Pokud by byla kulička moc rychlá a přerušila paprsek na velmi krátkou dobu, nemuselo by to RasPi vůbec zaznamenat.


Přesnější varianta

Proto jsem vymyslel ještě jiný způsob měření času, který je na RasPi téměř nezávislý. RasPi se v tom případě využívá jen pro uvedení časomíry do stavu připravenosti a po projetí kuličky cílem pak RasPi zajišťuje výpočet času.   

Celé měření času a vyhodnocování přerušení paprsků se provádí mimo Raspíčko.

Základem jsou dva obvody:
1) Generátor impulzů - Použil jsem RTC obvod PCF8563 (katalogový list).
2) Čítač impulzů - Pro tento účel jsem si pořídil obvod PCF8583. (katalogový list)

Oba tyto obvody je možné nastavovat (nebo číst jejich stav) pomocí sběrnice I2C.

Dále jsem použil několik běžných logických hradel NAND (MH8400) - MH8400 byl sice podle katalogu pod hranicí minimálního napájecího napětí, ale i přesto fungoval. 
Asi by bylo lepší kvůli napájení použít nějakou CMOS variantu hradel (HEF4011) - ty mají doporučené napájecí napětí od 3V. 

Nejdříve schéma zapojení:

 

Vysvětlení funkce:

V principu jde o to, že generátor impulzů (PCF8563) vysílá na vývodu CLKout neustále obdélníkové impulzy s frekvencí 32768Hz do vstupu prvního hradla NAND, které slouží jako logický spínač ovládaný stavem na vývodu č.10.

Když má hradlo na noze č. 10 logickou "0", nastaví svůj výstup do "1". 
Na noze č. 9 se pak může dít co chce, ale výstup bude pořád v "1". 

Pokud se ale na noze č. 10 objeví "1", začne hradlo propouštět impulzy do čítače (PCF8583).
Impulzy budou mít sice obrácenou úroveň, ale na funkci časomíry to nemá vliv.

 

Druhou částí schématu je klasický R-S klopný obvod, vytvořený ze dvou hradel NAND.
Princip tu nebudu rozebírat do detailů. Kdo potřebuje, může si to najít přes Google...

Jenom v rychlosti - krátké zavedení logické "0" na vývod č.1 překlopí výstup R-S klopného obvodu (nohu č.3) do "1". Tento stav bude trvat tak dlouho, než se "0" přivede na vývod č.5. Překlápěcí impulzy mohou být velice krátké (desítky nanosekund), takže je možné zaregistrovat i velice rychlý průjezd kuličky skrz fotozávoru.

A protože výstup klopného obvodu zároveň slouží jako řízení "logického spínače", o kterém jsem psal výše, platí, že po dobu, kdy je na výstupu R-S obvodu "1", počítá čítač impulzy dodávané z generátoru.

Když je známá přesná frekvence generátoru, je možné jednoduše z počtu nasčítaných impulzů spočítat čas, po který byl R-S klopný obvod překlopený do stavu "1".

Drobný zádrhel nastává v případě, že čítač překročí svou kapacitu. Pro obvod, který jsem použil já, je ta kapacita přesně milion impulzů. Při frekvenci generátoru 32768Hz se tato kapacita vyčerpá asi za 30,5 sekundy. Pak se čítač zresetuje a začne počítat znova od nuly. Je proto třeba ještě určit, kolikrát se během měření provedl takovýto reset čítače. 
K tomu stačí znát alespoň zhruba celkový čas měření, tento čas vydělit 30,5 sekundami a z výsledku oříznout desetinnou část.  

Obvod je ještě doplněn o LEDku, která signalizuje stav, při kterém časomíra běží. 

Protože po zapnutí napájení není jisté, v jakém stavu se bude nacházet R-S klopný obvod, dodělal jsem do programu ještě funkci resetu toho klopného obvodu. Jde o to, že se při startu programu přepne port GPIO22 na výstup a krátce se na něm nastaví "0". Tím se zajistí trvalé překlopení R-S do stavu, kdy má na výstupu "0" (čekání na startovní impulz). 
Odpor R3 slouží k tomu, aby při tomto, sice velice krátkém, ale "násilném" překlopení obvodu nedocházelo ke zkratu na GPIO.
Tuto část programu je možné vynechat, ale pak se musí před každým startem časomíry zajistit ruční uvedení R-S klopného obvodu do stavu "0" (například stiskem tlačítka STOP).

 

GPIO22 se také používá pro hrubé zjišťování času startu a zastavení časomíry (kvůli určování počtu resetů čítače). Na rozdíl od prvního pokusu v tomto článku tady vůbec nezáleží na přesnosti. Stav tohoto portu je možné číst například každou půlsekundu - takováto frekvence čtení portu už RasPi nezatěžuje. 

Přesnost tohoto způsobu měření času je 1/32768 s. To je rozlišení asi 30 mikrosekund. 

Program:


#!/usr/bin/env python

# PRESNA  CASOMIRA

# Pouzite obvody : PCF8583 (event counter), PCF8563 (RTC + zdroj signalu 32kHz), 3x hradlo NAND 
# adresa obvodu PCF8583 je 0x50 (noha c.3 na GND)
# adresa obvodu PCF8563 je 0x51 



import smbus
import time
import RPi.GPIO as GPIO

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)

bus = smbus.SMBus(1)    # novejsi verze Raspberry Pi (512MB)
#bus = smbus.SMBus(0)   # starsi verze Raspberry Pi (256MB)


# pin pro cteni stavu a reset 
pin=15  # cislo pinu 15 na konektoru odpovida GPIO22



# nasilne preklopeni R-S obvodu do stavu "STOP"
GPIO.setup(pin, GPIO.OUT)
time.sleep(0.01)
GPIO.output(pin, GPIO.LOW)
time.sleep(0.01)
GPIO.setup(pin, GPIO.IN)

# nulovani pocitadla - obvod PCF8583
bus.write_byte_data(0x50,0x00,0x20) # status registr nastavit na "EVENT COUNTER"

bus.write_byte_data(0x50,0x01,0x00) # vynulovat LSB
bus.write_byte_data(0x50,0x02,0x00) # vynulovat prostredni bajt
bus.write_byte_data(0x50,0x03,0x00) # vynulovat MSB


# nastaveni pinu RTC-OUT na obvodu PCF8563 na frekvenci 32768Hz
bus.write_byte_data(0x51,0x0d,0x80)




#cekani na preklopeni do stavu "START"
while (GPIO.input(pin) == 0): 
  time.sleep(0.5)

starttime = time.time()      # aktualni cas v sekundach od roku 1970
print "Hruby cas startu casomiry : " , starttime



#cekani na preklopeni do stavu "STOP"
while (GPIO.input(pin) == 1): 
  time.sleep(0.5)

stoptime = time.time()       # aktualni cas v sekundach od roku 1970
print "Hruby cas zastaveni casomiry : " , stoptime


rozdilsek = stoptime - starttime 
print "Nepresny celkovy cas v sekundach (rozdil hrubych casu) = " , rozdilsek


# zjisteni poctu impulzu v pocitadle a prevod do desitkove soustavy
counter =  bus.read_i2c_block_data(0x50,0x00)
rad1 = (counter[1] & 0x0F)             # rad jednotek
rad10 = (counter[1] & 0xF0) >> 4       # rad desitek
rad100 = (counter[2] & 0x0F)           # rad stovek
rad1000 = (counter[2] & 0xF0) >> 4     # rad tisicu
rad10000 = (counter[3] & 0x0F)         # rad desetitisicu
rad100000 = (counter[3] & 0xF0) >> 4   # rad stotisicu

count = (rad100000 * 100000) + (rad10000 * 10000) + (rad1000 * 1000) + (rad100 * 100) + (rad10 * 10) + rad1
print "Pocet impulzu v pocitadle = " , count


# po milionu impulzu (asi 30 sekundach) se pocitadlo nuluje a zacina dalsi cyklus
celycykly = int((rozdilsek * 32768) / 1000000)
print "Pocet celych cyklu = " , celycykly

# vypocet presneho casu : celkovy pocet impulzu / pocet impulzu za sekundu
presnycas = ((celycykly * 1000000) + count) / 32768.0


# Kontrola, ze souhlasi pocet cyklu s realnou hodnotou casu.
# Pokud je rozdil vypocteneho a nepresneho casu vetsi nez 20 s,
#   je treba zkorigovat pocet cyklu a znovu spocitat presny cas.
if (abs(presnycas - rozdilsek) > 20): 
  if (count > 500000):
    celycykly = celycykly - 1
  else:
    celycykly = celycykly + 1
  presnycas = ((celycykly * 1000000) + count) / 32768.0



print "Presny cas = " , presnycas , "sek."





Pro správnou funkci programu je třeba mít nainstalovanou podporu pro ovládání GPIO portu a pro I2C komunikaci (SMBUS).
Postup instalace těchto doplňků byl popsán v předchozích článcích (GPIO, SMBUS).

 

A na závěr ještě ukázkové video z měření času přesnou metodou:

Odkaz na YouTube
Za povšimnutí stojí červená LEDka na nepájivém poli, která se rozsvěcí 
 po průchodu kuličky startem a zhasíná při přerušení cílového paprsku. 

 


Doplnění 5.6.2013

Nové ovladače GPIO portu pro Python (od verze 0.5.1) umí tzv. "polling".
Popis tohoto vylepšení naleznete zde: https://code.google.com/p/raspberry-gpio-python/wiki/Inputs

Pro první příklad se stopkami lze polling využít při čekání na stisk tlačítka.
Odpadnou tedy ty rychlé smyčky "while .... ".

Navíc se tímto způsobem o hodně sníží zatížení procesoru a dojde k částečnému zpřesnění měření (původní smyčka while ... trvala více než 0,001 s.  Polling se v Pythonu provádí frekvencí 19kHz, takže testování stisknutého tlačítka je víc než 19x rychlejší.)
Pokud byste program přepsali do jazyka "C", tak tam je ta frekvence testování stavu tlačítek až 120kHz.

Takže ten jednodušší program pro měření času mezi stiskem dvou tlačítek by vypadal takto:


#!/usr/bin/env python

# JEDNODUCHA  CASOMIRA


import time
import RPi.GPIO as GPIO

# startovaci pin  
pin1=15  # cislo pinu 15 na konektoru odpovida GPIO22
# stopovaci pin  
pin0=16  # cislo pinu 16 na konektoru odpovida GPIO23

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(pin1, GPIO.IN)
GPIO.setup(pin0, GPIO.IN)

#cekani na preklopeni startovaciho pinu do stavu "0"
GPIO.wait_for_edge(pin1, GPIO.FALLING)


starttime = time.time()      # aktualni cas v sekundach od roku 1970
print "Cas na startu : " , starttime



#cekani na preklopeni stopovaciho pinu do stavu "0"
GPIO.wait_for_edge(pin0, GPIO.FALLING)


stoptime = time.time()      # aktualni cas v sekundach od roku 1970
print "Cas v cili : " , stoptime


rozdilsek = stoptime - starttime 
print "Celkovy cas = " , rozdilsek , " sek."


 


úvodní strana webu AstroMiK.org

poslední úprava stránky 5.6.2013