ESP32の低レベルGPIOアクセス その3 analogRead()

概要

前回はpinMode()でしたので、今回はアナログ入力をやってみたいと思います。

解析してからわかったのですが、2020年1月に大幅に変更が入っていて、次のリリースからESP-IDFの関数を呼び出す感じになりそうです。ただし、今回のバージョンの方が直接レジスタアドレスを操作しているので、ESP32の内部動作理解には適していると思います。

analogRead() アナログ入力

uint16_t IRAM_ATTR __analogRead(uint8_t pin)
{
    if(!__adcAttachPin(pin) || !__adcStart(pin)){
        return 0;
    }
    return __adcEnd(pin);
}

PINを割り当てて、ADCを開始して、終了って流れみたいです。

__adcAttachPin() ADCピン割当

bool IRAM_ATTR __adcAttachPin(uint8_t pin){

    int8_t channel = digitalPinToAnalogChannel(pin);
    if(channel < 0){
        return false;//not adc pin
    }

    int8_t pad = digitalPinToTouchChannel(pin);
    if(pad >= 0){
        uint32_t touch = READ_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG);
        if(touch & (1 << pad)){
            touch &= ~((1 << (pad + SENS_TOUCH_PAD_OUTEN2_S))
                    | (1 << (pad + SENS_TOUCH_PAD_OUTEN1_S))
                    | (1 << (pad + SENS_TOUCH_PAD_WORKEN_S)));
            WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, touch);
        }
    } else if(pin == 25){
        CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC | RTC_IO_PDAC1_DAC_XPD_FORCE);//stop dac1
    } else if(pin == 26){
        CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_XPD_DAC | RTC_IO_PDAC2_DAC_XPD_FORCE);//stop dac2
    }

    pinMode(pin, ANALOG);

    __analogInit();
    return true;
}

こちらはそこそこ処理が多いですね。

チャンネル取得

    int8_t channel = digitalPinToAnalogChannel(pin);
    if(channel < 0){
        return false;//not adc pin
    }

ここではアナログ入力できるかを確認していますね。RTCのチャンネル番号を取得しています。

#define digitalPinToAnalogChannel(pin)  (((pin) < 40)?esp32_gpioMux[(pin)].adc:-1)

こんな感じで構造体から取得しているだけでした。

タッチセンサー対応

    int8_t pad = digitalPinToTouchChannel(pin);
    if(pad >= 0){
        uint32_t touch = READ_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG);
        if(touch & (1 << pad)){
            touch &= ~((1 << (pad + SENS_TOUCH_PAD_OUTEN2_S))
                    | (1 << (pad + SENS_TOUCH_PAD_OUTEN1_S))
                    | (1 << (pad + SENS_TOUCH_PAD_WORKEN_S)));
            WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, touch);
        }
    } else if(pin == 25){
        CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC | RTC_IO_PDAC1_DAC_XPD_FORCE);//stop dac1
    } else if(pin == 26){
        CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_XPD_DAC | RTC_IO_PDAC2_DAC_XPD_FORCE);//stop dac2
    }

パット見た限りタッチセンサーを無効化しているのかな?

#define digitalPinToTouchChannel(pin)   (((pin) < 40)?esp32_gpioMux[(pin)].touch:-1)

タッチのチャンネル取得して、タッチがある場所とGPIO25と26だけ特殊処理していますね。

    if(pad >= 0){
        uint32_t touch = READ_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG);
        if(touch & (1 << pad)){
            touch &= ~((1 << (pad + SENS_TOUCH_PAD_OUTEN2_S))
                    | (1 << (pad + SENS_TOUCH_PAD_OUTEN1_S))
                    | (1 << (pad + SENS_TOUCH_PAD_WORKEN_S)));
            WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, touch);
        }

READ_PERI_REG()ははじめてできていました。

