電流・電圧測定IC INA226 搭載ボードを使ってみるテスツ

2021年6月18日Arduino/ESP32100mΩ,CJMCU-226,INA226,シャント抵抗,精度,電圧,電流

電流、電圧計測器を購入しますた。

界隈では安くて精度が高いと噂のINA226を使ったモジュールボードです。

電流、電圧モニタIC INA226搭載ボード(CJMCU-226)

なんかちょこっと汚れてますけどっ・・・

Texas Instruments製の高精度な電流、電圧モニタICのINA226を載せているとの事で、ちょっとワクワクして届くのを待っておりました。

勿論中華製ボードですから、本物かどうか知りませんが;

入力は0V~36Vまで、動作電圧は2.7V~5.5Vで、インターフェースはI2Cですので配線にも困らず、マイコン用途には大変使いやすい仕様になっています。

ESP32のADCが何だかフラフラとした値を出すので、外部の電流、電圧計が欲しいなぁと飛びついてしまぃ

乗っている抵抗は100mΩで、シャント抵抗としてはちょっと抵抗値が高めの模様。

えぇ、何にも判らずにポチったもので・・・

本当は12Vで瞬間40A~60Aくらい流れそうなハイパワーDCモーターの回路を測定したかったのですが、100mΩだと819.20mAを超えるとADCのレンジ外になってしまうそうで。

