ADCを使う

ESP32に内蔵されているADCの使い方です。

基準電圧1100mVと減衰器

ESP32は内部に基準電圧1100mVを持っていてこれと対象を比較しAD変換を行います。基本的には対象の測定範囲は基準電圧までですが、減衰器を使えば測定範囲を広げることが出来ます。減衰器は3つ。減衰器無しと合わせて4つレンジの測定が可能です。最大の減衰器を使用して最大測定範囲は、 約0から3.9Vとなります。

減衰器測定範囲(約)
ADC_0db0〜1100mV
ADC_2_5db0〜1467mV
ADC_6db0〜2195mV
ADC_11db0〜3903mV

それ以上の範囲は分圧を使用して測定します。例えばV2( > 3.9V)を測定したい場合下記の様な回路を製作します。

V1は V1 = V2 * R1 /(R1 + R2)。 このV1が < 3.9Vとなる様にR1,R2を設定すればV1をESP32で測定出来ます。V1が測定できればV2は V2 = V1 / R1 * (R1 + R2)で求める事が出来ます。

実機での検証

測定対象の電圧を12V近辺として実際に回路を製作しながら測定して見ます。使用する関数とその説明、

  • pinMode(pin, mode)
    • AD変換に使用するGPIOピンを指定します。
    • 例: GPIO36を使用する場合ー> pinMode(36, ANALOG);
  • analogSetAttenuation(ADC_11db)
    • この関数で減衰器を指定します。
    • 今回は最大減衰のADC_11dbを使用します。測定範囲は0〜3903mV。
  • analogRead(pin)
    • この関数でAD変換された値を読み込む事が出来ます。
    • ESP32の場合は12ビットの変換になります。
    • 最小0V: 0 / 最大3.9V: 4096(12ビット)

ネットで検索するとADC_11dbを使用して測定範囲を0〜3903mVに設定した場合、測定範囲の最初と終わりは正しく測定出来ない様です。最小を0.3V。最大を3V位で使用するのが良さそうです。そこで回路は下記の様に設定しました。

測定電圧 V2:12V。R2:220kΩ R1:51kΩとすると V1は V1=12*51/(220+51)≒2260mVとなります。これをESP32のGPIO36につないで測定します。測定プログラムは下記を使用。

arduino_adc.ino

void setup(){
    Serial.begin(115200);
}

void loop(){
  int a,v;
    pinMode(36, ANALOG);
    analogSetAttenuation(ADC_11db);
    v = 0;
    for(a = 0; a < 10; a++){
      v += analogRead(36);
      delay(50);
    }
    v /= 10;
    Serial.println("V1: " + String(v));
    delay(300);
}
v
  • 7行:使用するGPIOピンの設定
  • 8行:減衰器ADC_11dbを使用
  • 10から14行:10回測定し、その平均を計算
  • 15行:測定値をシリアルモニタに表示

このプログラムを実行したところ測定値が2749と表示されました。またGPIO36の電圧をテスタで測定すると2360mVでした。2360mVを12ビットの解像度でAD変換したところ2749となったという事です。一方で12ビッt(4096)の解像度で測定最大測定値3.9Vとすると、2749は 3900/4096*2749≒2617mVとなります。実際の値(テスタで測定した値)との間に誤差が生じます。これは下記が原因と思われます。

  • ESP32が持っている基準電圧1100mVに誤差がある
    • 製品を製造する場合製造誤差は当然生じる
    • 基準電圧は測定値に影響する。
  • 最大測定値3.9Vを4096とする事に無理がある。
    • 測定範囲の最初と最後の部分は正しく測定出来ないので3.9Vー>4096とするのは正しく無い

では測定値をどの様に補正するか。簡単は方法は下記です。

  • テスタで測定した値をAD変換の値で割った値を補正係数とする。
  • AD変換した値い上記の係数を掛けると実際の電圧に変換出来る。

今回の場合は 2360/2749≒0.86。変換した値に0.86をかければ電圧の値を得る事が出来ます。

最後にV1,R1,R2からV2を計算します。計算する前にR1,R2をテスタで測定したところ

  • R1:回路 51kΩ / 実際 50kΩ
  • R2:回路 220kΩ / 実際 218kΩ

