18F14K50のTimer1とTimer2を使用し、周波数カウンタを作成しました。
測定精度を高めるために測定の基本となるクロックは4MHzのクリスタルを使用しています。測定範囲は、1.2MHzまでしか実測していませんが、PIC内蔵Timerの動作上限である48MHzぐらいはOKと思われます。
無線実験などで使用するには、パルス入力にFET等による入力バッファーが必要でしょう。 私は、ロジック回路の実験が主なので、1KΩ程度の抵抗を直列に入れPICの入力端子にパルスを加える方法であまり不便は感じていません。
下の写真では、3.3vの電源で動作するように作成しています。ですから液晶のバイアス端子(Vo)にマイナスの電位を加えるため、電源IC、LTC1144、を使い負電圧を発生させる回路も映っています。この周波数カウンタ回路自体は、5vでも動作します、その場合には、回路図のように電源回路を取り外し、5vを直接VRに接続します。
LTC1144によるチャージポンプ方式の負電圧発生回路はここを見てください。
写真の回路には、 3.3vでLCDが動作するよう液晶バイアス端子(Vo)に負電圧を加える回路も組んであります。
Timer2で10mSec毎に割込みを発生させ、その回数を100回数え、基本となある1秒間の計測時間を作っています。
Timer1のON/OFF機能を使用し、Timer2で発生させた1秒間だけ入力パルスを カウントすします。 16bitのTimer1がオーバーフローした場合は、割込みを発生させ、その回数をソフトウエアでカウントし、48MHzまで使用できるよう処理しています。
プロジェクトファイル H11_FreqCounter.zip |
文字を画面に表示するLCDライブラリを利用しています。プロジェクトファイルには、同梱してありますが、このページに記載するのは省略します。
<プログラム>
main.c
//*********************************************************
// PIC18F14K50
// File name: FreqCounter Kazuo Iwamoto
//
// Timer2でクロック(4MHz)をプリスケラで1/4にして、PR2の値(249)で
// リセットを繰り返すと1KHzになる、ポストスケラでさらに1/10で、
// 100Hzになる。割込み100回で1秒になる。
// Timer1で外部パルスを計測する。16bitからあふれた分はソフトで
// T1Carryとして処理積算する。
// LED : PORTB RB4
// パルス入力: PORTC RC6
//
// Notes : 4MHzXtalクロック
// Language: MPLAB C18
// Target : PIC18F14K50
// ----------------------------------------------------------------
// Timer1を、パルスのカウントに使用する。低優先割込みに設定
// Timer2を、時間計測に使用する。高優先割込みに設定
// ----------------------------------------------------------------
#include <p18cxxx.h>
#include <timers.h>
#include "L_LCD.h"
#define LED LATBbits.LATB4
#pragma config FOSC = HS // Ttal 4MHzクロック
#pragma config USBDIV = OFF, CPUDIV = NOCLKDIV
#pragma config IESO = OFF, FCMEN = OFF, PLLEN = OFF
#pragma config BORV = 30, BOREN = OFF, PWRTEN = OFF
#pragma config WDTPS = 32768, WDTEN = OFF
#pragma config MCLRE = OFF, HFOFST = OFF, XINST = OFF
#pragma config BBSIZ = OFF, LVP = OFF, STVREN = ON
#pragma config CP1 = OFF, CP0 = OFF, CPD = OFF, CPB = OFF
#pragma config WRT1 = OFF, WRT0 = OFF, WRTB = OFF, WRTC = OFF
#pragma config EBTR1 = OFF, EBTR0 = OFF, EBTRB = OFF
//------- プロトタイプ ----------------
void ito_st(unsigned long, char *,char);
void isr_high(void);
void isr_low(void);
//------- 各インタラプトベクトル --------
#pragma interrupt isr_high
#pragma interruptlow isr_low save = WREG,BSR,STATUS
#pragma code h_int_vect = 0x0008
void _h_isr (void){ _asm goto isr_high _endasm}
#pragma code l_int_vect = 0x0018
void _l_isr (void){ _asm goto isr_low _endasm}
#pragma code
// -------------- 共通変数 ----------------------
unsigned int T1Carry = 0; //
char T2Carry = 0; //
//----------------------高優先インタラプト------------------------
void isr_high(){
if(PIR1bits.TMR2IF){ //Timer2からのインタラプトを確認
PIR1bits.TMR2IF = 0; // インタラプトフラッグをクリア
switch(T2Carry){
case 0:
T1CONbits.TMR1ON = 1; // Timer1計測開始
T2Carry++;
LED = 1;
break;
case 10:
LED = 0;
T2Carry++;
break;
case 100:
Nop();
Nop();
Nop();
Nop();
T1CONbits.TMR1ON = 0; // Timer1計測終了
T2Carry++;
break;
case 101:
break;
default:
T2Carry++;
break;
}
}
}
//-----------------低優先インタラプトの処理------------------------
void isr_low(){
if(PIR1bits.TMR1IF){ //Timer1からのインタラプトを確認
PIR1bits.TMR1IF = 0; // インタラプトフラッグをクリア
T1Carry++; // Timer1のカウントアップを積算
}
}
//----------------------メインプログラム-------------------------
void main(void){
unsigned long T1Count;
char MsgUpper[]= "12,456,890 Hz";
LATC = 0;
TRISC = 0b11000000;
TRISB = 0;
ANSEL = 0;
ANSELH = 0;
//----------------------
OpenTimer1( // Timer 1 設定
TIMER_INT_ON &
T1_16BIT_RW &
T1_SOURCE_EXT &
T1_PS_1_1 &
T1_OSC1EN_OFF &
T1_SYNC_EXT_OFF );
IPR1bits.TMR1IP = 0; // Timer 1 からのインタラプトを低優先に設定
PIR1bits.TMR1IF = 0; // Timer 1 からのインタラプトフラッグをクリア
OpenTimer2( // Timer 2 設定
TIMER_INT_ON & // 10mSで割込みが発生
T2_PS_1_4 &
T2_POST_1_10 );
PR2 = 249; // Timer2 のカウントが249でリセット
IPR1bits.TMR2IP = 1; // Timer2 からのインタラプトを高優先に設定
PIR1bits.TMR2IF = 0; // Timer2 からのインタラプトフラッグをクリア
//----------------------
T2Carry = 0;
WriteTimer1(0);
LCD_int();
RCONbits.IPEN=1; // 2段階のインタラプトに設定
INTCONbits.GIEH=1; // 高優先インタラプトを許可
INTCONbits.GIEL=1; // 低優先インタラプトを許可
while(1){
if(T2Carry == 101){
T1Count = T1Carry * 0x10000 + ReadTimer1();
WriteTimer1(0);
T1Carry = 0;
T2Carry = 0;
ito_st(T1Count, MsgUpper, 10);
LCD_posyx(0,0);
LCD_str(MsgUpper);
}
}
}
//****** 正整数を文字列に変換する関数 ************************
// num 変換する整数 (ゼロサプレスあり)
// strAdd 変換結果の格納される文字列 (コンマ あり)
// digit 変換される文字数
//************************************************************
void ito_st(unsigned long num, char *strAdd, char digit){
char i;
char flag=1; //ゼロサプレス Flag
char coma=0; //コンマ Flag
strAdd += digit; // 文字列の最後
for(i=digit; i>0; i--) { // 下位桁から処理
strAdd--; // 1桁上に
if (flag == 1){
if (coma++ == 3){ //コンマ の位置か
*strAdd = ','; //コンマ を格納
coma=0;
}else{ // 1桁の数値を文字で格納
*strAdd = (num % 10) + 0x30;
num = num / 10; // 次桁の準備
if (num == 0) flag=0; // ゼロなら以降は抑制
}
}else{ // 上位の0は抑制する
*strAdd = 0x20; // 0の代わりにスペース格納
}
}
}
// --- Copyright (C) 2011-2012 Kazuo Iwamoto All Rights Reserved. ---