//read value from register
#define READ_PERI_REG(addr) ({                                 \
            ASSERT_IF_DPORT_REG((addr), READ_PERI_REG);        \
            (*((volatile uint32_t *)ETS_UNCACHED_ADDR(addr))); \
        })

ASSERT_IF_DPORT_REG()を呼び出してからETS_UNCACHED_ADDR()で取得しているのかな?

#if !defined( BOOTLOADER_BUILD ) && defined( CONFIG_ESP32_DPORT_WORKAROUND ) && defined( ESP_PLATFORM )
#define ASSERT_IF_DPORT_REG(_r, OP)  TRY_STATIC_ASSERT(!IS_DPORT_REG(_r), (Cannot use OP for DPORT registers use DPORT_##OP));
#else
#define ASSERT_IF_DPORT_REG(_r, OP)
#endif

これはブートローダービルド中のときだけ表示される関数ですね。通常時には意味はなさそうです。

//Registers Operation {{
#define ETS_UNCACHED_ADDR(addr) (addr)
#define ETS_CACHED_ADDR(addr) (addr)

んー、昔は意味があったのですが中身が空なので、意味はなさそうですね。単純にレジスタアドレスから取得してきている処理でした。

読み込んでいるのはSENS_SAR_TOUCH_ENABLE_REG(0x3FF4888C)で、上記のレジスタでした。タッチセンサーの割り込みと利用中かのレジスタみたいですね。

        if(touch & (1 << pad)){
            touch &= ~((1 << (pad + SENS_TOUCH_PAD_OUTEN2_S))
                    | (1 << (pad + SENS_TOUCH_PAD_OUTEN1_S))
                    | (1 << (pad + SENS_TOUCH_PAD_WORKEN_S)));
            WRITE_PERI_REG(SENS_SAR_TOUCH_ENABLE_REG, touch);
        }

if文で利用中だった場合には、全部のフラグを落とす処理をしていました。

#define SENS_TOUCH_PAD_OUTEN1_S  20
#define SENS_TOUCH_PAD_OUTEN2_S  10
#define SENS_TOUCH_PAD_WORKEN_S  0

上記でビットシフトをしているのですが、if文もSENS_TOUCH_PAD_WORKEN_Sを足した方がわかりやすいと思うんですけれどね。

    } else if(pin == 25){
        CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC1_REG, RTC_IO_PDAC1_XPD_DAC | RTC_IO_PDAC1_DAC_XPD_FORCE);//stop dac1
    } else if(pin == 26){
        CLEAR_PERI_REG_MASK(RTC_IO_PAD_DAC2_REG, RTC_IO_PDAC2_XPD_DAC | RTC_IO_PDAC2_DAC_XPD_FORCE);//stop dac2
    }

GPIO25と26の特殊処理をみてみます。

//clear bits of register controlled by mask
#define CLEAR_PERI_REG_MASK(reg, mask) ({                          \
            ASSERT_IF_DPORT_REG((reg), CLEAR_PERI_REG_MASK);       \
            WRITE_PERI_REG((reg), (READ_PERI_REG(reg)&(~(mask)))); \
        })

マスクで渡したビットだけ落とすマクロを使っていました。

設定しているのは上記レジスタでDAC1とDAC2で同じ構成でした。RTC_IO_PDAC1_XPD_DAC | RTC_IO_PDAC1_DAC_XPD_FORCEのビットを落としていました。両方ともパワーオンのときに1になるので、DACを止めている処理になります。

ピンモード設定

    pinMode(pin, ANALOG);

ここでpinMode()を呼び出していますね。だったらタッチセンサーとDACを止めるのもpinMode()の中でやればいいのにって思います。。。

ピン初期化

__analogInit();

こちらは次の項目で説明します。

__analogInit() アナログ入力初期化

