|
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:
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."
|
|
|
|