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. ---