void IRAM_ATTR __analogInit(){
    static bool initialized = false;
    if(initialized){
        return;
    }

    __analogSetAttenuation(__analogAttenuation);
    __analogSetCycles(__analogCycles);
    __analogSetSamples(__analogSamples + 1);//in samples
    __analogSetClockDiv(__analogClockDiv);
    __analogSetWidth(__analogWidth + 9);//in bits

    SET_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR1_DATA_INV);
    SET_PERI_REG_MASK(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_DATA_INV);

    SET_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_MEAS1_START_FORCE_M); //SAR ADC1 controller (in RTC) is started by SW
    SET_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_SAR1_EN_PAD_FORCE_M); //SAR ADC1 pad enable bitmap is controlled by SW
    SET_PERI_REG_MASK(SENS_SAR_MEAS_START2_REG, SENS_MEAS2_START_FORCE_M); //SAR ADC2 controller (in RTC) is started by SW
    SET_PERI_REG_MASK(SENS_SAR_MEAS_START2_REG, SENS_SAR2_EN_PAD_FORCE_M); //SAR ADC2 pad enable bitmap is controlled by SW

    CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_M); //force XPD_SAR=0, use XPD_FSM
    SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_AMP, 0x2, SENS_FORCE_XPD_AMP_S); //force XPD_AMP=0

    CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_CTRL_REG, 0xfff << SENS_AMP_RST_FB_FSM_S);  //clear FSM
    SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT1_REG, SENS_SAR_AMP_WAIT1, 0x1, SENS_SAR_AMP_WAIT1_S);
    SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT1_REG, SENS_SAR_AMP_WAIT2, 0x1, SENS_SAR_AMP_WAIT2_S);
    SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_SAR_AMP_WAIT3, 0x1, SENS_SAR_AMP_WAIT3_S);
    while (GET_PERI_REG_BITS2(SENS_SAR_SLAVE_ADDR1_REG, 0x7, SENS_MEAS_STATUS_S) != 0); //wait det_fsm==

    initialized = true;
}

アナログの初期化関数ですね。引数にPINがないので全体初期化で、一度初期化だけ初期化すればよいようです。

多重初期化チェック

    static bool initialized = false;
    if(initialized){
        return;
    }

staticで初期化済みフラグを保存して、最初の一回だけ初期化処理をおこなうようにしてあります。

各種初期化

    __analogSetAttenuation(__analogAttenuation);
    __analogSetCycles(__analogCycles);
    __analogSetSamples(__analogSamples + 1);//in samples
    __analogSetClockDiv(__analogClockDiv);
    __analogSetWidth(__analogWidth + 9);//in bits

ここは多いので、別項目で見ていきます。アンダーバーの無い同名の関数と同じ初期化をしているようです。

信号反転

    SET_PERI_REG_MASK(SENS_SAR_READ_CTRL_REG, SENS_SAR1_DATA_INV);
    SET_PERI_REG_MASK(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_DATA_INV);

データの信号を反転しているようです。

//set bits of register controlled by mask
#define SET_PERI_REG_MASK(reg, mask) ({                         \
            ASSERT_IF_DPORT_REG((reg), SET_PERI_REG_MASK);      \
            WRITE_PERI_REG((reg), (READ_PERI_REG(reg)|(mask))); \
        })

上記のマクロはマスクのビットをセットする処理みたいでした。

SAR1とSAR2で同じ構造のレジスタがありました。セットしているのは28ビット目のデータを反転するフラグです。

ADC開始

    SET_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_MEAS1_START_FORCE_M); //SAR ADC1 controller (in RTC) is started by SW
    SET_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_SAR1_EN_PAD_FORCE_M); //SAR ADC1 pad enable bitmap is controlled by SW
    SET_PERI_REG_MASK(SENS_SAR_MEAS_START2_REG, SENS_MEAS2_START_FORCE_M); //SAR ADC2 controller (in RTC) is started by SW
    SET_PERI_REG_MASK(SENS_SAR_MEAS_START2_REG, SENS_SAR2_EN_PAD_FORCE_M); //SAR ADC2 pad enable bitmap is controlled by SW

