AVRを使ったコンデンサー容量計の製作

[公開:any]

[電子工作/AVR]
[電子工作/測定器]
[AVR,ATmega88,容量計,LCメーター,測定器]

origin 2008-08-30


 LCメーターでは、約0.2uF以上のコンデンサや極性のあるコンデンサの容量を測定することは出来ないので、「コンデンサ容量計の実験」で確認したコンデンサ容量計を製作します。
 測定範囲は、0〜9999uFまでを目標としますが、大きな値(どの辺りから怪しくなるかはよくわかりませんが)は、測定値が怪しくなると思われます。なお、大きな値の電解コンデンサは、本体に容量表示があるので測定する必要性は低いのですが、古いコンデンサの「容量抜け」の確認程度は出来ると思っています。

 コンデンサの容量の測定方法は、コンデンサの充電時の過渡特性を利用したものです。原理そのものは、「コンデンサ 過渡特性」をキーワードにインターネットで検索してください。
 この特性を利用した容量計の製作は、

で、詳しく解説されています。

 henteko.orgでも基本的には、このサイトを参考に製作しましたが、精密測定に向かない悪い例として紹介させていただきます。^^;

開発

 いつものようにブレッドボードで開発します。

コンデンサ容量計開発1

 ELMさんのコンテンツに、”浮遊容量を減らす(または安定させる)のが精密測定のコツ”とあります。・・・この点、ブレッドボードは、まったく向いていません。^^;
 ですが、とりあえず測定するコンデンサに流れる充電電流を決定する電流制限抵抗や測定開始と終了を決定するスレッショルド電圧を決めるべくいろいろとテストを重ねました。

コンデンサ容量計開発2

 前回の実験では、小さな容量のコンデンサの充電には、高速な充電時間を確保するために、電流制限抵抗を4.7KΩと小さな値にしたのですが、基準とする校正用コンデンサ100pFまたは1000pFから得られた解像度(容量あたりのAVRタイマー値)が十分な値(小さな値しか得られない)でないため、校正用コンデンサよりも小さな容量を測定した場合に誤差が大きくなるという問題点がありました。

 今回は、測定するコンデンサの容量に応じて手動で2段のレンジ切替で対応します。小さな容量のコンデンサを測定するLOWレンジは、大きな電流制限抵抗とし、逆に大きな容量のコンデンサを測定するHIGHレンジは、出来るだけ測定時間を短くするために小さな電流制限抵抗となるようにしました。

 2段のレンジそれぞれで校正用コンデンサを使用します。使用するコンデンサは、ポリプロピレンフィルムコンデンサで誤差1%のものを用意しました。浮遊容量的にはデメリットがありますが、リレーによる切替回路で校正用コンデンサを切り替える自動校正方式とします。(・・としてしまいました。^^;)

コンデンサ容量計開発3

回路図

 最終的な回路図をBsch3Vで作成しました。

コンデンサ容量計回路図

注意:AREFピンをGNDに接続するのは間違い。AREFピンは、AVccの接続する必要があります。(2008-09-03)


LCDユニットの電源とグランドが間違っています。本来は、VDDが2番ピン、Vssが1番ピンです。


 AVRは、ATmega88を使用します。測定精度をあげる為には、細かな時間分解能が必要となるので20MHzのセラミック発振器による外部クロックで動作させます。
 AVRのコンパレータ(アナログ比較器)とタイマー1を組み合わせたインプットキャプチャを使用して充電時間を測定します。前回の実験では、AVRのコンパレータ反転入力に加えるスレッショルド電圧をトランジスタで抵抗分圧の切替としていましたが、今回は、2つのスレッショルド電圧を加えたAD変換ポートを内蔵マルチプレクサで切り替えることによってコンパレータの反転入力を切り替える方式としました。

電源電圧5V時のスレッショルド電圧
1段目 5 / (22000 + 22000 + 6800) * 6800 = 669mV
2段目 5 / (22000 + 22000 + 6800) * (22000 + 6800) = 2.834V

 ただ、コンデンサの電圧が1段目の電圧になって、インプットキャプチャ割り込みがかかってからマルチプレクサで2段目の電圧に切り替えて2回目のインプットキャプチャが有効になるまでにある程度時間がかかります。HIGHレンジでは、電圧上昇が早すぎて、校正用のデータに必要な測定端子開放時の、有効な時間が測定できませんでした。

 このため、精密な測定が必要なLOWレンジでは、2段のスレッショルド電圧を利用する測定とし、多少の誤差は無視できるHIGHレンジでは、0V(厳密には0V付近)から2段目の電圧に達するまでの時間を測定するようにしました。

 PCBEで基板配線図を作成しました。

