elechobby:picdic:pic16f88:85

文書の過去の版を表示しています。


周波数カウンタV7

以前に、周波数カウンタV5(集大成版)を作成し、ほぼ終了と思っていましたが、更に検討を進めるうちに、
ちょっとした工夫で、思わぬ効果を得ることが出来ました。

  • プリスケーラ無(1/1)で、40MHzまで測定可能(実測値)
  • プリスケーラ有(1/8)で、80MHzまで測定可能(実測値)

PICを使用した周波数カウンタでは、一般的には、TIMER0とTIMER1を組み合わせて使います。

  • 信号をカウントするために、TIMER0(8ビット)を使用(外部クロック入力同期ON固定)
  • ゲートタイム(1秒、0.1秒)を得るために、TIMER1(16ビット)を使用

しかし、TIMER0は、TIMERのON/OFF制御が出来ないため、入力ピンを強制的に出力モードにして“0“を出力
する等の工夫(本来は余分な事)が必要となります。

そこで、TIMERでON/OFFの出来る、TIMER1とTIMER2を組み合わせて使いました。

  • 信号をカウントするために、TIMER1(16ビット)を使用(外部クロック入力同期制御ON/OFF可能)
  • ゲートタイム(1秒、0.1秒)を得るために、TIMER2(8ビット)を使用

特に、外部クロック入力同期制御をOFFにすることが肝心で、もしもこれをONにすると、20MHzの信号でも
1/8のプリスケーラに設定しないと測定が出来ません。多分、TIMER0方式でのカウントが高い周波数まで
伸びないのは、外部クロック入力同期制御がON固定に原因があるのかもしれません。

<TIMER0>
TIMERのON/OFF制御が出来ない。
外部クロック入力同期(Sync2Cycles)がONに固定されている。

<TIMER1>
外部クロック入力同期(Synchronize)をON/OFFすることが可能。
TMR1ONでTIMERを開始/停止することが可能。

<TIMER2>
TMR2ONでTIMERを開始/停止することが可能。

FreqCounterV7.c
//********************************************************************** 
/*
	<周波数カウンター>
 ■機能
  sw1:プリスケーラの切り替え 
    ・sw1=1 1/1 
    ・sw1=0 1/8 
  sw2:ゲートタイムの切り替え
    ・sw2=1 1秒
    ・sw2=0 0.1秒
  sw3:-455kHzの有無切り替え 
    ・sw3=1 -0kHz
    ・sw3=0 -455kHz 
  sw4:表示レンジの切り替え
    ・sw4=1 Hz表示
    ・sw4=0 kHz表示
 ■コンフィグ設定
  LVP_OFF
  MCLR_OFF
  WDT_OFF
  EXTCLK
 ■ピンアサイン 
  Pin-01 LCD:D5 
  Pin-02 LCD:D4 
  Pin-03 LED
  Pin-04 未使用
  Pin-05 Vss(GND) 
  Pin-06 プリスケーラの切替SW 
  Pin-07 ゲートタイムの切替SW 
  Pin-08 -455kHzの切替SW 
  Pin-09 表示レンジの切替SW 
  Pin-10 LCD:RS
  Pin-11 LCD:WR
  Pin-12 信号入力
  Pin-13 LCD:EN
  Pin-14 Vdd(+5V) 
  Pin-15 未使用
  Pin-16 クロック入力(20MHz入力) 
  Pin-17 LCD:D7 
  Pin-18 LCD:D6 
*/
//********************************************************************** 
 
#define		sw1		PORTB.F0
#define		sw2		PORTB.F1
#define		sw3		PORTB.F2
#define		sw4		PORTB.F3
 
#define		LED		PORTA.F4
 
#define		GATETIME_100MSEC	10
#define		GATETIME_1SEC		1
 
//********************************************************************** 
 
static	unsigned	int	MeasurementCnt;
 
void	interrupt()
{
	PIR1.TMR2IF = 0;
	//
	MeasurementCnt--;
	if (MeasurementCnt == 0) {
		T1CON.TMR1ON = 0;	// ゲートを閉める。 
		T2CON.TMR2ON = 0;	// TIMER2を停止する。 
	}
}
 
//********************************************************************** 
 
unsigned	long	FreqMeasurement(unsigned char gateTime)
{
	static	unsigned	long	freq;
	// TIMER1の設定
	PIR1.TMR1IF = 0;
	TMR1L = 0;
	TMR1H = 0;
	// TIMER2の設定
	PIR1.TMR2IF = 0;
	switch (gateTime) {
	case GATETIME_1SEC:
		MeasurementCnt = 1221;
		TMR2 = 0x4C;		// 312500=1/((1/20000000) * 4 * 16)
							// 0x4C=256-(312500-(256*1220))
		break;
	case GATETIME_100MSEC:
		MeasurementCnt = 123;
		TMR2 = 0xEE;		// 31250=0.1/((1/20000000) * 4 * 16)
							// 0xEE=256-(31250-(256*122))
		break;
	}
	//
	freq = 0;
	// 割り込みを許可する。 
	INTCON.PEIE = 1;
	INTCON.GIE = 1;
	// 開始 
	T2CON.TMR2ON = 1;	//タイマを開始する。 
	//	Delay
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	asm	nop;
	//
	T1CON.TMR1ON = 1;	//ゲートを開ける。
	// 測定 
	while (T2CON.TMR2ON != 0) {
		if (PIR1.TMR1IF == 1) {
			PIR1.TMR1IF = 0;
			freq++;
		}
	}
	if (PIR1.TMR1IF == 1) {
		PIR1.TMR1IF = 0;
		freq++;
	}
	//換算 
	freq = freq * 65536;
	freq = freq + ((unsigned)TMR1H * 256) + (unsigned)TMR1L;
	// 
	return (freq);
}
 
