まえがき

ストロベリーリナックスINA226基板太陽電池充電器(太陽電池充電アダプター)を製作したとき、充電器入出力の電圧と電流をする際に使用しました。バッテリーは充電時と放電時で電流の方向が変わるので正負とも計測する必要がありました。

電圧/電流計測用デバイスでTexasのINA226があります。
インターフェイスはI2C、16ビットA/Dコンバーター、
測定電圧は36V、電流はシャント抵抗により正負とも測定、
電源電圧は2.7Vから5.5Vです。
ただしピン間0.5ミリピッチのパッケージで手半田が厳しいため、ストロベリーリナックス社のDIP基板に載って0.002Ωのシャント抵抗と端子までついた型番がICと同じINA226を使いました。なお電流は+−20Aまで計測できます。入出力なので2つ使用しました。

INA226とPIC16F88の回路図

INA226回路図
PICでIC2インターフェースを使用するのは今回が初めてです。今まではUSARTかSPIで済んでいました。従いまして探りながら試した結果ですが無事動作しました。良く分かっていない部分も残っています。

プルアップ抵抗は3.3kΩを選択しました。
I2Cにはmasterとslaveがあることは知っていました。
当然PICがmasterとなりますが、全てのPICがハードウエアでmasterとして使える訳ではありません。そもそもPICはINA226のようにslaveとして使用される周辺機器を想定しているからでしょうか。上記回路図の16F88もハードウエアI2Cはslaveです。
でも、ソフトウエア(コンパイラ)にてmasterとして使用できるようです。使用するピンはハードウエアI2C用のピンをそのまま使用することとします。

INA226のslaveアドレスの設定

ストロベリーリナックスINA226基板のslaveアドレス設定の様子INA226のデータシート Table 7. INA226 Address Pins and Slave Addresses

IC2は1組のケーブルに複数のslave機器をぶら下げて使用しますので、masterから区別するためslaveアドレスが必要です。IN226ではアドレス設定がA1/A0とVS+/GND/SCL/SDAの「組み合わせショート」で16種類選択できます。写真のように半田でシートしています。
基板上の1→VS+
基板上のG→GND
基板上のC→SCL
基板上のD→SDA
従って、写真下の基板がVINで上の基板がVOUTとなっています。
このアドレス設定は不思議で仕組みが良く分かっていません。
これでハードウエアは完成です。

  

CCS Cの記述 定義部からポートの初期設定まで

