最終更新時間:2008年08月30日 11時02分48秒
[公開:any]
[電子工作/AVR]
[電子工作/測定器]
[AVR,ATmega88,容量計,LCメーター,測定器]
LCメーターでは、約0.2uF以上のコンデンサや極性のあるコンデンサの容量を測定することは出来ないので、「コンデンサ容量計の実験」で確認したコンデンサ容量計を製作します。
測定範囲は、0〜9999uFまでを目標としますが、大きな値(どの辺りから怪しくなるかはよくわかりませんが)は、測定値が怪しくなると思われます。なお、大きな値の電解コンデンサは、本体に容量表示があるので測定する必要性は低いのですが、古いコンデンサの「容量抜け」の確認程度は出来ると思っています。
コンデンサの容量の測定方法は、コンデンサの充電時の過渡特性を利用したものです。原理そのものは、「コンデンサ 過渡特性」をキーワードにインターネットで検索してください。
この特性を利用した容量計の製作は、
で、詳しく解説されています。
henteko.orgでも基本的には、このサイトを参考に製作しましたが、精密測定に向かない悪い例として紹介させていただきます。^^;
いつものようにブレッドボードで開発します。
ELMさんのコンテンツに、”浮遊容量を減らす(または安定させる)のが精密測定のコツ”とあります。・・・この点、ブレッドボードは、まったく向いていません。^^;
ですが、とりあえず測定するコンデンサに流れる充電電流を決定する電流制限抵抗や測定開始と終了を決定するスレッショルド電圧を決めるべくいろいろとテストを重ねました。
前回の実験では、小さな容量のコンデンサの充電には、高速な充電時間を確保するために、電流制限抵抗を4.7KΩと小さな値にしたのですが、基準とする校正用コンデンサ100pFまたは1000pFから得られた解像度(容量あたりのAVRタイマー値)が十分な値(小さな値しか得られない)でないため、校正用コンデンサよりも小さな容量を測定した場合に誤差が大きくなるという問題点がありました。
今回は、測定するコンデンサの容量に応じて手動で2段のレンジ切替で対応します。小さな容量のコンデンサを測定するLOWレンジは、大きな電流制限抵抗とし、逆に大きな容量のコンデンサを測定するHIGHレンジは、出来るだけ測定時間を短くするために小さな電流制限抵抗となるようにしました。
2段のレンジそれぞれで校正用コンデンサを使用します。使用するコンデンサは、ポリプロピレンフィルムコンデンサで誤差1%のものを用意しました。浮遊容量的にはデメリットがありますが、リレーによる切替回路で校正用コンデンサを切り替える自動校正方式とします。(・・としてしまいました。^^;)
最終的な回路図を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メーターと近似するようです。
大きな容量のコンデンサも測定してみました。
左上から順に、10uF積層セラミック、100uF電解、1000uF電解、3300uF電解の測定です。3300uFは、リードが太くてソケットに刺さらないため、手で押し付けながら測定しますが、時間は10秒程度かかるためちょっと面倒です。
同じように透明ケースに収容した測定器を並べてみました。こうなると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 |
#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関連のライブラリは省略しています。