Skip to content

Commit 700bd16

Browse files
committed
update to use ina2xx
1 parent 0291582 commit 700bd16

File tree

5 files changed

+36
-309
lines changed

5 files changed

+36
-309
lines changed

README.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ This driver depends on:
3131
* `Adafruit CircuitPython <https://github.com/adafruit/circuitpython>`_
3232
* `Bus Device <https://github.com/adafruit/Adafruit_CircuitPython_BusDevice>`_
3333
* `Register <https://github.com/adafruit/Adafruit_CircuitPython_Register>`_
34+
* `Adafruit CircuitPython INA228 <https://github.com/adafruit/Adafruit_CircuitPython_INA228>`_
3435

3536
Please ensure all dependencies are available on the CircuitPython filesystem.
3637
This is easily achieved by downloading

adafruit_ina23x.py

Lines changed: 29 additions & 306 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,15 @@
2525
2626
* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
2727
* Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
28+
* Adafruit CircuitPython INA228 library: https://github.com/adafruit/Adafruit_CircuitPython_INA228
2829
"""
2930

3031
import time
3132

32-
from adafruit_bus_device.i2c_device import I2CDevice
33-
from adafruit_register.i2c_bit import ROBit, RWBit
33+
from adafruit_ina228 import INA2XX, AlertType
34+
from adafruit_register.i2c_bit import ROBit
3435
from adafruit_register.i2c_bits import ROBits, RWBits
35-
from adafruit_register.i2c_struct import ROUnaryStruct, UnaryStruct
36+
from adafruit_register.i2c_struct import ROUnaryStruct
3637
from micropython import const
3738

3839
try:
@@ -45,225 +46,44 @@
4546
__version__ = "0.0.0+auto.0"
4647
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_INA23x.git"
4748

48-
# Register addresses
49-
_CONFIG = const(0x00)
50-
_ADCCFG = const(0x01)
51-
_SHUNTCAL = const(0x02)
52-
_VSHUNT = const(0x04)
53-
_VBUS = const(0x05)
54-
_DIETEMP = const(0x06)
55-
_CURRENT = const(0x07)
56-
_POWER = const(0x08)
57-
_DIAGALRT = const(0x0B)
58-
_SOVL = const(0x0C)
59-
_SUVL = const(0x0D)
60-
_BOVL = const(0x0E)
61-
_BUVL = const(0x0F)
62-
_TEMPLIMIT = const(0x10)
63-
_PWRLIMIT = const(0x11)
64-
_MFG_UID = const(0x3E)
65-
_DVC_UID = const(0x3F)
49+
_SOVL = const(0x0C) # Shunt Overvoltage Threshold
50+
_SUVL = const(0x0D) # Shunt Undervoltage Threshold
51+
_BOVL = const(0x0E) # Bus Overvoltage Threshold
52+
_BUVL = const(0x0F) # Bus Undervoltage Threshold
53+
_TEMPLIMIT = const(0x10) # Temperature Over-Limit Threshold
54+
_PWRLIMIT = const(0x11) # Power Over-Limit Threshold
6655

6756
# Constants
68-
_INA23X_DEFAULT_ADDR = const(0x40)
69-
_INA237_DEVICE_ID = const(0x238)
70-
_INA238_DEVICE_ID = const(0x238) # Same as INA237
71-
_TEXAS_INSTRUMENTS_ID = const(0x5449)
72-
73-
74-
class Mode:
75-
"""Operating mode constants for INA23X"""
76-
77-
SHUTDOWN = const(0x00)
78-
TRIG_BUS = const(0x01)
79-
TRIG_SHUNT = const(0x02)
80-
TRIG_BUS_SHUNT = const(0x03)
81-
TRIG_TEMP = const(0x04)
82-
TRIG_TEMP_BUS = const(0x05)
83-
TRIG_TEMP_SHUNT = const(0x06)
84-
TRIG_TEMP_BUS_SHUNT = const(0x07)
85-
CONT_BUS = const(0x09)
86-
CONT_SHUNT = const(0x0A)
87-
CONT_BUS_SHUNT = const(0x0B)
88-
CONT_TEMP = const(0x0C)
89-
CONT_TEMP_BUS = const(0x0D)
90-
CONT_TEMP_SHUNT = const(0x0E)
91-
CONT_TEMP_BUS_SHUNT = const(0x0F)
92-
93-
# Convenience aliases
94-
TRIGGERED = TRIG_TEMP_BUS_SHUNT
95-
CONTINUOUS = CONT_TEMP_BUS_SHUNT
96-
97-
# Valid modes set for validation
98-
_VALID_MODES = {
99-
SHUTDOWN,
100-
TRIG_BUS,
101-
TRIG_SHUNT,
102-
TRIG_BUS_SHUNT,
103-
TRIG_TEMP,
104-
TRIG_TEMP_BUS,
105-
TRIG_TEMP_SHUNT,
106-
TRIG_TEMP_BUS_SHUNT,
107-
CONT_BUS,
108-
CONT_SHUNT,
109-
CONT_BUS_SHUNT,
110-
CONT_TEMP,
111-
CONT_TEMP_BUS,
112-
CONT_TEMP_SHUNT,
113-
CONT_TEMP_BUS_SHUNT,
114-
}
115-
116-
117-
class ConversionTime:
118-
"""Conversion time constants for INA23X"""
119-
120-
TIME_50_US = const(0)
121-
TIME_84_US = const(1)
122-
TIME_150_US = const(2)
123-
TIME_280_US = const(3)
124-
TIME_540_US = const(4)
125-
TIME_1052_US = const(5)
126-
TIME_2074_US = const(6)
127-
TIME_4120_US = const(7)
128-
129-
_VALID_TIMES = {
130-
TIME_50_US,
131-
TIME_84_US,
132-
TIME_150_US,
133-
TIME_280_US,
134-
TIME_540_US,
135-
TIME_1052_US,
136-
TIME_2074_US,
137-
TIME_4120_US,
138-
}
139-
140-
141-
class AveragingCount:
142-
"""Averaging count constants for INA23X"""
143-
144-
COUNT_1 = const(0)
145-
COUNT_4 = const(1)
146-
COUNT_16 = const(2)
147-
COUNT_64 = const(3)
148-
COUNT_128 = const(4)
149-
COUNT_256 = const(5)
150-
COUNT_512 = const(6)
151-
COUNT_1024 = const(7)
152-
153-
_VALID_COUNTS = {
154-
COUNT_1,
155-
COUNT_4,
156-
COUNT_16,
157-
COUNT_64,
158-
COUNT_128,
159-
COUNT_256,
160-
COUNT_512,
161-
COUNT_1024,
162-
}
163-
164-
165-
class AlertType:
166-
"""Alert type constants for INA23X"""
167-
168-
NONE = const(0x0)
169-
CONVERSION_READY = const(0x1)
170-
OVERTEMPERATURE = const(0x2)
171-
OVERPOWER = const(0x4)
172-
UNDERVOLTAGE = const(0x8)
173-
OVERVOLTAGE = const(0x10)
174-
UNDERSHUNT = const(0x20)
175-
OVERSHUNT = const(0x40)
176-
177-
_VALID_TYPES = {
178-
NONE,
179-
CONVERSION_READY,
180-
OVERTEMPERATURE,
181-
OVERPOWER,
182-
UNDERVOLTAGE,
183-
OVERVOLTAGE,
184-
UNDERSHUNT,
185-
OVERSHUNT,
186-
}
187-
188-
189-
class INA23X: # noqa: PLR0904
57+
_INA237_DEVICE_ID = const(0x237)
58+
_INA238_DEVICE_ID = const(0x238)
59+
60+
61+
class INA23X(INA2XX): # noqa: PLR0904
19062
"""Driver for the INA237/INA238 current and power sensor.
19163
19264
:param ~busio.I2C i2c_bus: The I2C bus the INA23X is connected to.
19365
:param int address: The I2C device address. Defaults to :const:`0x40`
19466
:param bool skip_reset: Skip resetting the device on init. Defaults to False.
19567
"""
19668

197-
# Configuration register bits
198-
_reset = RWBit(_CONFIG, 15, register_width=2, lsb_first=False)
199-
_adc_range = RWBit(_CONFIG, 4, register_width=2, lsb_first=False)
200-
201-
# ADC Configuration register bits
202-
_mode = RWBits(4, _ADCCFG, 12, register_width=2, lsb_first=False)
203-
_vbus_conv_time = RWBits(3, _ADCCFG, 9, register_width=2, lsb_first=False)
204-
_vshunt_conv_time = RWBits(3, _ADCCFG, 6, register_width=2, lsb_first=False)
205-
_temp_conv_time = RWBits(3, _ADCCFG, 3, register_width=2, lsb_first=False)
206-
_avg_count = RWBits(3, _ADCCFG, 0, register_width=2, lsb_first=False)
207-
208-
# Diagnostic/Alert register bits
209-
_alert_latch = RWBit(_DIAGALRT, 15, register_width=2, lsb_first=False)
210-
_alert_conv = RWBit(_DIAGALRT, 14, register_width=2, lsb_first=False)
211-
_alert_polarity = RWBit(_DIAGALRT, 12, register_width=2, lsb_first=False)
212-
_alert_type = RWBits(7, _DIAGALRT, 5, register_width=2, lsb_first=False)
213-
_conversion_ready = ROBit(_DIAGALRT, 1, register_width=2, lsb_first=False)
214-
_alert_flags = ROBits(12, _DIAGALRT, 0, register_width=2, lsb_first=False)
215-
216-
# Measurement registers
217-
_raw_dietemp = ROUnaryStruct(_DIETEMP, ">h")
218-
_raw_vbus = ROUnaryStruct(_VBUS, ">H")
219-
_raw_vshunt = ROUnaryStruct(_VSHUNT, ">h")
220-
_raw_current = ROUnaryStruct(_CURRENT, ">h")
221-
_raw_power = ROUnaryStruct(_POWER, ">H")
222-
223-
# Calibration register
224-
_shunt_cal = UnaryStruct(_SHUNTCAL, ">H")
225-
226-
# ID registers
227-
_manufacturer_id = ROUnaryStruct(_MFG_UID, ">H")
228-
_device_id = ROUnaryStruct(_DVC_UID, ">H")
229-
230-
def __init__(
231-
self, i2c_bus: I2C, address: int = _INA23X_DEFAULT_ADDR, skip_reset: bool = False
232-
) -> None:
233-
self.i2c_device = I2CDevice(i2c_bus, address)
234-
235-
# Verify manufacturer ID
236-
if self._manufacturer_id != _TEXAS_INSTRUMENTS_ID:
237-
raise ValueError("Failed to find INA237/INA238 - incorrect manufacturer ID")
238-
# Verify device ID (both INA237 and INA238 use the same ID)
239-
if self.device_id not in {_INA237_DEVICE_ID, _INA238_DEVICE_ID}:
240-
raise ValueError("Failed to find INA237/INA238 - incorrect device ID")
69+
# INA23X-specific register bits
70+
_alert_type = RWBits(7, 0x0B, 5, register_width=2, lsb_first=False)
71+
_conversion_ready = ROBit(0x0B, 1, register_width=2, lsb_first=False)
72+
_alert_flags = ROBits(12, 0x0B, 0, register_width=2, lsb_first=False)
24173

242-
self._shunt_res = 0.1 # Default shunt resistance
243-
self._current_lsb = 0.0
244-
if not skip_reset:
245-
self.reset()
246-
time.sleep(0.002) # 2ms delay for first measurement
247-
self.set_calibration(0.015, 10.0)
248-
self.averaging_count = AveragingCount.COUNT_16
249-
self.bus_voltage_conv_time = ConversionTime.TIME_150_US
250-
self.shunt_voltage_conv_time = ConversionTime.TIME_280_US
74+
_raw_vshunt = ROUnaryStruct(0x04, ">h")
75+
_raw_current = ROUnaryStruct(0x07, ">h")
76+
_raw_power = ROUnaryStruct(0x08, ">H")
25177

252-
def reset(self) -> None:
253-
"""Reset the sensor to default configuration."""
254-
self._reset = True
255-
self._alert_conv = True
256-
self.mode = Mode.CONTINUOUS
78+
def __init__(self, i2c_bus: I2C, address: int = 0x40, skip_reset: bool = False) -> None:
79+
super().__init__(i2c_bus, address, skip_reset)
25780

258-
@property
259-
def device_id(self) -> int:
260-
"""Device ID"""
261-
return (self._device_id >> 4) & 0xFFF
81+
# Verify device ID (both INA237 and INA238 use compatible IDs)
82+
if self.device_id not in {_INA237_DEVICE_ID, _INA238_DEVICE_ID}:
83+
raise ValueError("Failed to find INA237/INA238 - incorrect device ID")
26284

263-
@property
264-
def shunt_resistance(self) -> float:
265-
"""The shunt resistance in ohms."""
266-
return self._shunt_res
85+
# Set INA23X defaults
86+
self.set_calibration(0.015, 10.0)
26787

26888
def set_calibration(self, shunt_res: float = 0.015, max_current: float = 10.0) -> None:
26989
"""Set the calibration based on shunt resistance and maximum expected current.
@@ -285,81 +105,6 @@ def _update_shunt_cal(self) -> None:
285105
shunt_cal = int(819.2e6 * self._current_lsb * self._shunt_res * scale)
286106
self._shunt_cal = min(shunt_cal, 0xFFFF)
287107