【定義部】
<省略>
PICは内部4MHZ発振、ウオッチドクタイマー使用、リセット外部としました。
#include <16f88.h> //*
#fuses INTRC_IO,WDT,NOPROTECT,NOBROWNOUT,PUT,NOLVP,NODEBUG,MCLR
#use delay(CLOCK = 4000000)
#use rs232(BAUD = 4800, XMIT = PIN_B5, RCV = PIN_B2)
#use fast_io(A)
ここでIC2の設定。左から順にマスター、ピン設定、低速度、ソフトウエアI2C
#use i2c(master, sda=PIN_B1, scl=PIN_B4,SLOW,FORCE_SW)
<省略>
//
下記「port_b_io」でBポートのIO設定。0が出力、1が入力
#define port_b_io 0b00000101//
//RB0 :in not used
//RB1 :i/o i2c sda 取りあえず出力モード
//RB2 :in RX
//RB3 :out
//RB4 :i/o i2c scl 取りあえず出力モード
//RB5 :out TX
//RB6 :out
//RB7 :out
<省略>
【MAIN部】
<省略
void main() {
<省略>
set_tris_B(port_b_io);//ここでPIN_B1とPIN_B4は出力モードになる。
output_float(PIN_B1); //I2CピンをFLOATモードにする。実際は入出力ピンなのでハイインピーダンスする
output_float(PIN_B4); //I2CピンをFLOATモードにする。実際は入出力ピンなのでハイインピーダンスする
<省略>
下記、CCS Cの記述 INA226との実際の通信へ続く

上記記の定義ではっきりしないことは、通信速度の指定です。
上記の例では低速であるSLOW指定をしましたが実際の速度の記載がCCS C Ver4マニュアルにはありません。(100KHZを指定したつもりです)
また、CCS C Ver4マニュアルではi2c_speed(baud)で速度を変更出来そうなのですが、「ハードウエアI2Cモジュールが使用されようとしているのみに動作します」との記載です。このデバイスはslaveIC2ハードウエアはしかありませんので、masterI2Cはソフトウエア設定になります。このような場合でもi2c_speed(baud)による速度変更ができるのか試してみましたが速度に変化はありませんでした。
また、仮に100KHZとした場合、波長は10μsとなりますがI2Cの信号を波形観測したとき、クロックのどこからどこまでが10μsに該当するのかがデータシートから読み取れませんでした。

  

INA226のデータレジスタの関係性

INA226のデータシート Figure 1. INA226 Register Block Diagram

Figure 1のようにINA226には幾つかのレジスタがあって、計測結果はレジスタを読むことで取得できます。
また、事前に設定が必要なレジスタもあります。
A/Dは自動的に無限ループで行われ、直近のA/Dの結果がレジスタに入っている、という具合です。必要なときにPICからA/Dを開始させA/D変換時間を待ってから取り出すのではありません。

Shunt Voltageレジスタは0.002Ωのシャント抵抗端の電圧測定結果が入ります。でもこのままでは電圧なので「計算」されてCurrentレジスタへ送られます。その時の「計算」にCalibrationレジスタが使われます。
Bus VoltageレジスタはVBUS端子の電圧測定結果が入ります。以上はINA226が自ら行う動作でPICの制御は不要なものです。

  

INA226のデータレジスタとConfigurationレジスタ

INA226のデータシート Table 1. Configure/Measure/Calculate Exampleの説明
Bus電圧と電流値は上記の説明の通りとなっています。上記をまとめると・・・
電圧はBus Voltageレジスタの値×1.25がmV
電流はCurrentレジスタの値がmA

Configurationレジスタがあり、各種設定を行います。他のレジスタは電源リセット後の初期値が0ですが、このレジスタにのみ「4127h」という初期値データが入っているようで、この値のままで良いなら設定は不要かもしれません。(試していません) (後述のTable 2. Summary of Register SetのPOWER-ON RESET項目に記載があります)
初期値ではデータの平均を取っていないので、16回へ変更しました。他に変更する可能性がある値としてはA/Dの変換時間で、Bus VoltageとShunt Voltageがそれぞれ設定出来ます。他は変更する必要はないようです。変更後は4527hとなりました。この設定はプログラムで1度実行すれば良さそうです。

  

INA226のBus VoltageレジスタのLSBが1.25mVであることの確認

INA226のBus VoltageレジスタのLSBが1.25mVであることの確認資料

  

INA226のShunt VoltageレジスタのLSBが2.5μVであることの確認

INA226のShunt VoltageレジスタのLSBが2.5μVであることの確認資料

  

CCS Cの記述 INA226との実際の通信

まず、下記「Table 2. Summary of Register Set」より設定可能なレジスタはTYPEの項目がR/Wとなっているもので、4つありますが「アラート」関係の2つは除きます。残るのはConfigurationレジスタとCalibrationレジスタです。   INA226データシート Table 2. Summary of Register Set
レジスタへの1連の読み書きは下記の様にi2c_start()とslaveアドレスのセットで始まり、i2c_stop()で終えます。
i2c_start(); //start condition
i2c_write(0x94);//VIN INA226 address send (0b1001010 0)最下位ビットはライトモードで0)
<省略>
i2c_stop(); //stop condition

ここで上記のslaveアドレスのセットについて説明します。
slaveを読むにしても書くにしても対象となるslaveアドレスを指定する必要があります。
VIN用のINA226slaveアドレス(前述のTable 7. INA226 Address Pins and Slave Addressesを参照) は1001010です。
これは7ビットなので最下位に1ビット加えて8ビットとすると1001 010Xとなります。
slaveに対して書き込む場合、slaveアドレスのXビットを0にしますので1001 0100=94hとなります。
slaveから読む場合は、slaveアドレスのXビットを1にしますので1001 0101=95hとなります。別な表現で表わすとslaveアドレス設定の後がi2c_writeだったら94h、i2c_readだったら95hということです。
従って上記の場合、<省略>の部分でConfigurationレジスタへ書き込むのつもりなので、slaveアドレスとして94hをWriteします。

実際にConfigurationレジスタへ4527hを、Configurationレジスタへ0A00hをそれぞれ書き込むソース「INA226レジスタ初期設定VIN側」を下記に示します。(各レジスタのアドレス及び書き込む値は前述のTable 1. Configure/Measure/Calculate ExampleのADDRESS項目とCONTENTS項目を参照)
レジスタにはそれぞれ1byteのアドレスが割り振られています。例えば、Configurationレジスタは00hです。従ってConfigurationレジスタのアドレス00hをWriteします。(これはこの後Configurationレジスタへ書込むので準備しなさいという意味)
また1度の通信では1byteしか送信出来ないので、4527hは2回に分けてWriteします。従ってConfigurationレジスタの上位1バイトデータ45hをWriteして、Configurationレジスタの下位位1バイトデータ27hをWriteします。
Calibrationレジスタも同様です。

//INA226レジスタ初期設定VIN側
i2c_start(); //start condition
i2c_write(0x94);//VIN INA226 address send (0b1001010 0)最下位ビットはライトモードで0)
i2c_write(0x00);//INA226 Configuration register address 00h send
i2c_write(0x45);//INA226 Configuration register 上位1バイトデータ send 16回の平均
i2c_write(0x27);//INA226 Configuration register 下位1バイトデータ send
i2c_stop(); //stop condition
i2c_start(); //start condition
i2c_write(0x94);//VIN INA226 address send (0b1001010 0)最下位ビットはライトモードで0)
i2c_write(0x05);//INA226 Calibration Register address 05h send
i2c_write(0x0a);//INA226 Calibration Register 上位1バイトデータ send
i2c_write(0x00);//INA226 Calibration Register 下位1バイトデータ send
i2c_stop(); //stop condition

上記設定は電投入後に1度だけ実行します。

次に電圧の取得です。
前述のレジスタへの設定と少し異なる点は、1連のことを実行するのにi2c_start()からi2c_stop()を2度行うことです。最初は対象のレジスタを指定すること、次に対象のレジスタから読み出すことです。
slaveアドレス設定するところまでは今までと一緒です。Bus registerを読みたいので、Bus registerのアドレスをWriteします。これは、この後Bus registerを読みますので準備しなさいという意味です。・・・ここまで半分完了。
再度、slaveアドレス設定をしますが、今度は読み込みなので95hをWriteします。1度の通信では1byteしか送信出来ないので、Bus registerを2回に分けてreadします。
ここで最初のreadがi2c_read()、後のreadがi2c_read(0)となっています。i2c_read()はi2c_read(1)と同じ意味です。i2c_read(0)は受信終了時にアクノリッジ・ビットを送信しない、i2c_read(1)は受信終了時にアクノリッジ・ビットを送信する、という違いがあります。下記のFigure 25. Timing Diagram for Read Word Formatをよく見ると最後だけNo ACK by masterとありますので、これを実現するためだと思います。

//Bus電圧取得VIN側
i2c_start(); //start condition
i2c_write(0x94);//VIN INA226 address send (0b1001010 0)最下位ビットはライドモードで0)
i2c_write(0x02);//INA226 Bus register address 02h send
i2c_stop(); //stop condition
i2c_start(); //start condition
i2c_write(0x95);//VIN INA226 address send (0b1001010 1)最下位ビットはリードモードで1)
vh1=i2c_read();
vl1=i2c_read(0);
i2c_stop();
voltage_16=(int16)make16(vh1,vl1)*1.25;//これは1バイトデータ2つを2バイトデータにしてから1.25倍しています。左辺の型は符号無しLONGです。
  INA226データシート Figure 25. Timing Diagram for Read Word Format
最後に電流値の取得です。

//Current電流取得VIN側
i2c_start(); //start condition
i2c_write(0x94);//VIN INA226 address send (0b1001010 0)最下位ビットはライドモードで0)
i2c_write(0x04);//INA226 Current register address 04h send
i2c_stop(); //stop condition
i2c_start(); //start condition
i2c_write(0x95);//VIN INA226 address send (0b1001010 1)最下位ビットはリードモードで1)
ch1=i2c_read();
cl1=i2c_read(0);
i2c_stop();
current_s16=make16(ch1,cl1);//これは1バイトデータ2つを2バイトデータにしています。左辺の型は符号有りLONGです。

  

solar_charger.c (CCS Cのソースのダウンロード。メインプログラム)

  

lcd_lib4.c (CCS Cのソースのダウンロード。参考用液晶表示モジュール用サブプログラム)

  

CCS Cの記述 電圧と電流の取得サイクル

当初、最短の無限ループ(周期数ミリ秒程度)で取得していましたが、おかしなデータが発生する問題がありました。I2Cの通信データをオシロ波形で調べたところ、INA226が出力した値とPICが受け取った値は同じでしたので、通信不良が原因ではありません。
今回のA/D変換時間は1.1ms、平均が16回、電圧と電流の両方、IN226は2台なので総変換時間は・・・
1.1ms×16回×2回×2台=70.4msかかります。
A/Dが終わっていないのにレジスタからデータを取り出すからデータがおかしくなるのかもしれないと思い100msの割り込みによる取得に変えたところ正常な値を取得できるようになりました。