DEV Community

codemee
codemee

Posted on • Edited on

4

ESP32 校正 ADC

根據 ESP32 ADC Calibration 文件的說明, 雖然說 ADC 的參考電壓是 1.1V, 但其實每一顆 ESP32 的參考電壓都是不一樣的, 這個參考電壓在出廠時會實測後燒寫入 eFuse 中, 因此實際上必須依照此參考電壓校正 ADC 值。

網路上已經有善心人士幫我們把校正版本的 MicroPython 模組寫好了, 可以在這裡找到 MicroPython-ADC_Cal, 它提供一個繼承自 ADCADC1Cal 類別, 內建有 voltage 屬性可以取得校正後以 V 為單位的電壓值, 使用範例如下:

from machine import Pin, ADC
from adc1_cal import ADC1Cal
import time

DIV       = 1                 # div = V_measured / V_input; here: no input divider

ubatt = ADC1Cal(
    Pin(36),          # 腳位
    1,                # 分壓電路的倍數 (分壓後的電壓/原始電壓), 1 表示沒有使用分壓電路
    None,             # 參考電壓, None 表示從 eFuse 中取得參考電壓
    10,               # 平均值計算的取樣次數
    "ADC1 Calibrated"
)

ubatt.width(ubatt.WIDTH_10BIT)
ubatt.atten(ubatt.ATTN_6DB) # 不支援 11dB

# 顯示參考電壓
print('ADC Vref: {:4}mV'.format(ubatt.vref))

while True:
    print('Voltage:      {:4.1f}mV'.format(ubatt.voltage))
    time.sleep(0.3)
Enter fullscreen mode Exit fullscreen mode

不過這個模組原本因為想要減少記憶體用量以及計算複雜度, 並不支援衰減設為 11dB 的模式, 因此我參考了 esp-idf 的原始碼, 加入了衰減設為 11dB 時針對非線性區域以查表及雙線性內插方式求值的功能。

更新:2021/12/06 我的修改版本已經提交並且併入原作者的版本中了, 所以已經沒有不支援 11dB 的限制了。

ESP32 ADC1 的 ADC 校正方式

實際上在 ESP32 中對於 ADC 的校正, 是把 ADC 值都應對到 12 位元的解析度, 然後將 ADC 值以線性方式對應到電壓值, 這個線性公式為:

mV = (a * adc + 32768) / 65536 + b
Enter fullscreen mode Exit fullscreen mode

而對 ADC1 來說:

a = (vref * atten_scales[atten]) / 4096
b = atten_offsets[atten]
Enter fullscreen mode Exit fullscreen mode

其中 atten_scales 與 atten_offsets 就是根據衰減值決定的倍率與位移, 對照表如下, 個別元素分別對應到 ADC.ATTN_0DB、ADC.ATTN_2_5DB、ADC.ATTN_6DB、ADC.ATTN_11DB:

vref_atten_scale = [57431, 76236, 105481, 196602];
vref_atten_offset = [75, 78, 107, 142];
Enter fullscreen mode Exit fullscreen mode

非線性區間的處理

如果是 ADC.ATTN_11DB, 那麼在 ADC 值在 2880~4095 的區間, 則是非線性區域, 它提供參考電壓為 1000mV 和 1200mV 時以 ADC 值 64 為區隔的對應電壓值表格供參考, 以下是個別表格內容:

# LUT for VREF 1000mV
lut_adc1_low = [2240, 2297, 2352, 2405, 2457, 2512, 2564, 2616, 2664, 2709,
                2754, 2795, 2832, 2868, 2903, 2937, 2969, 3000, 3030, 3060]
# LUT for VREF 1200mV
lut_adc1_high = [2667, 2706, 2745, 2780, 2813, 2844, 2873, 2901, 2928, 2956,
                 2982, 3006, 3032, 3059, 3084, 3110, 3135, 3160, 3184, 3209]
Enter fullscreen mode Exit fullscreen mode

由於是從 2880~4096 間隔為 64, 所以共有 (4096-2880)/64 = 20 項資料。當 ADC 值落入特定區間時, 就以參考電壓為 X 軸、ADC 值為 Y 軸, 電壓值為 Z 軸, 以雙線性內插法求得電壓值。舉例來說, 若 ADC 值為 3040, 從 eFuse 取得的參考電壓為 1070, 那麼首先可知 ADC 值 2976 是 2880+ 64 + 64 + 32, 所以得知落在第 3 個區間, 即可以 (1000, 3008, 2352), (1000, 3072, 2405) 及 (1200, 3008, 2745)、(1200, 3072, 2780) 為已知點, 以雙線性內插求 (1070, 3040) 對應的 Z 值:

        ADC
         ^
         |   2405  R2      2780
    3072 +----●----●--------●
         |    |    |        |
         |    |    P        |
    3040 +----|----●--------|
         |    |    |        |
         |   2352  R1      2745
    3008 +----●----●--------●
         |    |    |        |
         +----+----+--------+--->VREF
           1000mV 1070mV 1200mV
Enter fullscreen mode Exit fullscreen mode

過程如下:

  1. 先在 Y 軸 2880+64 這條線上求內插值:

    R1 = ((1070-1000) * 2745 + (1200-1070) * 2352)/ (1200-1000)
    R1 = 2489.55
    
  2. 再在 Y 軸 2880+128 這條線上求內插值:

    R2 = ((1070-1000) * 2780 + (1200-1070) * 2405)/ (1200-1000)
    R2 = 2536.25
    
  3. 再在 X 軸 1070mV 這條線上求內插值:

    P = ((3040 - 3008) * 2536.25 + (3072 - 3040) * 2489.55)/ 64
    P = 2512.9
    

線性到非線性的交界區

對於 ADC.ATTN_11DB 的處理, 除了上述非線性區間外, 如果 ADC 值落在 2880~2880+64 的這個區間, 因為是從線性區往非線性區的邊緣, 所以還會再以上述提到的方式分別計算出線性值與非線性值後, 用 ADC 值在 (2880, 線性值) 與 (2880+64, 非線性值) 間以內插法再求值當成實際的電壓值。

Image of Stellar post

Check out Episode 1: How a Hackathon Project Became a Web3 Startup 🚀

Ever wondered what it takes to build a web3 startup from scratch? In the Stellar Dev Diaries series, we follow the journey of a team of developers building on the Stellar Network as they go from hackathon win to getting funded and launching on mainnet.

Read more

Top comments (0)

Image of Stellar post

Check out Episode 1: How a Hackathon Project Became a Web3 Startup 🚀

Ever wondered what it takes to build a web3 startup from scratch? In the Stellar Dev Diaries series, we follow the journey of a team of developers building on the Stellar Network as they go from hackathon win to getting funded and launching on mainnet.

Read more

👋 Kindness is contagious

DEV is better (more customized, reading settings like dark mode etc) when you're signed in!

Okay