となりました。よってV2は V2=2749*0.86*(218+50)/50=1267mV となります。テスタで実際にV2を測定したところ1269mVでした。よってこの回路でこのESP32を使うとV1,V2の電圧は

  • V1: AD変換値 * (2360/2749)
  • V2: V1 * (218+50) / 50

で求める事が出来ます。下記はV1,V2を表示するプログラムです。

arduino_adc2.ino

void setup(){
    Serial.begin(115200);
}

void loop(){
  int a,v;
  float v1,v2,v_cont;
    pinMode(36, ANALOG);
    analogSetAttenuation(ADC_11db);
    v_cont = 2360.0 / 2749 ;
    v = 0;
    for(a = 0; a < 10; a++){
      v += analogRead(36);
      delay(50);
    }
    v /= 10;
    v1 = v * v_cont;
    v2 = v1 * 268 / 50;
    Serial.println("V1: " + String(v1) + "[mV] / V2: " + String(v2) + "[mV]");
    delay(300);
}

補正を自動で行う関数

Arduinoで使用出来るESP-IDE関連の関数に 

esp_err_t esp_adc_cal_get_voltage(adc_channel_t channel, const esp_adc_cal_characteristics_t *chars, uint32_t *voltage)

が有ります。これを使うと補正を関数が行ってくれます。

この関数を使うには、 “driver/adc.h”と “esp_adc_cal.h”をincludeし下記の関数の設定を行います。

  • esp_err_t adc1_config_width(adc_bits_width_t width_bit)
    • AD変換の解像度の指定
  • esp_err_t adc1_config_channel_atten(adc1_channel_t channel, adc_atten_t atten)
    • ADCに使用するGPIOピンと減衰器の指定
  • esp_adc_cal_value_t esp_adc_cal_characterize(adc_unit_t adc_num, adc_atten_t atten, adc_bits_width_t bit_width, uint32_t default_vref, esp_adc_cal_characteristics_t *chars)
    • 補正計算に必要はパラメータを指定する関数
    • 引数
      • 第1:adc_num。ADC_UNIT_1を使用。
      • 第2:減衰器の指定
      • 第3:解像度の指定
      • 第4:ESP32が自身の基準電圧を持っていない場合使用する基準電圧
      • 第5:パラメータ保存用のポインタ

これらの関数を使った測定用のプログラムは以下の通り。

adc_ide.ino

#include "driver/adc.h" 
#include "esp_adc_cal.h"

void setup(){
    Serial.begin(115200);
}

void loop(){
  uint32_t  voltage,vol;
  int a;
  esp_adc_cal_characteristics_t adcChar;
  
    vol = 0;
    adc1_config_width(ADC_WIDTH_BIT_12);
    adc1_config_channel_atten(ADC1_CHANNEL_0, ADC_ATTEN_DB_11);
    esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, ADC_WIDTH_BIT_12, 1100, &adcChar);
    
    for(a = 0; a < 10; a++){
      esp_adc_cal_get_voltage(ADC_CHANNEL_0, &adcChar, &voltage);
      vol += voltage;
      delay(50);
    }

    voltage = vol / 10;
    vol = voltage * 268 / 50;
    Serial.println("V1: " + String(voltage) + "[mV] / V2: " + String(vol) + "[mV]");
    delay(300);
}
  • 14行:解像度を指定
  • 15行:ADC用のピンと減衰器を指定
    • adc1で使えるGPIOピンは以下の通り
    • 定数GPIOピン
      ADC1_CHANNEL_036
      ADC1_CHANNEL_339
      ADC1_CHANNEL_432
      ADC1_CHANNEL_533
      ADC1_CHANNEL_634
      ADC1_CHANNEL_735
    • 今回はGPIO36を使用するので、ADC1_CHANNEL_0。
  • 16行:計算の前準備
  • 18から24行:10回測定してその平均を取る
    • 19行:ここでADCを実行。
    • esp_adc_cal_get_voltage()は測定値を電圧(mV)で返してくる
  • 25行:V1からV2を計算
  • 26行:値を表示

プログラムを実行し表示される値と実際に測定した値を比較してみました。

  • V1:プログラム 2373mV   実際 2354mV
  • V2:プログラム 12719mV  実際 12620mV

補正された値が実際の値にかなり近い事が分かります。この関数は便利なのですが、ESP-IDE関連の関数はバージョンの変更が激しく前回コンパイル出来たのに今回は出来ないって事がよく有るんですよね。