概要
前回は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_0db | 0db | 1 : 1 | 0.9 V | 0.90 V |
ADC_2_5db | 2.5db | 約1 : 1.34 | 1.2 V | 0.90 V |
ADC_6db | 6db | 約1 : 2 | 1.8 V | 0.90 V |
ADC_11db | 11db | 約1 : 3.6 | 3.3 V | 0.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()を呼び出しても返却されるビット数は変わらないので、こちらを呼び出す必要があります。
資料
- https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/adc.html
- https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf
英語の資料ですが、上記を参考にしました。
まとめ
かなり処理が多かったですが、いままであまりわかっていなかったADC周りの理解がすすんだ気がします。次期バージョンからはESP-IDFのラッパーに変わるので、Arduino Coreのコードを解析して、さらにESP-IDFのソースをみる必要がありそうです。
コメント