PIC18Fを使ったLCメーターの製作

[公開:any]

[電子工作/PIC]
[電子工作/一般]
[電子工作/測定器]
[PIC,PIC18F,PIC18F2550,LCメーター,容量計,測定器]

origin 2009-04-16
fix 2009-07-11
fix 2009-07-27


スミマセン。長文です。画像も多いです。

コイルのインダクタンスが1.0〜999.9uHの時に小数点以下が正しく表示されないプログラムミスがありました。ご指摘ありがとうございます。→西山さま(2009-07-11)・・・1.0以下の表示も直しました。(2009-07-27)

開発

 PIC18Fは、MPLAB IDEとMPLAB C18 student editionでフリー(無料)な開発環境が構築できます。秋月電子でお手ごろ価格でPIC18F1320とPIC18F2550を調達してあるのでせっかくだから何か作ろうと考えて思いついたのがLCメーターです。
 「LCメーターの製作」ですでに製作しています。・・が、製作当時、PICで作り始めたのですが、開発環境のmikroC試供版の制限を回避するために途中でワンチップマイコンをAVRに変更したという経緯がありました。今回のPIC18Fは、ほぼ一部(ある期間を過ぎると最適化が無効となるらしい)を除いて制限なしに開発できるので初志貫徹でPICを使って作ってみようと思います。

 LCメーターとしての原理は以前のものと同じです。アンバッファインバーター74HCU04を使用したフランクリン発振回路の発振周波数変化から未知のコンデンサ容量やコイルのインダクタンスを測定します。

 まず最初に、発振回路の周波数を測定するために周波数カウンタを作成します。PIC18F2550で周波数カウンタを実験していますが、前回のLCメーターの仕様から18ピンのPIC18F1320でも十分だと気づいて途中でPIC18F1320に変更しています。どちらもMPLAB C18で開発でき、デバイス独自のコンフィグレーションが異なるだけで同じソースプログラムで動作しました。

PIC18F2550でLCメーターの開発PIC18F1320でLCメーターの開発

 ところが・・・・・・・、コーディングテクニックがないので、どうやってもPIC18F1320のフラッシュメモリ容量の8Kを超えてしまいます。・・・ということで、再びPIC18F2550に戻します。^^;

 今回は、発振回路に使用するインダクタを色々とテストしてみましたが、これといって選択の決め手が見つかりませんでした。言い換えればどの種類を使用しても大きな問題はなさそうです。ただ、値(インダクタンス)は、発振する周波数に直接影響があるので、測定対象のコンデンサやインダクタを接続しても安定して発振する値を選択する必要があります。例えば、電源用パワーインダクタの100uHを使用したときには、1uFを超える容量まで測定できましたが、100pF以下の値で大きな誤差が出ました。大きな容量は「AVRを使ったコンデンサー容量計の製作」で作成したもので測定できるので、今回は、測定最大容量を0.47uFを目標にしてインダクタを選択しました。

LCメーター発振回路LCメーター用インダクタの選択

 フランクリン発振回路の発振波形です。LC回路からは、きれいな正弦波を確認できました。今回は、アンバッファインバーター74HUC04の出力信号をさらにバッファアンプで整形してPICのTimer0でカウントすることにしました。

フランクリン発振回路の出力波形

 消費電流は、キャリブレーションリレーが動作した時の最大値で60mA程度です。測定時は30mA弱となります。電池とDC-DCコンバーターでも問題なさそうですが、今回は外部電源とします。

LCメーターの消費電流

回路

 回路図です。前回のLCメーターを大きく異なるところはありません。部品数も少なく簡単です。ただ、PIC18Fは、十分なI/Oポートがありそうですが、入力専用だったり(たとえばRC4やRC5など)、ディジタル入出力に切り替えるのに手間がかかったりと変な仕様になっています。あと、内蔵プルアップがPORTBのみしか出来なかったりと、AVRを一度でも使うと使いづらいと感じてしまいます。

単に私が使い方を知らないだけ・・・ということも考えられますが・・・

 
PIC18Fを使ったLCメーター回路図

 基板の部品実装図です。上にも書きましたが、PIC18Fのポートに変な制限があるので、単純な回路なのに配線の取り回しが難しかったです。

LCメーター部品実装図