んー(汁

抵抗値が高ければ精度(粒度)が増す代わりに電流の測定範囲が狭くなってしまぃ、抵抗値が低いほど測定範囲は広いけれど精度(粒度)が粗くなる様で。

んん~

ひとまず普通のマイコン回路では精度が高い方が便利ですし、1Aを超える様な事にはなかなか遭遇しないでしょうから、100mΩという設定はバリューゾーンなのかもしれません。

まぁなんか私の事情などはどうでも良いのでちょっと使ってみます。

どうやらシャント抵抗を変えれば広いレンジにも対応出来るみたいですので、今はまだ・・・

INA226を使う準備を。

I2Cアドレスはデフォルトが40hとの事ですので、とりあえず何にもせずにそのまま使います。

ピンヘッダ―が付いてきましたのでササっとはんだ付けしまして・・・

見てくだせぇ

足付けが多少はマシになったでしょう?( ノД`)

・・・あんまり変わって無いですって・・・? ( ノД`)

さていつもの中華製ですので、どうやって使うのかは調べなくてはいけません。

ここでもINA226のデータシートを参照します。

Texas Instruments INA226 データシート

https://www.tij.co.jp/product/jp/INA226

なんかボードのレイアウトと回路はアプリケーションノートのリファレンスデザインと殆ど一緒ですね。

高精度にするにはフィルタ回路を追加しろとありますが、この中華ボードにはそんな気の利いたものは付いていません。

ボードレイアウトを見ても、シャント抵抗とINA226の間に挟み込めそうなブレイクアウト端子を用意してくれたりはしない様ですので、もしこのボードにノイズフィルタを追加するならシャント抵抗を外して外部でフィルタ回路を作らないとダメそうです。

尚、シャント抵抗とか言ってますが、多分これはそれほど高精度な抵抗ではなく、ごく普通のチップ抵抗だと思います。

サイズは恐らく2512サイズで、大変はんだ付けがしやすい大きさになっていますね

本当に精度が欲しい場合には、ちゃんとした高精度なシャント抵抗を買うべきなんでしょう。

私はこれでいいですが・・・

さて、ピンヘッダ―も付けたので、マイコンに接続します。

・・・・

・・・・・・・・

いかにもショートさせて破壊しそうなカオス

気を付けてくださいねホント

今回はI2C接続ですので、0.96インチのOLEDモニタも同時に使います。

シリアルモニタとか見てもつまらないですしお寿司

接続方法は、SCLとSDAをそれぞれESP32のSCL(GPIO 22)とSDA(GPIO 21)に繋ぎます。

これは液晶モニタもI2Cですので、混ぜます。並列に接続します。

ごく普通のI2C接続ですね。

電源は外部電源、GNDはESP32のGNDと外部電源のマイナスを混ぜてます。

Alertは外に接続していません。

一応何かの電流と電圧を計らないと意味がないのでLCDの電源にハイサイドで噛ませています。

一応回路図出しますか・・・

なんかゴチャっとしててアレですが

とにかくLCDとCJMCUボードをI2CでESP32に接続してまっせ、LCDの電流と電圧を計りまっせ

という事で。

INA226を使うコードを書く

接続もできたところで、コードを書いてみます。

データシートをよく読まないとそれぞれの意味は判りづらいのですが、、

ざっくりとこのICでは、以下の様な調整が出来る様です。

・1回計測あたりに平均値を出すサンプル数の変更(1~1024)
・バス電圧測定間隔(140us~8.244ms)
・シャント抵抗電圧測定間隔(140us~8.244ms)
・動作モード設定(シャットダウン検知、シャント・バス電圧トリガー検知、シャント・バス電圧継続測定)
・シャント抵抗値に合わせたキャリブレーション
・アラート機能に監視させる値の種類の設定
・アラート機能の閾値設定

今回は動作確認が主目的ですのでアラート機能は使いませんが、トリガーを設置できたりラッチ出来たりと、監視ICとしての一通りの動作をさせる事ができそうです。

これはちょっとしたリーク調査や突入電流、電圧低下の調査にも使えそうですね

それはさておきコードです。

#include <Arduino.h>
#include <Wire.h>

const uint32_t I2C_Freq = 4000000UL;  // I2C 周波数
const uint32_t I2C_Normal = 100000UL; // I2C 周波数

// GPIO I2C Setting
const uint8_t I2C_SCL = 22; // SCL 22
const uint8_t I2C_SDA = 21; // SDA 21

const uint8_t ADDRES_OLED = 0x3C;
const uint8_t ADDRES_INA226 = 0x40;

// -------------------------------------------------------------------------------
//	シャント抵抗値
const uint8_t ShuntR = 100; // 単位はmohm(ミリオーム)
// -------------------------------------------------------------------------------
//	INA226 レジスター値
//	コンフィグ設定値は16bit値らしいので2byteに揃える
// -------------------------------------------------------------------------------
// ---------------------------------------------------
// 00h ConfigurationRegister
//	default : 0B:01000001 00100111 0x:4127h
const uint8_t INA226_CONFIG = 0x00;
// -----------------------------------------------
// AVGBit Settings 平均値モードのサンプル数 D11-D9 << 9
//const uint16_t INA226_CONFIG_AVG = 0x0000U; // default 1@000  128@100 MAX:1024@111
const uint16_t INA226_CONFIG_AVG = 0x0002U; // 16回計測の平均
// Bus VoltageConversionTime 電圧測定間隔 D8-D6  << 6
//const uint16_t INA226_CONFIG_VCT = 0x0004U; // default:1.1ms@100
const uint16_t INA226_CONFIG_VCT = 0x0002U; // 16回計測するので332 μsに変更
// ShuntVoltageConversionTime シャント抵抗電圧測定間隔 D5-D3 << 3
//const uint16_t INA226_CONFIG_SVCT = 0x0004U; // default:1.1ms@100
const uint16_t INA226_CONFIG_SVCT = 0x0002U; // 16回計測するので332 μsに変更
// ModeSettings 動作モード D2-D0 << 0
const uint16_t INA226_CONFIG_MODE = 0x0007U; // default:Shuntand Bus,Continuous@111
// -----------------------------------------------

// ---------------------------------------------------
// 01h ShuntVoltageRegister (ReadOnly)
//	0h と出るけど固定 8000 (1F40h)
const uint8_t INA226_SHUNTV = 0x01;
// ---------------------------------------------------
// 02h Bus VoltageRegister (ReadOnly)
//	0h と出るけど固定 1.25mV / bit = 9584 (2570h)
const uint8_t INA226_BUSV = 0x02;
// ---------------------------------------------------
// 03h PowerRegister (ReadOnly)
//	0h と出るけど固定 Power = CurrentRegister * VoltageRegister / 20000 = 4792 (12B8h)
const uint8_t INA226_POWER = 0x03;
// ---------------------------------------------------
// 04h CurrentRegister (ReadOnly)
//	0h と出るけど固定 10000 (2710h)
const uint8_t INA226_CURRENT = 0x04;
// ---------------------------------------------------0.000002
// 05h CalibrationRegister
//	default 2560 (A00h)
//	CAL(2560) = 0.00512 / Current 0.001A(04h 10000) * Shunt 0.002ohm(2m ohm)
//		1mohm=5120, 2mohm=2560 10mohm=512 25mohm=204.8 50m=102.4 100mohm=51.2 1ohm=5.12
//	CurrentRegister(04h 10000) = ShuntVoltage(01h 8000) * CalibrationRegister / 2048
const uint8_t INA226_CALIB = 0x05;
// ---------------------------------------------------
// 06h Mask/EnableRegister
//	アラートトリガーの設定らしい(今回未接続)
const uint8_t INA226_MASK = 0x06;
// ---------------------------------------------------
// 07h AlertLimitRegister
//	アラートの閾値の設定らしい(今回未接続)
const uint8_t INA226_ALERTL = 0x07;
// ---------------------------------------------------
// FEh ManufacturerID Register (ReadOnly)
const uint8_t INA226_MANU_ID = 0x08;
// ---------------------------------------------------
// FFh Die ID Register (ReadOnly)
const uint8_t INA226_DIE_ID = 0xff;
// ---------------------------------------------------

// -------------------------------------------------------------------------------
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

const uint8_t SCREEN_WIDTH = 128; // OLED display width, in pixels
const uint8_t SCREEN_HEIGHT = 64; // OLED display width, in pixels
// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
const int8_t OLED_RESET = -1;

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET, I2C_Freq, I2C_Normal);
// -------------------------------------------------------------------------------

