目次

周波数カウンタ

 PIC16F873が残っていたので、周波数カウンタを作成しました。



 回路図は、以下。



 2行x16桁のLCDを接続し、周波数と条件を表示します。
 周波数は上位行、条件は下位行に表示します。



 条件を4つのスイッチで指定します。
 スイッチと条件の関係は、以下。

 各スイッチで利用している内容を説明します。

 内部プリスケーラ

  周波数をカウントする場合、2つのタイマーに
  役割を与えます。

  タイマー1に、与えられたクロック数を計測させ
  タイマー2に、ゲートタイムを生成させます。

  高い周波数をカウントするためには、タイマー1に
  接続されているプリスケーラの分周比を大きくして
  おきます。



  1:1か1:8を選べるようにします。

 ゲートタイム

  周波数を1sの間に、何回カウントされたかを
  利用するので、1sか0.1sを選択できるように
  します。ゲートタイムを、0.1sにすると実際
  のカウントは、10倍になります。

  ゲートタイムの時間カウントは、タイマー2に
  担当させます。タイマー2のブロック図から
  使い方を考えます。



  20MHzの1/4の5MHzが、タイマー2モジュールに与えられます。
  プリスケーラを1:4として、タイマーカウンタに与える周波数
  を、1.25MHzにします。
  PR2の値を250に設定し、1.25MHz/250=5kHzごとに、一致割込み
  を発生させます。

  ゲートタイムを0.1sにするには、10Hzになるように
  カウント値を500にします。

  ゲートタイムを1sにするには、1Hzになるように
  カウント値を5000にします。

 受信周波数減算

  アマチュア無線のリグ(無線機)では、受信周波数を
  中間周波数に変換して処理する方式があります。

  中間周波数は10.7MHzか455kHzですが、受信に利用する
  周波数を455kHz上げてカウントできるようにします。

  測定値なのか下げた値なのかを、選んで表示
  できるようにします。

 Hz、kHzの表示

  大まかに周波数を測定したい場合kHzを利用し
  細かく周波数を見たい場合Hzを使います。

  プリスケーラとゲートタイムを合わせて使わないと
  測定に対する精度との整合が取れなくなります。

 ユーザーインタフェースの仕様を確定したので
 カウント値に応じた計算手順を考えますが、動作
 シーケンスを決めておきます。
  1. 内部カウンタクリア
  2. 指定ゲートタイムから、タイマー2で刻むカウント値計算
  3. タイマー2動作開始
  4. タイマー1動作開始
  5. カウントアップまで待つ
  6. タイマー1動作開始
  7. タイマー2動作開始
  8. カウント値を計算、1:8指定の場合、カウント値を8倍する
  9. -455kHzが指定されていれば、減算
  10. カウント値を8けたの数字に変換
  11. Hz、kHzにあわせた桁数で表示
  12. 1に戻る
 計算処理と表示に分け、コードを考えます。  計算処理   タイマー1は、16ビットのカウンタを持つので   65536をカウントした回数とタイマー1の値を   加算します。次の計算式を利用します。    freq = fover * 65536 + 256 * TMR1H + TMR1L ;   1:8のプリスケーラを指定されているとき   左に3ビットシフトして対応します。 if ( prescaler == 8 ) {     freq <<= 3 ; }   -455kHzの指定があれば、この値より大きいことを   確認してから減算します。 if ( freq > 455000 ) {     freq -= 455000 ; }   指定条件になっているかの判定は、各々フラグを   用意して判断します。  表示   8桁の数字で表示したいので、配列を用意し   10で割ったときの余りを、逆順に配列の中に   入れます。 for ( i = 0 ; i < 8 ; i++ ) { *(xdig+7-i) = freq % 10 + '0' ; freq /= 10 ; }   8桁を逐次求めると、時間がかかるので   上位4桁、下位4桁に分割して計算します。 ufreq = (freq / 10000) ; lfreq = (freq % 10000) ; for ( i = 0 ; i < 4 ; i++ ) { *(xdig+3-i) = ufreq % 10 + '0' ; *(xdig+7-i) = lfreq % 10 + '0' ; ufreq /= 10 ; lfreq /= 10 ; }   数値から数字への変換は、文字定数'0'を加えるだけに。   8桁の表示は、Hz指定になっているので   kHzの場合は、5桁表示にします。   8桁と5桁の表示切替えは、フラグを見て判断します。 *(disp+0) = *(xdig+0) ; if ( *(xdig+0) == '0' ) { *(disp+0) = ' ' ; } *(disp+1) = *(xdig+1) ; *(disp+2) = ',' ; if ( *(disp+0) == ' ' && *(xdig+1) == '0' ) { *(disp+1) = ' ' ; *(disp+2) = ' ' ; } *(disp+3) = *(xdig+2) ; *(disp+4) = *(xdig+3) ; *(disp+5) = *(xdig+4) ; if ( KFLAG == ON ) { *(disp+ 6) = ' ' ; *(disp+ 7) = ' ' ; *(disp+ 8) = ' ' ; *(disp+ 9) = ' ' ; *(disp+10) = 'k' ; } else { *(disp+ 6) = ',' ; *(disp+ 7) = *(xdig+5) ; *(disp+ 8) = *(xdig+6) ; *(disp+ 9) = *(xdig+7) ; *(disp+10) = ' ' ; } *(disp+11) = 'H' ; *(disp+12) = 'z' ;  以上の仕様をまとめてソースコードにしました。 /* redefine data type */ typedef unsigned char UBYTE ; typedef unsigned int UWORD ; typedef unsigned long ULONG ; #define OFF 0 #define ON OFF+1 #define GTIME_A 500 #define GTIME_B 5000 #define SEL_PRE PORTA.F0 #define SEL_GATE PORTA.F1 #define SEL_DIF PORTA.F2 #define SEL_FORMAT PORTA.F3 #define LED PORTB.F0 #define LCD_E 3 #define LCD_RS 2 #define MASKFF 0xff #define MASK30 0x30 #define MASK03 0x03 #define MASK0F 0x0f typedef union { struct { unsigned B7:1; unsigned B6:1; unsigned B5:1; unsigned B4:1; unsigned B3:1; unsigned B2:1; unsigned B1:1; unsigned B0:1; } BIT ; unsigned char DR ; } FLAGSP ; volatile FLAGSP xflags ; #define EFLAG xflags.BIT.B0 #define DFLAG xflags.BIT.B1 #define SFLAG xflags.BIT.B2 #define UFLAG xflags.BIT.B3 #define GFLAG xflags.BIT.B4 volatile ULONG timcnt ; volatile UBYTE timv ; volatile UBYTE prescale ; volatile UWORD gate_count ; volatile UBYTE fover ; volatile ULONG freq ; volatile ULONG pre_freq ; volatile UBYTE xq ; volatile UBYTE xr ; volatile UBYTE sfreq[8] ; volatile UBYTE loop ; volatile UWORD ufreq ; volatile UWORD lfreq ; volatile UBYTE uline[11] ; volatile UBYTE dline[11] ; volatile UBYTE pre_dline[11] ; /* function prototype */ void init_usr(void); void delay_ms(UWORD x); void put_lcd_function(UBYTE xdat); void put_lcd_data(UBYTE xdat); void put_lcd_primitive(UBYTE which,UBYTE dat); void put_lcd_p4(UBYTE which,UBYTE dat); void put_lcd_str(UBYTE r,UBYTE *ptr); void show_uline(void); void show_dline(void); void init_lcd(void); void xstrcpy(UBYTE *dst,UBYTE *src); UBYTE is_same(void); /* interrupt handler */ void interrupt(void) { /* timer0 overflow interrupt (generate 1ms) */ if ( INTCON.T0IF == ON ) { /* clear flag */ INTCON.T0IF = OFF ; /* initialize */ TMR0 = 131 ; /* increment */ timv++ ; /* judge */ if ( timv == 5 ) { timv = 0 ; timcnt++ ; } } /* timer 2 interrupt */ if ( PIE1.TMR2IF == ON ) { /* clear flag */ INTCON.T0IF = OFF ; /* decrement */ gate_count-- ; /* judge */ if ( gate_count == 0 ) { T1CON.TMR1ON = OFF ; T2CON.TMR2ON = OFF ; EFLAG = ON ; } } } void main(void) { /* user initialize */ init_usr(); /* initialize LCD */ init_lcd(); /* endless loop */ while ( ON ) { /* measure handling */ { LED = ON ; /* state 0 (initialize) */ { /* clear counters */ TMR1H = 0 ; TMR1L = 0 ; /* prescaler 1:1 */ T1CON &= ~0x30 ; if ( prescale == 8 ) { T1CON |= 0x30 ; } /* clear counters */ TMR2 = 0 ; freq = 0 ; fover = 0 ; } /* state 1 (start timers) */ { EFLAG = OFF ; PIE1.TMR2IE = ON ; T2CON.TMR2ON = ON ; { 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 = ON ; } /* state 2 (measure)*/ while ( EFLAG == OFF ) { /* judge overflow */ if ( PIE1.TMR1IF == ON ) { PIE1.TMR1IF = OFF ; fover++ ; } } EFLAG = OFF ; if ( PIE1.TMR1IF == ON ) { PIE1.TMR1IF = OFF ; fover++ ; } PIE1.TMR2IE = OFF ; LED = OFF ; /* state 3 */ xq = TMR1H ; xr = TMR1L ; freq = (fover << 16) + (xq << 8) + xr ; if ( GFLAG == ON ) { freq *= 10 ; } if ( prescale == 8 ) { freq <<= 3 ; } /* judge */ UFLAG = OFF ; if ( pre_freq != freq ) { UFLAG = ON ; } /* update */ if ( UFLAG == ON ) { /* difference */ if ( DFLAG == ON && freq > 500000 ) { freq -= 455000 ; } /* convert */ ufreq = (UWORD)(freq / 10000) ; lfreq = (UWORD)(freq % 10000) ; for ( loop = 0 ; loop < 4 ; loop++ ) { *(sfreq+3-loop) = (ufreq % 10) + '0' ; *(sfreq+7-loop) = (lfreq % 10) + '0' ; ufreq /= 10 ; lfreq /= 10 ; } /* show */ *(uline+0) = *(sfreq+0) ; *(uline+1) = *(sfreq+1) ; *(uline+3) = *(sfreq+2) ; *(uline+4) = *(sfreq+3) ; *(uline+5) = *(sfreq+4) ; *(uline+7) = *(sfreq+5) ; *(uline+8) = *(sfreq+6) ; *(uline+9) = *(sfreq+7) ; *(uline+10) = ' ' ; if ( SFLAG == ON ) { xstrcpy((UBYTE *)(uline+7),(UBYTE *)" k") ; } show_uline(); } /* update */ pre_freq = freq ; } /* get condition */ *(dline+2) = '1' ; prescale = 1 ; if ( SEL_PRE == ON ) { *(dline+2) = '8' ; prescale = 8 ; } xstrcpy((UBYTE *)(dline+4),(UBYTE *)"0.") ; gate_count = GTIME_A ; GFLAG = ON ; if ( SEL_GATE == ON ) { xstrcpy((UBYTE *)(dline+4),(UBYTE *)" ") ; gate_count = GTIME_B ; GFLAG = OFF ; } xstrcpy((UBYTE *)(dline+9),(UBYTE *)" ") ; DFLAG = OFF ; if ( SEL_DIF == ON ) { xstrcpy((UBYTE *)(dline+9),(UBYTE *)"-455kHz") ; DFLAG = ON ; } SFLAG = OFF ; if ( SEL_FORMAT == ON ) { SFLAG = ON ; } /* judge */ if ( is_same() == OFF ) { show_dline(); } /* update */ xstrcpy((UBYTE *)pre_dline,(UBYTE *)dline) ; /* delay */ delay_ms(500); } } /* define function body */ void init_usr(void) { /* disable compare module */ CMCON = 0x07 ; /* disable A/D conveter */ ADCON0.ADON = OFF ; /* I/O state */ PORTA = 0x00 ; PORTB = 0x80 ; PORTC = 0x00 ; /* I/O directions */ TRISA = 0xff ; /* all inputs */ TRISB = 0x00 ; /* all outputs */ TRISC = 0xff ; /* all inputs */ /* initialize Timer 0 */ { /* 20MHz/8 = 5MHz -> 5MHz/8 = 625kHz prescaler = 1:8 */ OPTION_REG = 0x02 ; /* 256 - 125 = 131 */ TMR0 = 131 ; } /* initialize Timer 1 */ { /* clear counters */ TMR1H = 0 ; TMR1L = 0 ; /* select prescaler 1:1 */ T1CON &= ~0x30 ; /* select external clock */ T1CON.TMR1CS = ON ; /* asynchronous */ T1CON.T1SYNC = ON ; /* disable RC clock generator */ T1CON.T1OSCEN = OFF ; } /* initialize Timer 2 */ { /* clear counters */ TMR2 = 0 ; PR2 = 250 ; /* select prescaler and postscaler prescaler 1:4 postscaler 1:1 */ T2CON = 0x01 ; } /* enable general interrupt */ INTCON.GIE = ON ; /* clear flags */ xflags.DR = 0 ; /* initialize variables */ timcnt = 0 ; timv = 0 ; prescale = 1 ; gate_count = GTIME_A ; /* 100 -> 100ms */ prescale = 1 ; /* clear LCD area */ xstrcpy((UBYTE *)uline,(UBYTE *)" , , Hz"); xstrcpy((UBYTE *)dline,(UBYTE *)"1/1 0.1s "); xstrcpy((UBYTE *)pre_dline,(UBYTE *)dline); } void delay_ms(UWORD x) { ULONG target ; /* initialize counter */ TMR0 = 131 ; /* enable timer0 interrupt */ INTCON.T0IE = ON ; /* calcualte last count */ target = timcnt + x ; /* wait */ while ( timcnt < target ) ; /* disable timer0 interrupt */ INTCON.T0IE = OFF ; } void put_lcd_function(UBYTE xdat) { put_lcd_primitive(OFF,xdat); } void put_lcd_data(UBYTE xdat) { put_lcd_primitive(ON,xdat); } void put_lcd_primitive(UBYTE which,UBYTE dat) { /* upper nibble */ put_lcd_p4(which,dat); /* lower nibble */ put_lcd_p4(which,(dat << 4)); } void put_lcd_p4(UBYTE which,UBYTE dat) { UBYTE i ; /* LCD_RS : ON or OFF */ PORTB &= ~(1 << LCD_RS) ; if ( which ) { PORTB |= (1 << LCD_RS) ; } for ( i = 0 ; i < 250 ; i++ ) ; /* clear upper nibble */ PORTB &= 0x0f ; for ( i = 0 ; i < 250 ; i++ ) ; /* impress upper nibble */ PORTB |= (dat & 0xf0) ; for ( i = 0 ; i < 250 ; i++ ) ; /* trigger H */ PORTB |= (1 << LCD_E) ; for ( i = 0 ; i < 250 ; i++ ) ; /* trigger L */ PORTB &= ~(1 << LCD_E) ; for ( i = 0 ; i < 250 ; i++ ) ; } void put_lcd_str(UBYTE r,UBYTE *ptr) { UBYTE adr ; /* check range */ if ( r > 1 ) return ; if ( c > 15 ) return ; /* set address */ adr = 0x00 ; if ( r ) { adr += 0x40 ; } adr |= 0x80 ; put_lcd_function(adr); /* send charactor */ for ( adr = 0 ; adr < 16 ; adr++ ) { if ( *ptr == '\0' ) break ; put_lcd_data(*ptr); ptr++ ; } } void show_uline(void) { put_lcd_str(0,(UBYTE *)uline); } void show_dline(void) { put_lcd_str(1,(UBYTE *)dline); } void init_lcd(void) { /* initialize hardware */ delay_ms(45) ; put_lcd_p4(OFF,0x30); /* select 8 bits */ delay_ms(5) ; put_lcd_p4(OFF,0x30); /* select 8 bits */ delay_ms(1); put_lcd_p4(OFF,0x30); /* select 8 bits */ delay_ms(1); /* set function */ put_lcd_p4(OFF,0x20); /* select 4 bits */ delay_ms(1); /* select 2 line display and bus width 4 bits */ put_lcd_function(0x28); delay_ms(1); /* display off */ put_lcd_function(0x08); delay_ms(1); /* display on , cursor disappear */ put_lcd_function(0x0c); delay_ms(1); /* entry mode*/ put_lcd_function(0x06); delay_ms(1); /* clear */ put_lcd_function(0x01); delay_ms(2); } void xstrcpy(UBYTE *dst,UBYTE *src) { while ( *src ) { *dst = *src ; src++ ; dst++ ; } } UBYTE is_same(void) { UBYTE result ; /* default */ result = ON ; /* compare */ for ( loop = 0 ; loop < 16 ; loop++ ) { /* compare */ if ( *(pre_dline+loop) != *(dline+loop) ) { result = OFF ; } /* exit */ if ( result == OFF ) break ; } return result ; }  周波数カウンタの対象信号をバッファするのに  NPNPトランジスタ2SC1923を利用している。  消費電力を小さくするには、バッファとして  接合型FETを利用するのが、定番である。  接合型FETとして入手性がよいのは、2SK241で  あるが、海外通販サイトから入手できるJ310  を使うことも可能。ただし、バイアス電圧の  与え方やピンアサインが異なるので、完全に  互換にはできない。

目次

inserted by FC2 system