Skip to content

Commit 9b37671

Browse files
authored
Merge pull request #9 from barbudor/master
Fixes Issue#5 and use adafruit_register
2 parents c57fb34 + 3056c2c commit 9b37671

File tree

4 files changed

+181
-108
lines changed

4 files changed

+181
-108
lines changed

adafruit_ina219.py

Lines changed: 148 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -46,71 +46,73 @@
4646
from micropython import const
4747
from adafruit_bus_device.i2c_device import I2CDevice
4848

49+
from adafruit_register.i2c_struct import ROUnaryStruct, UnaryStruct
50+
from adafruit_register.i2c_bits import ROBits, RWBits
51+
from adafruit_register.i2c_bit import ROBit
52+
4953
__version__ = "0.0.0-auto.0"
5054
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_INA219.git"
5155

5256
# Bits
5357
# pylint: disable=bad-whitespace
54-
_READ = const(0x01)
58+
# pylint: disable=too-few-public-methods
5559

5660
# Config Register (R/W)
57-
_REG_CONFIG = const(0x00)
58-
_CONFIG_RESET = const(0x8000) # Reset Bit
59-
60-
_CONFIG_BVOLTAGERANGE_MASK = const(0x2000) # Bus Voltage Range Mask
61-
_CONFIG_BVOLTAGERANGE_16V = const(0x0000) # 0-16V Range
62-
_CONFIG_BVOLTAGERANGE_32V = const(0x2000) # 0-32V Range
63-
64-
_CONFIG_GAIN_MASK = const(0x1800) # Gain Mask
65-
_CONFIG_GAIN_1_40MV = const(0x0000) # Gain 1, 40mV Range
66-
_CONFIG_GAIN_2_80MV = const(0x0800) # Gain 2, 80mV Range
67-
_CONFIG_GAIN_4_160MV = const(0x1000) # Gain 4, 160mV Range
68-
_CONFIG_GAIN_8_320MV = const(0x1800) # Gain 8, 320mV Range
69-
70-
_CONFIG_BADCRES_MASK = const(0x0780) # Bus ADC Resolution Mask
71-
_CONFIG_BADCRES_9BIT = const(0x0080) # 9-bit bus res = 0..511
72-
_CONFIG_BADCRES_10BIT = const(0x0100) # 10-bit bus res = 0..1023
73-
_CONFIG_BADCRES_11BIT = const(0x0200) # 11-bit bus res = 0..2047
74-
_CONFIG_BADCRES_12BIT = const(0x0400) # 12-bit bus res = 0..4097
75-
76-
_CONFIG_SADCRES_MASK = const(0x0078) # Shunt ADC Resolution and Averaging Mask
77-
_CONFIG_SADCRES_9BIT_1S_84US = const(0x0000) # 1 x 9-bit shunt sample
78-
_CONFIG_SADCRES_10BIT_1S_148US = const(0x0008) # 1 x 10-bit shunt sample
79-
_CONFIG_SADCRES_11BIT_1S_276US = const(0x0010) # 1 x 11-bit shunt sample
80-
_CONFIG_SADCRES_12BIT_1S_532US = const(0x0018) # 1 x 12-bit shunt sample
81-
_CONFIG_SADCRES_12BIT_2S_1060US = const(0x0048) # 2 x 12-bit shunt samples averaged together
82-
_CONFIG_SADCRES_12BIT_4S_2130US = const(0x0050) # 4 x 12-bit shunt samples averaged together
83-
_CONFIG_SADCRES_12BIT_8S_4260US = const(0x0058) # 8 x 12-bit shunt samples averaged together
84-
_CONFIG_SADCRES_12BIT_16S_8510US = const(0x0060) # 16 x 12-bit shunt samples averaged together
85-
_CONFIG_SADCRES_12BIT_32S_17MS = const(0x0068) # 32 x 12-bit shunt samples averaged together
86-
_CONFIG_SADCRES_12BIT_64S_34MS = const(0x0070) # 64 x 12-bit shunt samples averaged together
87-
_CONFIG_SADCRES_12BIT_128S_69MS = const(0x0078) # 128 x 12-bit shunt samples averaged together
88-
89-
_CONFIG_MODE_MASK = const(0x0007) # Operating Mode Mask
90-
_CONFIG_MODE_POWERDOWN = const(0x0000)
91-
_CONFIG_MODE_SVOLT_TRIGGERED = const(0x0001)
92-
_CONFIG_MODE_BVOLT_TRIGGERED = const(0x0002)
93-
_CONFIG_MODE_SANDBVOLT_TRIGGERED = const(0x0003)
94-
_CONFIG_MODE_ADCOFF = const(0x0004)
95-
_CONFIG_MODE_SVOLT_CONTINUOUS = const(0x0005)
96-
_CONFIG_MODE_BVOLT_CONTINUOUS = const(0x0006)
97-
_CONFIG_MODE_SANDBVOLT_CONTINUOUS = const(0x0007)
61+
_REG_CONFIG = const(0x00)
62+
63+
class BusVoltageRange:
64+
"""Constants for ``bus_voltage_range``"""
65+
RANGE_16V = 0x00 # set bus voltage range to 16V
66+
RANGE_32V = 0x01 # set bus voltage range to 32V (default)
67+
68+
class Gain:
69+
"""Constants for ``gain``"""
70+
DIV_1_40MV = 0x00 # shunt prog. gain set to 1, 40 mV range
71+
DIV_2_80MV = 0x01 # shunt prog. gain set to /2, 80 mV range
72+
DIV_4_160MV = 0x02 # shunt prog. gain set to /4, 160 mV range
73+
DIV_8_320MV = 0x03 # shunt prog. gain set to /8, 320 mV range
74+
75+
class ADCResolution:
76+
"""Constants for ``bus_adc_resolution`` or ``shunt_adc_resolution``"""
77+
ADCRES_9BIT_1S = 0x00 # 9bit, 1 sample, 84us
78+
ADCRES_10BIT_1S = 0x01 # 10bit, 1 sample, 148us
79+
ADCRES_11BIT_1S = 0x02 # 11 bit, 1 sample, 276us
80+
ADCRES_12BIT_1S = 0x03 # 12 bit, 1 sample, 532us
81+
ADCRES_12BIT_2S = 0x09 # 12 bit, 2 samples, 1.06ms
82+
ADCRES_12BIT_4S = 0x0A # 12 bit, 4 samples, 2.13ms
83+
ADCRES_12BIT_8S = 0x0B # 12bit, 8 samples, 4.26ms
84+
ADCRES_12BIT_16S = 0x0C # 12bit, 16 samples, 8.51ms
85+
ADCRES_12BIT_32S = 0x0D # 12bit, 32 samples, 17.02ms
86+
ADCRES_12BIT_64S = 0x0E # 12bit, 64 samples, 34.05ms
87+
ADCRES_12BIT_128S = 0x0F # 12bit, 128 samples, 68.10ms
88+
89+
class Mode:
90+
"""Constants for ``mode``"""
91+
POWERDOW = 0x00 # power down
92+
SVOLT_TRIGGERED = 0x01 # shunt voltage triggered
93+
BVOLT_TRIGGERED = 0x02 # bus voltage triggered
94+
SANDBVOLT_TRIGGERED = 0x03 # shunt and bus voltage triggered
95+
ADCOFF = 0x04 # ADC off
96+
SVOLT_CONTINUOUS = 0x05 # shunt voltage continuous
97+
BVOLT_CONTINUOUS = 0x06 # bus voltage continuous
98+
SANDBVOLT_CONTINUOUS = 0x07 # shunt and bus voltage continuous
9899

