PICのキャプチャ機能でパルスを計測してみる

〔PICの動かせ方入門に戻る〕


PICにはCCP(Capture/Compare/PWM Modules)モジュールが有り、PWM機能に付いてはMyHPでも
記事にしておりますが、Capture/Compare機能に付いては記事になっていませんでした。
このページで"Capture"機能に付いて記事にして置きたいと思います
"Compare"機能は指定した時刻になるとピン出力(LOW/HIGH/トグル)をさせるものですがぁ、
この機能に付いてはいずれ又にします。
PS. こちらにサラッとぉ記事にして置きました。 *1)

"Capture"機能はピンに外部パルスの立上がり/立下りエッジのイベントが発生した時にカウントしている
タイマーの値(TMR1L/TMR1H)をCCPRxL/CCPRxHレジスタに取り込む機能です、
なのでぇ、指定したエッジとエッジ間のパルスの長さを計測する事が出来ます

ここの実験ではPIC16F1829を使いましたが、どのPICも操作は殆ど同じ様な感じです、
PICによる違いも一応記事にして置きますがぁ...

キャプチャ機能構成図 左図は16F1829のデータシートからの拝借図です。 Timer1は16bitなので0〜65535までカウントアップして行きます。
Pinでエッジイベントが発生すればその時のカウント値を
CCPRxL/CCPRxHレジスタにコピーされ割込みが発生。

PICによっては他の16bitTimer(Timer3/5等)が有れば
それを使用する事が出来ます。

又、デバイスのCCPモジュールの個数分キャプチャ機能でのPin入力可能です。
ここの実験の16F1829はTimer1のみで、CCP1/2/3/4からCCP4(RC6)を使った記事です。
(CCP4ピン1本にパルスを入力して周波数測定や時間計測を行っています。)

キャプチャ機能の特徴と注意点など
・今回のTimer1は1カウント1usで設定していますが、最小カウントは40us程(MAX25KHz程まで)以上の
 パルス幅が必要です。
・Timer1で使用するクロックは"Fosc/4"を使う事とFosc同期モードで行う事です。
・計測に使用するTimer1はスタートさせると、止めるまではイベントが有ろうと無かろうと0〜65535の
 カウントを繰り返しています。
・キャプチャ機能もスタートさせると、指定したエッジが発生すれば止めるまでCCPRxL/CCPRxHに
 コピーし続けます。
 なのでぇ、CCPRxL/CCPRxHを読む前に次のエッジが発生すれば上書きされる事に注意しましょう。

《制御レジスタ》

制御レジスタの"T1CON"と"CCPxCON"のみ説明して置きます、他の割込みレジスタ等は
サンプルプログラムを見ましょう。

T1CONの設定
 T1CON = 0b00110001 ;
 青数字部分でTimer1のクロックソースを選択します。
  00 = Fosc/4  01 = Fosc  10 = 外部入力  11 = 内蔵の容量検知オシレータ (CAPOSC)
 緑数字部分でプリスケーラの設定を行います。
  00 = 1:1  01 = 1:2  10 = 1:4  11 = 1:8
 橙色数字部分で外部クロック入力を同期させるか設定します。
  1=同期させない 0=システムクロックと同期させる
 赤数字部分は通常0です。
  PIC18F2xK22等は、1 = TMR1レジスタを16bitで読書きする
  0 = TMR1レジスタを8bitで読書きする、となっているデバイスの場合は"1"を設定します。
 紫色数字部分 1=TIMER1を動作させる 0=TIMER1を動作させない

ワンカウントの時間計算
システムクロックが32MHzとする場合での計算
・クロックソースが 00 = Fosc/4 でプリスケーラの設定が 11 = 1:8 なら
  ((1/32MHz)x 4)x8 = 1us
・クロックソースが 00 = Fosc/4 でプリスケーラの設定が 00 = 1:1 なら *2)
  ((1/32MHz)x 4)x1 = 125ns

