周波数カウンターの実装について

SCCPモジュールの使い方

周波数のカウント方式について

今回は、マイコンを使って信号の周波数を計測する方法について考察します。

一般的な計測器周波数カウンタには、ダイレクトカウント方式レシプロカル方式を採用している機器が多いと思われます。

ダイレクトカウント方式

ダイレクトカウント方式は一定時間内に到達した信号のパルス数をカウントします。ゲート時間を長くすることで非常に正確な周波数がカウントできます。
例えばゲート時間が1秒の場合、カウント数がそのまま周波数になります。
デメリットとしてゲート時間中に計測信号周波数が変化すると出力結果に影響が出てしまいます。またゲート時間が必要なため、パルスバイパルスで時間を計測することはできません。

(fig.1-1)ダイレクトカウント方式

レシプロカル方式

レシプロカル方式とは、逆数を意味し、周波数が周期の逆数であることから、周期を計測して周波数を求める方法です。たとえば、クロック信号が1MHzの場合、計測信号の1周期あたりに100回パルスが到達したとすると、計算式は1/(100×1μsec)となり、周波数は10kHzとなります。
つまり計測信号周期ごとに周波数が計測可能です。

クロック信号の周波数が高いほど分解能が向上し、逆に低くなると分解能は低下します。しかし、クロックには限界があるため、高精度で速い周波数を計測することは難しくなります。

(fig.1-2)レシプロカル方式

アナログ方式

あまり一般的な計測器では採用されていませんが、以下のようなアナログ方式でも周波数を計測することが可能です。原理としては、既知のコンデンサに対して、パルスがHiのときに定電流で充電し、パルスがLowのときに放電する回路を構成します。このとき、放電する直前にADCでコンデンサの電圧Vを計測します。
この手法により、以下の式で充電時間を表せるため、周波数を求めることができます。

この方法は、定電流源の精度、コンデンサの容量精度、ADCの精度など、多くの誤差要因が重なるため校正処理が必要となりますが、レシプロカル方式のクロック周波数の限界を超えた場合でも、定電流源やコンデンサの容量を調整することで計測可能な手法といえます。

(fig.1-3)アナログ方式

最近のdsPIC33CやdsPIC33Aシリーズには採用されていませんが、dsPIC33Eシリーズなどには、このような計測を簡単に実現できるCTMU(Charge Time Measurement Unit)というモジュールが搭載されています。CTMUは、定電流源と組み合わせてコンデンサの充放電時間を精密に測定できるため、アナログ方式による周波数計測が可能です。

概要

今回はdsPIC33CH Curiosityボードを利用して、レシプロカル方式についてSCCPモジュールを使用する通常の方法と高分解能PWMモジュールを使用する方法を検証します。

(fig.2-1)dsPIC33CH Curiosity Board

関連記事

記事リンク
SCCPモジュールの使い方4(インプットキャプチャ)SCCPモジュールの使い方4(インプットキャプチャ) – ぴくおの電子工作的な何かWP (electricpico.com)
高分解能PWMモジュールの使い方5(PCI機能)高分解能PWMモジュールの使い方5(PCI機能) – ぴくおの電子工作的な何かWP (electricpico.com)

ハードウェア構成と制御ブロック

外部発振器からの信号をRP70ピンを経由してSCCP2とPG1に入力します。SCCP2はインプットキャプチャモード、PG1は外部周期リセットモードに設定します。SYNCトリガが入力された際に、タイマーの値をキャプチャすることで、信号の周期を測定します。

(fig.3-1)全体ブロック図

外部リセット機能はPWMモジュールの同期PCI機能を用いて実現します。
設定した周期よりも早い周期の信号の立ち上がりを検出し自己PWMのスタートを同期させます。

(fig.3-2)高分解能PWM リファレンスマニュアル DS70005320B_JP – p. 126より抜粋

ソースコード

コンフィグレーション設定についてはコンフィグレーション設定に記載しております。

コンフィグレーション設定について

コピーして下記のソースコードの「 //ここにコンフィグレーション設定を挿入する// 」の位置に挿入してください。

クロック設定について

クロック設定用関数 vds_Main_Init_Clock_Register(); のソースコードはクロック設定のページに記載しております。

