もよいめも

不定期更新ものづくりブログ

【Raspberry Pi Pico】PIOを使ってナノ秒単位で計測する!

今回はRaspberry Pi Pico君の秘められしポテンシャルを拝ませてもらうべく、
PIO(Programable IO)に触れていきたいと思います。

PIO(Programable IO)とは

これ↓見てください
https://interface.cqpub.co.jp/wp-content/uploads/if2108_176.pdf

つまりはCPUのリソースを消費しない、超小規模マイコンが計8つ載ってるようなイメージになります

なにをやるか

こちら↓に公式がMicroPythonで書いた例がいくつかあります。
github.com
LチカからWS2812(通称:流れるテープLED)の制御、
uartやspiまで、様々な処理をCPUに頼らずに行えます。


なんて魅力的なんでしょうか…


さあ、そんなPIOを使いたいな~と思っていた矢先に、友人より

「ns(ナノ秒)単位で時間計測したいんだが」

という相談を受けました。

ナノ秒というと相当早いので並大抵のマイコンでは計測は不可能ですし、PythonをPC上で動かして実験してみても厳しかったです。
こういうのは基本的にFPGAやらなんやらを使うのが定番って感じでした。


いやしかし、PIOならできるのでは??


ということで、今回はPIOでナノ秒単位時間計測をしていこうと思います!

開発環境

Raspberry Pi Picoはいくつかの言語・環境に対応しているわけですが
今回は私のPython嫌いを治すべく、MicroPythonを使って開発を行いました。

使ったIDEはこちら↓の「Thonny」を使用しました。
thonny.org

なんかこう、なんとなくおぞましいアイコンをしていますね...

.pyファイルを開く標準ソフトに設定すると、たくさんアイコンが並んでしましい、なんだか恐怖を感じます。

f:id:moyoi:20220307105705p:plain
恐ろしい


しかしながら、ソフト自体はシンプルかつ簡単で、即座にRaspberry Pi Picoの開発に着手できました。

プログラム

import rp2
from machine import Pin

PIO_IN_PIN0  = Pin(6, Pin.IN, Pin.PULL_UP)#GP6を入力・プルアップに設定

@rp2.asm_pio(in_shiftdir=rp2.PIO.SHIFT_LEFT)
def count():
    pull()#TX FIFOのデータをosrに移動
    mov(x,osr)#x=osr
    wait(0, pin,0)#ピンがLOWになるまで待機
    label("loop")
    in_(pins,1)#ピンの状態1bit分だけisrに移動
    mov(y,isr)#y=isr
    jmp(y_dec,"end")#y=0 で通過 それ以外はy=y-1してラベル:endにジャンプ
    jmp(x_dec,"loop")#x=0 で通過 それ以外はx=x-1してラベル:loopにジャンプ
    mov(x,osr)#x=osr
    label("end")
    mov(isr,x)#isr=x
    push()#isrのデータをRX FIFOに移動

sm = rp2.StateMachine(0, count, freq =-1 , in_base=PIO_IN_PIN0)#CPUの周波数と同スピードで設定
sm.active(1)#動作開始

machine.freq(200000000)#CPU周波数を200MHzに変更

print("begin")
while True:
    sm.put(20000000)#TX FIFOに2千万を設定
    print(20000000-sm.get(),end="count\n")#RX FIFOにデータが入るまで待機して、入ったら取り出してカウント数を計算、出力
    #繰り返し

このプログラムを走らせた状態で、適当にGP6とGNDをショートさせると、以下のように大量のデータが吐き出されます。
これは接触時のチャタリングが原因ですかね

begin
0count
0count
0count
0count
12364939count
203count
4533count
60count
11929count
98count
514count
39count
27count
109count
243count
311count
13274count
5179count
667count
57count
10538828count
64count
35count
39count
24count
26count

※ちなみに0countは計測可能時間以上ショートさせた場合

今回周波数が200Mhz、PIOの処理1サイクルあたりにかかる時間は

1/200Mhz

なので5nsとなります。


PIOの命令はすべて1サイクルなので

開始処理にサイクル、1カウントあたり4サイクル、終了処理で3サイクル使い、(自信ないので適当)

(1+4×(カウント数-1)+3)×5ns
= 4×カウント数×5ns

に多分なると思います。
それで、デッドタイム(クールタイム?)は100nsくらいになるんじゃないですかね?(すっとぼけ)


そのため、最短計測可能は20ns、今回2千万を初期値に設定しているので最大計測時間は2千万×20nsで400msとなるはずです。

本当に測れてるか確認

実際に以下のプログラムを走らせたPicoに接続してみたところ、

from machine import Pin
import time

led=machine.Pin(22,machine.Pin.OUT)
led1=machine.Pin(25,machine.Pin.OUT)

while True:
    led.value(0)
    led1.value(1)
    time.sleep(0.1)
    led.value(1)
    led1.value(0)
    time.sleep(1)

以下のような出力が得られました。

begin
5001488count
5001640count
5001640count
5001539count
5001629count
5001529count

大体500万カウント、つまり
500万×20nsで1億ns=0.1秒
となりますので、ピンをON,OFFする時間を考慮すれば正しく計測できていることがわかります。

終わりに

ちなみにPIOで切り替え時間を20nsまで短くしたPicoを接続したとき、
ぎりぎり読めないこともありましたが、1カウントと出力されていました。

まあ私はオシロなんて持ってないので本当なのかは確認できませんね。



ということで私はㄑそ忌々しい受験勉強に戻りたいと思います。


では皆さんも良きPIOライフをお送りください~
(私へオシロを送ってくださってもいいですよ)

参考

rp2 --- RP2040 に固有の機能 — MicroPython 1.18 ドキュメント
Raspberry Pi Picoの仕様書を読んでみる(3) – 楽しくやろう。
RP2040のPIOで2bitバスで入力を受け付ける - chakokuのブログ(rev4)
MicroPython的午睡(15) ラズパイPico、プログラマブルIOの威力 | デバイスビジネス開拓団































そういえば最近「あなたのお子さんは私のことが好きですか?」
って勉強(??)が訴えかけてくる塾かなんかCMがありますが、イライラするのでやめてほしいです。

勉強好きなやつなんていねーよ!
ばーーーーーか!







失礼しました。