目次
前
次
周波数カウンタ
PIC16F873が残っていたので、周波数カウンタを作成しました。
回路図は、以下。
2行x16桁のLCDを接続し、周波数と条件を表示します。
周波数は上位行、条件は下位行に表示します。
条件を4つのスイッチで指定します。
スイッチと条件の関係は、以下。
- SW0 内部プリスケーラ選択
- SW1 ゲートタイム選択
- SW2 受信周波数減算選択
- SW3 Hz、kHzの表示選択
各スイッチで利用している内容を説明します。
内部プリスケーラ
周波数をカウントする場合、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を使います。
プリスケーラとゲートタイムを合わせて使わないと
測定に対する精度との整合が取れなくなります。
ユーザーインタフェースの仕様を確定したので
カウント値に応じた計算手順を考えますが、動作
シーケンスを決めておきます。
- 内部カウンタクリア
- 指定ゲートタイムから、タイマー2で刻むカウント値計算
- タイマー2動作開始
- タイマー1動作開始
- カウントアップまで待つ
- タイマー1動作開始
- タイマー2動作開始
- カウント値を計算、1:8指定の場合、カウント値を8倍する
- -455kHzが指定されていれば、減算
- カウント値を8けたの数字に変換
- Hz、kHzにあわせた桁数で表示
- 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
を使うことも可能。ただし、バイアス電圧の
与え方やピンアサインが異なるので、完全に
互換にはできない。
目次
前
次