AVRシリーズのADCを使ったことがある人は意外に誤差が大きいと思っているはずだ。ADC入力を短絡してもゼロにならず不安定だったりもする。原因の一つはノイズ誤差。特にMCU内蔵のADCというものはMCU自体がノイズ発生源であるためその影響が避けられないが、複数の方法を組み合わせることでノイズ誤差を可能な限り少なくし高精度化することは可能である。回路(PCB)設計以外で効果の大きい対策を順に紹介していこう。
■ 複数サンプリングし平均値を取る
ノイズは平均的にバラつく特性があるので平均値を取ることは効果がある。8-10個程度のサンプリングで効果はあるが必要なサンプリング・レートを満たすためにも可能な限り高速変換すべきである。
AVRの実用的な最大変換速度は2MHzである。他サイトの調査結果によると2MHzでは若干誤差が増えるような傾向が見られるが1MHz未満にしても実感できるほどの効果が感じられないため変換速度は1MHzが妥当に思える。
■ 良質な基準電圧を使う
AVRのADCは基準電圧として電源電圧/内部基準電圧/外部基準電圧の3種類が選択できるが、ノイズ順に並べると
電源電圧>内部基準電圧>[(良質な)外部基準電圧]
となる。ちなみに電源電圧は数ミリボルト~数10ミリボルトものノイズが乗っているので選択すべきではない。内部基準電圧であっても電源ノイズの影響は受けるため良質な外部基準電圧は必須である。
■ MCU動作ノイズを排除する
ADC変換時にMCU動作ノイズを排除するにはMCUを停止するしかない。そのためのスリープ命令が存在するので利用すべきである。利用可能なスリープモードをノイズ順に並べると
[SLEEP_MODE_IDLE]>SLEEP_MODE_ADC
となるが、SLEEP_MODE_ADCはタイマーも停止し時間が狂ってしまうので現実的な選択としてはSLEEP_MODE_IDLEしかないだろう。
下記はADCをスリープ起動するコードの抜粋である。変換中にタイマー割り込みが発生しスリープ復帰した場合は、そのタイミングにより、変換終了、再度の変換終了待ちスリープ、再度の変換開始スリープのどれかとなる。完全なコードとは言えないがこれ以上複雑にするメリットもないと思う。Simple is Best !!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <avr/io.h> #include <avr/sleep.h> #include <avr/interrupt.h> // reti()だけの割り込み処理 ISR(ADC_vect, ISR_NAKED) { reti(); } // ADC変換処理(ADCはセットアップ済みであること) void conversion(void) { ADCSRA |= _BV(ADIE); set_sleep_mode(SLEEP_MODE_IDLE); do { sleep_enable(); sleep_cpu(); sleep_disable(); } while (ADCSRA & _BV(ADSC)); ADCSRA &= ~_BV(ADIE); } |
■ デジタル回路からの影響を排除する
アナログ入力端子からデジタル回路を切り離すことが出来る。デジタル回路へ流れる微電流をカットしデジタル回路からの影響を排除すべきである。
■ ゲイン・アンプの出力ドリフトを確認する
ゲイン・アンプ内蔵の場合、高ゲイン時に出力ドリフトが発生するものが個体によってはあるようだ。アナログ入力短絡時に安定はしているがゼロにならないときはドリフトの可能性がある。ノイズとは関係ないが要確認項目である。
■ 最後の手段(笑)
どうやってもLSBが不安定な場合はLSBをマスクしてしまおう。分解能は犠牲になるが...
以上の組み合わせにより、外付けADCと比べても遜色のない精度まで高めることが出来るはず。参考まで。