ESP32マウス(shota)マウス自作研修

ESP32マウス Part.30 ADCのサンプルコードを読み解く

ESP32マウス(shota)
ESP32-DevKitC

こんにちは、shotaです。

前回の記事ではLEDを点灯させました。

今回はESP-IDFのADC(Analog to Digital Converter)のサンプルを動かして、コードを読み解きます。

初めてのオリジナルマウス Especial

ESP-IDFバージョンについて訂正

前々回の記事では最新バージョンのESP-IDFを使用していましたが、
ドキュメントとサンプルコードの乖離がいくつかあったので、今回から安定バージョンに変更します。

そのため、ESP-IDFのインストール手順もGet Started(安定版)を参照してください。

私が使用している安定版のバージョンはv3.3.1です。

ADCサンプルの実行

ADC(Analog to Digital Converter)は、ピンにかかる電圧値をデジタル値に変換してくれる機能です。
バッテリの電圧や、センサの出力電圧等、1(Hi)か0(Lo)かではなく細かい電圧値を知りたいときに使う機能です。

今回はESP32の開発ボードであるESP32-DevKitC-32Dを用いて、サンプルコードを実行します。
ADCの使い方を確認した後に、ESP32マウスでプログラムを実行します。(ESP32マウスを使うのは次回の記事です)

ESP32-DevKitC

サンプルコードはperipherals/adcです。
https://github.com/espressif/esp-idf/tree/release/v3.3/examples/peripherals/adc

まずサンプルコードをコピーします。

$ cd ~/esp
$ cp -r esp-idf/examples/peripherals/adc .  

ビルドして書き込み、シリアルモニタを表示します。

$ cd ~/esp/adc
# ビルド、書き込み、モニタ表示
$ make flash monitor

--- 省略 ---
I (290) cpu_start: Starting scheduler on PRO CPU.
I (0) cpu_start: Starting scheduler on APP CPU.
eFuse Two Point: NOT supported
eFuse Vref: Supported
Characterized using eFuse Vref
Raw: 15	Voltage: 79mV
Raw: 44	Voltage: 86mV
Raw: 36	Voltage: 84mV
Raw: 27	Voltage: 82mV
Raw: 11	Voltage: 78mV
Raw: 0	Voltage: 75mV
Raw: 0	Voltage: 75mV

画面にRawVoltageが表示されました。

ADCサンプルコードを読み解く

それではRawとVoltageがそれぞれ何を表すのかコードを読み解きます。

公式ドキュメントのADCのページとサンプルコードを見比べます。

どのピンの電圧値を変換しているのか

adc/main/adc1_example_main.cに次のコードが書かれています。

static esp_adc_cal_characteristics_t *adc_chars;
static const adc_channel_t channel = ADC_CHANNEL_6;     //GPIO34 if ADC1, GPIO14 if ADC2
static const adc_atten_t atten = ADC_ATTEN_DB_0;
static const adc_unit_t unit = ADC_UNIT_1;

--- 省略 ---