288-
@property
289-
def adc_range(self) -> int:
290-
"""ADC range setting. 0 = ±163.84mV, 1 = ±40.96mV"""
291-
return self._adc_range
292-
293-
@adc_range.setter
294-
def adc_range(self, value: int) -> None:
295-
if value not in {0, 1}:
296-
raise ValueError("ADC range must be 0 or 1")
297-
self._adc_range = value
298-
self._update_shunt_cal()
299-
300-
@property
301-
def mode(self) -> int:
302-
"""Operating mode of the sensor."""
303-
return self._mode
304-
305-
@mode.setter
306-
def mode(self, value: int) -> None:
307-
if value not in Mode._VALID_MODES:
308-
raise ValueError(f"Invalid mode 0x{value:02X}. Must be one of the Mode.* constants")
309-
self._mode = value
310-
311-
@property
312-
def averaging_count(self) -> int:
313-
"""Number of samples to average."""
314-
return self._avg_count
315-
316-
@averaging_count.setter
317-
def averaging_count(self, value: int) -> None:
318-
if value not in AveragingCount._VALID_COUNTS:
319-
raise ValueError(
320-
f"Invalid averaging count {value}. Must be one of the AveragingCount.* constants"
321-
)
322-
self._avg_count = value
323-
324-
@property
325-
def bus_voltage_conv_time(self) -> int:
326-
"""Bus voltage conversion time setting."""
327-
return self._vbus_conv_time
328-
329-
@bus_voltage_conv_time.setter
330-
def bus_voltage_conv_time(self, value: int) -> None:
331-
if value not in ConversionTime._VALID_TIMES:
332-
raise ValueError(
333-
f"Invalid conversion time {value}. Must be one of the ConversionTime.* constants"
334-
)
335-
self._vbus_conv_time = value
336-
337-
@property
338-
def shunt_voltage_conv_time(self) -> int:
339-
"""Shunt voltage conversion time setting."""
340-
return self._vshunt_conv_time
341-
342-
@shunt_voltage_conv_time.setter
343-
def shunt_voltage_conv_time(self, value: int) -> None:
344-
if value not in ConversionTime._VALID_TIMES:
345-
raise ValueError(
346-
f"Invalid conversion time {value}. Must be one of the ConversionTime.* constants"
347-
)
348-
self._vshunt_conv_time = value
349-
350-
@property
351-
def temp_conv_time(self) -> int:
352-
"""Temperature conversion time setting."""
353-
return self._temp_conv_time
354-
355-
@temp_conv_time.setter
356-
def temp_conv_time(self, value: int) -> None:
357-
if value not in ConversionTime._VALID_TIMES:
358-
raise ValueError(
359-
f"Invalid conversion time {value}. Must be one of the ConversionTime.* constants"
360-
)
361-
self._temp_conv_time = value
362-
363108
@property
364109
def die_temperature(self) -> float:
365110
"""Die temperature in degrees Celsius."""
@@ -418,28 +163,6 @@ def alert_type(self, value: int) -> None:
418163
)
419164
self._alert_type = value
420165

421-
@property
422-
def alert_polarity(self) -> int:
423-
"""Alert pin polarity. 0 = active high, 1 = active low."""
424-
return self._alert_polarity
425-
426-
@alert_polarity.setter
427-
def alert_polarity(self, value: int) -> None:
428-
if value not in {0, 1}:
429-
raise ValueError("Alert polarity must be 0 or 1")
430-
self._alert_polarity = value
431-
432-
@property
433-
def alert_latch(self) -> int:
434-
"""Alert latch enable. 0 = transparent, 1 = latched."""
435-
return self._alert_latch
436-
437-
@alert_latch.setter
438-
def alert_latch(self, value: int) -> None:
439-
if value not in {0, 1}:
440-
raise ValueError("Alert latch must be 0 or 1")
441-
self._alert_latch = value
442-
443166
@property
444167
def alert_flags(self) -> int:
445168
"""Current alert flags."""

0 commit comments

Comments
 (0)