ちょっとTea Time!? PICO(RP2040)の憂鬱?あ、PICのカウンタを使おう!(周波数カウンタ) 2025.7.3

PICO(RP2040)を使いだして、PICに対する性能差から、なかなかPICを使おうという気が起きないのですが、
現時点でPICにあって、PICOにない機能があります。それも、重要な機能です。

PICOには外部クロックのカウンターがない!PICには16Bitのカウンターがある!


この差は大きいです。16Bitでも外部クロックのカウンタがあると、周波数カウンタなどが気楽に作れます。
このカウンターを外付けで準備しようとなると、8Bitのカウンターロジック2個と、それを読み出す8ビットのシフトレジスタが
2個の合計4個のロジックICがすくなくとも必要になってきます。そして、面倒なのが、その配線です。
なぜ、PICOにカウンタがないのだろう?まあ、愚痴を言っても始まりません。
ひょっとしてあるのだけれど、気付いていないだけかなあ?
とりあえず、PICOにはないとして、なにか、簡単にカウンタが使える良い手はないかなあ〜?

ん?PICをカウンタとして使えばいいのでは?

ふと、外付けのロジックICの代わりにPICを使えば、1個で済みそうなことに気付きました。
そして、なにより配線が数本で済んでしまいます。
まあ、PIC側にプログラムを書く必要がありますが、大したものにはならないでしょう。

こんな感じでPICを外部カウンタとして使えば、配線がもの凄く楽です。

PICの内部カウンタは16Bitですが、オーバフローを検知することで16Bit以上のカウンタにすることも簡単です。
PICのカウンターの最大動作周波数は50MHz程度ですが、16Bitのカウンタだとオーバフローするタイミングは
1.3ms毎です。したがって1ms毎にオーバフローをチェックすれば十分です。具体的には、1ms前のカウンタ値
が現時点のカウンタより大きな値になっていればオーバフローしているとみなして、上位のカウンタビットを
+1します。

そうだ!周波数カウンタを考えてみよう!

PICを外付けカウンタとしてつかえるかの実験だけでは、おもしろくないので、
この際、ダイレクト計測とレシプロカル計測の両方がつかえる周波数カウンターを考えてみましょう。

ダイレクト計測  :一定時間内(たとえば1秒間)のクロックを数える方法
レシプロカル計測:入力クロックの周期を計測して、周波数を換算する方法

ダイレクト計測の場合は、カウントした値を表示するだけですが、レシプロカル計測を行う場合は、
すくなくとも倍精度の演算が必要になってきます。しかし、PICOだとC言語で倍精度の演算ルーチンが
ついているので簡単です。以前はPICで同じことをしようとして、結構苦労しました。
レシプロカル計測ができると、低周波数でも高精度が測定ができます。

極力回路は簡単に!

以前に作った周波数カウンターではICは都合、14素子ほどつかいました。
が、今回はできるだけ数を少なくしてみました。全部で7素子ほどですから、半分になりました。
内訳はロジックIC3個、CPU2個、レギュレータ1個、そしてSi5351モジュール1個です。


結構シンプルな回路図になりました。


部品を配置してみました。空いたスペースは入力のアナログアンプなどを実装しましょう。

予備テスト

今回の回路では、部品点数をすくなくするために、ちょっとトリッキーなことをしています。
その機能がうまく動くかどうかの事前テストです。これが、予想通りでないと回路図はすこし書き直しです。

なにを確認するかというと、Si5351の設定可否です。下記のような設定ができるかどうかの確認です。

Si5351
OUT0
Si5351
OUT1
Si5351
OUT2
備考
ダイレクト計測用 5kHz HIGH HIGH U3:7474 P5:Q出力は1sのクロックゲートとして使用
レシプロカル計測用 LOW 40MHz
(OUT2の逆相)
40MHz
(OUT1の逆相)
U3:7474 P5:Q出力は40MHzのクロックとして使用

そう上手くはいかないなあ〜 2025.7.4