コンデンサ容量計基板配線図

 部品点数は少ないので実装は楽ですが、「浮遊容量を減らす」という観点からは問題の多い配線の引き回しとなっています。^^;
 特に、校正用コンデンサを切り替えるためのリレー回路周辺が怪しい状況です。(悪い配線例としてみてください。)

製作

 ユニバーサル基板に実装しました。

コンデンサ容量計基板裏コンデンサ容量計基板表

 LED内蔵の3個のプッシュスイッチは、「手動校正」、「レンジ切替」、「測定」に割り当てています。今回使用した部品でLCDユニットを除けば一番単価の高い部品だったりします。^^;

 電源とLCDユニットを接続して動作させて見ました。
 光るプッシュスイッチは、一番下の緑が電源ステータス、真ん中の赤が測定中表示(測定中は消灯します。)、一番上の青がキャリブレーション完了(校正完了)を示します。

コンデンサ容量計動作試験

 電源を入れると、リレーがカチカチと音がして自動校正が完了します。なお、校正時は、浮遊容量を測定するので測定端子を開放しておく必要があります。

 計算では浮遊容量が約20pF程度あるようです。ブレッドボードでは、15pF程度でしたので悪くなっています。

 基板上にシングルICソケットピンを使って測定端子を設けました。テストで測定してみると、ブレッドボードのときよりも浮遊容量が大きいようです。校正しても誤差1%のフィルムコンデンサの測定値が大きめに測定されるようです。(・・・ただし、絶対値は不明なのでどの程度の誤差かは不明です。)やはり、基板上の浮遊容量が大きいため校正してもきちんと浮遊容量がキャンセルできていない可能性があります。

 今回の製作の目標である0.2uF以上のコンデンサの測定では、あまり影響のない値だと思われるので、これで我慢することにします。

完成

 ケースは、秋月電子のポリカーボネートケース中サイズを使用します。透明ケースはLCDユニットなどの表示部を開口加工しなくても良いので楽です。また、作成した基板が見えるのも自作欲を満たしてくれます。(^^;・・裏は見られたくありませんが・・・)

コンデンサ容量計完成画像

 測定端子は、悩んだのですが「外だし」にしてしまいました。しかも下の画像のようにかなりトリッキーな接続です。^^;

コンデンサ容量計測定端子詳細

 ELMさんのサイトでは、浮遊容量の問題等があるため出来るだけ基板外に出すのを避けるように注意されています。1000pF以下を基板上と「外だし」で測定した値を比較してみると、値そのものは校正があるので大きく変わることはありませんが、「外だし」では、最小桁が測定ごとに変動するなど測定値の安定度が悪化するようです。また、測定中に端子付近に指を近づけると測定値が大きく変動するなどのボディエフェクトもあります。最小桁といえども小さな容量では大きな誤差となるので、精密測定を目標とするなら、ELMさんのコンテンツとおりに基板上に測定端子を設けるべきでしょう。

テスト

 以前作成したLCメーターと比較してみました。なお、当然、LCメーターも校正されたものではないため比較しても精度的には意味はありません。

 左上から2pFセラミック(誤差2025%)、10pFセラミック(誤差10%)、100pFポリプロピレンフィルム(誤差1%)、6800pFセラミック(誤差10%)、0.1uFポリプロピレンフィルム(誤差5%)となっています。

セラミックコンデンサの誤差が間違っていました。・・・ということで、一応、測定値は誤差範囲内に収まっているようです。(2008-09-07)


 最初の2pFは、LCメーターも今回の容量計も小数点以下の最小桁が測定ごとにばらつくので安定しませんでしたが、10pFから上は今回の容量計が高めの値を計測するようです。

 今回の容量計には、レンジが2段あるのでLCメーターと比較できる0.1uFまでレンジごとに比較しましたが、0.01uFよりも上ではHIGHレンジの測定値がLCメーターと近似するようです。

容量計とLCメーターの比較1容量計とLCメーターの比較2
容量計とLCメーターの比較3容量計とLCメーターの比較4
容量計とLCメーターの比較5

 大きな容量のコンデンサも測定してみました。
 左上から順に、10uF積層セラミック、100uF電解、1000uF電解、3300uF電解の測定です。3300uFは、リードが太くてソケットに刺さらないため、手で押し付けながら測定しますが、時間は10秒程度かかるためちょっと面倒です。

コンデンサ容量測定1コンデンサ容量測定2
コンデンサ容量測定3コンデンサ容量測定4

測定器シリーズ

 同じように透明ケースに収容した測定器を並べてみました。こうなるとLCメーターも同じ透明ケースで作り直したくなります。^^;

