Raspberry Pi Picoでレシプロカル周波数カウンタ

更新日 2025.08.20 登録日 2025.08.05

Raspberry Pi Picoの周波数カウンタには単純なゲート方式の Raspberry Pi Picoで周波数カウンタ を作りましたが、ゲート時間が1秒だと1Hz単位でしか周波数が分かりません。そこで、入力パルスの周期をカウントして周波数を求めるレシプロカル方式の周波数カウンタを試してみました。

最初に断っておきますが、この方式では約2kHz以下では16ビットカウンタのオーバーフロー処理対策が不十分なので時々変な値が出てしまいます。ゲート方式のカウンタの時に既存ライブラリにケチを付けたのがブーメランで帰って来ました。それでも2kHz以上では原理通りに機能するし、2kHz以下でもほとんどの場合は正しい値が出ます。

ここでも Accurate frequency measurement on the Pi Pico RP2040 using C という記事からヒントを得たDMAを使ったキャプチャーもどきの機能を利用します。

測定原理

入力周波数の複数周期を基本的には133MHzのシステムクロックをSlice0の16ビットカウンタで数えます。このカウンタが1周するのは 133MHz/65536=2029Hz すなわち 0.49msec 掛かるので、それよりも短い間隔でキャプチャーして積算すればより長い周期を計測出来ます。 このキャプチャーするタイミングを入力周波数の何波分にするかをSlice3の16ビットカウンタのWRAP値Nで調整します。そしてその間隔を何回繰り返せば約1秒になるかをMとし、ソフトウェアでM回目の積算値をM*N波分の周期として周波数に換算します。

入力周波数が2029Hz以下では1波でも0.49msを超えてしまうのでSlice0のオーバーフローをソフトウェアインタラプトでカウントして合成したものの差分を取って1波の周期とします。キャプチャーしたカウントとオーバーフローをカウントした値のタイミングのずれを完全に解消するのは困難で、前述の様な変動が出ます。

*

カウントのキャプチャーを実現するためには入力周波数をN分周するスライス (図中Slice3) のWRAPイベントでDMAを起動してカウント値 (図中Slice0) を特定の変数 cap1 に転送します。転送はソフトウェアの流れに較べて瞬時に終了するので、WRAPのインタラプト処理で次のWRAPイベントが起こる前までにカウント値の差分を積算すれば良いことになります。

システムクロックの精度は通常50ppm以下ですが、Slice0のChannel Bからより精度の良いTCXOやGPSモジュールからの10MHzを入力することによって改善することが出来ます。TCXO は10MHzから50MHzまでの1MHz単位を想定しています。

測定周波数の上限ですが一般的にはクロック周波数133MHzの1/3の44.3MHzまでが安全ですが、65MHzまで何となく測れます。250MHzにオーバークロックすれば83.3MHzまでが安全で上限は125MHzです。300MHzにオーバークロックすれば100MHzまでが安全で上限は150MHzです。オーバークロックですがここまでダイレクトにカウントできるのはRasberry Pi Picoならではでしょう。

ゲート時間はPeriodCount.begin(msec)のmsecで設定できます。msecは1msから65535msまで設定できます。 ゲート時間は動作中でもPeriodCount.gatetime(msec)のmsecで変更できます。

開発環境

開発環境は Arduino IDE 1.8.19 + Raspberry Pi Pico/RP2040/RP2350 by Earle F. Philhower, III version 4.6.1です。
ボードは "Raspberry Pi Pico" または "Waveshare RP2040 Zero" を指定します。

ソースコード

ZIPファイルを展開したフォルダを library フォルダに置けばArduino IDEの「ファイル=>スケッチ例=>PeriodCountRPP」からサンプルプログラムを選択することが出来ます。

FreqMeasureRPP101.zip (update 2025.08.20)