99100
# SHUNT VOLTAGE REGISTER (R)
100-
_REG_SHUNTVOLTAGE = const(0x01)
101+
_REG_SHUNTVOLTAGE = const(0x01)
101102

102103
# BUS VOLTAGE REGISTER (R)
103-
_REG_BUSVOLTAGE = const(0x02)
104+
_REG_BUSVOLTAGE = const(0x02)
104105

105106
# POWER REGISTER (R)
106-
_REG_POWER = const(0x03)
107+
_REG_POWER = const(0x03)
107108

108109
# CURRENT REGISTER (R)
109-
_REG_CURRENT = const(0x04)
110+
_REG_CURRENT = const(0x04)
110111

111112
# CALIBRATION REGISTER (R/W)
112-
_REG_CALIBRATION = const(0x05)
113-
# pylint: enable=bad-whitespace
113+
_REG_CALIBRATION = const(0x05)
114+
# pylint: enable=too-few-public-methods
115+
114116

115117
def _to_signed(num):
116118
if num > 0x7FFF:
@@ -119,63 +121,115 @@ def _to_signed(num):
119121

120122
class INA219:
121123
"""Driver for the INA219 current sensor"""
124+
125+
# Basic API:
126+
127+
# INA219( i2c_bus, i2c_addr) Create instance of INA219 sensor
128+
# :param i2c_bus The I2C bus the INA219is connected to
129+
# :param i2c_addr (0x40) Address of the INA219 on the bus (default 0x40)
130+
131+
# shunt_voltage RO : shunt voltage scaled to Volts
132+
# bus_voltage RO : bus voltage (V- to GND) scaled to volts (==load voltage)
133+
# current RO : current through shunt, scaled to mA
134+
# power RO : power consumption of the load, scaled to Watt
135+
# set_calibration_32V_2A() Initialize chip for 32V max and up to 2A (default)
136+
# set_calibration_32V_1A() Initialize chip for 32V max and up to 1A
137+
# set_calibration_16V_400mA() Initialize chip for 16V max and up to 400mA
138+
139+
# Advanced API:
140+
# config register break-up
141+
# reset WO : Write Reset.RESET to reset the chip (must recalibrate)
142+
# bus_voltage_range RW : Bus Voltage Range field (use BusVoltageRange.XXX constants)
143+
# gain RW : Programmable Gain field (use Gain.XXX constants)
144+
# bus_adc_resolution RW : Bus ADC resolution and averaging modes (ADCResolution.XXX)
145+
# shunt_adc_resolution RW : Shunt ADC resolution and averaging modes (ADCResolution.XXX)
146+
# mode RW : operating modes in config register (use Mode.XXX constants)
147+
148+
# raw_shunt_voltage RO : Shunt Voltage register (not scaled)
149+
# raw_bus_voltage RO : Bus Voltage field in Bus Voltage register (not scaled)
150+
# conversion_ready RO : Conversion Ready bit in Bus Voltage register
151+
# overflow RO : Math Overflow bit in Bus Voltage register
152+
# raw_power RO : Power register (not scaled)
153+
# raw_current RO : Current register (not scaled)
154+
# calibration RW : calibration register (note: value is cached)
155+
122156
def __init__(self, i2c_bus, addr=0x40):
123157
self.i2c_device = I2CDevice(i2c_bus, addr)
124-
125158
self.i2c_addr = addr
126-
# Multiplier in mA used to determine current from raw reading
127-
self._current_lsb = 0
128-
# Multiplier in W used to determine power from raw reading
129-
self._power_lsb = 0
130159