CCPxCONの設定
 CCPxCON = 0b00000000
 青数字部分でイベントを発生させるエッジの指定をします。
  0000 = CCPモジュールOFF
  0100 = すべての立ち下がりエッジでイベントを発生させる
  0101 = あらゆる立ち上がりエッジでイベントを発生させる
  0110 = 立ち上がりエッジを4回カウントしたらイベントを発生させる
  0111 = 立ち上がりエッジを16回カウントしたらイベントを発生させる

その他にPIC18F2xK22等ではTimer1以外でTimer3/5も利用出来るので、"CCPTMRS"レジスタにて どのタイマーを使うのか指定する必要が有ります。

   CCPTMRS0 = 0b00000000 ;
   青数字部分がCCP3用設定で緑数字部分がCCP2用設定で橙色数字部分がCCP1用設定の場所です。
   CCPTMRS1 = 0b00000001 ;
   赤数字部分がCCP5用設定で紫色数字部分がCCP4用設定の場所です。
   Timer1=00 , Timer3=01 , Timer5=10 この設定でCCP4はTimer3を使用です。

《サンプルプログラムについて》

↓ここからサンプルプログラムソースファイルをダウンロードして下さい。
Capture.zip

プログラムソースをダウンロードしたら、MPLAB Xにてプロジェクトを作成します。
以下のファイルをプロジェクトディレクトリにコピーしてプロジェクトに取込んで下さい。
次にコンパイルPIC書き込みを実行して下さい。
MPLAB(R) XC8 C Compiler Version 1.40コンパイラを使用しています。

ダウンロードファイルを解凍すると下記の様なファイル構成です。
 Capture.c・・・・・・・・ キャプチャ(CCP4)のテストサンプル1(周波数の計測)プログラム
 Stopwatch.c・・・・・・ キャプチャ(CCP4)のテストサンプル(ストップウォッチ)
 HICapture.c・・・・・・ キャプチャ(CCP4)のテストサンプル2(高い周波数の計測)プログラム *2)

 尚、CPUのクロックは32MHzを想定しています。
 このサンプルでは"CCP4"を使用し入力ピンは"RC6"となります。

Capture.c

RC6ピンに計測したい波形(MAX25KHz程)を入力し、起動させると周波数とデュティ比を表示します。
ってぇ、表示部分のロジックは無いのでご自分でプログラムして下さい。
[1000Hz]
[39.9%]
って感じですぅ。
私は、周波数ジェネレーター等持っていないので今回の実験はPIC12F1822でPWMを作り入力しました。

関数の使い方を説明します。

以下の関数はライブラリ化をしていないので使う場合はコピッペして下さい。

 void interrupt InterFunction(void)
  キャプチャ機能に関する割り込み処理

 void CaptureSet(unsigned char kosu)
  キャプチャ機能を使用する為の初期化処理
  Timer1を1カウント1usで設定しCCP4を使用する様にセットしています。
  (1usなのでTimer1は16bit:65536usでオーバフローする事になりますよ)

  ※ この関数を呼ぶ前に必ず、PEIEとGIEの割り込みフラグを有効にします。

 ans = PulseCount(edge1,edge2)
  キャプチャ機能の開始とパルス計測を待つ処理
  開始したらパルスエッジのイベント発生(2回目のエッジ)を待ちます、
  なのでぇ、終了するまでは実行中ですので長いパルスは避けた方が良さげぇみたいなぁかんじぃ。
  (エッジ間はMIN40us程でそれより短くなると何んじゃかぁあやしくなってくるんじゃよ)
   edge1 : 計測パルスの開始エッジを指定します(0=LOW 1=HIGH)
   edge2 : 計測パルスの終了エッジを指定します(0=LOW 1=HIGH)
   ans  : 計測したパルスの長さ(us)を返す(MAX約71分程)
  例)       ┌──┐        ┌──┐
        ──┘  └────┘  └─
  PulseCount(1,0) ↑  ↑
  PulseCount(1,1) ↑       ↑

