Tech Notes

AVRマイコンでADCを自動実行する

最近久しぶりに電子工作をやっている。AVRマイコンを触っているのだが、AD変換器に自動実行機能があるのを今更にして知ったのでそれについてまとめる。

1つ大きな注意点も見つけたのでそれも含めてメモ。

AVRのADCの使い方

先にADCの大雑把な使い方についてまとめておく。

主に扱うレジスタは以下の4つだ。

ADMUX: ポート選択

  • MUX: ADC入力とするピンが選べる。
  • REFS: ADC入力の電圧基準とする電源を選べる。

ADCSRA: 設定用レジスタA

  • ADEN: ADCの有効化。絶対必要。
  • ADSC: AD変換を開始するビット。これを1にすると変換開始。
  • ADATE: ADC自動実行の有効化。この記事のメイン。
  • ADIF: AD変換が終了したサイン。変換終了時に自動で1になる。
  • ADIE: ADC終了時割り込みの有効化。
  • ADPS: ADC用のクロックのプリスケーラ指定。
    • ADC用クロックが速すぎると精度に支障を来たすのでほどほどの速さにする。最高精度を出すには200kHz以下が理想らしいので、例えばメインクロック1MHzなら8分周で125kHzに落としたりするとよいと思われる。

ADCSRB: 設定用レジスタB

  • ADLAR: 出力ビット右詰め(2bit+8bit)/左詰め(8bit+2bit)を選べる。
    • ATTinyではADCSRBレジスタに含まれるが、ATMegaシリーズではADMUXレジスタの方に入っている。
  • ADTS: ADC自動実行のトリガ選択。

ADC: 結果の出力

  • 16bitレジスタ。結果(10bit)がここに格納される。

またおまけとして、DIDRレジスタというものもある。これはデジタル入力の機能を切ってしまうことで少し電力の節約になるらしい。

AD変換完了の検知

AD変換は一瞬で終わる訳ではなく、少し待つ必要がある。この検知の方法は主に2つある。

  • ADIFビットの監視
  • 割り込みの使用

AD変換が完了するとADCSRAレジスタのADIFビットが1になるので、これをwhile文で見張る方法が1つある。

do { } while(!(ADCSRA & _BV(ADIF)));  // 1になるまでループ

loop_until_bit_is_set(ADCSRA,ADIF);  // このようにも書ける

そしてもう1つ、ADIEビットを1にしていれば割り込みで通知してくれるので、これを利用する方法がある。

ISR(ADC_vect) {
    // AD変換完了時の処理
}

用途に応じて使い分ければよい。

ADCの自動実行(Auto Trigger)

ネットでよく見るコードだとADSCを1にしてAD変換開始→ADIF監視で終了を検知、というやり方が多い。

一方で、AVRにはADCの自動実行機能というものがある。これはADSCビットを手動で1にするのではなく、タイマなどをトリガにして自動でAD変換を実行できるという機能だ。

具体的には以下のものがトリガとして使える。

  • タイマ0のオーバーフロー
  • タイマ0の比較一致A
  • タイマ1のオーバーフロー
  • タイマ1の比較一致B
  • タイマ1の入力キャプチャ
  • 外部割込み
  • コンパレータ出力

使用するトリガはADCSRBレジスタのADTSビットで選択できる。

使い方

データシートを読む限り有効化の方法はシンプルで、単純にADCSRAADATEビットを1にするだけだ。他は通常のADCと変わらない。

以下は内臓8MHzクロック駆動のATTiny44で、ADCを5kHzの周期で自動実行するコードである。

ADMUX = 0b10000001U;  // ADC: PA1
ADCSRA = 0b10101110U; // ADC有効, 自動実行有効, 割り込み有効, 64分周
ADCSRB = 0b00000011U; // タイマ0の比較一致Aをトリガとする

// タイマ0を5kHzで駆動
TCCR0A = 0b00000010U; // OCR0Aと比較一致でリセット
OCR0A = 200U;
TCCR0B = 0b00000010U; // タイマクロック8分周

AD変換にはADC用クロックで13クロックが必要とされており、あまり高速に自動実行すると恐らく問題が起きるのでそこは注意。例えば上記のコードであれば、8MHzを64分周→ADC125kHzクロックで、それの13クロックだからおよそ9kSPSが限度になる。5kHzはそれを下回るので問題ない。

で、ここからが本題だ

ADC自動実行時の変換完了割り込みについて

割り込みでADCの値を取ってみようとしたところ、不思議なことが起こった

ISR(ADC_vect) {
    // AD変換完了時の処理
}

これが1回しか呼ばれないのである。データシートを読んでも原因が分からず正直意味が分からないのだが、以下の方法でなぜか解決した。

ADMUX = 0b10000001U;  // ADC: PA1
ADCSRA = 0b10101110U; // ADC有効, 自動実行有効, 割り込み有効, 64分周
ADCSRB = 0b00000011U; // タイマ0の比較一致Aをトリガとする

TCCR0A = 0b00000010U; // OCR0Aと比較一致でリセット
OCR0A = 200U;
TCCR0B = 0b00000010U; // タイマクロック8分周

// タイマ0の比較一致割り込みを許可
TIMSK0 = 0b00000010U;

そう、対応するタイマ割り込みを許可するとなぜか割り込みがちゃんと呼ばれるのである。

これがタイマ0の比較一致A以外をトリガにしてもそうなのかは実験していないし、ATTiny44特有の現象なのかどうかも分からない。またAD変換は実行されているのに割り込みが呼ばれていないのか、あるいはAD変換自体が行われていないのかもよく分かっていない。

ただこのどうしようもない現象でしばらく詰まってしまったので、取り急ぎ記事として公開する。

何かわかったことがあれば追記する。

コメント