ADC1と2を使えるように初期化しているようです。

上記がADC1のレジスタです。SENS_MEAS1_START_FORCE_Mでスタンバイにして、SENS_SAR1_EN_PAD_FORCE_MでULPではなく、SW(ソフトウエア?)側で使えるように設定しています。同じ構造のADC2がありました。

同じレジスタなので、1行にすればいいと思いますがわかりやすさのためか、2行になっていました。

センサー初期化

    CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_SAR_M); //force XPD_SAR=0, use XPD_FSM
    SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_FORCE_XPD_AMP, 0x2, SENS_FORCE_XPD_AMP_S); //force XPD_AMP=0

ここでアクセスしている0x3ff4880cのレジスタアドレスについて、資料がありませんでした。

/* SENS_FORCE_XPD_SAR : R/W ;bitpos:[19:18] ;default: 2'd0 ; */
/*description: */
#define SENS_FORCE_XPD_SAR  0x00000003
#define SENS_FORCE_XPD_SAR_M  ((SENS_FORCE_XPD_SAR_V)<<(SENS_FORCE_XPD_SAR_S))
#define SENS_FORCE_XPD_SAR_V  0x3
#define SENS_FORCE_XPD_SAR_S  18
#define SENS_FORCE_XPD_SAR_SW_M (BIT1)
#define SENS_FORCE_XPD_SAR_FSM 0 // Use FSM to control power down
#define SENS_FORCE_XPD_SAR_PD  2 // Force power down
#define SENS_FORCE_XPD_SAR_PU  3 // Force power up
/* SENS_FORCE_XPD_AMP : R/W ;bitpos:[17:16] ;default: 2'd0 ; */
/*description: */
#define SENS_FORCE_XPD_AMP  0x00000003
#define SENS_FORCE_XPD_AMP_M  ((SENS_FORCE_XPD_AMP_V)<<(SENS_FORCE_XPD_AMP_S))
#define SENS_FORCE_XPD_AMP_V  0x3
#define SENS_FORCE_XPD_AMP_S  16
#define SENS_FORCE_XPD_AMP_FSM 0 // Use FSM to control power down
#define SENS_FORCE_XPD_AMP_PD  2 // Force power down
#define SENS_FORCE_XPD_AMP_PU  3 // Force power up

ソースを見る限り18ビット目からの2ビット(0b11)をクリアして、16ビット目に0b10をセットしていました。コメントからみるとFSMを使ってSARをパワーダウンして、アンプもパワーダウンしていました。

とりあえず未使用にして省電力にしているのかもしれません。ESP-IDFだとブートローダで初期化していました。

謎の初期化

    CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_CTRL_REG, 0xfff << SENS_AMP_RST_FB_FSM_S);  //clear FSM
    SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT1_REG, SENS_SAR_AMP_WAIT1, 0x1, SENS_SAR_AMP_WAIT1_S);
    SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT1_REG, SENS_SAR_AMP_WAIT2, 0x1, SENS_SAR_AMP_WAIT2_S);
    SET_PERI_REG_BITS(SENS_SAR_MEAS_WAIT2_REG, SENS_SAR_AMP_WAIT3, 0x1, SENS_SAR_AMP_WAIT3_S);

上4行も資料がありませんでした。この4行はESP-IDFのコードでも使われていないので設定しなくても動きそうなきがします。

初期化待ち

    while (GET_PERI_REG_BITS2(SENS_SAR_SLAVE_ADDR1_REG, 0x7, SENS_MEAS_STATUS_S) != 0); //wait det_fsm==

初期化待ちをしている感じです。

//get field of register
#define GET_PERI_REG_BITS2(reg, mask,shift) ({              \
            ASSERT_IF_DPORT_REG((reg), GET_PERI_REG_BITS2); \
            ((READ_PERI_REG(reg)>>(shift))&(mask));         \
        })