131160
# Set chip to known config values to start
132-
self._cal_value = 4096
161+
self._cal_value = 0
162+
self._current_lsb = 0
163+
self._power_lsb = 0
133164
self.set_calibration_32V_2A()
134165

135-
def _write_register(self, reg, value):
136-
seq = bytearray([reg, (value >> 8) & 0xFF, value & 0xFF])
137-
with self.i2c_device as i2c:
138-
i2c.write(seq)
166+
# config register break-up
167+
reset = RWBits( 1, _REG_CONFIG, 15, 2, False)
168+
bus_voltage_range = RWBits( 1, _REG_CONFIG, 13, 2, False)
169+
gain = RWBits( 2, _REG_CONFIG, 11, 2, False)
170+
bus_adc_resolution = RWBits( 4, _REG_CONFIG, 7, 2, False)
171+
shunt_adc_resolution = RWBits( 4, _REG_CONFIG, 3, 2, False)
172+
mode = RWBits( 3, _REG_CONFIG, 0, 2, False)
173+
174+
# shunt voltage register
175+
raw_shunt_voltage = ROUnaryStruct(_REG_SHUNTVOLTAGE, ">h")
139176

140-
def _read_register(self, reg):
141-
buf = bytearray(3)
142-
buf[0] = reg
143-
with self.i2c_device as i2c:
144-
i2c.write(buf, end=1, stop=False)
145-
i2c.readinto(buf, start=1)
177+
#bus voltage register
178+
raw_bus_voltage = ROBits( 12, _REG_BUSVOLTAGE, 3, 2, False)
179+
conversion_ready = ROBit( _REG_BUSVOLTAGE, 1, 2, False)
180+
overflow = ROBit( _REG_BUSVOLTAGE, 0, 2, False)
146181

147-
value = (buf[1] << 8) | (buf[2])
148-
return value
182+
# power and current registers
183+
raw_power = ROUnaryStruct(_REG_POWER, ">H")
184+
raw_current = ROUnaryStruct(_REG_CURRENT, ">h")
185+
186+
# calibration register
187+
_raw_calibration = UnaryStruct(_REG_CALIBRATION, ">H")
188+
189+
@property
190+
def calibration(self):
191+
"""Calibration register (cached value)"""
192+
return self._cal_value # return cached value
193+
194+
@calibration.setter
195+
def calibration(self, cal_value):
196+
self._cal_value = cal_value # value is cached for ``current`` and ``power`` properties
197+
self._raw_calibration = self._cal_value
149198

