__REG_CURRENT = 0x04
__REG_CALIBRATION = 0x05
__RST = 15
__BRNG = 13
__PG1 = 12
__PG0 = 11
__BADC4 = 10
__BADC3 = 9
__BADC2 = 8
__BADC1 = 7
__SADC4 = 6
__SADC3 = 5
__SADC2 = 4
__SADC1 = 3
__MODE3 = 2
__MODE2 = 1
__MODE1 = 0
__OVF = 1
__CNVR = 2
__BUS_RANGE = [16, 32]
__GAIN_VOLTS = [0.04, 0.08, 0.16, 0.32]
__CONT_SH_BUS = 7
__AMP_ERR_MSG = ('Expected current %.3fA is greater '
'than max possible current %.3fA')
__RNG_ERR_MSG = ('Expected amps %.2fA, out of range, use a lower '
'value shunt resistor')
__VOLT_ERR_MSG = ('Invalid voltage range, must be one of: '
'RANGE_16V, RANGE_32V')
__LOG_FORMAT = '%(asctime)s - %(levelname)s - INA219 %(message)s'
__LOG_MSG_1 = ('shunt ohms: %.3f, bus max volts: %d, '
'shunt volts max: %.2f%s, '
'bus ADC: %s, shunt ADC: %s')
__LOG_MSG_2 = ('calibrate called with: bus max volts: %dV, '
'max shunt volts: %.2fV%s')
__LOG_MSG_3 = ('Current overflow detected - '
'attempting to increase gain')
__SHUNT_MILLIVOLTS_LSB = 0.01 # 10uV
__BUS_MILLIVOLTS_LSB = 4 # 4mV
__CALIBRATION_FACTOR = 0.04096
# Max value supported value (65534 decimal) of the calibration register
# (D0 bit is always zero, p31 of spec)
__MAX_CALIBRATION_VALUE = 0xFFFE
# In the spec (p17) the current LSB factor for the minimum LSB is
# documented as 32767, but a larger value (100.1% of 32767) is used
# to guarantee that current overflow can always be detected.
__CURRENT_LSB_FACTOR = 32800
def __init__(self, shunt_ohms, i2c, max_expected_amps=None,
address=__ADDRESS, log_level=logging.ERROR):
"""Construct the class.
At a minimum pass in the resistance of the shunt resistor and I2C
interface to which the sensor is connected.
Arguments:
shunt_ohms -- value of shunt resistor in Ohms (mandatory).
i2c -- an instance of the I2C class from the *machine* module, either
I2C(1) or I2C(2) (mandatory).
max_expected_amps -- the maximum expected current in Amps (optional).
address -- the I2C address of the INA219, defaults to
*0x40* (optional).
log_level -- set to logging.DEBUG to see detailed calibration
calculations (optional).
"""
logging.basicConfig(level=log_level)
self._log = logging.getLogger("ina219")
self._i2c = i2c
self._address = address
self._shunt_ohms = shunt_ohms
self._max_expected_amps = max_expected_amps
self._min_device_current_lsb = self._calculate_min_current_lsb()
self._gain = None
self._auto_gain_enabled = False
def configure(self, voltage_range=RANGE_32V, gain=GAIN_AUTO,
bus_adc=ADC_12BIT, shunt_adc=ADC_12BIT):
"""Configure and calibrate how the INA219 will take measurements.
Arguments:
voltage_range -- The full scale voltage range, this is either 16V
or 32V represented by one of the following constants;
RANGE_16V, RANGE_32V (default).
gain -- The gain which controls the maximum range of the shunt
voltage represented by one of the following constants;
GAIN_1_40MV, GAIN_2_80MV, GAIN_4_160MV,
GAIN_8_320MV, GAIN_AUTO (default).
bus_adc -- The bus ADC resolution (9, 10, 11, or 12-bit) or
set the number of samples used when averaging results
represent by one of the following constants; ADC_9BIT,
ADC_10BIT, ADC_11BIT, ADC_12BIT (default),
ADC_2SAMP, ADC_4SAMP, ADC_8SAMP, ADC_16SAMP,
ADC_32SAMP, ADC_64SAMP, ADC_128SAMP
shunt_adc -- The shunt ADC resolution (9, 10, 11, or 12-bit) or
set the number of samples used when averaging results
represent by one of the following constants; ADC_9BIT,
ADC_10BIT, ADC_11BIT, ADC_12BIT (default),
ADC_2SAMP, ADC_4SAMP, ADC_8SAMP, ADC_16SAMP,
ADC_32SAMP, ADC_64SAMP, ADC_128SAMP
"""
self.__validate_voltage_range(voltage_range)
self._voltage_range = voltage_range
if self._max_expected_amps is not None:
if gain == self.GAIN_AUTO:
self._auto_gain_enabled = True
self._gain = self._determine_gain(self._max_expected_amps)
else:
self._gain = gain
else:
if gain != self.GAIN_AUTO:
self._gain = gain
else:
self._auto_gain_enabled = True
self._gain = self.GAIN_1_40MV
self._log.info('gain set to %.2fV', self.__GAIN_VOLTS[self._gain])
self._log.debug(
self.__LOG_MSG_1,
self._shunt_ohms, self.__BUS_RANGE[voltage_range],
self.__GAIN_VOLTS[self._gain],
self.__max_expected_amps_to_string(self._max_expected_amps),
self.__ADC_CONVERSION[bus_adc], self.__ADC_CONVERSION[shunt_adc])
self._calibrate(
self.__BUS_RANGE[voltage_range], self.__GAIN_VOLTS[self._gain],
self._max_expected_amps)
self._configure(voltage_range, self._gain, bus_adc, shunt_adc)
def voltage(self):
"""Return the bus voltage in volts."""
value = self._voltage_register()
return float(value) * self.__BUS_MILLIVOLTS_LSB / 1000
def supply_voltage(self):
"""Return the bus supply voltage in volts.
This is the sum of the bus voltage and shunt voltage. A
DeviceRangeError exception is thrown if current overflow occurs.
"""
return self.voltage() + (float(self.shunt_voltage()) / 1000)
def current(self):
"""Return the bus current in milliamps.
A DeviceRangeError exception is thrown if current overflow occurs.
"""
self._handle_current_overflow()
return self._current_register() * self._current_lsb * 1000
def power(self):
"""Return the bus power consumption in milliwatts.
A DeviceRangeError exception is thrown if current overflow occurs.
"""
self._handle_current_overflow()
return self._power_register() * self._power_lsb * 1000
def shunt_voltage(self):
"""Return the shunt voltage in millivolts.
A DeviceRangeError exception is thrown if current overflow occurs.
"""
self._handle_current_overflow()
return self._shunt_voltage_register() * self.__SHUNT_MILLIVOLTS_LSB
def sleep(self):
"""Put the INA219 into power down mode."""
configuration = self._read_configuration()
self._configuration_register(configuration & 0xFFF8)
def wake(self):
"""Wake the INA219 from power down mode."""
configuration = self._read_configuration()
self._configuration_register(configuration | 0x0007)
# 40us delay to recover from powerdown (p14 of spec)
utime.sleep_us(40)
def current_overflow(self):
"""Return true if the sensor has detect current overflow.
In this case the current and power values are invalid.
"""
return self._has_current_overflow()
def reset(self):
"""Reset the INA219 to its default configuration."""
self._configuration_register(1 << self.__RST)
def _handle_current_overflow(self):
if self._auto_gain_enabled:
while self._has_current_overflow():
self._increase_gain()
else:
if self._has_current_overflow():
raise DeviceRangeError(self.__GAIN_VOLTS[self._gain])
def _determine_gain(self, max_expected_amps):
shunt_v = max_expected_amps * self._shunt_ohms
if shunt_v > self.__GAIN_VOLTS[3]:
raise ValueError(self.__RNG_ERR_MSG % max_expected_amps)
gain = min(v for v in self.__GAIN_VOLTS if v > shunt_v)
return self.__GAIN_VOLTS.index(gain)
def _increase_gain(self):
self._log.info(self.__LOG_MSG_3)
gain = self._read_gain()
if gain < len(self.__GAIN_VOLTS) - 1:
gain = gain + 1
self._calibrate(self.__BUS_RANGE[self._voltage_range],
self.__GAIN_VOLTS[gain])
self._configure_gain(gain)
# 1ms delay required for new configuration to take effect,
# otherwise invalid current/power readings can occur.
utime.sleep_ms(1)
else:
self._log.info('Device limit reach, gain cannot be increased')
raise DeviceRangeError(self.__GAIN_VOLTS[gain], True)
def _configure(self, voltage_range, gain, bus_adc, shunt_adc):
configuration = (
voltage_range << self.__BRNG | gain << self.__PG0 |
bus_adc << self.__BADC1 | shunt_adc << self.__SADC1 |
self.__CONT_SH_BUS)
self._configuration_register(configuration)
def _calibrate(self, bus_volts_max, shunt_volts_max,
max_expected_amps=None):
self._log.info(self.__LOG_MSG_2,
bus_volts_max, shunt_volts_max,
self.__max_expected_amps_to_string(max_expected_amps))
max_possible_amps = shunt_volts_max / self._shunt_ohms
self._log.info("max possible current: %.3fA", max_possible_amps)
self._current_lsb = \
self._determine_current_lsb(max_expected_amps, max_possible_amps)
self._log.info("current LSB: %.3e A/bit", self._current_lsb)
self._power_lsb = self._current_lsb * 20
self._log.info("power LSB: %.3e W/bit", self._power_lsb)
max_current = self._current_lsb * 32767
self._log.info("max current before overflow: %.4fA", max_current)
max_shunt_voltage = max_current * self._shunt_ohms
self._log.info("max shunt voltage before overflow: %.4fmV",
max_shunt_voltage * 1000)
calibration = trunc(self.__CALIBRATION_FACTOR /
(self._current_lsb * self._shunt_ohms))
self._log.info("calibration: 0x%04x (%d)", calibration, calibration)
self._calibration_register(calibration)
def _determine_current_lsb(self, max_expected_amps, max_possible_amps):
if max_expected_amps is not None:
if max_expected_amps > round(max_possible_amps, 3):
raise ValueError(self.__AMP_ERR_MSG %
(max_expected_amps, max_possible_amps))
self._log.info("max expected current: %.3fA", max_expected_amps)
if max_expected_amps < max_possible_amps:
current_lsb = max_expected_amps / self.__CURRENT_LSB_FACTOR
else:
current_lsb = max_possible_amps / self.__CURRENT_LSB_FACTOR
else:
current_lsb = max_possible_amps / self.__CURRENT_LSB_FACTOR
if current_lsb < self._min_device_current_lsb:
current_lsb = self._min_device_current_lsb
return current_lsb
def _configuration_register(self, register_value):
self._log.debug("configuration: 0x%04x", register_value)
self.__write_register(self.__REG_CONFIG, register_value)
def _read_configuration(self):
return self.__read_register(self.__REG_CONFIG)
def _calculate_min_current_lsb(self):
return self.__CALIBRATION_FACTOR / \
(self._shunt_ohms * self.__MAX_CALIBRATION_VALUE)
def _read_gain(self):
configuration = self._read_configuration()
gain = (configuration & 0x1800) >> self.__PG0
self._log.info("gain is currently: %.2fV", self.__GAIN_VOLTS[gain])
return gain
def _configure_gain(self, gain):
configuration = self._read_configuration()
configuration = configuration & 0xE7FF
self._configuration_register(configuration | (gain << self.__PG0))
self._gain = gain
self._log.info("gain set to: %.2fV" % self.__GAIN_VOLTS[gain])
def _calibration_register(self, register_value):
self._log.debug("calibration: 0x%04x" % register_value)
self.__write_register(self.__REG_CALIBRATION, register_value)
def _has_current_overflow(self):
ovf = self._read_voltage_register() & self.__OVF
return (ovf == 1)
def _voltage_register(self):
register_value = self._read_voltage_register()
return register_value >> 3
def _read_voltage_register(self):
return self.__read_register(self.__REG_BUSVOLTAGE)
def _current_register(self):
return self.__read_register(self.__REG_CURRENT, True)
def _shunt_voltage_register(self):
return self.__read_register(self.__REG_SHUNTVOLTAGE, True)
def _power_register(self):
return self.__read_register(self.__REG_POWER)
def __validate_voltage_range(self, voltage_range):
if voltage_range > len(self.__BUS_RANGE) - 1:
raise ValueError(self.__VOLT_ERR_MSG)
def __write_register(self, register, register_value):
self.__log_register_operation("write", register, register_value)
register_bytes = self.__to_bytes(register_value)
self._i2c.writeto_mem(self._address, register, register_bytes)
def __to_bytes(self, register_value):
return bytearray([(register_value >> 8) & 0xFF, register_value & 0xFF])
def __read_register(self, register, negative_value_supported=False):
register_bytes = self._i2c.readfrom_mem(self._address, register, 2)
register_value = int.from_bytes(register_bytes, 'big')
if negative_value_supported:
# Two's compliment
if register_value > 32767:
register_value -= 65536
self.__log_register_operation("read", register, register_value)
return register_value
def __log_register_operation(self, msg, register, value):
# performance optimisation
if logging._level == logging.DEBUG:
binary = '{0:#018b}'.format(value)
self._log.debug("%s register 0x%02x: 0x%04x %s",
msg, register, value, binary)
def __max_expected_amps_to_string(self, max_expected_amps):
if max_expected_amps is None:
return ''
else:
return ', max expected amps: %.3fA' % max_expected_amps
class DeviceRangeError(Exception): """This exception is throw to prevent invalid readings.
Invalid readings occur When the current is greater than allowed given
calibration of the device.
"""
__DEV_RNG_ERR = ('Current out of range (overflow), '
'for gain %.2fV')
def __init__(self, gain_volts, device_max=False):
"""Construct the class."""
msg = self.__DEV_RNG_ERR % gain_volts
if device_max:
msg = msg + ', device limit reached'
super(DeviceRangeError, self).__init__(msg)
self.gain_volts = gain_volts
self.device_limit_reached = device_max
**六、参考资料**
1, scruss.com
**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**


**[如果你需要这些资料,可以戳这里获取](https://gitee.com/vip204888)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**
**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**