// レジスタ書き込み関数
void INA226_write(uint8_t reg, uint16_t val)
{
	Wire.beginTransmission(ADDRES_INA226);
	Wire.write(reg);
	// I2Cは8bitづつ書き込みらしい
	Wire.write(val >> 8);	  // 上位ビット送信
	Wire.write(val & 0x00ff); // ビットマスクして送信
	Wire.endTransmission();
}

// 読み込み
uint16_t INA226_read(uint8_t reg)
{
	uint16_t ret = 0;
	// リクエストするレジスタをコール
	Wire.beginTransmission(ADDRES_INA226);
	Wire.write(reg);
	Wire.endTransmission();
	// 2バイトリクエスト
	Wire.requestFrom((uint8_t)ADDRES_INA226, (uint8_t)2);
	// 2バイト取り込み
	while (Wire.available())
	{
		//	初回は0なので下位ビットに埋まる
		//	2回目は下位ビットを上位にずらして、新しく来たものを下位ビットに埋める
		ret = (ret << 8) | Wire.read();
	}
	return ret;
}

void setup()
{
	while (!Serial)
		;

	//	Serial.begin(9600);
	Serial.begin(115200);

	// I2C GPIO 内部プルアップを使用
	pinMode(I2C_SDA, OPEN_DRAIN | PULLUP | INPUT | OUTPUT); //	SDA
	pinMode(I2C_SCL, OPEN_DRAIN | PULLUP | INPUT | OUTPUT); //	SCL

	// I2C開始
	Wire.begin();

	// 適度にディレイ
	delay(1000);

	// INA226 初期設定
	uint16_t config_ina = 0x4000U; // ベースビット 0B0100000000000000
	config_ina =	config_ina
					| (INA226_CONFIG_AVG << 9)
					| (INA226_CONFIG_VCT << 6)
					| (INA226_CONFIG_SVCT << 3)
					| (INA226_CONFIG_MODE);
	// 初期設定書き込み
	INA226_write(INA226_CONFIG, config_ina);

	// キャリブレーション書き込み
	//	5120 : VAOhm order ((0.0000025V x 2048) / 0.001A ) * 1000(to milli ohm at ShuntR unit order)
	INA226_write(INA226_CALIB, (uint16_t)round(5120 / ShuntR)); // 5120 = 2.5 * 2048


	// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
	//	if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3D))
	if (!display.begin(SSD1306_SWITCHCAPVCC, ADDRES_OLED))
	{ // Address 0x3D for 128x64
		Serial.println(F("SSD1306 allocation failed"));
		for (;;)
			; // Don't proceed, loop forever
	}
	display.clearDisplay();
	display.display();

	delay(5000);

	display.setCursor(0, 0);
	display.setTextSize(1);
	display.setTextColor(WHITE);
	display.println("-- Serial monitor --");
	display.println(" display under here.");

	// レジスタ値の確認用
	display.printf("%04x", INA226_read(INA226_CONFIG));
	display.print(" ");
	display.printf("%04x", INA226_read(INA226_SHUNTV));
	display.print(" ");
	display.printf("%04x", INA226_read(INA226_BUSV));
	display.print(" ");
	display.printf("%04x", INA226_read(INA226_POWER));
	display.println("");

	display.printf("%04x", INA226_read(INA226_CURRENT));
	display.print(" ");
	display.printf("%04x", INA226_read(INA226_CALIB));
	display.print(" ");
	display.printf("%04x", INA226_read(INA226_MASK));
	display.print(" ");
	display.printf("%04x", INA226_read(INA226_ALERTL));
	display.println("");

	display.printf("%04x", INA226_read(INA226_MANU_ID));
	display.print(" ");
	display.printf("%04x", INA226_read(INA226_DIE_ID));
	display.print(" ");

	display.display();

	delay(5000);

	display.clearDisplay();
	display.display();

	display.setCursor(0, 0);
	display.setTextSize(1);
	display.setTextColor(WHITE);
	display.println("-- Serial monitor --");
	display.println(" display under here.");

	display.display();
}

