-
Notifications
You must be signed in to change notification settings - Fork 11
/
Copy pathadafruit_ina260.py
309 lines (266 loc) · 13.6 KB
/
adafruit_ina260.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
# SPDX-FileCopyrightText: Bryan Siepert 2019 for Adafruit Industries
#
# SPDX-License-Identifier: MIT
"""
`adafruit_ina260`
================================================================================
CircuitPython driver for the TI INA260 current and power sensor
* Author(s): Bryan Siepert
Implementation Notes
--------------------
**Hardware:**
* `INA260 Breakout <https://www.adafruit.com/products/4226>`_
**Software and Dependencies:**
* Adafruit CircuitPython firmware for the supported boards:
* https://github.com/adafruit/circuitpython/releases
* Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
* Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
"""
# imports
try:
import typing # pylint: disable=unused-import
from busio import I2C
except ImportError:
pass
__version__ = "0.0.0+auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_INA260.git"
from micropython import const
import adafruit_bus_device.i2c_device as i2cdevice
from adafruit_register.i2c_struct import ROUnaryStruct
from adafruit_register.i2c_bits import ROBits, RWBits
from adafruit_register.i2c_bit import ROBit, RWBit
_REG_CONFIG = const(0x00) # CONFIGURATION REGISTER (R/W)
_REG_CURRENT = const(0x01) # SHUNT VOLTAGE REGISTER (R)
_REG_BUSVOLTAGE = const(0x02) # BUS VOLTAGE REGISTER (R)
_REG_POWER = const(0x03) # POWER REGISTER (R)
_REG_MASK_ENABLE = const(0x06) # MASK ENABLE REGISTER (R/W)
_REG_ALERT_LIMIT = const(0x07) # ALERT LIMIT REGISTER (R/W)
_REG_MFG_UID = const(0xFE) # MANUFACTURER UNIQUE ID REGISTER (R)
_REG_DIE_UID = const(0xFF) # DIE UNIQUE ID REGISTER (R)
# pylint: disable=too-few-public-methods
class Mode:
"""Modes avaible to be set
+--------------------+---------------------------------------------------------------------+
| Mode | Description |
+====================+=====================================================================+
| ``Mode.CONTINUOUS``| Default: The sensor will continuously measure the bus voltage and |
| | shunt voltage across the shunt resistor to calculate ``power`` and |
| | ``current`` |
+--------------------+---------------------------------------------------------------------+
| ``Mode.TRIGGERED`` | The sensor will immediately begin measuring and calculating current,|
| | bus voltage, and power. Re-set this mode to initiate another |
| | measurement |
+--------------------+---------------------------------------------------------------------+
| ``Mode.SHUTDOWN`` | Shutdown the sensor, reducing the quiescent current and turning off|
| | current into the device inputs. Set another mode to re-enable |
+--------------------+---------------------------------------------------------------------+
"""
SHUTDOWN = const(0x0)
TRIGGERED = const(0x3)
CONTINUOUS = const(0x7)
class ConversionTime:
"""Options for ``current_conversion_time`` or ``voltage_conversion_time``
+----------------------------------+------------------+
| ``ConversionTime`` | Time |
+==================================+==================+
| ``ConversionTime.TIME_140_us`` | 140 us |
+----------------------------------+------------------+
| ``ConversionTime.TIME_204_us`` | 204 us |
+----------------------------------+------------------+
| ``ConversionTime.TIME_332_us`` | 332 us |
+----------------------------------+------------------+
| ``ConversionTime.TIME_588_us`` | 588 us |
+----------------------------------+------------------+
| ``ConversionTime.TIME_1_1_ms`` | 1.1 ms (Default) |
+----------------------------------+------------------+
| ``ConversionTime.TIME_2_116_ms`` | 2.116 ms |
+----------------------------------+------------------+
| ``ConversionTime.TIME_4_156_ms`` | 4.156 ms |
+----------------------------------+------------------+
| ``ConversionTime.TIME_8_244_ms`` | 8.244 ms |
+----------------------------------+------------------+
"""
TIME_140_us = const(0x0)
TIME_204_us = const(0x1)
TIME_332_us = const(0x2)
TIME_588_us = const(0x3)
TIME_1_1_ms = const(0x4)
TIME_2_116_ms = const(0x5)
TIME_4_156_ms = const(0x6)
TIME_8_244_ms = const(0x7)
@staticmethod
def get_seconds(time_enum: int) -> float:
"""Retrieve the time in seconds giving value read from register"""
conv_dict = {
0: 140e-6,
1: 204e-6,
2: 332e-6,
3: 588e-6,
4: 1.1e-3,
5: 2.116e-3,
6: 4.156e-3,
7: 8.244e-3,
}
return conv_dict[time_enum]
class AveragingCount:
"""Options for ``averaging_count``
+-------------------------------+------------------------------------+
| ``AveragingCount`` | Number of measurements to average |
+===============================+====================================+
| ``AveragingCount.COUNT_1`` | 1 (Default) |
+-------------------------------+------------------------------------+
| ``AveragingCount.COUNT_4`` | 4 |
+-------------------------------+------------------------------------+
| ``AveragingCount.COUNT_16`` | 16 |
+-------------------------------+------------------------------------+
| ``AveragingCount.COUNT_64`` | 64 |
+-------------------------------+------------------------------------+
| ``AveragingCount.COUNT_128`` | 128 |
+-------------------------------+------------------------------------+
| ``AveragingCount.COUNT_256`` | 256 |
+-------------------------------+------------------------------------+
| ``AveragingCount.COUNT_512`` | 512 |
+-------------------------------+------------------------------------+
| ``AveragingCount.COUNT_1024`` | 1024 |
+-------------------------------+------------------------------------+
"""
COUNT_1 = const(0x0)
COUNT_4 = const(0x1)
COUNT_16 = const(0x2)
COUNT_64 = const(0x3)
COUNT_128 = const(0x4)
COUNT_256 = const(0x5)
COUNT_512 = const(0x6)
COUNT_1024 = const(0x7)
@staticmethod
def get_averaging_count(avg_count: int) -> float:
"""Retrieve the number of measurements giving value read from register"""
conv_dict = {0: 1, 1: 4, 2: 16, 3: 64, 4: 128, 5: 256, 6: 512, 7: 1024}
return conv_dict[avg_count]
# pylint: enable=too-few-public-methods
class INA260:
"""Driver for the INA260 power and current sensor.
:param ~busio.I2C i2c_bus: The I2C bus the INA260 is connected to.
:param int address: The I2C device address for the sensor. Default is ``0x40``.
"""
def __init__(self, i2c_bus: I2C, address: int = 0x40) -> None:
self.i2c_device = i2cdevice.I2CDevice(i2c_bus, address)
if self._manufacturer_id != self.TEXAS_INSTRUMENT_ID:
raise RuntimeError(
"Failed to find Texas Instrument ID, read "
+ f"{self._manufacturer_id} while expected {self.TEXAS_INSTRUMENT_ID}"
" - check your wiring!"
)
if self._device_id != self.INA260_ID:
raise RuntimeError(
f"Failed to find INA260 ID, read {self._device_id} while expected {self.INA260_ID}"
" - check your wiring!"
)
_raw_current = ROUnaryStruct(_REG_CURRENT, ">h")
_raw_voltage = ROUnaryStruct(_REG_BUSVOLTAGE, ">H")
_raw_power = ROUnaryStruct(_REG_POWER, ">H")
# MASK_ENABLE fields
overcurrent_limit = RWBit(_REG_MASK_ENABLE, 15, 2, False)
"""Setting this bit high configures the ALERT pin to be asserted if the current measurement
following a conversion exceeds the value programmed in the Alert Limit Register.
"""
under_current_limit = RWBit(_REG_MASK_ENABLE, 14, 2, False)
"""Setting this bit high configures the ALERT pin to be asserted if the current measurement
following a conversion drops below the value programmed in the Alert Limit Register.
"""
bus_voltage_over_voltage = RWBit(_REG_MASK_ENABLE, 13, 2, False)
"""Setting this bit high configures the ALERT pin to be asserted if the bus voltage measurement
following a conversion exceeds the value programmed in the Alert Limit Register.
"""
bus_voltage_under_voltage = RWBit(_REG_MASK_ENABLE, 12, 2, False)
"""Setting this bit high configures the ALERT pin to be asserted if the bus voltage measurement
following a conversion drops below the value programmed in the Alert Limit Register.
"""
power_over_limit = RWBit(_REG_MASK_ENABLE, 11, 2, False)
"""Setting this bit high configures the ALERT pin to be asserted if the Power calculation
made following a bus voltage measurement exceeds the value programmed in the
Alert Limit Register.
"""
conversion_ready = RWBit(_REG_MASK_ENABLE, 10, 2, False)
"""Setting this bit high configures the ALERT pin to be asserted when the Conversion Ready Flag,
Bit 3, is asserted indicating that the device is ready for the next conversion.
"""
# from 5 to 9 are not used
alert_function_flag = ROBit(_REG_MASK_ENABLE, 4, 2, False)
"""While only one Alert Function can be monitored at the ALERT pin at time, the
Conversion Ready can also be enabled to assert the ALERT pin.
Reading the Alert Function Flag following an alert allows the user to determine if the Alert
Function was the source of the Alert.
When the Alert Latch Enable bit is set to Latch mode, the Alert Function Flag bit
clears only when the Mask/Enable Register is read.
When the Alert Latch Enable bit is set to Transparent mode, the Alert Function Flag bit
is cleared following the next conversion that does not result in an Alert condition.
"""
_conversion_ready_flag = ROBit(_REG_MASK_ENABLE, 3, 2, False)
"""Bit to help coordinate one-shot or triggered conversion. This bit is set after all
conversion, averaging, and multiplication are complete.
Conversion Ready flag bit clears when writing the configuration register or
reading the Mask/Enable register.
"""
math_overflow_flag = ROBit(_REG_MASK_ENABLE, 2, 2, False)
"""This bit is set to 1 if an arithmetic operation resulted in an overflow error.
"""
alert_polarity_bit = RWBit(_REG_MASK_ENABLE, 1, 2, False)
"""Active-high open collector when True, Active-low open collector when false (default).
"""
alert_latch_enable = RWBit(_REG_MASK_ENABLE, 0, 2, False)
"""Configures the latching feature of the ALERT pin and Alert Flag Bits.
"""
reset_bit = RWBit(_REG_CONFIG, 15, 2, False)
"""Setting this bit t 1 generates a system reset. Reset all registers to default values."""
averaging_count = RWBits(3, _REG_CONFIG, 9, 2, False)
"""The window size of the rolling average used in continuous mode"""
voltage_conversion_time = RWBits(3, _REG_CONFIG, 6, 2, False)
"""The conversion time taken for the bus voltage measurement"""
current_conversion_time = RWBits(3, _REG_CONFIG, 3, 2, False)
"""The conversion time taken for the current measurement"""
mode = RWBits(3, _REG_CONFIG, 0, 2, False)
"""The mode that the INA260 is operating in. Must be one of
``Mode.CONTINUOUS``, ``Mode.TRIGGERED``, or ``Mode.SHUTDOWN``
"""
mask_enable = RWBits(16, _REG_MASK_ENABLE, 0, 2, False)
"""The Mask/Enable Register selects the function that is enabled to control the ALERT pin as
well as how that pin functions.
If multiple functions are enabled, the highest significant bit
position Alert Function (D15-D11) takes priority and responds to the Alert Limit Register.
"""
alert_limit = RWBits(16, _REG_ALERT_LIMIT, 0, 2, False)
"""The Alert Limit Register contains the value used to compare to the register selected in the
Mask/Enable Register to determine if a limit has been exceeded.
The format for this register will match the format of the register that is selected for
comparison.
"""
TEXAS_INSTRUMENT_ID = const(0x5449)
INA260_ID = const(0x227)
_manufacturer_id = ROUnaryStruct(_REG_MFG_UID, ">H")
"""Manufacturer identification bits"""
_device_id = ROBits(12, _REG_DIE_UID, 4, 2, False)
"""Device identification bits"""
revision_id = ROBits(4, _REG_DIE_UID, 0, 2, False)
"""Device revision identification bits"""
@property
def current(self) -> float:
"""The current (between V+ and V-) in mA"""
if self.mode == Mode.TRIGGERED:
while self._conversion_ready_flag == 0:
pass
return self._raw_current * 1.25
@property
def voltage(self) -> float:
"""The bus voltage in V"""
if self.mode == Mode.TRIGGERED:
while self._conversion_ready_flag == 0:
pass
return self._raw_voltage * 0.00125
@property
def power(self) -> int:
"""The power being delivered to the load in mW"""
if self.mode == Mode.TRIGGERED:
while self._conversion_ready_flag == 0:
pass
return self._raw_power * 10