PSoC 4200 Prototyping Kit (9)

レシプロカル周波数カウンタ (周期カウンタ) としてのソフトウェア構成は、以前に作成した Arduino でレシプロカル周波数カウンタ・ライブラリと同様にしました。
入力信号をカウンタのキャプチャ入力に直結しているので、入力周波数のレートで割り込みが掛かり、高い周波数には対応しません。 実測では正確に測定できるのは 120 kHz 程度まででした。 応用としてはオーディオ帯域を考えているので、これで十分です。
周期カウンタ (Period Counter) の意で関数名や struct 名に「pcnt」プリフィクスを付けることにしました。 定義を下に示します。

//
// period counter struct
//
typedef struct tag_pcnt_t {
  int32_t  ncyc;   // # of cycles (#_of_edges - 1)
  uint32_t cap;    // 32 bit capture of recent edge
  uint32_t cap0;   // 32 bit capture of the first edge
  int32_t  tc_cnt; // TC count
  int32_t  tc_max; // TC MAX count for the gatetime
  uint32_t dur;    // calculated duration
  int32_t  ready;  // measurement finish flag
}  pcnt_t;
//
// period counter variables
// 
volatile pcnt_t pcnt;
//
// period counter functions
//    
void     pcnt_Stop( void );
void     pcnt_Start( int16_t ms );
uint32_t crt_calc(uint16_t c1, uint16_t c2);    

pcnt_Start() 関数の呼び出しで周波数/周期測定を開始します。
引数「ms」にゲート・タイムをミリ・セカンド単位で指定します。 int16_t 型なので、最大 32767 ms = 約 33 秒までのゲート・タイムが指定可能です。
「ms」がゼロまたは負の値の場合は pcnt_Stop() 関数の呼び出しと同じ効果になります。
pcnt_Stop() 関数を呼び出すと、即座に測定を中止します。
結果や測定完了を示すフラグの類は「pcnt」という struct にまとめられています。
Arduino (Atmel AVR ATmega) の場合には、それぞれを単独の「グローバル」変数として扱っていましたが、Cortex プロセッサでは struct にまとめておいた方が「オフセット付きインデクス・アドレッシング」が使えてコード効率が良くなります。
pcnt_Start() 関数の呼び出しで pcnt.ready == 0 となり、測定中を表します。
測定を開始すると、以降はすべて割り込みハンドラ内で処理されるので、メイン・ループ側は pcnt.ready を監視して pcnt.ready == 1 となるのを待つだけです。
それを確認してから結果の変数をアクセスします。
pcnt.ncyc はゲート・タイム中に検出した入力信号のサイクル数を示します。 その値は (検出した入力信号の有効エッジ数 - 1) となっています。 pcnt.ncyc の値の意味を下に示します。

  • pcnt.ncyc == -1 ならゲート・タイム中に有効エッジがひとつも検出されなかったことを示す
  • pcnt.ncyc == 0 ならゲート・タイム中に有効エッジがひとつだけ検出されたことを示す
  • pcnt.ncyc >= 1 ならゲート・タイム中に入力信号が pcnt.ncyc サイクル検出されたことを示す

pcnt.ncyc >= 1 なら少なくとも入力信号が 1 サイクル検出されているので、測定は「成功」であり周期/周波数が求まります。
pcnt.ncyc <= 0 なら測定は「失敗」で周期/周波数を求めることはできません。
入力信号の周期に対してゲート・タイムが短すぎる場合にはゲート・タイムを伸ばせば測定に成功する可能性があります。
入力信号がなくてレベルに変化がない場合には pcnt.ncyc == -1 となります。 この場合には、当然、入力信号を加えなければ測定は成功しません。
pcnt.dur が pcnt.ncyc サイクル分の信号の期間を示す値です。
カウンタ・タイマは 48 MHz クロックで動作させているので、周波数/周期は次の式で求まります。 (四捨五入は省略)

  • 周波数 [Hz] = (48 × 106 [Hz] * pcnt.ncyc) / pcnt.dur
  • 周期 [μs] = pcnt.dur / (pcnt.ncyc * 48 [MHz])

aitendo の小型 I2C LCD に表示させた例の写真を示します。

クロックは外部クリスタル・オシレータではなく、内蔵オシレータ (IMO) を使っていますが、タイマのクロックと系統が同じ HFCLK (48 MHz) を分周して得た 1 kHz を測定しているので、内蔵オシレータの周波数が変動していても全く誤差なく 1000.000 Hz、1000.000 μs の表示になっています。
32 kHz の低速クロック (LFCLK) は別系統の内蔵オシレータ (ILO) となるので、LFCLK 由来の周波数を測定すると変動しているのが分かります。