void loop()
{
	float shuntVoltage;
	float shuntCurrentAmps;
	int16_t busVoltage;
	int16_t currentAmps;

	shuntVoltage = INA226_read(INA226_SHUNTV) * 2.5;	// 2.5 : ShuntLSB unit 2.5uV to cf
	shuntCurrentAmps = shuntVoltage / ShuntR;	// mA

	busVoltage = INA226_read(INA226_BUSV) * 1.25;		// 1.25 : BusLSB unit 1.25mV to cf
	currentAmps = INA226_read(INA226_CURRENT);			// mA

	// 下までいったら消してしまう
	if (display.getCursorY() >= 64)
	{
		display.setCursor(0, 16);
		display.writeFillRect(0, 15, 127, 63, BLACK);
		display.display();
	}
	display.setTextSize(1);
	display.setTextColor(WHITE);

	display.println("");

	display.print("Shunt V: ");
	display.print(shuntVoltage);		// シャント電圧
	display.println(" mV");

	display.print("Bus V: ");
	display.print(busVoltage);			// バス電圧
	display.println(" mV");

	display.println("");

	display.print("ShuntLSBA: ");
	display.print(shuntCurrentAmps);	// 計算値
	display.println(" mA");

	display.print("Current A: ");
	display.print(currentAmps);			// INA226出力値
	display.println(" mA");

	display.display();

	delay(333);
}

NAGEEEEよ無駄にorz

大半がプリント文ですのでん

設定値などはこんな感じに。

0x00hのレジスタ(基本設定)

平均値モード:16回
バス電圧測定間隔:332us (デフォルト1.1ms)
シャント電圧測定間隔:332us (デフォルト1.1ms)
動作モード:シャント・バス電圧 継続測定
画面更新頻度:3回/秒

デフォルトでは平均を取らないので数値が荒れまくるかもしか。

