AD変換 / 入力電圧のスケーリング、スリープモード、オーバーサンプリング

pp.245-254

0~15Vを簡易的に抵抗分圧で0~5Vにスケーリングし、それをAD変換する。併せてスリープモードのひとつであるADC Noise Reductionモードと16倍オーバーサンプリングとを試す。

AVRのADCは10ビット分解能なので16倍オーバーサンプリングによって12ビット分解能が得られる(ただし0~4095ではなくて0~4092 (= 0b111111111100))。テキストは分圧比と基準電圧値とを基にキャリブレーションして0~15Vの電圧値を表示しているが、ここではAD変換値を取得するだけにする(だから結局スケーリングする意味がなかった)。

set_sleep_mode(SLEEP_MODE_ADC);SMCR |= (1 << SM0);を実行するのと同じ。

サンプリングは函数からのコマンドで実行され、かつADCが完了するまでスリープしたままなので、ISRは不要であるが、ADC完了割り込みでスリープモードから復帰するしくみになっているので、コードはどこかへ進まなければならない。そのためだけにEMPTY_INTERRUPT(ADC_vect);がある。これは、元の場所へ戻すためだけのフェイク割り込みを生成するようGCCコンパライへ命令する働きをする。

ADCSRA |= (1 << ADIE);
ADCSRAはADC Control and Status Register Aの略。ADIEはADC Interrupt Enableの略。このビットに1を書き込んで、SREG (Status Register)のIビット(Global Interrupt Enable)がセットされたときに、ADC Conversion Complete Interrupt (AD変換完了割り込み)が発生する。

f:id:ti-nspire:20191009043416p:plain:h250

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
extern "C" {
    #include "USART.h"
}

void initADC(){
    ADMUX  |= ((ADMUX & 0xF0) | PC5);                       // チャンネル選択ビットを一旦全部クリアしてから目的のチャンネルをセットする。
    ADMUX  |= (1 << REFS0);                                 // AVCCを基準電圧として使う。
    ADCSRA |= ((1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0)); // ここではADCクロックプリスケーラーを128分周にセットすることにする。
    ADCSRA |= (1 << ADEN);                                  // ADCを有効化する。
}
void setupADCSleepmode(){
    set_sleep_mode(SLEEP_MODE_ADC);
    ADCSRA |= (1 << ADIE);
    sei();
}

EMPTY_INTERRUPT(ADC_vect);

uint16_t oversample16x(){
    uint16_t oversampledValue = 0;
    for(int i=0; i<16; i++){
        sleep_mode();
        oversampledValue += ADC;
    }
    return (oversampledValue >> 2);
}

int main(){
    uint16_t ad_val;
   
    initUSART();
    initADC();
    setupADCSleepmode();

    while(1){
        ad_val = oversample16x();
        
        printString("AD ");
        printWord(ad_val);
        printString("\r\n");
        
        _delay_ms(500);
    }
    
    return 0;
}