Frekvenční čítač přímý nebo reciproční?

Frekvenční čítač má změřit frekvenci vstupního signálu. Na Raspberry Pi Pico (RP2040) postavíme frekvenční čítač, který měří do 70MHz. Umí být přesný i pro frekvence menší než 100kHz.

pico_citac_autorange.ino [ke stažení

    Komunikuje přes sériový monitor.  Při spuštění vyžaduje zadat hledanou frekvenci, kterou měříme, aby mohl dopočítat chybu ppm. Pokud zadáme 0, tak se spustí automatické měření, které zobrazí pouze frekvenci, kdy změří frekvenci vstupního signálu pomocí PWM periferie a pokud vychází menší než 100kHz zvolí přesnější metodu recipročního měření frekvence.

hledáme 70MHz


Přímé měření frekvence 

    Obvykle si čítač frkvence představím tak, že po stanovenou dobu počítame náběžné hrany signálu a pak spočteme frekvenci. Obecně N pulzů za t sekund f = N/t. Když budeme počítat pulzy po dobu jedné sekundy, tak počet hran je roven frekvenci v Hz. 

Přímý frekvenční čítač pomocí Raspberry Pi Pico (RP2040)

    Použijeme periferii PWM, procesor má 8 nezávislých bloků, každý blok má 2 kanály (A,B).  Každý blok je 16bitový čítač (0 – 65535). Čítač bloku B nemusí tikat podle systémových hodin, ale dá se přepnou do režimu, kdy se inkrementuje pouze, když přijde náběžná hrana pulzu z vnějšího zdroje.
    Drobné hardwarové omezení je, že počítání hran funguje výhradně na kanálu B, což jsou liché piny GPIO.     Aby nám fungovalo počítání frekvence musíme vyřešit přetečení. PWM registr má jen 16bitů, takže když napočítá do 65535, musí vyvolat přerušení a inkrementovat 32bitovou proměnnou.
    Finální počet pulzů získáme jako počet přetečení × 65536 + zbytek, který zůstal v hardwarovém čítači.
Více v komentářích přímo v programu viz [pico_primy_citac].

Problém pomalých signálů

    Možný problém u měření pomalých signálů je kvantizační šum. Vstupní signál měříme procesorem. Hodinový takt procesoru není nijak synchronizován či jinak propojen s generátorem vstupního signálu. Na okrajích měřícího okna se může dít to, že pokud se hrana signálu objeví před zavřením počítacího okna, napočítáme N pulzů, ale pokud hrana uteče třeba jen o nanosekundu až po zavření okna, už napočítáme N–1 pulsů. Chybě říkáme ±1 count error.   


f vstupního signálu v Hz
t čas měření v sekundách

pro signál 10Hz měřený v okně 1s
1/10=0,1=10% 
ppm je 0,1 × 10E6 =  ±100000ppm

Reciproční měření frekvence

    Otočíme logiku měření. Nečekáme až se pomalé pulzy naskládají do časového okna, ale pomalý signál je časové okno do kterého natlačíme takty interních hodin procesoru. Kvantizanční chyba bude záviset pouze na frekvenci procesoru a délce měření.
f cpu v Hz
t čas měření v sekundách

pro signál 10Hz a takt procesoru 200MHz, délka meření 1s
10Hz za sekundu má 10 period
takt procesoru trvá 5ns, za sekundu napočítá 20 000 000 taktů
±1 pulz bude nejistota 1/20000000= 0,00000005 
ppm je 0,00000005 × 10E6 = ±0,05ppm 
Srovnejme s předchozí chybou ±100000ppm. Ano, je to opravdu o tolik lepší!

Příklad praktický, měření hodinového krystalu

signál 32768Hz měřený v okně 16 sekund

Přímé měření
počet napočítaných pulsů 32768 × 16 = 524 288
chyba ±1 pulz ≈ 1/524288 = 1,9073486E-6 
ppm 1,9073486E-6 × 10E6 = ±1,91ppm
1,9073486E-6 × 32768= ±0,0625Hz (to je minimální krok s jakým umí přímý čítač měřit; tedy menší odchylka např 0,01Hz se ztratí v kvantizačním šumu)

Reciproční měření
kvantizační chyba ±1 takt procesoru ±1/(200E6 × 16)= 3,125E-10 
ppm je 3,125E-10 × 10E6 = ±0,0003125ppm
odchylka Hz 3,125E-10 × 32768 = ±0, 00001024Hz

Charakteristiky čítačů viz [kapitola 3].
Pro přesné měření nízkých frekvencí (cca do 100kHz) je výhodnější použít reciproční frekvenční čítač, který dosahuje výrazně vyšší přesnosti.

Reciproční frekvenční čítač pomocí Raspberry Pico (RP2040)

RP2040 má programovatelné stavové automaty PIO, které běží deterministicky. Jsou zcela nezávislé na procesoru. Takt PIO je takt procesoru, při 200MHz je to 5ns. 

Přesnému změření délky jedné celé periody vstupního signálu (tedy doby, po kterou je signál ve stavu HIGH + doby ve stavu LOW) pomocí počítání hodinových taktů mikrokontroléru. Program funguje tak, že si nastaví počítadlo (registr X) na maximum a neustále od něj odečítá jedničku, dokud se nedokončí celá jedna perioda signálu.


 0: wait 0 pin 0 
 1: wait 1 pin 0 
 2: set x, 0       
 3: mov x, ~x      
 4: jmp pin 6      
 5: jmp 8          
 6: nop            
 7: jmp x-- 4      
 8: jmp pin 11     
 9: jmp 10         
10: jmp x-- 8      
11: mov isr, x     
12: push           

Jak to přesně funguje?
0: wait 0 pin 0
Synchronizace
PIO automat čeká, dokud měřený pin (index 0) neklesne do logické nuly (LOW). Pokud už na nule je, okamžitě pokračuje dál. Zajišťuje to, že měření nezačne uprostřed již probíhajícího pulzu.
1: wait 1 pin 0
Detekce startu měření
Automat vyčkává na náběžnou hranu – okamžik, kdy se pin překlopí z logické 0 na logickou 1 (HIGH). Přesně v tento moment začíná nová perioda a odstartuje se počítání.
2: set x, 0
Inicializace počítadla
Nastaví hodnotu v interním 32bitovém pracovním registru X na nulu (0x00000000).
3: mov x, ~x
Příprava maxima
Provede bitovou inverzi registru X. Ze samých nul se rázem stane maximální 32bitová hodnota, tedy 0xFFFFFFFF (desítkově 4 294 967 295). Registr se používá jako odpočítávadlo (od maxima směrem dolů).
4: jmp pin 6
Smyčka pro úroveň HIGH
Zkontroluje aktuální stav pinu, který byl nastaven jako skokový (jmp_pin v C kódu). Pokud je na pinu stále logická 1, program skočí na instrukci 6. Pokud je na pinu už logická 0 (začala druhá půlka periody), instrukce neprovede skok a program pokračuje na řádek 5.
5: jmp 8
Opuštění HIGH smyčky
Tento řádek se vykoná pouze ve chvíli, kdy měřený signál spadl z HIGH do LOW. Jedná se o nepodmíněný skok na instrukci 8, čímž se opustí první měřící smyčka a plynule se přejde do druhé měřící smyčky.
6: nop
Vyrovnání taktu (Smyčka HIGH)
Prázdná operace (No Operation), která nedělá nic, ale spotřebuje přesně 1 hodinový takt procesoru. Slouží k tomu, aby jeden oběh HIGH smyčky trval přesně stejný počet taktů bez ohledu na to, kudy kód prochází (udržuje precizní a konstantní časování celého čítače).
7: jmp x-- 4
Odkrojení taktu (Smyčka HIGH)
Sníží hodnotu v registru X o 1 (dekrementuje). Následně skočí zpět na instrukci 4, čímž uzavírá smyčku, která počítá takty, dokud je signál v úrovni HIGH.
8: jmp pin 11
Smyčka pro úroveň LOW
Jsme v měření úseku, kdy je signál dole (LOW). Tato instrukce se ptá: "Naskočil už signál znovu na logickou 1?" Pokud ano (detekována další náběžná hrana = celá perioda je u konce!), skočí se na instrukci 11 a měření se ukončí. Dokud je signál stále v nule, pokračuje se dál na řádek 9.
9: jmp 10
Vyrovnání taktu (Smyčka LOW)
Znovu trik pro udržení stejného počtu taktů. Skočením rovnou na následující řádek 10 se úmyslně spotřebují (spálí) hodinové takty procesoru, aby byla délka LOW smyčky naprosto symetrická (trvala stejný čas) jako HIGH smyčka výše.
10: jmp x-- 8
Odkrojení taktu (Smyčka LOW)
Sníží hodnotu v registru X o 1 a skočí zpět na instrukci 8. Toto uzavírá smyčku, která běží po celou dobu, kdy je signál na logické nule.
11: mov isr, x
Uložení výsledku
Jakmile perioda skončila (na pinu se objevila jednička), program se dostal sem. Hodnota z registru X (od čísla 0xFFFFFFFF se mezitím v obou smyčkách odečetl počet uplynulých taktů procesoru) se překopíruje do vstupního posuvného registru (ISR).
12: push
Odeslání výsledku
Hodnota z ISR se protlačí do RX FIFO paměťové fronty. Z této paměti si už výsledek přečte hlavní C kód (pio_sm_get). Po provedení push se program – díky wrap v hlavním programu – automaticky a bez jakéhokoliv zdržení zacyklí zpět na začátek měření pro další periodu.

Další informace jsou v komentářích viz program [pico_reciprocni_citac] pro Arduino IDE.

Novější Starší