16サンプルの平均値とする事でそこそこの信頼性が出て来ると思います。

そして16サンプル分の時間を短縮する為に少し計測サイクルを速めています。

このモジュールでは電流値を計測しているのではなく、電圧値の差動から電流値を求めているごく一般的な(?)シャント測定方法ですので、電圧の測定サイクルが速くなれば計測速度も速くなるという訳。

0x05hのレジスタ(キャリブレーション)

これはシャント抵抗の値である100mΩにあわせ、データシートにある計算式で作ります。

シャント抵抗キャリブレーション:0x0033(51 100mΩ)

例えば1mΩのシャント抵抗を使ったら、この値は5120に
10mΩの抵抗なら512に、という風にキャリブレーションの値を変えてやる必要があります。

計算式は、データシートによるとこんな感じ。

2mΩの例

CAL(2560) = 0.00512(内部係数) / Current 0.001A(10000 固定) * Shunt 0.002 ohm

このモジュールは100mΩの抵抗がついていますので、シャントの箇所は0.1Ωで計算します。

この内部係数は2.5uV x 2048(レジスタの範囲に入れる為の係数)で、 このモジュールの抵抗100mΩの場合は単位を揃えると

51.2 = (0.0000025V x 2048) / (0.001A x 0.1ohm)

こんな感じになるわけです。

レジスタには小数点は入れられないので、51(0x0033h)を設定しています。

勿論INA226が取得したシャント側の電圧値も取得できるので、INA226にキャリブレーションに基づいて計算してもらわず、手動(マイコン側)で電流値を計算しても良いです。

51.2とかいう0.2勿体ない感じなので、コードではShuntLSBA: で計算して出力させていますが、マイコンパワーを使いますので、精度がそれほど必要無いならばIC側にお任せ出来るという訳です。

シャント抵抗値のキャリブレーションの値が整数値になる様な抵抗だと良いですね

また、このボードモジュールの様に、出どころ不明の抵抗がついていて、抵抗値にばらつきがある場合には少し調整してやると良いと思います。

( ノД`)

・・・ミリオームオーダーの抵抗値なんてウチのテスターでは計測出来ませんけどNE ( ノД`)

さてじゃちょっと動かしてみまするね

【やっとか】INA226で電流・電圧を測定してみるテスツ

えぇ、、カオスなまんまですが

本当にそのうち壊すますね( ノД`)

大体ソレっぽい値が取れていると思います。

16サンプルも要らなかったかもですね

手元のテスターの値と比べると、0.5mAくらい高めに出る様子。

大きく異なっていないのでまぁ良しとします。

激安テスターですから、テスターの方が正確とは限りませんですしおすし

このINA226はそこそこ高速に動作する様ですので、沢山収集してプロッターなんかに書き出すと面白いかもしれません。

平均を取らずに出力したら突入電流なんかも捕まえられるかもしれませんね

ただI2Cの速度は超えられませんので、過大な期待は出来ないカモシカ。

データシートを読むのがだるかったですが、ひとまずこんな感じで動作の確認ができました。

一度理解すれば非常に使いやすいICだと思います。

シャント抵抗も大きなものがついていて簡単に交換できそうですので、そのうち当初の目的であった40A超えの電流を計測する為にシャント抵抗を交換するかもしか。

40Aを計るなら、80~100Aのレンジは欲しいですよね

するといくつだろう

2mΩだと分解能が1.25mAとすると、16bit ADCで1.25×32768でざっくり±40960mA(40A)か

余裕は欲しいので1mΩにしてみると、分解能2.5mAのx32768でおよそ±81920mA(80A)か

しかし 80Aも流せる高精度な1mΩ抵抗なんてあるんじゃろか?( ノД`)

3.3Vだと264W、5Vだと400W、12Vだと960Wにもなるんじゃがいも・・・?

1kW流すって、それ金属板ですがな・・・( ノД`)

まぁひとまず足掛かりはできました。

そのうちに・・・。