150199
@property
151200
def shunt_voltage(self):
152201
"""The shunt voltage (between V+ and V-) in Volts (so +-.327V)"""
153-
value = _to_signed(self._read_register(_REG_SHUNTVOLTAGE))
154202
# The least signficant bit is 10uV which is 0.00001 volts
155-
return value * 0.00001
203+
return self.raw_shunt_voltage * 0.00001
156204

157205
@property
158206
def bus_voltage(self):
159207
"""The bus voltage (between V- and GND) in Volts"""
160-
raw_voltage = self._read_register(_REG_BUSVOLTAGE)
161-
162208
# Shift to the right 3 to drop CNVR and OVF and multiply by LSB
163209
# Each least signficant bit is 4mV
164-
voltage_mv = _to_signed(raw_voltage >> 3) * 4
165-
return voltage_mv * 0.001
210+
return self.raw_bus_voltage * 0.004
166211

167212
@property
168213
def current(self):
169214
"""The current through the shunt resistor in milliamps."""
170215
# Sometimes a sharp load will reset the INA219, which will
171216
# reset the cal register, meaning CURRENT and POWER will
172-
# not be available ... athis by always setting a cal
217+
# not be available ... always setting a cal
173218
# value even if it's an unfortunate extra step
174-
self._write_register(_REG_CALIBRATION, self._cal_value)
219+
self._raw_calibration = self._cal_value
220+
# Now we can safely read the CURRENT register!
221+
return self.raw_current * self._current_lsb
175222

223+
@property
224+
def power(self):
225+
"""The power through the load in Watt."""
226+
# Sometimes a sharp load will reset the INA219, which will
227+
# reset the cal register, meaning CURRENT and POWER will
228+
# not be available ... always setting a cal
229+
# value even if it's an unfortunate extra step
230+
self._raw_calibration = self._cal_value
176231
# Now we can safely read the CURRENT register!
177-
raw_current = _to_signed(self._read_register(_REG_CURRENT))
178-
return raw_current * self._current_lsb
232+
return self.raw_power * self._power_lsb
179233

180234
def set_calibration_32V_2A(self): # pylint: disable=invalid-name
181235
"""Configures to INA219 to be able to measure up to 32V and 2A of current. Counter
@@ -249,15 +303,14 @@ def set_calibration_32V_2A(self): # pylint: disable=invalid-name
249303
# MaximumPower = 102.4W
250304

251305
# Set Calibration register to 'Cal' calculated above
252-
self._write_register(_REG_CALIBRATION, self._cal_value)
306+
self._raw_calibration = self._cal_value
253307

254308
# Set Config register to take into account the settings above
255-
config = _CONFIG_BVOLTAGERANGE_32V | \
256-
_CONFIG_GAIN_8_320MV | \
257-
_CONFIG_BADCRES_12BIT | \
258-
_CONFIG_SADCRES_12BIT_1S_532US | \
259-
_CONFIG_MODE_SANDBVOLT_CONTINUOUS
260-
self._write_register(_REG_CONFIG, config)
309+
self.bus_voltage_range = BusVoltageRange.RANGE_32V
310+
self.gain = Gain.DIV_8_320MV
311+
self.bus_adc_resolution = ADCResolution.ADCRES_12BIT_1S
312+
self.shunt_adc_resolution = ADCResolution.ADCRES_12BIT_1S
313+
self.mode = Mode.SANDBVOLT_CONTINUOUS
261314

262315
def set_calibration_32V_1A(self): # pylint: disable=invalid-name
263316
"""Configures to INA219 to be able to measure up to 32V and 1A of current. Counter overflow
@@ -332,15 +385,14 @@ def set_calibration_32V_1A(self): # pylint: disable=invalid-name
332385
# MaximumPower = 41.94176W
333386

334387
# Set Calibration register to 'Cal' calculated above
335-
self._write_register(_REG_CALIBRATION, self._cal_value)
388+
self._raw_calibration = self._cal_value
336389

337390
# Set Config register to take into account the settings above
338-
config = (_CONFIG_BVOLTAGERANGE_32V |
339-
_CONFIG_GAIN_8_320MV |
340-
_CONFIG_BADCRES_12BIT |
341-
_CONFIG_SADCRES_12BIT_1S_532US |
342-
_CONFIG_MODE_SANDBVOLT_CONTINUOUS)
343-
self._write_register(_REG_CONFIG, config)
391+
self.bus_voltage_range = BusVoltageRange.RANGE_32V
392+
self.gain = Gain.DIV_8_320MV
393+
self.bus_adc_resolution = ADCResolution.ADCRES_12BIT_1S
394+
self.shunt_adc_resolution = ADCResolution.ADCRES_12BIT_1S
395+
self.mode = Mode.SANDBVOLT_CONTINUOUS
344396