起動方法

  • PeriodCount.begin(uint16_t msec);
  • 計測時間をmsecで指定できます。1秒の場合は1000を指定します。周波数入力ピンはGP7、基準周波数をカウントするスライスに対応するピン番号はGP0になります。

  • PeriodCount.begin(uint16_t msec, uint8_t fpin, uint8_t gpin);
  • 計測時間をmsecで、周波数入力ピンをgpinで、基準周波数をカウントするスライスに対応するピン番号をfpinで指定できます。fpinは偶数、gpinは奇数を指定します。

  • PeriodCount.begin(uint16_t msec, uint8_t fpin, uint8_t gpin, uint16_t khz);
  • CPUクロックではなく外部から基準周波数を入力する場合にその周波数をkhzで指定します。25MHzの場合は khz=25000 を指定します。基準周波数の入力ピンは (fpin+1) になります。khz=0 ならCPUクロックを使います。

    サンプルプログラム

    回路図は「Raspberry Pi Picoで周波数カウンタ」と同じです。

    Serial

    Arduino IDEのシリアルモニターに結果を表示します。 PWMを使って14番ピンに44.3MHzを出してテストできるようにしました。 周波数入力ピンはGP7、基準周波数をカウントするスライスに対応するピン番号はGP0になります。

    *

    MAX7219

    MAX7219を使用した8桁7-segment LEDに結果を表示します。 PWMを使って14番ピンに44.3MHzを出してテストできるようにしました。 周波数入力ピンはGP7、基準周波数をカウントするスライスに対応するピン番号はGP0になります。

    *

    OLED1306

    SSD1306を使用した128x32のOLEDに結果を表示します。 PWMを使って14番ピンに44.3MHzを出してテストできるようにしました。 周波数入力ピンはGP7、基準周波数をカウントするスライスに対応するピン番号はGP0になります。

    *

    *

    OLED1306BigFont

    SSD1306を使用した128x32のOLEDに結果を表示します。大きい数字フォントで表示します。 PWMを使って14番ピンに44.3MHzを出してテストできるようにしました。 周波数入力ピンはGP7、基準周波数を入力するピン番号はGP1になります。
    TCXOから基準周波数を入力します。TCXOの周波数は簡易式の周波数カウンタで測定して自動的に設定します。周波数は10MHzから50MHzまでなら使えるはずですが、1MHz単位に四捨五入しているので100kHz以下の端数があるものは使えません。TCXOを接続しない場合は自動的にCPUクロックに切り替えます。入力アンプには74HCU04のアンバッファインバータを使用しています。100MHz近くまで使えると思います。下手にFETやトランジスタを使うより簡単です。

    *

    *

    写真は25MHzで0.5ppmのTCXOを使用してシステムクロックから1kHzのテスト信号を発生して計測したものです。 1000.0365HzとなりCPUクロックがTCXOの基準から36.5ppm高いということが分かります。

    周波数誤差について

    Raspberry Pi Picoのシステムクロックは水晶発振器が使われていて50ppm以内の精度に収まるようです。

    計算で周波数を補正するには、例えば正確な16MHzを測定して16.000123MHzになったら、
    freq = freq * 16.0 / 16.000123;
    と補正してしまえばとりあえず補正出来ます。それでも温度変動には無力で、水晶発振回路の性能次第になります。

    周波数偏差をソースコードで修正するのは面倒なので、例えば基準周波数の16MHz, 12MHz, 10MHz, 8MHz, 1MHzと100ppm以内の誤差ならばCalibrationボタンを押して自動的に補正して補正値を保存するようにすることも考えられます。これは STM32F103C8T6で192MHz周波数カウンタ を参照してください。

    システムクロックをカウントしているスライスにchannel BからTCXOやGPS受信モジュールからの10MHz信号を与えればより精度を上げることが出来ます。測定時間1秒の時の基準周波数と分解能の対応は下表の通り。

    周波数 分解能 有効桁数
    10MHz 0.1ppm 7桁
    20MHz 0.05ppm 7桁以上
    25MHz 0.04ppm 7桁以上
    30MHz 0.033ppm 7桁以上
    40MHz 0.025ppm 7桁以上
    50MHz 0.02ppm 7桁以上

    GPIOとSliceとchannelの対応

    周波数入力ピンを変更するには以下の表を参照してGPIOとSliceとchannelを決定することになります。周波数入力ピンと基準周波数入力ピンはchannel Bにする必要があります。

    GPIO 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
    PWM channel 0A 0B 1A 1B 2A 2B 3A 3B 4A 4B 5A 5B 6A 6B 7A 7B
    GPIO 16 17 18 19 20 21 22 23 24 25 26 27 28 29
    PWM channel 0A 0B 1A 1B 2A 2B 3A 3B 4A 4B 5A 5B 6A 6B

    References

  • Dan Ellis A frequency counter library for the RP2040
  • Jeremy P Bentham Accurate frequency measurement on the Pi Pico RP2040 using C
  • 居酒屋ガレージ店主 Arduino UNO R3で周波数を計る
  • siliconvalley4066 Arduino UNO R4でレシプロカル周波数カウンタ
  • siliconvalley4066 Arduino UNO R4で周波数カウンタ
  • siliconvalley4066 STM32F103C8T6で192MHz周波数カウンタ
  • siliconvalley4066 Raspberry Pi Picoで周波数カウンタ

  • <=Arduinoで電子工作に戻る
    <=Siliconvalley4066番地に戻る
    ゲストブックへ
    siliconvalley4066@yahoo.co.jp