製作


 さっそく作成しました。下の画像は鏡面画像ではありません。間違い探しでもありません。ひさびさにやってしまいました。・・・・PICの1番ピンを間違えて作ってしまいました。つまり、左のものはPICを基板の裏に実装しなければ動作しないものです。最初に実装図を間違えて作ったことが原因です。PCBEで28ピンのICソケットを貼り付けるときに「回転」させようとして「反転」させたようです。
 電源つないでもまったく動作しないので、まる1日以上、はまってしまいました。いくら実装図と比較しても間違いは無いし、回路図と実装図のチェックでも見逃しました。

LCメーター失敗作LCメーター基板

 あらたに作り直して電源をつなぐと一発で動作しました。 

LCメーター基板裏LCメーター基盤表

 今回は8文字2行の小型LCDの手持ちが少ないので、秋月電子で最近発売された白抜き文字の8文字2行のキャラクタ液晶モジュールを調達して見ました。ところが、思っていたより大きくて厚みも倍ほどあります。ケースは、同じく秋月電子のポリカーボネートケース中サイズを使用しますが、場合によっては、この液晶モジュールのサイズでは収容が難しいかもしれません。
 実際に使用してみましたが、文字サイズも多少大きくて見やすい液晶です。ただ、バックライトを点灯しないと見にくいのでバックライトを点灯すると消費電流が60mA程度20mA程度も流れます。今回の3端子レギュレータは、定格100mA出力までなのでちょっと無理があります。・・・ということで、今回はこの液晶モジュールの使用を見送ることにしました。

8文字2行LCDの比較18文字2行LCDの比較2

完成


 完成しました。電源を入れると自動的にキャリブレーションを実施し、標準コンデンサとインダクタの値を表示して測定待ちになります。赤いLEDのボタンは、手動キャリブレーションボタンで、赤いLED は通電表示です。青いLEDのボタンは、測定実行ボタンで青いLEDがキャリブレーション完了表示となっています。キャリブレーションが完了して青いLEDが点灯していないと測定が実行できないようになっています。
 測定端子は、「AVRを使ったコンデンサー容量計の製作」と同じようにICソケットを使用しました。
 
 コンデンサの容量は、最小0.1pFから最大で0.47uF程度まで測定できます。

LCメーターキャリブレーション完了LCメーター0.47uFコンデンサ測定

 コイルやインダクタのインダクタンスは、最小0.01uHから手持ちの最大の5.6mHまでは問題なく測定できました。手作りの空芯コイルのインダクタンスも測定できます。

LCメーターインダクタンス測定LCメーターで空芯コイルのインダクタンス測定

 FCZコイルなど測定端子にささらないものは、ワニ口クリップやICクリップをつけた短いケーブルを使用します。インダクタンス測定側にスイッチを切り替えて、測定用の短いケーブルをつけた状態でキャリブレーションします。その後、FCZコイル等を測定すればおおよその値を測定することが出来ます。(この画像のケーブルは明らかに長すぎます。短いものを作っておけばいいでしょう。ただ、この状態で測定してもFCZコイルのインダクタンスは、ほぼスペックとおりの値が測定できました。)

LCメーター測定コードLCメーター測定コード2

比較


 「AVRを使ったコンデンサー容量計の製作」で作った容量計と比較してみました。測定方法はまったく異なるのですが、大体同じような値を示します。特に誤差1%のフィルムコンデンサは、どちらも誤差範囲内の値で測定できました。

LCメーター測定100pFLCメーター測定0.33uF
LCメーター測定0.1uF

 以前作ったLCメーターと比較してみました。キャパシタンスもインダクタンスも多少、値はバラツキますが、大きな差は無いようです。古いLCメーターは、測定ごとに細かな値の変動があるのですが、今回、作成したものは、何回測定しても安定して同じ値を測定します。古いLCメーターは、内部の配線の取り回し等の影響が出ているのかもしれません。



 回路図を見てもらえればわかると思いますが、インダクタンスの測定側にトグルスイッチを切り替えた状態で測定端子を開放してキャリブレーションすると正常な発振が得られなくてエラーとなります。インダクタンス測定側でキャリブレーションするには、測定端子をショートしておく必要があります。ただ、測定端を延長しない限りは、どちらでキャリブレーションしても大きな差はないので、キャリブレーション時はキャパシタンス測定側に切り替えて測定端をオープンした状態で実施します。

LCメーターキャリブレーションエラー