GET_PERI_REG_BITS2()はビットシフトして、マスクしたビットを取得するマクロでした。

取得しているレジスタはここで、reservedの22ビットから3ビット分を取得しています。こちらの処理もESP-IDFではなかったので、なくても動く処理かもしれません。

初期化完了

    initialized = true;

以上にて初期化が終わりました。謎の初期化処理はリリース前の最新版ソースではごっそりなくなっていました。

__analogSetAttenuation() 減衰率設定

ESP32のADCは0Vから1.1Vまでしか計測できません。そのため減衰器を使って、電圧を下げてから測定しています。

減衰率の設定

宣言減衰db減衰率減衰前(入力)減衰後
ADC_0db0db1 : 10.9 V0.90 V
ADC_2_5db2.5db約1 : 1.341.2 V0.90 V
ADC_6db6db約1 : 21.8 V0.90 V
ADC_11db11db約1 : 3.63.3 V0.93 V

上記の指定ができます。デフォルトは11dbで、3.3Vの入力が約0.92Vに減衰されますので、ESP32の1.1VまでのADCでも測定することができます。

とはいえ、0.9VぐらいがADCのマックスである4095に設定されているようです。3.6Vを入力すると内部で1V程度になりますが、同じく4095ですので3.3Vが最大値となります。

実装

void __analogSetAttenuation(adc_attenuation_t attenuation)
{
    __analogAttenuation = attenuation & 3;
    uint32_t att_data = 0;
    int i = 10;
    while(i--){
        att_data |= __analogAttenuation << (i * 2);
    }
    WRITE_PERI_REG(SENS_SAR_ATTEN1_REG, att_data & 0xFFFF);//ADC1 has 8 channels
    WRITE_PERI_REG(SENS_SAR_ATTEN2_REG, att_data);
}

減衰率をADCに設定しています。

2ビット単位でADCのチャンネルが16個分あるようです。実際にはADC1は0から7チャンネルまで、ADC2は0から9チャンネルまでですので、10個分のチャンネルを設定していました。

データシートは2ビット単位で全部書いてもらいたいですが、面倒だったんでしょうね。

__analogSetCycles() 読み込みサイクル設定

ADCの読み込みサイクル数を設定します。ただしESP-IDFでは設定をしておらず、次期バージョンでは廃止される関数です。

void __analogSetCycles(uint8_t cycles){
    __analogCycles = cycles;
    SET_PERI_REG_BITS(SENS_SAR_READ_CTRL_REG, SENS_SAR1_SAMPLE_CYCLE, __analogCycles, SENS_SAR1_SAMPLE_CYCLE_S);
    SET_PERI_REG_BITS(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_SAMPLE_CYCLE, __analogCycles, SENS_SAR2_SAMPLE_CYCLE_S);
}

レジスタアドレスに数値をセットしているだけですね。

ESP32のデフォルトは9サイクルです。

static uint8_t __analogCycles = 8;

Arduino Coreだと8が設定されていました。次回のバージョンからは無指定ですので9固定になるはずです。

__analogSetSamples() サンプル数設定

複数回ADCを実行して、平均を取得する設定です。ただし、次期バージョンではなくなるので、本当に実行しているのかは謎です。

void __analogSetSamples(uint8_t samples){
    if(!samples){
        return;
    }
    __analogSamples = samples - 1;
    SET_PERI_REG_BITS(SENS_SAR_READ_CTRL_REG, SENS_SAR1_SAMPLE_NUM, __analogSamples, SENS_SAR1_SAMPLE_NUM_S);
    SET_PERI_REG_BITS(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_SAMPLE_NUM, __analogSamples, SENS_SAR2_SAMPLE_NUM_S);
}

上記に書き込んでいます。アドレスはサイクルと一緒ですね。

#define SENS_SAR1_SAMPLE_NUM_S  19