void app_main()
{
    //Check if Two Point or Vref are burned into eFuse
    check_efuse();

    //Configure ADC
    if (unit == ADC_UNIT_1) {
        adc1_config_width(ADC_WIDTH_BIT_12);
        adc1_config_channel_atten(channel, atten);
    } else {
        adc2_config_channel_atten((adc2_channel_t)channel, atten);
    }

ESP32のADCにはADC1とADC2の2つのユニットがあります。
サンプルコードではunitをADC_UNIT_1にするか、ADC_UNIT_2にするかで切り替えています。

チャンネルはchannel = ADC_CHANNEL_6で指定してます。
コメントを読むとADC1のCH6はGPIO34であると書かれていますが、本当にそうなのでしょうか?

モジュールのデータシートで確認してみましょう。
ADC1_CH6で検索すると、3ページにすすみます。
ここで、ADC1_CH6がIO34の機能であることがわかりました。

ESP32-WROOM-32D ADC1_CH6

ADCを使うためのその他の設定

それでは、main関数内の他のコードを見てます。

void app_main()
{
    //Check if Two Point or Vref are burned into eFuse
    check_efuse();


check_efuse()はサンプル用に作られた関数で、ESP内のeFuseメモリにADCの設定値が書き込まれているのかをチェックしています。
この設定値はADCに使われるリファレンス電圧値Vrefで、工場出荷時にeFuseに書き込まれます。
2018年以降のESP32にはVrefが書き込まれているようです。
先程の実行結果でもeFuse Vref: Supportedと表示されていましたね。

次のコードでADCのbit幅とチャンネルを設定します。

//Configure ADC
    if (unit == ADC_UNIT_1) {
        adc1_config_width(ADC_WIDTH_BIT_12);
        adc1_config_channel_atten(channel, atten);
    } else {
        adc2_config_channel_atten((adc2_channel_t)channel, atten);
    }

ADC1を使う場合はadc1_config_widthでビット幅を決めます。
ADC_WIDTH_BIT_12の場合、電圧値が12bit(0~4095)で表現されます。

attenにはADC_ATTEN_DB_0が設定されていますが、これはADCの減衰量を意味します。
ADC_ATTEN_DB_0は0dBの減衰で、フルスケールレンジは1.1Vになります。
(0dB減衰では、ADCで変換できる電圧範囲がおおよそ0 ~ 1.1 Vになる)

詳しくは公式ドキュメントを参照してください。

次のコードで、ADCの特性を設定します。

//Characterize ADC
    adc_chars = calloc(1, sizeof(esp_adc_cal_characteristics_t));
    esp_adc_cal_value_t val_type = esp_adc_cal_characterize(unit, atten, ADC_WIDTH_BIT_12, DEFAULT_VREF, adc_chars);
    print_char_val_type(val_type);

adc_charsには設定値が格納されます。
val_typeにはどのリファレンス値で特性を設定したのか、判別値が入ります。
先程の実行結果では、Characterized using eFuse Vrefと表示されているので、eFuseに書き込まれたリファレンス値が使われていることがわかります。

次のコードがADC1_CH6の電圧変換結果を取得しているところです。

        uint32_t adc_reading = 0;
        //Multisampling
        for (int i = 0; i < NO_OF_SAMPLES; i++) {
            if (unit == ADC_UNIT_1) {
                adc_reading += adc1_get_raw((adc1_channel_t)channel);
            } else {
                int raw;
                adc2_get_raw((adc2_channel_t)channel, ADC_WIDTH_BIT_12, &raw);
                adc_reading += raw;
            }
        }
        adc_reading /= NO_OF_SAMPLES;

adc1_get_raw()で、ADC1_CH6の電圧の変換結果を取得しています。
このサンプルでは値の変動を抑えるため、複数回(NO_OF_SAMPLES = 64)値を取得して、平均値を求めています。

次のコードでは、ADCで得たデジタル値(0~4095)を、電圧値(0~1100mV)に変換しています。
先程設定した特性adc_charsとAD変換結果adc_readingesp_adc_cal_raw_to_voltage()に渡して変換しています。

        //Convert adc_reading to voltage in mV
        uint32_t voltage = esp_adc_cal_raw_to_voltage(adc_reading, adc_chars);
        printf("Raw: %d\tVoltage: %dmV\n", adc_reading, voltage);
        vTaskDelay(pdMS_TO_TICKS(1000));

ここで最初の疑問
RawVoltageがそれぞれ何を表すのか。
の答えがやっとわかりました。

RawはADC1_CH6(GPIO34)の電圧値の変換結果(デジタル値)で、VoltageはRawを電圧値に変換したものです。

次回の記事

次回は、ADCのサンプルコードを編集してマウスのバッテリ電圧値を取得します。

タイトルとURLをコピーしました