測定器シリーズ


ソフトウェア

 ATmega88のソフトウェアは、AVRStudio4とWINAVR(gcc)で作成しました。あまり参考にはなりませんが、ソースを公開します。突貫工事で作成しましたので、ヒジョーに見にくいソースになっています。(しかも手抜きコメント^^;)

 AVRは20MHzのクロックで動作しているので時間分解能は0.05usとなります。LOWレンジでは、16ビットタイマを1200回までのリミットとしているので3.9秒までカウントできます。計算では約12uFまでは測定できるはずです。
 HIGHレンジでは12000回のリミットとなっているので39秒までカウントできます。気長に待てば10000uFを超える容量まで測定可能と思われます。なお、手持ちのコンデンサは3300uFが最大値なので確認は出来ていません。

大きな容量の測定値には、基準となる校正用コンデンサの測定誤差がn倍(1000uFの場合、1000/0.01で10万倍)も入っています。・・・ということで、大きな容量の測定値が意味のある数字かどうかも疑問です。


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
#include <avr/io.h>
#include <util/delay.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <math.h>

#include "lcd.h"

#define cbi(addr, bit)     addr &= ~(1<<bit)
#define sbi(addr, bit)     addr |=  (1<<bit)

void delay_ms(unsigned int t) {    while(t--)  _delay_ms(1); }

FILE *fp;
double cap;                // capacitance
volatile unsigned long int cnt;        // total timer count
unsigned long int refcnt_l, refcnt_h;    // refarence capacitor timer count
unsigned int ocnt_l, ocnt_h;        // open cirkit capacitance timer count
unsigned int t1cnt;            // timer1 overflow count
unsigned int lt_max;            // limit timer1 overflow count
unsigned char nt, mes_go, mes_err;    // flags
unsigned char mode;            // Hi/Lo mode flag
unsigned char cal_ok;            // calibration check flag


ISR(TIMER1_CAPT_vect)
{
    if(nt > 0) {
        cnt = t1cnt * 65536 + ICR1;
        sbi(PORTD, PD5);
        mes_go = 0;
        sbi(PORTC, PC3);
    } else {
        ADMUX = 1;
        nt++;
        t1cnt = 0;
        TCNT1 = 0x0000;
    }
}

ISR(TIMER1_OVF_vect)
{
    t1cnt++;
    if(mes_go) {
        if(t1cnt > lt_max) {
            mes_err = 1;
            sbi(PORTD, PD5);
            mes_go = 0;
        }
    }
}

void measure()        // measuring capacitance
{
    cbi(PORTC, PC3);
    if(mode == 0) {            // Low mode
        ADMUX = 0;
        nt = 0;
        lt_max = 1200;        // timer1 limit 1200 * 65536 * 0.05us = 3.9s  
        sbi(PORTD, PD2);
    } else {            // High mode
        ADMUX = 1;
        nt = 1;
        lt_max = 12000;        // timer1 limit 12000 * 65536 * 0.05us = 39s
        cbi(PORTD, PD2);
    }
    t1cnt = 0;
    mes_go = 1;
    mes_err = 0;
    cnt = 0;
    TCNT1 = 0x0000;
    cbi(PORTD, PD5);
}

void lcd_write()
{
    lcd_cls();
    lcd_goto(0, 0);
    if(mode)
        fprintf(fp, "Hi mode");
    else 
        fprintf(fp, "Lo mode");
    lcd_goto(1, 0);
    if(mes_err) {
        fprintf(fp, "Error");
    } else {
        if(cap < 0.01) {
            fprintf(fp, "%6.1fpF", cap * 1000000);
        } else {
            if(cap > 10000)
                fprintf(fp, "%6duF", cap);
            else if(cap > 1000)
                fprintf(fp, "%6.1fuF", cap);
            else if(cap > 100)
                fprintf(fp, "%6.2fuF", cap);
            else if(cap > 10)
                fprintf(fp, "%6.3fuF", cap);
            else
                fprintf(fp, "%6.4fuF", cap);
        }
    }
}
void cal_err()
{
    lcd_cls();
    lcd_goto(0, 0);
    fprintf(fp, "Cal Err!");
}