ソースプログラム

 PIC18Fのプログラムは、MPLAB IDE v8.30とMPLAB C18 Student Editionで新たに作成しました。実は古いLCメーターのソースプログラム(AVR用)は、開発用PCのディスククラッシュでロストしてしまいました。印刷もしていなかったのでまったく新たに作り直すことになりました。ただ、そんなにややこしい処理はないので大して手間もかかりませんが、PIC18Fのクセ?(ちょっと変な仕様)にとまどいました。ほぼ前回と同じ機能のプログラムとなっています。参考になるかどうかわかりませんが、ロストしないように今回は貼り付けておきます。^^;
 LCD表示回りのソースプログラムは、もともとが趣味関係のメモ帳さんの公開されたAVR用のコードを趣味の電子工作の部屋byすんさんがPIC24用に改造したものをさらにテキトーに改造して使用させていただきました。この場を借りてお礼申し上げます。

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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/***************************************************************
    PIC18F LC-Meter
        MPLAB IDE v8.30 and MPLAB C18 student Edition
        Use to PIC18F2550     

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

#pragma config FOSC = HS
#pragma config WDT = OFF
#pragma config USBDIV = 1
#pragma config FCMEN = OFF
#pragma config PWRT = ON
#pragma config BOR = OFF
#pragma config BORV = 2
#pragma config LVP = OFF
#pragma config VREGEN = ON
#pragma config MCLRE = OFF
#pragma config PBADEN = OFF

unsigned long cnt;        // timer0 overflow counter
unsigned char t1cnt;    // timer1 loop counter
unsigned char t1flg;    // timer1 loop complete flag
unsigned char tmp[10];    // lcd output srting
double cref;            // refarence capacitance
double lref;            // refarence inductance
unsigned long fref;        // refarence frequency
unsigned char calflag;    // calibration complete flag

#define M_PI 3.141593    // pai
#define CCAL 1000    // calibration capaciter

#pragma interrupt isr
#pragma code isrcode = 0x8
/***************************************************************
***************************************************************/
void isr_direct(void)
{
    _asm
        goto isr
    _endasm
}
#pragma code
/***************************************************************
    Timer0 overflow interrupt
    Timer1 compare match interrpt 
***************************************************************/
void isr(void)
{
    if(PIR1bits.CCP1IF) {        // Timer1 
        PIR1bits.CCP1IF = 0;
        t1cnt--;
        if(t1cnt == 0) {
            T0CONbits.TMR0ON = 0;
            T1CONbits.TMR1ON = 0;
            t1flg = 1;
        }
    }

    if(INTCONbits.TMR0IF) {        // Timer0
        INTCONbits.TMR0IF = 0;
        cnt++;
    }
}
/***************************************************************
    ms step delay (This is doubtful. ^^;)
***************************************************************/
void delay_ms(unsigned int t)
{
    for(; t > 0; t--)        // 12MHz clock
        Delay1KTCYx(3);        // 1ms: 0.001 / ((1 / (12000000 / 4)) * 1000) = 3 
}
/***************************************************************
    calibration error message    
***************************************************************/
void cal_error(void)
{
    lcd_gotopos(0, 0);
    sprintf(tmp, "CalError");
    lcd_putstr(tmp);
    lcd_gotopos(0, 1);
    sprintf(tmp, "reExcCal");
    lcd_putstr(tmp);
}
/***************************************************************
    measurement error messege
***************************************************************/
void mes_error(void)
{
    lcd_gotopos(0, 0);
    sprintf(tmp, "MesError");
    lcd_putstr(tmp);
    lcd_gotopos(0, 1);
    sprintf(tmp, "reExcMes");
    lcd_putstr(tmp);
}
/***************************************************************
    measurement frequency
***************************************************************/
unsigned long f_measure(void)
{
    unsigned long freq;

    cnt = 0;
    t1flg = 0;
    t1cnt = 5;
    TMR0L = 0;

    INTCONbits.GIE = 1;        // General Interrupt Enable

    T0CONbits.TMR0ON = 1;
    T1CONbits.TMR1ON = 1;

    while(t1flg == 0);

    freq = (cnt * 256 + TMR0L) * 2;
    
    INTCONbits.GIE = 0;        // General Interrupt Disable

    return freq;
}
/***************************************************************
    calibration
***************************************************************/
void calibration(void)
{
    unsigned long  freq;
    double otemp, ctemp;

    LATCbits.LATC2 = 0;        // calibration LED off
    calflag = 0;

    sprintf(tmp, "LC-Meter");
    lcd_gotopos(0, 0);
    lcd_putstr(tmp);
    sprintf(tmp, "Exec Cal");
    lcd_gotopos(0, 1);
    lcd_putstr(tmp);

    delay_ms(1000);
    fref = f_measure();        // get refarence frequency

    LATAbits.LATA3 = 1;
    delay_ms(2000);
    freq = f_measure();
    LATAbits.LATA3 = 0;

    otemp = fref * 0.001;
    ctemp = freq * 0.001;
    cref = (ctemp * ctemp) / (otemp * otemp - ctemp * ctemp) * CCAL;
    lref = 100000000 / (4 * M_PI * M_PI * otemp * otemp * cref * 0.0001);

    if(cref < 1010.0 || cref > 1050.0) {    // refarence capacitance value check
        cal_error();
        return;
    }
    if(lref < 60.0 || lref > 80.0) {        // refarence inductance value check
        cal_error();
        return;
    }

    lcd_cls();

    sprintf(tmp, "%3d.%02uuH", (int)lref, (int)((lref - (int)lref) * 100));
    lcd_gotopos(0, 0);
    lcd_putstr(tmp);
    sprintf(tmp, "%4d.%01upF", (int)cref, (int)((cref - (int)cref) * 10));
    lcd_gotopos(0, 1);
    lcd_putstr(tmp);

    LATCbits.LATC2 = 1;        // caribration LED on
    calflag = 1;
}
/***************************************************************
     measurement LC
***************************************************************/
void measure(void)
{
    unsigned long freq;
    double otemp, ctemp;
    double x;
    double val;

    if(calflag == 0) {
        cal_error();
        return;
    }

    freq = f_measure();
    if(freq < 10000) {
        mes_error();
        return;
    }

    lcd_cls();
    sprintf(tmp, "%6luHz", freq);
    lcd_gotopos(0, 0);
    lcd_putstr(tmp);

    otemp = fref * 0.001;
    ctemp = freq * 0.001;
    x = ((otemp * otemp) / (ctemp * ctemp)) - 1.0;
    if(x < 0.0)
        x = 0.0;

    lcd_gotopos(0, 1);

    if(PORTAbits.RA0) {
        val = x * cref;
        if(val < 10000.0) {        // 0.0pF - 9999.9pF
            sprintf(tmp, "%4d.%01upF", (int)val, (int)((val - (int)val) * 10));
            lcd_putstr(tmp);
        } else {                // 0.01uF - max
            sprintf(tmp, "0.%04uuF", (int)(val * 0.01));
            lcd_putstr(tmp);
        }
    } else {        // ここの計算式に誤りがあったので修正しました。(2009-07-11)(2009-07-27)
        val = x * lref;
        if(val > 1000.0) {        // 1.0mH - max
            val = val * 0.001;
            sprintf(tmp, "%3d.%02dmH", (int)val, (int)((val - (int)val) * 100));
            lcd_putstr(tmp);
        } else     if(val > 1.0) {    // 1.0uH - 999.9uH
            sprintf(tmp, "%3d.%02duH", (int)val, (int)((val - (int)val) * 100));
            lcd_putstr(tmp);
        } else {                // 0.00uH - 0.99uH
            sprintf(tmp, "  0.%02duH", (int)(val * 100));
            lcd_putstr(tmp);
        }
    }
}
/***************************************************************
    main
***************************************************************/
void main(void)
{
    unsigned char sw0_state, sw1_state;
    unsigned long freq;

    TRISA = 0b00010001;        // RA0 L or C toggle SW input
                            // RA4 Osciration signal input
    ADCON1 = 0b01111111;    // A/D converter disable
    TRISB = 0b00000000;        // for LCD
    TRISC = 0b11000000;        // RC2 calibration complite LED
    LATAbits.LATA3 = 0;

    lcd_init();
    lcd_cls();

    T0CON = 0b11111000;        // 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

    CCPR1L = 0x7c;            // 1 / (12000000 / 4 / 8) = 2.66...us
    CCPR1H = 0x92;            // 100ms .. 37500=0x927c

    sw0_state = sw1_state = 0;

    freq = f_measure();        // Dummy measure for the first time
    calibration();

    while(1){
        if(PORTCbits.RC7 == 0) {
            sw0_state = 1;
            delay_ms(10);
        }
        if(sw0_state && PORTCbits.RC7 == 1) {
            sw0_state = 0;
            calibration();
        }
        if(PORTCbits.RC6== 0) {
            sw1_state = 1;
            delay_ms(10);
        }
        if(sw1_state && PORTCbits.RC6 == 1) {
            sw1_state = 0;
            measure();
        }
    }
}

▲ページ Top へ...