PIC18Fを使った周波数カウンターの製作

[公開:any]

[電子工作/PIC]
[周波数カウンタ,PIC,PIC18F,PIC18F1320,MB504,測定器]

origin 2009-04-25


 前回の「PIC18Fを使ったLCメーターの製作」に続いてPIC18Fを使った電子工作です。PICと言えば、「周波数カウンター」ですね。←意味不明ですが・・・周波数カウンタは、AVRよりはPICに軍配が・・・・というような意味です。^^;
 LCメーターは、周波数カウンターで発振周波数を測定して、そこからコンデンサの容量やコイルのインダクタを計算しています。ということで・・周波数カウンターは、すでに出来たようなものです。
 過去、「PICを使った周波数カウンター」で製作していますが、今回は外付けのプリスケーラを使ってVHFからUHFまでKHz単位で測定できるものを作ります。プリスケーラは、PLLなどに使用される富士通製のMB504を使用します。MB504は、入力が10MHz〜500MHzまで対応し、分周比は、32,33,64,65を選択できます。今回は、32分周を使用します。

 いつものようにブレッドボードで開発します。入力は、V,UHF帯対応の10〜500MHzとそれ以下のHF帯対応の0〜20MHzの2系統とします。PICは、18ピンのPIC18F1320を使用します。
 V,UHFは、入力アンプを内蔵しているMB504へ直接入力とします。MB504の出力はECL(電流モード)なので抵抗で電圧変換し、さらにシュミットトリガインバータの74HC14でロジックレベルに変換します。
 0〜20MHzは、2SK241とトランジスタを使った入力アンプへ入力することにします。アンプの出力もシュミットトリガインバータを使用して信号レベルを安定化します。

ブレッドボード開発

 PIC18Fは、Timer1でゲート時間を生成して、Timer0でカウントするというPICの周波数カウンターでは一般的な方法で使用します。ゲート時間は、0.2秒、0.5秒、1秒の3段階とします。
 PIC18Fを外部クロックモジュールから供給する12.8MHzで動作させると、Timer0ではプリスケーラなしで2MHz程度までしか測定できません。よってTimer0のプリスケーラを1/1、1/4、1/8の3段階切替として最大20MHzまでカウントできるようにします。
 なお、外部プリスケーラからのカウントは、内部プリスケーラを強制的に1/8とします。これにより最小測定単位が、32*8=256Hzに固定されますが、KHz単位の表示には十分と判断しました。

 回路図です。2系統の入力は、ロジックレベルでの切替となるのでNANDなどの汎用ロジックを使ったマルチプレクサなども検討しましたが、基板実装の制限から単純にトグルスイッチによる切替としました。シュミットトリガインバータも74HC14ではなく、シングルロジックのLMOSを2個使用することにしました。

PIC18F周波数カウンター回路図

 PCBEで作成した基盤の実装図です。基板は、共立電子で販売されているガラスエポキシのユニバーサル基板で秋月電子のポリカーボネートケース中サイズにピッタリです。(この基板サイズと同じものが秋葉原の千石電商の店頭でも販売されていた記憶があります。)

PIC18F周波数カウンター基板実装図

 基板に部品を実装しました。ゲート時間とプリスケーラ(PICのTimer0前置プリスケーラ)の切替はLED内蔵プッシュスイッチとしました。

PIC18F周波数カウンター基板表PIC18F周波数カウンター裏

 液晶モジュールは、秋月電子の8文字2行の白文字小型液晶モジュールを使用します。全体の消費電流は最大で60mA程度となりました。バラックで動作確認した後、いつものポリカーボネートケースに組み込みました。緑LEDのスイッチがプリスケーラ切替でLEDが電源ステータスを示します。青LEDのスイッチがゲート時間の切替でLEDがゲート時間に応じて点滅します。

周波数カウンターバラックテスト周波数カウンターケース

 左は、信号発生器から12.345678MHzの入力時です。プリスケーラは1/8なので最小周波数単位の8Hzで丸められた値を表示します。右は、1.234567MHzを入力したときです。プリスケーラは1/1なので1Hz単位の測定が可能です。なお、プリスケーラが1/1では2.4MHzまで、1/4では12MHzまで、1/8では24MHzまで測定できました。

