概要
前回はデジタル入出力でしたが、今回はアナログ入力のADCを調べてみます。
公式サンプル
上記がサンプルコードとなります。
use esp_idf_hal::adc::config::Config;
use esp_idf_hal::adc::AdcChannelDriver;
use esp_idf_hal::adc::AdcDriver;
use esp_idf_hal::adc::Atten11dB;
use esp_idf_hal::delay::FreeRtos;
use esp_idf_hal::peripherals::Peripherals;
use esp_idf_hal::gpio::Gpio32;
fn main() -> anyhow::Result<()> {
let peripherals = Peripherals::take().unwrap();
let mut adc = AdcDriver::new(peripherals.adc1, &Config::new().calibration(true))?;
let mut adc_pin: esp_idf_hal::adc::AdcChannelDriver<'_, Gpio32, Atten11dB<_>> =
AdcChannelDriver::new(peripherals.pins.gpio32)?;
loop {
println!("ADC value: {}", adc.read(&mut adc_pin).unwrap());
FreeRtos::delay_ms(100);
}
}
ESP32向けにちょっと手を入れたのが上記になります。
ADCユニットの指定
ADCユニット | チャンネル | ESP32 | ESP32-S2 | ESP32-S3 | ESP32-C3 | ESP32-H2 |
---|---|---|---|---|---|---|
ADC1 | 0 | 36 | 1 | 1 | 0 | 0 |
ADC1 | 1 | 37 | 2 | 2 | 1 | 1 |
ADC1 | 2 | 38 | 3 | 3 | 2 | 2 |
ADC1 | 3 | 39 | 4 | 4 | 3 | 3 |
ADC1 | 4 | 32 | 5 | 5 | 4 | 4 |
ADC1 | 5 | 33 | 6 | 6 | ||
ADC1 | 6 | 34 | 7 | 7 | ||
ADC1 | 7 | 35 | 8 | 8 | ||
ADC1 | 8 | 9 | 9 | |||
ADC1 | 9 | 10 | 10 | |||
ADC2 | 0 | 4 | 11 | 11 | 5 | 5 |
ADC2 | 1 | 0 | 12 | 12 | ||
ADC2 | 2 | 2 | 13 | 13 | ||
ADC2 | 3 | 15 | 14 | 14 | ||
ADC2 | 4 | 13 | 15 | 15 | ||
ADC2 | 5 | 12 | 16 | 16 | ||
ADC2 | 6 | 14 | 17 | 17 | ||
ADC2 | 7 | 27 | 18 | 18 | ||
ADC2 | 8 | 25 | 19 | 19 | ||
ADC2 | 9 | 26 | 20 | 20 |
ESP32シリーズではADCユニットが2つあり、ADC2は無線利用時には使えなくなる制約があったりします。チャンネルは内部的には利用していますが、通常は意識する必要ありません。
ArduinoではGPIOだけ指定すればADCのユニットも自動的に決まりますが、RustのライブラリではGPIOとADCユニットを両方指定する必要があります。ちょっと面倒ですね。。。
let mut adc = AdcDriver::new(peripherals.adc1, &Config::new().calibration(true))?;
上記でADCユニットを取得しています。
GPIOの指定
let mut adc_pin: esp_idf_hal::adc::AdcChannelDriver<'_, Gpio32, Atten11dB<_>> =
AdcChannelDriver::new(peripherals.pins.gpio32)?;
上記で指定しています。
ちなみにGPIO32はADC1の4チャンネルですが、間違ってADC2を指定するとADC2の4チャンネルであるGPIO13に連動した値が取れましたが、取得できた値がGNDで3Vに近い値、3V3で0に近いひっくり返った値になっていました。
あまりイケてない設計ですね。
電圧取得
println!("ADC value: {}", adc.read(&mut adc_pin).unwrap());
こちらは普通に取得できますね。
ADC value: 634
ADC value: 610
ADC value: 142
ADC value: 142
ADC value: 386
ADC value: 533
ADC value: 587
ADC value: 551
ADC value: 3165
ADC value: 3165
ADC value: 3165
この値はmVの電圧値ですね。
Arduinoでいうと上記にあるanalogReadMilliVolts()関数相当になります。つまりこの端末だとGNDに接続しても142mVまでしか下がらず、3V3に接続しても3165mVまでとなります。
ソースコード見た限り、このライブラリでは生のADCデータを取得することはできないようです。ESP-IDFの関数を直接呼ばないとだめそうですね。
生ADCを取得してみる
use esp_idf_hal::adc::config::Config;
use esp_idf_hal::adc::AdcChannelDriver;
use esp_idf_hal::adc::AdcDriver;
use esp_idf_hal::adc::Atten11dB;
use esp_idf_hal::delay::FreeRtos;
use esp_idf_hal::peripherals::Peripherals;
use esp_idf_sys::adc1_get_raw;
use esp_idf_hal::gpio::Gpio32;
fn main() -> anyhow::Result<()> {
let peripherals = Peripherals::take().unwrap();
let mut adc = AdcDriver::new(peripherals.adc1, &Config::new().calibration(true))?;
let mut adc_pin: esp_idf_hal::adc::AdcChannelDriver<'_, Gpio32, Atten11dB<_>> =
AdcChannelDriver::new(peripherals.pins.gpio32)?;
loop {
print!("ADC value: {}", adc.read(&mut adc_pin).unwrap());
println!(" ({})", unsafe { adc1_get_raw(4) });
FreeRtos::delay_ms(100);
}
}
adc1_get_raw()を直接呼び出すことで生データを取得しています。アッテネーターとかの設定が本来必要ですが、正規の呼び出しをしているので設定済みになっているはずです。
ADC value: 142 (0)
ADC value: 142 (0)
ADC value: 380 (279)
ADC value: 528 (460)
ADC value: 637 (553)
ADC value: 628 (575)
ADC value: 3165 (4095)
ADC value: 3165 (4095)
ADC value: 3165 (4095)
ちゃんとADCの最小値0と最大値4095が取得できていました。とはいえ、ESP-IDFの関数を直接呼び出すのはあまり好ましく無いはずです。
まとめ
ちょっと使いにくいADCですね。ここでArduino互換の関数とかを作ってしまうと面倒だと思いますので、全部作りきってクレートとして公開するか、標準関数を使うしかないと思います。。。
コメント