ESP32の内部ADC値をi2sで取得した場合のサンプリングレート確認


1. はじめに

ESP32の内部ADCをi2s経由で取得すると、(Arduino-IDEで)割り込みとanalogReadを使った場合よりも高いサンプリングレートを設定できます。そのサンプリングレートを確認しました。

2. 結論

このコードを使った場合は、.sample_rateに設定した値の約2倍のレートでサンプリングが行われている。ただし、正確な2倍ではなく、この点の原因は不明。

3. 検証

このコード(#define I2S_SAMPLE_RATE 38000*2 つまり、76000 にしています)を書き込んだESP32のピン33番に、agilent (現 Keysight Technologies) 33500Bから1Vオフセット開放端1.5Vppで、1kHzの方形波や正弦波を入力しました。入力信号の確認はTektronix MDO-4104を用いて目視および信号解析機能で行いました。 シリアルモニタから取得した波形データは87040点。これをDFTし、パワースペクトラムのピーク位置を確認しました。なお、DFT前の波形には、DCオフセットを除去しblackmam窓をかけました。

取得した波形(方形波)

パワースペクトラム(方形波)

スペクトラムのピーク付近を拡大

ピーク位置$P_{peak}$、サンプリング周波数$f_s$、参照信号の周波数$f_c$、全データ数$N_{dat}$の関係は、次になります。
$$
f_s = f_c \cdot \frac{N_{dat}}{P_{peak}}
$$

ピークは方形波、正弦波共に575でした。データ点は87040で、ピークは1kHzを指しているので、サンプリングレートは
151374 Sa/s
となります。

念のため、時間波形の時間間隔は目視で約150サンプルでした。1kHzの周期1msが150サンプルということで、ここからも約150kSa/sとなることが判ります。

.sample_rateに設定した値は、76000なので、この値の約2倍のサンプルレートになっていることになります。
ただし、152000に対して差がある理由についてはまだ判ってません。(ピークが1サンプルずれた場合の結果の差は263Hz程度なので、計算上の誤差よりも大きい)

4. 感想

ESP32に基準信号を入力して、内部ADCをi2sで取得する際のサンプリングレートを確認しました。ESP32の内部ADCをi2sで読み込むモードについては、私自身、まだ理解しきれていません。1つずつ確認しながら進めていくことが重要だと、今回の結果から痛感しました。

参考

確認で利用したpythonスクリプト

analyFreq.py
import numpy as np

fname='squar1kHz0.txt'
org = np.loadtxt(fname)
strm = org - org.mean()
Ndat=len(strm)
spc = np.fft.fft(strm * np.blackman(Ndat))
sps = np.real(spc)**2 + np.imag(spc)**2
psp = 10*np.log10(sps)
vmax=np.max(psp)
pmax=np.argmax(psp)
nfc = pmax/Ndat

fs = 38000*4
cfc=nfc*fs
fc=1000
cfs=fc/nfc

print(pmax)
print(cfs)
print(cfc)


import matplotlib.pyplot as plt

plt.figure(1)
plt.plot(psp[1:1000])
plt.axis([0,1000,60,np.max(psp)*1.01])
plt.xlabel('freq [*1/87040*fs Hz]')
plt.ylabel('relative power [dB]')
plt.show(block=False)
plt.savefig('spctrum.png')

plt.figure(2)
plt.plot(org[1:500])
plt.axis([0,500,0,np.max(org)*1.01])
plt.xlabel('time [*1/fs s]')
plt.ylabel('voltage [*3.3/4096 V]')
plt.show(block=False)
plt.savefig('waveform.png')

plt.figure(3)
plt.plot(np.arange(-1*Ndat/2,Ndat/2,1)/Ndat ,np.fft.fftshift(psp))
#plt.axis([0,1000,60,np.max(psp)*1.01])
plt.xlabel('normalized frequency')
plt.ylabel('relative power [dB]')
plt.show(block=False)
plt.savefig('spctrumall.png')