ダイレクト計測用の設定は問題なくできましたが、レシプロカル計測用の設定がうまくいきません。
Si5351の設定データの作成ツールで、Invertの項目があったので、てっきり出力位相を反転させるものと
思っていましたが、どうやら違うようです。これって、何の機能かな?


Invertをチェックすれば位相が判定すると思っていましたが、違うようです。



残念ながら、2つの出力の位相差がほとんどありません。

回路変更

Si5351だけで2つの位相の異なる出力が欲しかったのですが、出来ないようなので
回路をすこし変更です。運よくNANDゲートが1つ余っていたので、それを活用です。
そのため、ICの数を増やさずに済みそうです。


回路をすこし変更です。

組み立てましょう!

回路図もできたことなので、組み立てましょう。
部品も少ないので、比較的短時間で組み立てられるでしょう。


実装完了です。



半田面側です。割と少ない配線で済みました。


ソフト作成!

まずは配線チェックしたのにち、RasPi上で開発環境の整備です。具体的には、PICOのC言語開発の
プロジェクトを作成して、必要なライブラリをかき集めてきます。必要なのはグラフィック用やI2C用の
ライブラリです。
 とりあえず、動作確認のためOLED(グラフィックパネル)に表示させて、Si5351にデータを書き込めるか
をチェックです。

配線チェックも兼ねながら、ぼちぼちとソフト製作です。


さて、いつ完成するかなあ〜。

PIC側のプログラム 2025.7.5

 今回の周波数カウンターではクロックカウントはPIC側で行います。PICでのカウントビットは32Bitとしました。
これなら、レシプロカル計測において40MHzのクロックにおいても100秒以上はカウントできますから、0.01Hzまで
対応可能になります。まあ、そこまで気長な計測はしませんが。
 カウントしたクロック数はPICOからのリクエスト(REQ)を受ければ、調歩同期で送信します。
データフォーマットは16bitデータとして1 Start Bit ,1 Stop Bit です。通信速度は20kbpsとしました。
前半が上位16bitで、後半が下位16bitです。全体の通信時間は1.36msとノンビリモードです。


PICでのカウントデータの通信フォーマットです。

PIC側では通信が終了したら、内部のカウンタはすべてリセットします。

まずは、ダイレクト計測から。をを!

まずは手始めにダイレクト計測モードでのソフト作成です。
簡単に1s間でのクロックを数えます。

クロック信号の発生には、以前にブレッドボード用につくったオーディオクロックをつかいました。
まだ、秋月電子でクロックモジュール扱っているかな〜と思ってみましたが、入荷未定になっていました。
まあ、ALIで「Si5351」で探せば、いっぱいでてくるので、必要ならそちらから調達ですね。.


クロック源には以前作成したオーディオ用クロックをつかいました。

まずは、動作確認から進めます。難しいプログラムではないので、すんなりと周波数計測値が
表示されました。となると、どこまで計測できるかを試したくなります。50MHzあたりも問題ありません。
調子にのって、100MHzでも試してみました。事前にPIC側のクロックカウントのオーバフローチェックの
タイミングを短くしておきます。すると、ををを!問題なく100MHzあたりでも計測できそうです。
さらに、調子にのって200MHzにしてみましたが、こちらはだめでした。
クロック源では100MHzの次が200MHzの設定にしているので、その中間でどのあたりまで
測定できるかは、試しておきたいところです。しかし、100MHzまで測定できるとなると、
まあ.私の測定の用途に関しては十分なところです.


20MHzのクロック計測です.問題なさそうです.


50MHzのクロック計測です.こちらも、問題なさそうです.


100MHzのクロック計測です。なんと!計れそうな感じです。