周波数カウンターテスト1周波数カウンターテスト2

 V,UHF対応の外部プリスケーラ側で周波数を測定してみます。信号発生器から123.456MHzを入力したときにゲート時間を1秒と0.2秒に切り替えてみましたが、ともに正確に計測します。

周波数カウンターVHF入力テスト1周波数カウンターVHF入力テスト2

 500MHzまでは問題なく測定できました。ためしに555.555MHzを入力してみました。一応は計測できるようですが、この辺りが外部プリスケーラの測定限界です。

周波数カウンターUHF入力限界周波数

 秋月電子の液晶は、バックライトの光源漏れがすごいです。白色LED懐中電灯としても使用できそうです。通常は、透明ケースでの使用を考慮しないので問題ないのでしょう。また、小型と言えどもいつも使っている8文字2行の液晶モジュールと比較すると縦横と厚みも大きいので小型ケースに収納するには注意が必要です。今回もBNCコネクタと干渉するのでBNCコネクタ側を加工する必要がありました。

秋月電子白色文字液晶

 0〜20MHz対応入力ポートの周波数別最小入力電圧です。この電圧以上であれば正常に測定することが出来ます。

入力周波数 最小電圧(mVrms)
10KHz 1.5
100KHz 1.5
1MHz 2.0
2MHz 2.0
5MHz 5.0
10MHz 10.0
20MHz 22.0

 V,UHF対応入力ポートの周波数別最小入力電圧です。100MHzで-29dBm以上あれば正常に測定できます。

入力周波数 最小電圧(mVrms)
10MHz 52.0
20MHz 19.0
50MHz 10.0
100MHz 8.0
200MHz 8.0
300MHz 10.0
400MHz 12.0
500MHz 15.0

 ソースプログラムです。MPLAB IDE v8.30とMPLAB C18で開発しました。周波数カウンタの部分は、前回のLCメーターとほぼ同じです。0.1秒単位のTimer1のコンペアマッチ割り込みをゲート時間分だけ繰り返してTimer0でカウントする方法です。各ゲート時間に合わせた処理ディレイも適当にいれて(ほぼ)正確に周波数が測定できるようにしてあります。(校正に使用した信号発生器の周波数精度は保障されていませんが・・・・)
 ゲート時間とプリスケーラは、それぞれ3段階の切替としましたが、実質3段階も必要かといえば疑問です。2段階にすれば安いトグルスイッチが使えるメリットも考慮すればちょっと無駄だったかなぁ・・というところです。
 LCD関係のライブラリは「PIC18Fを使ったLCメーターの製作」と同じく他のサイトのものを改造して利用しているのでここへは掲載していません。・・・が、よく調べてみるとMPLAB C18には標準でLCD関連のライブラリが用意されているようです。(でも使うのが大変そう・・・)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/**************************************************************
    PIC18F Frequency Counter
        MPLAB IDE v8.30 and MPLAB C18 student Edition
        Use to PIC18F1320     

    2009.04.21 www.henteko.org
**************************************************************/
#include <p18cxxx.h>
#include <delays.h>
#include <stdio.h>
#include "lcd.h"

#pragma config OSC = ECIO
#pragma config WDT = OFF
#pragma config PWRT = ON
#pragma config BOR = OFF
#pragma config BORV = 27
#pragma config LVP = OFF
#pragma config MCLRE = OFF

unsigned long cnt;
unsigned char t1cnt;
unsigned char t1flg;
unsigned char tmp[10];

#pragma interrupt isr
#pragma code isrcode = 0x8

void isr_direct(void)
{
    _asm
        goto isr
    _endasm
}
#pragma code

void isr(void)
{
    if(PIR1bits.CCP1IF) {
        PIR1bits.CCP1IF = 0;
        t1cnt--;
        if(t1cnt == 0) {
            T0CONbits.TMR0ON = 0;
            T1CONbits.TMR1ON = 0;
            t1flg = 1;
        }
    }

    if(INTCONbits.TMR0IF) {
        INTCONbits.TMR0IF = 0;
        cnt++;
    }
}

void delay_ms(unsigned int t)
{
    for(; t > 0; t--)
        Delay100TCYx(32);    // 0.001s / (1 / (12800000 / 4)) = 3200
}