345397
def set_calibration_16V_400mA(self): # pylint: disable=invalid-name
346398
"""Configures to INA219 to be able to measure up to 16V and 400mA of current. Counter
@@ -416,12 +468,11 @@ def set_calibration_16V_400mA(self): # pylint: disable=invalid-name
416468
# MaximumPower = 6.4W
417469

418470
# Set Calibration register to 'Cal' calculated above
419-
self._write_register(_REG_CALIBRATION, self._cal_value)
471+
self._raw_calibration = self._cal_value
420472

421473
# Set Config register to take into account the settings above
422-
config = (_CONFIG_BVOLTAGERANGE_16V |
423-
_CONFIG_GAIN_1_40MV |
424-
_CONFIG_BADCRES_12BIT |
425-
_CONFIG_SADCRES_12BIT_1S_532US |
426-
_CONFIG_MODE_SANDBVOLT_CONTINUOUS)
427-
self._write_register(_REG_CONFIG, config)
474+
self.bus_voltage_range = BusVoltageRange.RANGE_16V
475+
self.gain = Gain.DIV_1_40MV
476+
self.bus_adc_resolution = ADCResolution.ADCRES_12BIT_1S
477+
self.shunt_adc_resolution = ADCResolution.ADCRES_12BIT_1S
478+
self.mode = Mode.SANDBVOLT_CONTINUOUS

examples/ina219_simpletest.py

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,42 @@
1-
import time
1+
"""Sample code and test for adafruit_in219"""
22

3-
from board import SCL, SDA
4-
import busio
3+
import time
4+
import board
5+
from adafruit_ina219 import ADCResolution, BusVoltageRange, INA219
56

6-
import adafruit_ina219
77

8-
i2c_bus = busio.I2C(SCL, SDA)
8+
i2c_bus = board.I2C()
99

10-
ina219 = adafruit_ina219.INA219(i2c_bus)
10+
ina219 = INA219(i2c_bus)
1111

1212
print("ina219 test")
1313

14+
# display some of the advanced field (just to test)
15+
print("Config register:")
16+
print(" bus_voltage_range: 0x%1X" % ina219.bus_voltage_range)
17+
print(" gain: 0x%1X" % ina219.gain)
18+
print(" bus_adc_resolution: 0x%1X" % ina219.bus_adc_resolution)
19+
print(" shunt_adc_resolution: 0x%1X" % ina219.shunt_adc_resolution)
20+
print(" mode: 0x%1X" % ina219.mode)
21+
print("")
22+
23+
# optional : change configuration to use 32 samples averaging for both bus voltage and shunt voltage
24+
ina219.bus_adc_resolution = ADCResolution.ADCRES_12BIT_32S
25+
ina219.shunt_adc_resolution = ADCResolution.ADCRES_12BIT_32S
26+
# optional : change voltage range to 16V
27+
ina219.bus_voltage_range = BusVoltageRange.RANGE_16V
28+
29+
# measure and display loop
1430
while True:
15-
print("Bus Voltage: {} V".format(ina219.bus_voltage))
16-
print("Shunt Voltage: {} mV".format(ina219.shunt_voltage / 1000))
17-
print("Load Voltage: {} V".format(ina219.bus_voltage + ina219.shunt_voltage))
18-
print("Current: {} mA".format(ina219.current))
31+
bus_voltage = ina219.bus_voltage # voltage on V- (load side)
32+
shunt_voltage = ina219.shunt_voltage # voltage between V+ and V- across the shunt
33+
current = ina219.current # current in mA
34+
35+
# INA219 measure bus voltage on the load side. So PSU voltage = bus_voltage + shunt_voltage
36+
print("PSU Voltage: {:6.3f} V".format(bus_voltage + shunt_voltage))
37+
print("Shunt Voltage: {:9.6f} V".format(shunt_voltage))
38+
print("Load Voltage: {:6.3f} V".format(bus_voltage))
39+
print("Current: {:9.6f} A".format(current/1000))
1940
print("")
2041

2142
time.sleep(2)

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
Adafruit-Blinka
22
adafruit-circuitpython-busdevice
3+
adafruit-circuitpython-register

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
author='Adafruit Industries',
3535
author_email='[email protected]',
3636

37-
install_requires=['Adafruit-Blinka', 'adafruit-circuitpython-busdevice'],
37+
install_requires=['Adafruit-Blinka', 'adafruit-circuitpython-busdevice', 'adafruit-circuitpython-register'],
3838

3939
# Choose your license
4040
license='MIT',

0 commit comments

Comments
 (0)