コピーして下記のソースコードの「 //ここにクロック設定ソースをコピペする// 」の位置に挿入してください。

/*----------------------------------------------------------------------------*/
/*【INC】インクルードファイル*/
/*----------------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <xc.h>
#include "PeripheralDriver/pwm_driver.h"
#include "PeripheralDriver/pmd_driver.h"
#include "PeripheralDriver/cmp_dac_driver.h"
/*----------------------------------------------------------------------------*/
/* macros */
/*----------------------------------------------------------------------------*/

/*----------------------------------------------------------------------------*/
/*変数定義*/
/*----------------------------------------------------------------------------*/
unsigned int u2g_CapPG1[256];
unsigned int u2g_CapCnt1;
unsigned int u2g_Cap2[256];
unsigned int u2g_CapCnt2;

 //ここにコンフィグレーション設定を挿入する//
/*----------------------------------------------------------------------------*/
/* Main関数 */
/*----------------------------------------------------------------------------*/
int main(int argc, char** argv) 
{
    /*------------------------------------------------------------------------*/
    /* クロック初期化*/
    /*------------------------------------------------------------------------*/
        vds_Main_Init_Clock_Register();     /* クロック初期化 */
    /*------------------------------------------------------------------------*/
    /* GPIO初期化*/
    /*------------------------------------------------------------------------*/
    /*------------------------------------------------------------------------*/
    /* リマッパブルピン初期化*/
    /*------------------------------------------------------------------------*/
        RPINR4bits.ICM2R = 70u;       /* IC[SCCP2] */
	RPINR37bits.PCI17R = 70u;
  /*------------------------------------------------------------------------*/
    /* CCP2初期化*/
    /*------------------------------------------------------------------------*/
        CCP2CON1L = 0x0000u;
	  CP2CON1Lbits.CLKSEL = 0;
            CCP2CON1Lbits.MOD	= 1u;	/* 立ち上がり間 */
            CCP2CON1Lbits.CCSEL	= 1u;	/* キャプチャモード */
            CCP2CON1Lbits.TMRPS = 0;
        CCP2CON1H = 0x0000u;
            CCP2CON1Hbits.SYNC = 3u;    /* [重要]SCCP2に同期 */
            CCP2CON1Hbits.ALTSYNC = 1u; /* [重要]代替同期出力有効 */
        CCP2CON2H = 0x0000u;
            CCP2CON2Hbits.ICS = 0u;     /* ICxPIN(PPS) */
            CCP2CON2Hbits.AUXOUT = 3u;  /* [重要]キャプチャイベントで同期出力 */
        CCP2CON3H = 0x0000u;
        
        CCP2PRL	= 0x0000u;     
        CCP2RA	= 0x0000u;
        CCP2RB	= 0x0000u;

        CCP2CON1Lbits.CCPON = 1u ;
		
    /*------------------------------------------------------------------------*/
    /* PWM初期化*/
    /*------------------------------------------------------------------------*/
        PG1CONLbits.ON = 0u;
    /*------------------------------------------------------------------------*/
    /* PWMクロックの設定*/
    /*------------------------------------------------------------------------*/
        PCLKCONbits.MCLKSEL = 3u;    /* Auxiliary PLL post-divider output*/
    /*------------------------------------------------------------------------*/
    /* PGx制御レジスタLの設定*/
    /*------------------------------------------------------------------------*/
        PG1CONLbits.CLKSEL = 1u;
        PG1CONLbits.MODSEL = 0u;
    /*------------------------------------------------------------------------*/
    /* PGx制御レジスタHの設定*/
    /*------------------------------------------------------------------------*/
        PG1CONH = 0x0000;
	  PG1CONHbits.TRGMOD = 1;
	  PG1CONHbits.SOCS = 0x0F;
    /*------------------------------------------------------------------------*/
    /* PGxIOレジスタHの設定*/
    /*      PWMジェネレータがピンを使用する*/
    /*------------------------------------------------------------------------*/
        PG1IOCONH = 0x000C;
	  PG1IOCONHbits.CAPSRC = 1; //SyncSignal
    /*------------------------------------------------------------------------*/
    /* PGxIOレジスタLの設定*/
    /*------------------------------------------------------------------------*/
        PG1IOCONL = 0x0000;
    /*------------------------------------------------------------------------*/
    /* 周期/Duty/フェーズの設定*/
    /*------------------------------------------------------------------------*/
        PG1PER = 50000;
        PG1DC = 100;
        PG1PHASE = 0;
        
        PG1TRIGA = 0;
        PG1TRIGB = 0;
        PG1TRIGC = 0;
    /*------------------------------------------------------------------------*/
    /* デッドタイムの設定*/
    /*------------------------------------------------------------------------*/
        PG1DTH = 0;
        PG1DTL = 0; 
    /*------------------------------------------------------------------------*/
    /* ブランキングの設定*/
    /*      PWMxHの立ち上がりでLEBカウンタをクリアする*/
    /*------------------------------------------------------------------------*/
        PG1LEBH = 0x0008u;
        PG1LEBL = 1000u;
    /*------------------------------------------------------------------------*/
    /* イベントレジスタの設定*/
    /*------------------------------------------------------------------------*/
        PG1EVTL = 0x0000;
        PG1EVTH = 0x0000;
      
    /*------------------------------------------------------------------------*/        
    /* PGx同期PCIレジスタLow */
    /*------------------------------------------------------------------------*/    
        PG1SPCIL	= 0x0000u;	
            PG1SPCILbits.PSS	= 17;	/* PCIソース選択ビット */
            PG1SPCILbits.PPS	= 0;	/* PCI極性ビット */
            PG1SPCILbits.PSYNC	= 0u;   /* PCI同期選択ビット */
            PG1SPCILbits.SWTERM = 0u;   /* PCIソフトウェアターミネーションビット */
            PG1SPCILbits.AQSS	= 0u;	/* 受け入れソース選択ビット */
            PG1SPCILbits.AQPS	= 0u;	/* 受け入れソース極性ビット */
            PG1SPCILbits.TERM	= 1u;	/* ターミネーションイベント選択ビット */
            PG1SPCILbits.TSYNCDIS	= 1u;	/* ターミネーション同期不許可ビット */
    /*------------------------------------------------------------------------*/        
    /* PGx同期PCIレジスタHigh */
    /*------------------------------------------------------------------------*/    		
        PG1SPCIH	= 0x0000u;	
            PG1SPCIHbits.TQSS	= 0u;	/* ターミネーション資格ソース選択ビット */
            PG1SPCIHbits.TQPS	= 0u;   /* ターミネーション資格極性ビット */
            PG1SPCIHbits.LATMOD = 0u;   /* PCI SRラッチモードビット*/
            PG1SPCIHbits.SWPCIM = 0u;   /* ソフトウェアPCI制御モードビット */
            PG1SPCIHbits.SWPCI	= 0u;   /* ソフトウェアPCI制御ビット */
            PG1SPCIHbits.ACP	= 1u;   /* PCI合否基準選択ビット */
            PG1SPCIHbits.BPSEL	= 0u;	/* PCIバイパスソース選択ビット */
            PG1SPCIHbits.BPEN	= 0u;	/* PCIバイパス有効化ビット */

    /*------------------------------------------------------------------------*/
    /* PWMの有効化*/
    /*------------------------------------------------------------------------*/
        PG1CONLbits.ON = 1u;

    /*------------------------------------------------------------------------*/
    /* メインルーチン*/
    /*------------------------------------------------------------------------*/
        while(1)
        {
		if (CCP2STATLbits.ICBNE == 1u)
		{
			u2g_Cap2[u2g_CapCnt2 ++] = CCP2BUFL;
			u2g_CapCnt2 &= 0xFF;
		}
			 
		if (PG1STATbits.CAP == 1u)
		{
			u2g_CapPG1[u2g_CapCnt1 ++] = PG1CAP;
			u2g_CapCnt1 &= 0xFF;
			PG1STATbits.CAP = 0;
		}
        }
}

結果

外部発振器から1MHzの信号を入力した場合の結果を示します。SCCPのクロックが90MHzに対し、PWMモジュールのクロックが500MHzであるため、想定通り5倍以上の分解能でパルス幅が計測できていることを確認しました。

■SCCP2取得結果

■PG1取得結果

コメント

タイトルとURLをコピーしました