19ビット目から書き込んでいますが、データシート上はreservedで未定義です。1-256回を指定できるようですが、利用しないほうが良さそうです。

__analogSetClockDiv() 分周を設定

ADCのクロックを指定します。1-255までを指定できますが、1以外で使うことはないようです。ESP-IDFでは無指定のようでした。

void __analogSetClockDiv(uint8_t clockDiv){
    if(!clockDiv){
        return;
    }
    __analogClockDiv = clockDiv;
    SET_PERI_REG_BITS(SENS_SAR_READ_CTRL_REG, SENS_SAR1_CLK_DIV, __analogClockDiv, SENS_SAR1_CLK_DIV_S);
    SET_PERI_REG_BITS(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_CLK_DIV, __analogClockDiv, SENS_SAR2_CLK_DIV_S);
}

1を指定することが多いですが、データシートを見る限り2がデフォルトみたいです。ESP-IDFでは普通は設定しないので、Arduino CoreとESP-IDFで設定値が違いそうです。

__analogSetWidth() ビットレート指定

ESP32のADCは12ビットの解像度をもっていますが、9-12ビットを指定することができます。一般的に指定する必要はないですが、Arduino UNOに合わせて10ビットにすると0-1023までの返却になります。ただし、この関数で設定しても戻り値は12ビットになってしまうので、analogReadResolution()関数で変更をしないと、あまり意味はないです。

void __analogSetWidth(uint8_t bits){
    if(bits < 9){
        bits = 9;
    } else if(bits > 12){
        bits = 12;
    }
    __analogReturnedWidth = bits;
    __analogWidth = bits - 9;
    SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_SAR1_BIT_WIDTH, __analogWidth, SENS_SAR1_BIT_WIDTH_S);
    SET_PERI_REG_BITS(SENS_SAR_READ_CTRL_REG, SENS_SAR1_SAMPLE_BIT, __analogWidth, SENS_SAR1_SAMPLE_BIT_S);

    SET_PERI_REG_BITS(SENS_SAR_START_FORCE_REG, SENS_SAR2_BIT_WIDTH, __analogWidth, SENS_SAR2_BIT_WIDTH_S);
    SET_PERI_REG_BITS(SENS_SAR_READ_CTRL2_REG, SENS_SAR2_SAMPLE_BIT, __analogWidth, SENS_SAR2_SAMPLE_BIT_S);
}

レジスタアドレスに書き込んでいるだけですね。

上記のBIT_WIDTHにビット数を設定しているのと、いつものSENS_SAR_READ_CTRL_REGレジスタのSAMPLE_BITに同じビット数をセットしています。

__adcStart() ADC測定開始

bool IRAM_ATTR __adcStart(uint8_t pin){

    int8_t channel = digitalPinToAnalogChannel(pin);
    if(channel < 0){
        return false;//not adc pin
    }

    if(channel > 9){
        channel -= 10;
        CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START2_REG, SENS_MEAS2_START_SAR_M);
        SET_PERI_REG_BITS(SENS_SAR_MEAS_START2_REG, SENS_SAR2_EN_PAD, (1 << channel), SENS_SAR2_EN_PAD_S);
        SET_PERI_REG_MASK(SENS_SAR_MEAS_START2_REG, SENS_MEAS2_START_SAR_M);
    } else {
        CLEAR_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_MEAS1_START_SAR_M);
        SET_PERI_REG_BITS(SENS_SAR_MEAS_START1_REG, SENS_SAR1_EN_PAD, (1 << channel), SENS_SAR1_EN_PAD_S);
        SET_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_MEAS1_START_SAR_M);
    }
    return true;
}

digitalPinToAnalogChannel()関数でADCのチャンネルを取得しています。ADC1はそのままのチャンネルで、ADC2は10を足したチャンネルが取得されます。

ADC1のレジスタアドレスを確認してみます。

