【ESP32入門】AD変換ICを使用する(MCP3208)【6回目】

2023/01/24

Arduino ESP32 HOW2 MCP3208 レベルメーター 電子工作

t f B! P L
レベルメータ作成のまとめはこちら

はじめに 

どうもtakaです。コーヒー片手に自作アンプでラジオを聴きながらのブログ執筆・・・
最高です!

さてさて、前回はESP32でFFTを行う方法に関して投稿いたしました。
今回はESP32でAD変換ICのMCP3208を使用し電圧測定を行います。オーディオレベルメータを作成するにあたり音声信号をESP32に取り込むために必要です。

AD変換とはなにか

詳細はgoogle先生に任せるとして・・・
アナログ電圧をデジタル値に変換する処理の事です。ESP32のようなマイコンは内部は全てデジタル動作していますので、音声信号のようなアナログ電圧をそのまま扱うことは不可能です。
そこで、マイコンでも扱い可能なデジタル情報へ変換してやります。

内蔵ADC

ESP32は多chの12bitADCを持っており外部回路無しでAD変換可能です。しかし、変換速度がオーディオレベルメータ用としては遅い(変換1回あたり60[us])ため今回は使用しません。
前回のFFTのプログラムで分かるように、FFTで解析可能な最大周波数はサンプリング周波数の半分までです。AD変換60[us]はつまり17[kHz]ということですのでFFT解析上限は8.3[kHz]となってしまいます。可聴域が最大18[kHz]としてもサンプリングは最低36[kHz](28[us])は必要です。

AD変換IC(MCP3208)

内蔵ADCが使用不可となりどうしようかなぁ~と悩んでいたのですがRaspberryPi用に買っておいたMCP3208がありましたので使ってみることにしました。
このICはESP32と同様に12bitのADCを8ch持っておりADC結果をSPI通信で送信するICです。
試しに1000回のADCの速度を計測してみました。
1000回で33[ms]つまり一回で33[us]

これでも最速で33[us]でしたので、FFTの最大周波数は15[kHz]となります。
もう少し欲しいところですが今回はこれで妥協します。

MCP3208を使ってみる

ハード構成

回路図を示します。

MCP3208は3.3[V]でも動作するのですがわざわざ双方向電圧シフタを挟んで5[V]で動作させているのは5[V]だとSPI通信速度が最大2[MHz]で通信できるためです。また、グランド分離型なので本来はアナログ側とデジタル側でグランドを分けて使用するのが正しいですが今回はそこまで精度を求めないので共通としています。

MCP3208と双方向電圧シフタはこちらを使用しています。
(MCP3208は昔買い込んだ部品ですので、今ならこれにこだわらずとも上位互換品があると思います)

サンプルプログラム

今回はMCP3208を扱うコードは使いまわしが効くようクラスにまとめています。クラスが何かはここでは説明しません。とりあえず以下の三つ(サンプルプログラム、MCP3208.cpp、とMCP3208.h)を同一階層に保存して実行してください。

・サンプルプログラム
//////////////////////////////////////////////////////////
//AD変換IC(MCP3208)と自作クラス(MCP3208.h)を使用したAD変換サンプル
//
//SPI通信を使用する。ピンアサインは下記参照
//GPIO番号:MCP3208端子名
//18:CLK
//19:DOUT
//23:DIN
//5:CS/SHDN
//
///////////////////////////////////////////////////////////

#include "MCP3208.h"

//----------------------MCP3208の設定-----------------------
//電源電圧[V]
//5VだとSPI通信クロックが最速(2MHz)で使用可能。
#define MCP3208_VCC 5
//基準電圧[mV]
#define MCP3208_REF_MILLIVOLT 5000

//--------------------------SPI設定-------------------------
//SPIのCSピンとして使用しているGPIOピン番号
#define SPI_CSPIN 5

//--------------------------その他--------------------------
//AD変換するMCP3208のチャンネル(0~7まである)
#define MCP3208_CH0 0

//----------------------オブジェクト定義----------------------
//自作クラスのオブジェクトを作成する
mtMCP3208 adcObj(MCP3208_VCC,SPI_CSPIN,MCP3208_REF_MILLIVOLT);

//----------------------セットアップ関数----------------------
void setup() {
	Serial.begin(115200);
	delay(3000);

	//オブジェクトの初期化(SPI通信が初期化される)
	adcObj.adcInit();
}
//----------------------------------------------------------

