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
3031import 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
3435from adafruit_register .i2c_bits import ROBits , RWBits
35- from adafruit_register .i2c_struct import ROUnaryStruct , UnaryStruct
36+ from adafruit_register .i2c_struct import ROUnaryStruct
3637from micropython import const
3738
3839try :
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