電流・電力監視IC INA219 をライブラリーを使わずに読む

お手頃価格でモジュールが売っている，電流の測定ができるIC INA219 を，勉強のため，偉大な先人たちが作ったライブラリーを使わずに，データシートを参照しながら読んでみました。すっごく勉強になりました。

# デフォルトで電圧と電流値が得られる

きちんと接続して，レジスターの値を読むだけで電圧と電流が得られます。

# 電圧を読む

`((POINTER ADDRESS 02番のレジスター値)>>3)/*4`で，[mV]が得られます。

```MicroPythonスクリプト例.py
# 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))
```

```実行例.REPL
Bus Voltage Register: 0x2582 9602(d)
  Voltage: 4800[mV]
```

分解能は4mVです。データシートに記載があります。
d[2]には16ビットのレジスター値が入っている前提です。以下同じ

ここではLSBの3ビットは無視します。

# 電流を読む

Calibration Register を設定することで，デバイスから電流（計算）値を読むことができます。

しかし，シャント抵抗の両端電圧は，Calibration Register を設定しなくても読めます。`(POINTER ADDRESS 01番のレジスター値)/100`で，[mV]が得られます。

シャント抵抗の両端の電圧がわかれば，それをシャント抵抗値で割れば電流値が計算できます。

電流 =（シャント抵抗の両端の電圧）/（シャント抵抗の値）

```MicroPythonスクリプト例.py
# 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))
```

```実行例.REPL
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]が得られます。

```MicroPythonスクリプト例.py
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])))
```

```実行例.REPL
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]が得られます。

```MicroPythonスクリプト例.py
print("Power Register: 0x%04X %d(d)" % (d[3],d[3]))
print("  Power: %f[W]"%(d[3]*0.02))
```

```実行例.REPL
Power Register: 0x000B 11(d)
  Power: 0.220000[W]
```

# 補足

- `電流と電力への換算はバックグラウンドで行われる`旨，データシートに記載がありました。そのため，電流と電力をデバイスに計算させても，サンプリング時間には影響がないようです。

# そもそもですが...

I2Cデバイスのレジスターを読み書きするには... 

```MicroPythonスクリプト例.py
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)
```

```実行例.REPL
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]

という感じです。

# 使ったスクリプト

```MicroPythonスクリプト例.py
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))
```

```実行例.REPL
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