//----------------------メインループ関数----------------------
void loop() {
	int adcRes,adcResMilliVolt;

	//AD変換値を取得する
	//取得用関数は下記の2種類用意した。
	
	//変換結果を0~4095で返す関数。
	//動作が早い(33[us])
	adcRes=adcObj.readAdc(MCP3208_CH0);

	//変換結果を電圧値に換算して返す関数。
	//動作が若干遅い(37[us])
	adcResMilliVolt=adcObj.readMilliVolt(MCP3208_CH0);

	Serial.printf("adcRes = %d [-]¥n",adcRes);
	Serial.printf("adcResMilliVolt = %d [mV]¥n",adcResMilliVolt);
}

	

・MCP3208.cpp
#include <Arduino.h>
#include <SPI.h>
#include "MCP3208.h"

mtMCP3208::mtMCP3208(uint8_t vcc,uint8_t csPinNo,int refMilliVolt){
	this->vcc=vcc;
	this->csPinNo=csPinNo;
	this->refMilliVolt=refMilliVolt;
}

void mtMCP3208::adcInit() const{
	//SPI設定
	SPI.begin();				//SPIを行う為の初期化
	SPI.setBitOrder(MSBFIRST);	//bitオーダー
	SPI.setDataMode(SPI_MODE0);
	SPI.setFrequency(vcc == 5 ? 2000000 : 1000000);	//MCP3208が5Vの時は最速の2MHzで通信できる
	pinMode(csPinNo, OUTPUT) ;
	digitalWrite(csPinNo, HIGH) ;
}

int mtMCP3208::readAdc(uint8_t channel)const{
	int d1 , d2 ;

	// ADCから指定チャンネルのデータを読み出す
	digitalWrite(csPinNo, LOW) ;
	SPI.transfer(0x06 | (channel >> 2) ) ;
	d1 = SPI.transfer( (channel & 0x03) << 6 ) ;
	d2 = SPI.transfer(0x00) ;
	digitalWrite(csPinNo, HIGH) ;
	return ((d1 & 0x0F) << 8 | d2) ;
}

int mtMCP3208::readMilliVolt(uint8_t channel)const{
	int adcRes=readAdc(channel);
	return refMilliVolt*adcRes/4095.0;
}

	

・MCP3208.h
class mtMCP3208{
	private:
		uint8_t csPinNo;
		uint8_t vcc;
		int adcRes;
		int refMilliVolt;
		int offsetMillVolt;
		
	public:
		mtMCP3208(uint8_t,uint8_t,int);
		void adcInit()const;
		int readAdc(uint8_t)const;
		int readMilliVolt(uint8_t)const;
};

	

結果

readAdcに電圧を測定したいMCP3208のchを指定し、うまくいけばシリアルモニタに
adcRes=(0~4095) [-]
adcResMilliVolt = (電圧) [mV]
と表示されれば成功です!

今回は以上です!
ESP32の欠点?なのかわかりませんがAD変換が遅いのでAD変換ICを使用してみました。
AD変換のみインラインアセンブラで書くと高速化できたりするのか?とかいろいろやってみたいことがありますがこれくらいにしておきます。
このままでは0-5[V]の範囲の電圧しかAD変換できないため、次回はいよいよ負電圧を含んだ音声信号のAD変換を行いますのでこうご期待!

全投稿のまとめページは こちら

中の人

自分の写真
モノ作りが好きです。GUIアプリの作成からアナログ回路まで手当たり次第です。 アンプの修理を紹介するためにブログを始めました。 (Twitter:@TakaElc)

記事カテゴリ

44 (1) 5M21 (10) A-6 (5) A-717 (2) A-8 (4) A-950 (1) AB級 (8) Arduino (2) A級 (30) C-21 (1) C29 (1) CA-2000 (2) ESP32 (9) EUMIG (13) FFT (2) HOW2 (6) LED (2) LUXMAN (10) M-1000 (13) M-22 (24) MATLAB (1) mcintosh (1) MCP3208 (3) OPアンプ (1) Pioneer (28) QUAD (2) SA-9800 (1) Simulink (1) SONY (1) TA-F555ESX2 (1) WS2812B (4) YAMAHA (12) アンプ (65) スピーカ (3) プリ (1) プリアンプ (2) プリメイン (14) レベルメーター (10) 化石 (1) 山水 (3) 自作アンプ (7) 電子工作 (29) 日記 (1)

QooQ