diff --git a/adafruit_ina260.py b/adafruit_ina260.py index 4774ba5..997a311 100644 --- a/adafruit_ina260.py +++ b/adafruit_ina260.py @@ -5,26 +5,17 @@ """ `adafruit_ina260` ================================================================================ - CircuitPython driver for the TI INA260 current and power sensor - - * Author(s): Bryan Siepert - Implementation Notes -------------------- - **Hardware:** - * `INA260 Breakout `_ - **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 +* 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 @@ -36,8 +27,8 @@ import adafruit_bus_device.i2c_device as i2cdevice from adafruit_register.i2c_struct import ROUnaryStruct -from adafruit_register.i2c_bits import RWBits -from adafruit_register.i2c_bit import ROBit +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) @@ -48,6 +39,7 @@ _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 @@ -108,6 +100,21 @@ class ConversionTime: TIME_4_156_ms = const(0x6) TIME_8_244_ms = const(0x7) + @staticmethod + def get_seconds(time_enum): + """Retrieve the time in seconds giving value read from register""" + conv_dict = { + 0: 140e-6, + 1: 204e-6, + 2: 332e-6, + 3: 558e-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`` @@ -143,6 +150,12 @@ class AveragingCount: COUNT_512 = const(0x6) COUNT_1024 = const(0x7) + @staticmethod + def get_averaging_count(avg_count): + """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 @@ -158,12 +171,80 @@ class INA260: def __init__(self, i2c_bus, address=0x40): 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 {} while expected {}" + " - check your wiring!".format( + self._manufacturer_id, self.TEXAS_INSTRUMENT_ID + ) + ) + + if self._device_id != self.INA260_ID: + raise RuntimeError( + "Failed to find INA260 ID, read {} while expected {}" + " - check your wiring!".format(self._device_id, self.INA260_ID) + ) + _raw_current = ROUnaryStruct(_REG_CURRENT, ">h") _raw_voltage = ROUnaryStruct(_REG_BUSVOLTAGE, ">H") _raw_power = ROUnaryStruct(_REG_POWER, ">H") - _conversion_ready = ROBit(_REG_MASK_ENABLE, 3, 2, False) + # 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) @@ -177,13 +258,32 @@ def __init__(self, i2c_bus, address=0x40): """ 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): """The current (between V+ and V-) in mA""" if self.mode == Mode.TRIGGERED: - while self._conversion_ready == 0: + while self._conversion_ready_flag == 0: pass return self._raw_current * 1.25 @@ -191,7 +291,7 @@ def current(self): def voltage(self): """The bus voltage in V""" if self.mode == Mode.TRIGGERED: - while self._conversion_ready == 0: + while self._conversion_ready_flag == 0: pass return self._raw_voltage * 0.00125 @@ -199,6 +299,6 @@ def voltage(self): def power(self): """The power being delivered to the load in mW""" if self.mode == Mode.TRIGGERED: - while self._conversion_ready == 0: + while self._conversion_ready_flag == 0: pass return self._raw_power * 10 diff --git a/examples/ina260_latch.py b/examples/ina260_latch.py new file mode 100644 index 0000000..85da4df --- /dev/null +++ b/examples/ina260_latch.py @@ -0,0 +1,58 @@ +# SPDX-FileCopyrightText: Gabriele Pongelli 2021 for Adafruit Industries +# +# SPDX-License-Identifier: MIT + +import time +import board +from adafruit_ina260 import INA260, Mode, ConversionTime + + +if __name__ == "__main__": + try: + i2c = board.I2C() + ina260 = INA260(i2c) + except RuntimeError as r_e: + # catch exception on init, no INA260 chip found + print(r_e) + raise r_e + else: + # set overcurrent limit flag and threshold value + # 0x0008 x 1,25 mA = 10 mA as alert limit + ina260.alert_limit = 0x0008 + + # alert pin is asserted, can be check with gpiozero + ina260.overcurrent_limit = True + + # keep the flag high until MASK_ENABLE register will be read + ina260.alert_latch_enable = True + + ina260.mode = Mode.CONTINUOUS + + # set higher conversion time and wait its value before each read + ina260.current_conversion_time = ConversionTime.TIME_8_244_ms + for _ in enumerate(range(5)): + time.sleep(ConversionTime.get_seconds(ina260.current_conversion_time)) + print( + "Current: %.2f mA Voltage: %.2f V Power:%.2f mW" + % (ina260.current, ina260.voltage, ina260.power) + ) + + # supposing meanwhile the alert limit was exceeded, setting an higher limit + # and clear the ALERT + # 0x0100 x 1,25 mA = 320 mA as alert limit + ina260.alert_limit = 0x0100 + + # alert function flag bit should be true if alert threshold was exceeded + print("Alert function flag: {}".format(ina260.alert_function_flag)) + + # in latch mode, reading the register clears the ALERT & alert function flag + print("MASK register: {}".format(ina260.mask_enable)) + + # reset the whole chip and wait 2 sec + ina260.reset_bit = True + time.sleep(2) + print( + "MASK_REGISTER check, must be 0x0000 after reset: {}".format( + ina260.mask_enable + ) + )