void calibration()
{
    unsigned char tmp_mode;

    tmp_mode = mode;
    cbi(PORTC, PC4);

    mode = 0;
    measure();        // measuring to Low mode open cirkit capacitance
    loop_until_bit_is_set(PIND, PD5);
    delay_ms(200);
    if((cnt > 300) || mes_err) {    // measuring terminal not open 
        cal_err();
        cal_ok = 0;
        return;
    }
    ocnt_l = cnt;
    sbi(PORTD, PD3);        // Low mode refarence capacitor ON 
    delay_ms(200);
    measure();
    loop_until_bit_is_set(PIND, PD5);
    delay_ms(200);
    cbi(PORTD, PD3);
    delay_ms(1);
    refcnt_l = cnt;
    delay_ms(200);

    mode = 1;
    measure();        // measuring to High mode open cirkit capacitance
    loop_until_bit_is_set(PIND, PD5);
    delay_ms(200);
    ocnt_h = cnt;
    sbi(PORTD, PD4);
    delay_ms(200);
    measure();        // High mode refarence capacitor ON 
    loop_until_bit_is_set(PIND, PD5);
    delay_ms(200);
    cbi(PORTD, PD4);
    delay_ms(1);
    refcnt_h = cnt;
    delay_ms(200);

    lcd_cls();
    lcd_goto(1, 0);
    fprintf(fp, "Cal OK"); 
    lcd_goto(0, 0);
    if(tmp_mode == 1)
        fprintf(fp, "Hi mode");
    else
        fprintf(fp, "Lo mode");

    mode = tmp_mode;
    cal_ok = 1;
    sbi(PORTC, PC4);
    sbi(PORTC, PC3);
}

int main()
{
    unsigned char sw1_state, sw2_state, sw3_state;
     
     DDRB = 0b11111111;    // LCD
    DDRC = 0b00011000;    // PC3-4 LED, PC5 SW-input 
    DDRD = 0b00111100;    // PD2-5 output, PD0-1 SW-input

    PORTD = 0b00000011;    // PD0-1 pull up
    PORTC = 0b00100000;    // PC5 pull up

    ADCSRB |= _BV(ACME);    // AIN0 -> ADC0-ADC7
    ADCSRA &= ~_BV(ADEN);    // Disable A/D Conv

    TCCR1A = 0;            // Timer1
    TCCR1B = _BV(ICES1)|_BV(CS10);    // Timer1 InputCapture Edge Select
                    //   and no prescaler
    TIMSK1 = _BV(ICIE1)|_BV(TOIE1);    // Timer1 InputCapture Enable
                    //   and Counter Overflow Enable
    ACSR = _BV(ACIC)|_BV(ACIS1)|_BV(ACIS0);    // Analog Comparator Status Register
    DIDR1 = 3;

    lcd_init();
    lcd_cls();
    fp = fdevopen(lcd_putch, NULL);
    lcd_goto(0, 0);
    fprintf(fp, "CapMeter");
    lcd_goto(1, 0);
    fprintf(fp, "Ver 1.0a");

    sw1_state = sw2_state = sw3_state = 0;
    mes_go = 0;
    mode = 0;
    cal_ok = 0;

    sbi(PORTD, PD5);

    sei();

    delay_ms(1500);

    calibration();

    while(1) {
        if(mes_go) {
            if(t1cnt > lt_max) {    // timer1 timeout chekch
                mes_err = 1;
                sbi(PORTD, PD5);
                mes_go = 0;
            }
        }
        if(bit_is_clear(PINC, PC5)) {
            sw3_state = 1;            
            delay_ms(10);
        }
        if(sw3_state && bit_is_set(PINC, PC5)) {
            sw3_state = 0;
            calibration();
        }
        if(bit_is_clear(PIND, PD0)) {
            sw2_state = 1;            
            delay_ms(10);
        }
        if(sw2_state && bit_is_set(PIND, PD0)) {
            sw2_state = 0;
            lcd_cls();
            lcd_goto(0, 0);
            if(mode == 0) {
                mode = 1;
                fprintf(fp, "Hi mode");
            } else {
                mode = 0;
                fprintf(fp, "Lo mode");
            }
        }
        if(bit_is_clear(PIND, PD1)) {
            sw1_state = 1;            
            delay_ms(10);
        }
        if(sw1_state && bit_is_set(PIND, PD1)) {
            sw1_state = 0;
            if(cal_ok) {
                sbi(PORTD, PD5);
                delay_ms(10);
                measure();
                loop_until_bit_is_set(PIND, PD5);
                if(mode == 0) {
                    if(cnt <= ocnt_l)
                        cap = 0.0;
                    else 
                        cap = (cnt - ocnt_l) / (double)(refcnt_l - ocnt_l) * 0.0001;
                } else {
                    if(cnt <= ocnt_h)
                        cap = 0.0;
                    else
                        cap = (cnt - ocnt_h) / (double)(refcnt_h - ocnt_h) * 0.01;
                }
                lcd_write();
            } else {
                cal_err();
            }
        }
        delay_ms(10);
    }
}

LCD関連のライブラリは省略しています。


▲ページ Top へ...