Stopwatch.c

ストップウォッチ配線図  PIC16F1829での実態配線図です。

 スイッチを押すと計測開始です。
 (計測パルスの開始エッジが立ちます)
 次にスイッチを押すと計測終了です。
 (計測パルスの終了エッジが立ちます)
 計測終了で時間(MAX71分程)を表示します。
 ってぇ、これも表示ロジックは有りません。
 表示は[xxxxxxus]となります。

関数の使い方を説明します。

 void StopwatchStart(edge1,edge2)
  ストップウォッチ機能の計測を開始する処理
  開始したらパルスエッジの割り込み待ち状態となります。
   edge1 : 計測パルスの開始エッジを指定します(0=LOW 1=HIGH)
   edge2 : 計測パルスの終了エッジを指定します(0=LOW 1=HIGH)

  ※ スイッチの立上がり間を計測するので、"StopwatchStart(1,1)"としましょう。

 ans = StopwatchCount(void)
  ストップウォッチ機能の計測終了を待つ処理
  "PulseCount()"関数は終了イベントまで待つ処理でしたが、その他の処理をPICに仕事させる場合は
  "PulseCount()"は使えません、そこでこの関数が存在します。
  なのでこの関数は、ループ処理内に置き常に実行させないとダメです。
  この関数は、パルスの終了エッジを待ちます、それまでは0を返し、
  終了すれば計測したパルスの長さをus時間で返します。
   ans : 0=まだ計測中 それ以外は計測したパルスの長さ(us)を返す(MAX約71分程)
    例)この例にはStopwatchStart()が入っていません。
    void main()
    {
          char buf[16];
          long c ;

          CaptureSet() ;

          while(1) {
               c = StopwatchCount() ;
               if (c != 0) {
                    sprintf(buf,"%ldus",c) ;
               }
          }
    }

HICapture.c *2)

上記の"Capture.c"では25KHz位までしか測定出来なかったので、このサンプルプログラムは
もう少し高い周波数まで計測させて見ました。
上記では1カウント1usでしたが、今度は1カウント125nsで行い、キャプチャモードを
"立ち上がりエッジを16回カウントしたらイベントを発生させる"にして行いました。
ですのでぇ、計測結果を16で割ると1サイクルとなるので16回分の平均値となりますね。
もう少し高い周波数・・・100KHz位までで500KHz位は計測出来ますが誤差が大きくなります。

関数の使い方を説明します。

以下の関数はライブラリ化をしていないので使う場合はコピッペして下さい。

 void interrupt InterFunction(void)
  キャプチャ機能に関する割り込み処理

 void CaptureSet(unsigned char kosu)
  キャプチャ機能を使用する為の初期化処理
  Timer1を1カウント125nsで設定しCCP4を使用する様にセットしています。

 ans = CycleCount(void)
  キャプチャ機能の開始とパルスの周波数を計測する処理
  開始したらパルスエッジのイベント発生(16回目のエッジ)を待ちます、
   ans  : 計測したパルスの周波数(Hz)を返す
       2Hz〜100KHz位まで、それ以上は誤差が大きくなる(500KHz位まで測定可)

《その他》

今回の実験では、PICket3のデバッグ機能を利用して行ったので計測した時間を表示する
ロジックが入っていませんが皆さんで御自由にしましょう。

又、Timer1のゲート機能でもパルスの計測が可能ですが、この記事はいずれまた今度、
それとぉ、PIC12F1612/16F16xxはSMT機能が有りこれを使うとパルスの計測が出来ます。



100KHz辺りまで測定出来る記事追記(*2) 2018/04/15
記事一部追記(*1) 2018/04/11


【きむ茶工房ガレージハウス】
Copyright (C) 2006-2018 Shigehiro Kimura All Rights Reserved.