ADC2も同じ構成です。SENS_MEAS1_START_SARのフラグをクリアしています。設定前に無効にしているようです。その後にSENS_SAR1_EN_PADで対象ADCチャンネルのビットを立てています。設定が完了したので、SENS_MEAS1_START_SARをセットして測定を開始しています。

__adcEnd() ADC測定結果取得

uint16_t IRAM_ATTR __adcEnd(uint8_t pin)
{

    uint16_t value = 0;
    int8_t channel = digitalPinToAnalogChannel(pin);
    if(channel < 0){
        return 0;//not adc pin
    }
    if(channel > 7){
        while (GET_PERI_REG_MASK(SENS_SAR_MEAS_START2_REG, SENS_MEAS2_DONE_SAR) == 0); //wait for conversion
        value = GET_PERI_REG_BITS2(SENS_SAR_MEAS_START2_REG, SENS_MEAS2_DATA_SAR, SENS_MEAS2_DATA_SAR_S);
    } else {
        while (GET_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_MEAS1_DONE_SAR) == 0); //wait for conversion
        value = GET_PERI_REG_BITS2(SENS_SAR_MEAS_START1_REG, SENS_MEAS1_DATA_SAR, SENS_MEAS1_DATA_SAR_S);
    }

    // Shift result if necessary
    uint8_t from = __analogWidth + 9;
    if (from == __analogReturnedWidth) {
        return value;
    }
    if (from > __analogReturnedWidth) {
        return value >> (from - __analogReturnedWidth);
    }
    return value << (__analogReturnedWidth - from);
}

データを取得する部分と、ビットシフト部分にわかれています。ADCチャネルを取得する場所はほかと共通なので省略します。

        while (GET_PERI_REG_MASK(SENS_SAR_MEAS_START1_REG, SENS_MEAS1_DONE_SAR) == 0); //wait for conversion
        value = GET_PERI_REG_BITS2(SENS_SAR_MEAS_START1_REG, SENS_MEAS1_DATA_SAR, SENS_MEAS1_DATA_SAR_S);

上記がADC1の場合の処理です。while文でSENS_SAR_MEAS_START1_REGレジスタのSENS_MEAS1_DONEが1になるまでWaitしています。このフラグはADCのデータ取得が完了すると1になるようです。adcBusy()関数で同じ処理が実装されています。

その後にSENS_MEAS1_DATAに入っている実際のデータを取得しています。ADCは結果のデータ部分を使いまわしているので、同時実行は難しそうです。ADC1とADC2であれば同時取得できるかもしれません。

    // Shift result if necessary
    uint8_t from = __analogWidth + 9;
    if (from == __analogReturnedWidth) {
        return value;
    }
    if (from > __analogReturnedWidth) {
        return value >> (from - __analogReturnedWidth);
    }
    return value << (__analogReturnedWidth - from);

データシフト部分はADCのビットと、返却用のビット数が違う場合にシフトして返却をしています。

__analogReadResolution() ADC返却ビット数設定

void __analogReadResolution(uint8_t bits)
{
    if(!bits || bits > 16){
        return;
    }
    __analogSetWidth(bits);         // hadware from 9 to 12
    __analogReturnedWidth = bits;   // software from 1 to 16
}

この関数で返却値とADCのビット数の両方を設定しています。つまり__analogSetWidth()を呼び出しても返却されるビット数は変わらないので、こちらを呼び出す必要があります。

資料

英語の資料ですが、上記を参考にしました。

まとめ

かなり処理が多かったですが、いままであまりわかっていなかったADC周りの理解がすすんだ気がします。次期バージョンからはESP-IDFのラッパーに変わるので、Arduino Coreのコードを解析して、さらにESP-IDFのソースをみる必要がありそうです。

番外編

コメントする

メールアドレスが公開されることはありません。

管理者承認後にページに追加されます。公開されたくない相談はその旨本文に記載するかTwitterなどでDM投げてください。またスパム対策として、日本語が含まれない投稿は無視されますのでご注意ください。