void main(void)
{
    unsigned char sw0_state, sw1_state;
    unsigned long freq;
    unsigned int hz;
    unsigned int gatecount;
    unsigned int prescaler;

    TRISA = 0b11110100;        // RA2 toggle sw, RA2 gate LED
    ADCON1 = 0b01111111;    // AD converter disable
    TRISB = 0b00000011;        // for RB2-RB7 for LCD, RB0,RB1 is SW-input
    PORTAbits.RA3 = 0;

    lcd_init();
    lcd_cls();

    T0CON = 0b11110010;        // Timer0 8bit mode, prescaler is not assigned.
    INTCONbits.TMR0IE = 1;    // Timer0 Interrupt Enable
    T1CON = 0b11110001;        // Timer1 16bit mode, 1/8 prescaler
    CCP1CON = 0b00001011;    // Compare mode
    PIE1bits.CCP1IE = 1;    // CCP1 Interrupt Enable
    INTCONbits.PEIE = 1;    // Peripheral Interrupt Enable
    INTCON2bits.RBPU = 0;    // PORTB pull up enable by latch value
    LATBbits.LATB0 = 1;        // PB0 pull up
    LATBbits.LATB1 = 1;        // PB1 pull up

    CCPR1L = 0x40;            // 1 / (12800000 / 4 / 8) = 2.5us
    CCPR1H = 0x9c;            // 100ms .. 40000=0x9c40

    sw0_state = sw0_state = 0;
    gatecount = 10;
    prescaler = 8;
    t1cnt = gatecount;

    INTCONbits.GIE = 1;        // General Interrupt Enable

    while(1){
        if(t1flg) {
            freq = (cnt * 256 + TMR0L) * (10 / gatecount) * prescaler;
            cnt = 0;
            t1flg = 0;
            t1cnt = gatecount;
            TMR0L = 0;

              if(gatecount == 10) {    // Timer1 and Timer0 start
                T1CONbits.TMR1ON = 1;
                Delay10TCYx(8);            // delay for 10 count loop 
                T0CONbits.TMR0ON = 1;
            } else if(gatecount == 5) {
                T1CONbits.TMR1ON = 1;
                Delay10TCYx(4);            // delay for 4 count loop
                T0CONbits.TMR0ON = 1;
            } else {
                T1CONbits.TMR1ON = 1;
                Delay10TCYx(1);            // delay for 1 count loop
                T0CONbits.TMR0ON = 1;
            }

            lcd_gotopos(0, 0);
            if(PORTAbits.RA2 == 0) {    // toggle SW is High frequency
                prescaler = 8;
                T0CON = 0b11110010;
                freq = freq * 32;
                hz = freq  - ((freq / 1000) * 1000);
                sprintf(tmp, "%2d %d KHz", gatecount, prescaler);
                lcd_putstr(tmp);
                sprintf(tmp, "%6ld.%d", freq / 1000, hz / 100);
            } else {                    // toggle SW is Low frequency
                sprintf(tmp, "%2d %d  Hz", gatecount, prescaler);
                lcd_putstr(tmp);
                sprintf(tmp, "%8ld", freq);
            }
            lcd_gotopos(0, 1);
            lcd_putstr(tmp);
            LATAbits.LATA3 ^= 1;        // toggle gate LED
        }
        if(PORTBbits.RB1 == 0) {
            sw0_state = 1;
            delay_ms(10);
        }
        if(sw0_state && PORTBbits.RB1 == 1) {
            sw0_state = 0;
            if(gatecount == 10)
                gatecount = 2;
            else if(gatecount == 2)
                gatecount = 5;
            else if(gatecount == 5)
                gatecount = 10;
        }
        if(PORTBbits.RB0 == 0) {
            sw1_state = 1;
            delay_ms(10);
        }
        if(sw1_state && PORTBbits.RB0 == 1) {
            sw1_state = 0;
            if(prescaler == 1) {
                prescaler = 4;
                T0CON = 0b11110001;
            } else if(prescaler == 4) {
                prescaler = 8;
                T0CON = 0b11110010;
            } else if(prescaler == 8) {
                prescaler = 1;
                T0CON = 0b11111000;
            }
        }
    }
}

▲ページ Top へ...