200MHzのクロック計測です。こちらは沈黙してしまいました(笑。

それにして、測定の誤差が結構あります。20MHzの測定値が19.9965MHzですから
0.0.17%の誤差です。50MHzの測定時は49.9913MHzですから0.017%の誤差です。
どちらも同じ誤差です。発信源の誤差なのか、それとも測定側のクロック誤差なのか
どちらだろう?たぶんどちらもだろうなあ〜。多分測定側だろうな〜。
なんせ、中華製のクロックモジュールで水晶発振器の精度が100ppm以上悪かった
覚えがあるものなあ〜。

最終的には、測定器は我が家の基準で校正する必要があります。

次はレシプロカル計測。何kHzまで対応できるかな?

レシプロカル計測では、周期を測定して周波数に換算します。
周期の計測には、40MHzのクロックを使います(これも100MHzくらいまで上げられそうだけど、
すこし余裕をみていいます)。
 例えば10kHzを測定したとすると、ダイレクト計測では1秒間のカウントだと1Hz毎の分解能に
なります。それに対してレシプロカル計測だと、10kHzだと周期は100usですから、40MHz(T=25ns)
でカウントすると4000カウントになります。このままだと、分解能は2.5Hzになってしまいます。
しかし、ダイレクト計測と同様に1秒間の計測時間をかけると、10000回分を加算することができ、
分解能は0.25mHzまで向上します。計測対象の周波数が1MHzになると、同様に2.5uHzまで
分解能を上げることができます。
 ハードウエアでカウントするなら問題はありませんが、ソフトでやるとなるとクロックのHigh/Lowを
1MHzで直接的に処理しきれるかという問題がでてきます。PICOの命令サイクルが8nsで、測定クロックの
High/Lowの処理と、測定クロック数のカウントを含めると100kHz(T=10000ns、1250命令サイクル)
程度が関の山という気がしますが、まあソフトを組んでみないとわからないです。

まずは、対象信号の10000回周期分の40MHzクロックの個数をカウントするプログラムを組んで、
どのくらいの周波数に対応できるか調べてみましょう。

結果としては500kHzまでは測定できることがわかりました。


200kHz入力時:計算上は2000000なので正しいでしょう。



500kHz入力時:計算上は800000なので正しいでしょう。


1MHz入力時:計算上は400000なのでうまく計測できていません。

ただ、現時点ではタイムアウト機能は一切いれていないので、測定中に信号が途絶えると
ハングアップしてしまします。タイムアウト機能を入れると、まあ最大100kHz程度としたほうが
いいでしょう。

そろそろ全体の計測手順を考えましょう

ダイレクト計測もレシプロカル計測も簡単なプログラムで確認できたので、
そろそろ全体の計測手順を検討です。なにもしなくても測定できるのが嬉しいので、
まずは、下記のような感じです。

※周波数測定モード
1)ダイレクト計測(0.1s間隔)でfを粗測定
2) 1)のf粗測定の結果から下記を実施
         f  <  100kHz  : 測定回数をf回としてレシプロカル計測
100kHz =< f           :  測定時間1sとしてダイレクト計測

面倒くさくなってきました(笑

計測周波数に応じて、ダイレクト計測とレシプロカル計測をつかい分けようかと思いましたが、
段々面倒臭くなってきました(笑。
スイッチでモード切替をすることにしました。
そしてソフトの仕上げです。ついでに、10MHzの基準発振源を用いて校正を行いました。
(単に、補正係数を加えただけです)。


ダイレクト計測モードで基準の10MHz計測した状態です。校正済です。



レシプロカル計測で5kHzのDDS発振器の出力を計測した状況です。
DDSには20ppmほどの誤差があるようです。


一応これで、周波数カウンターができました。
週末のお楽しみでした。

備忘録

最終的な回路図です。


本当は、パルス幅などの計測が出来るようにハード的にはなっていますが、ソフトが段々面倒くさくなってきてしまいました。
また、思い出したら手を加えていきましょう(って、まずありませんが)。


レシプロカル計測の40MHz発振についても校正です。


校正前。内蔵の水晶発振の誤差が150ppmほどあるようです。



校正後。誤差の修正係数を加えました。

資料

回路図(水魚堂) ※OLEDはSH1106ドライバのもの。
PICバイナリー'PIC18F27Q43)
PICOバイナリ(水晶発振の補正なし)

(たぶん、おしまい)