お手頃価格でモジュールが売っている,電流の測定ができるIC INA219 を,勉強のため,偉大な先人たちが作ったライブラリーを使わずに,データシートを参照しながら読んでみました。すっごく勉強になりました。
デフォルトで電圧と電流値が得られる
きちんと接続して,レジスターの値を読むだけで電圧と電流が得られます。
電圧を読む
((POINTER ADDRESS 02番のレジスター値)>>3)/*4
で,[mV]が得られます。
# d[2] = Register Value [2]
print("Bus Voltage Register: 0x%04X %d(d)" % (d[2],d[2]))
print(" Voltage: %d[mV]" % ((d[2]>>3)*4))
Bus Voltage Register: 0x2582 9602(d)
Voltage: 4800[mV]
分解能は4mVです。データシートに記載があります。
d[2]には16ビットのレジスター値が入っている前提です。以下同じ
ここではLSBの3ビットは無視します。
電流を読む
Calibration Register を設定することで,デバイスから電流(計算)値を読むことができます。
しかし,シャント抵抗の両端電圧は,Calibration Register を設定しなくても読めます。(POINTER ADDRESS 01番のレジスター値)/100
で,[mV]が得られます。
シャント抵抗の両端の電圧がわかれば,それをシャント抵抗値で割れば電流値が計算できます。
電流 =(シャント抵抗の両端の電圧)/(シャント抵抗の値)
# d[1] = Register Value [1]
print("====")
print("Shunt Voltage Register: 0x%04X %d(d)" % (d[1],d[1]))
print(" Shunt Voltage: %f[mV]"%(toSigned16(d[1])/100))
Shunt Voltage Register: 0xFE19 65049(d)
Shunt Voltage: -4.869999[mV]
ここでは0.1オームのシャント抵抗を使っていますので,-4.869999 / 0.1 ≒ -48.6[mA]となります。
この Shunt Register Voltage は符号付き16ビットです。上記はそれを変換する自前の関数を使っています。
電流を読む 続き
シャント抵抗の値を加味したレジスター値を設定することで,電流の計算をしてくれます。
- Calibrationレジスター(POINTER ADDRESS 05番)に,
40.96/シャント抵抗の値
を書き込みます。 -
POINTER ADDRESS 4番のレジスター値
を読むことで,電流値[mA]が得られます。
writeRegisterI2C16(5, 40.96 / 0.1) # Rshunt=0.1
d=readAllRegister()
print("Current Register: 0x%04X %d(d)" % (d[4],d[4]))
print(" Current: %f[mA]"%(toSigned16(d[4])))
Current Register: 0xFFCF 65487(d)
Current: -49.000001[mA]
このレジスターは符号付きです。
writeRegisterI2C16
は自前で書いたINA219のレジスターに書く関数,readAllRegister
は6個すべてのINA219のレジスター値を読む関数です。
消費電力を読む
Calibrationレジスターが設定されていればINA219が計算してPowerレジスター(POINTER ADDRESS 03番)に書いてくれます。(POINTER ADDRESS 03番のレジスター値)*0.02
で,[W]が得られます。
print("Power Register: 0x%04X %d(d)" % (d[3],d[3]))
print(" Power: %f[W]"%(d[3]*0.02))
Power Register: 0x000B 11(d)
Power: 0.220000[W]
補足
-
電流と電力への換算はバックグラウンドで行われる
旨,データシートに記載がありました。そのため,電流と電力をデバイスに計算させても,サンプリング時間には影響がないようです。
そもそもですが...
I2Cデバイスのレジスターを読み書きするには...
from machine import Pin, I2C
i2c = I2C(sda=Pin(4), scl=Pin(5), freq=400000)
def readRegisterI2C16(a): # a=pointer address
i2c.writeto(64,bytearray([a]))
d=list(i2c.readfrom(64,2))
return(d[0]*256+d[1])
def writeRegisterI2C16(a, d): # a=pointer address, d=16bit data
i2c.writeto(64,bytearray([a, int(d)>>8, int(d) & 0xff]))
def readAllRegister():
return([
readRegisterI2C16(0), # Configuration
readRegisterI2C16(1), # Shunt Voltage
readRegisterI2C16(2), # Bus Voltage
readRegisterI2C16(3), # Power
readRegisterI2C16(4), # Current
readRegisterI2C16(5) # Calibration
])
def printRegisterSummery(d):
print("ADD: 0:con 1:s-v 2:b-v 3:pow 4:cur 5:cal")
print("REG: ",end="")
for i in range(6):
print("%04X "%(d[i]),end="")
print("")
writeRegisterI2C16(5, 40.96 / 0.1) # Rshunt=0.1
d=readAllRegister()
printRegisterSummery(d)
ADD: 0:con 1:s-v 2:b-v 3:pow 4:cur 5:cal
REG: 399F FE19 2582 000B FFCF 0198
(2022/4/8 修正)
データシート16ページ目に記載がありました。
レジスターに16ビット書く場合は,
[pointer address W] [上位 8バイト W] [下位 8ビット W] [ストップビット]
の順で書きます。
レジスターから読む場合は,
[pointer address W] [ストップビット] [上位 8バイト R] [下位 8ビット R]
という感じです。
使ったスクリプト
from machine import Pin, I2C
i2c = I2C(sda=Pin(4), scl=Pin(5), freq=400000)
def readRegisterI2C16(a): # a=pointer address
i2c.writeto(64,bytearray([a]))
d=list(i2c.readfrom(64,2))
return(d[0]*256+d[1])
def writeRegisterI2C16(a, d): # a=pointer address, d=16bit data
i2c.writeto(64,bytearray([a, int(d)>>8, int(d) & 0xff]))
def readAllRegister():
return([
readRegisterI2C16(0), # Configuration
readRegisterI2C16(1), # Shunt Voltage
readRegisterI2C16(2), # Bus Voltage
readRegisterI2C16(3), # Power
readRegisterI2C16(4), # Current
readRegisterI2C16(5) # Calibration
])
# summery
def printRegisterSummery(d):
print("ADD: 0:con 1:s-v 2:b-v 3:pow 4:cur 5:cal")
print("REG: ",end="")
for i in range(6):
print("%04X "%(d[i]),end="")
print("")
# set shunt resister
print("Set Calibration register as R[SHUNT]=0.1")
print("====")
writeRegisterI2C16(5, 40.96 / 0.1) # Rshunt=0.1
d=readAllRegister()
printRegisterSummery(d)
# Bus Voltage
print("====")
print("Bus Voltage Register: 0x%04X %d(d)" % (d[2],d[2]))
print(" OVF:",(d[2] & 1)) # bit0
print(" CNVR:",((d[2]>>1) & 1)) # bit1
print(" Voltage: %d[mV]" % ((d[2]>>3)*4))
# convert 2's complement (ref)
# https://note.com/suujyou3/n/n35fca266f7b6
def toSigned16(d):
return((d>>15) * (2**15) * (-1) + (d & 0x7FFF))
# Shunt Voltage
print("====")
print("Shunt Voltage Register: 0x%04X %d(d)" % (d[1],d[1]))
print(" Shunt Voltage: %f[mV]"%(toSigned16(d[1])/100))
# Current
print("====")
print("Current Register: 0x%04X %d(d)" % (d[4],d[4]))
print(" Current: %f[mA]"%(toSigned16(d[4])))
# Power
print("====")
print("Power Register: 0x%04X %d(d)" % (d[3],d[3]))
print(" Power: %f[W]"%(d[3]*0.02))
Set Calibration register as R[SHUNT]=0.1
====
ADD: 0:con 1:s-v 2:b-v 3:pow 4:cur 5:cal
REG: 399F FE19 2582 000B FFCF 0198
====
Bus Voltage Register: 0x2582 9602(d)
OVF: 0
CNVR: 1
Voltage: 4800[mV]
====
Shunt Voltage Register: 0xFE19 65049(d)
Shunt Voltage: -4.869999[mV]
====
Current Register: 0xFFCF 65487(d)
Current: -49.000001[mA]
====
Power Register: 0x000B 11(d)
Power: 0.220000[W]
負の電流値を確認するために,あえて
VBUS - VIN- ... Vin+ - 100Ω抵抗 - GND
と接続してみました。モジュール内蔵のシャント抵抗は0.1オームです。
Thanks to 感謝です!
https://qiita.com/kanade_k_1228/items/3234317e456a5999f663
https://garchiving.com/current-voltage-measurement-with-arduino/
https://qiita.com/z589app/items/2818483fbbbc233762ec