//********************************************************************** 
 
void main()
{
	static	char*				msg;
	static	unsigned	long	freq, temp;	// 0...4294967295
	static	unsigned	char	buf[20], prescaler, gateTime;
	// アナログの設定
	ANSEL  = 0b00000000;	// 使用しない。
	// ポートの設定
	TRISA  = 0b11100000;
	TRISB  = 0b01001111;
	OPTION_REG.F7 = 0;		// PORTBをプルアップする。 
	// TIMER2の設定
	PIE1.TMR2IE = 1;
	PIR1.TMR2IF = 0;
	T2CON.TOUTPS0 = 0;
	T2CON.TOUTPS1 = 0;
	T2CON.TOUTPS2 = 0;
	T2CON.TOUTPS3 = 0;
	T2CON.TMR2ON = 0;
	T2CON.T2CKPS0 = 1;
	T2CON.T2CKPS1 = 1;
	TMR2 = 0;
	// TIMER1の設定
	PIE1.TMR1IE = 0;
	PIR1.TMR1IF = 0;
	T1CON.T1RUN = 0;
	T1CON.T1CKPS0 = 0;
	T1CON.T1CKPS1 = 0;
	T1CON.T1OSCEN = 0;
	T1CON.NOT_T1SYNC = 1;
	T1CON.TMR1CS = 1;
	T1CON.TMR1ON = 0;
	TMR1L = 0;
	TMR1H = 0;
	// 変数の初期化 
	prescaler = 1;
	gateTime = GATETIME_1SEC;
	// LCD(液晶モニタ)の初期化 
	Lcd_Custom_Config(&PORTA,0,1,2,3,&PORTB,4,5,7);
	Lcd_Custom_Cmd(LCD_CURSOR_OFF);
	Lcd_Custom_Out(1, 1, "FreqCounter V7");
	Delay_ms(1000);
	Lcd_Custom_Cmd(LCD_CLEAR);
	//
	while (1) {
		// 周波数の測定 
		LED = 1;
		freq = FreqMeasurement(gateTime);
		LED = 0;
		//換算 
		freq = freq * prescaler * gateTime;
		// プリスケーラの切り替え
		if (sw1 == 1) {
			T1CON.T1CKPS0 = 0;
			T1CON.T1CKPS1 = 0;
			prescaler = 1;
			msg = "1/1 ";
		} else {
			T1CON.T1CKPS0 = 1;
			T1CON.T1CKPS1 = 1;
			prescaler = 8;
			msg = "1/8 ";
		}
		Lcd_Custom_Out(2, 1, msg);
		// ゲートタイムの切り替え
		if (sw2 == 1) {
			gateTime = GATETIME_1SEC;
			msg = "1sec   ";
		} else {
			gateTime = GATETIME_100MSEC;
			msg = "0.1sec ";
		}
		Lcd_Custom_Out(2, 5, msg);
		// -455kHzの有無 
		if (sw3 == 0) {
			freq -= 455000;
			msg = "-455k";
		} else {
			msg = "     ";
		}
		Lcd_Custom_Out(2, 12, msg);
		// 表示レンジの切り替え 
		if (sw4 == 1) {
			LongToStr(freq, buf);
			msg = "Hz ";
		} else {
			temp = freq / 1000;
			if ((freq - (temp * 1000)) > 500) {
				temp++;
			}
			LongToStr(temp, buf);
			msg = "kHz";
		}
		Lcd_Custom_Out(1, 9, msg);
		// 周波数の表示 
		Lcd_Custom_Out(1, 1, &buf[3]);
		//
		Delay_ms(500);
	}
}
 
//**********************************************************************



20MHzを測定しました。(プリスケーラ無し、ゲートタイム1秒)

40MHzを測定しました。(プリスケーラ無し、ゲートタイム1秒)

左側:60MHzを測定しました。(プリスケーラ無し、ゲートタイム1秒)測定不能
右側:60MHzを測定しました。(プリスケーラ有り、ゲートタイム1秒)測定可能

70MHzです。

80MHzです。ここまで測れるとは。。。!?

測定したクロックモジュールです。

左側:プリスケーラ値(1/1)、ゲートタイム(1秒)
右側:プリスケーラ値(1/1)、ゲートタイム(0.1秒)

左側:プリスケーラ値(1/8)、ゲートタイム(0.1秒)
右側:プリスケーラ値(1/1)、ゲートタイム(0.1秒)、kHz表示

プリスケーラ値(1/8)、ゲートタイム(0.1秒)、kHz表示、-455kHz

如何ですか?
少し視点を変えるだけで、大きな回路変更もなく、80MHzの周波数が測定できました。{^_^}!

  • elechobby/picdic/pic16f88/85.1588161922.txt.gz
  • 最終更新: 2025/10/17 14:28
  • (外部編集)