INA_TI
/
ina_ti.py
448 строк · 28.7 Кб
1"""INAxxx Texas Instruments sensors module.
2
3Внимание! для долговременной непрерывной работы токового шунта, не допускайте выделения на нем более половины(!) от его
4максимальной рассеиваемой мощности! Если установка будет работать 24/7, то допускайте(!) выделения на нем не более 1/3 от его
5максимальной рассеиваемой мощности!!!
6Мощность, выделяемая на любом сопротивлении (постоянный ток), рассчитывается по формуле: P=I**2 * R
7где: I - ток в Амперах; R - сопротивление в Омах.
8
9Attention! for long-term continuous operation of the current shunt, do not allow more than half(!) of its maximum
10dissipated power to be allocated on it!!
11The power dissipated on any resistance (direct current) is calculated by the formula: P=I**2 * R
12where: I - current in Amperes; R - resistance in ohms"""
13import math14from sensor_pack_2 import bus_service15from sensor_pack_2.base_sensor import BaseSensorEx, IBaseSensorEx, Iterator, check_value16
17from collections import namedtuple18from sensor_pack_2.bitfield import bit_field_info19from sensor_pack_2.bitfield import BitFields20
21def get_exponent(value: float) -> int:22"""Возвращает десятичную степень числа.23Returns the decimal power of a number"""
24return int(math.floor(math.log10(abs(value)))) if 0 != value else 025
26
27# расшифровка поля MODE, регистра конфигурации
28# Если continuous в Истина, то измерения проводятся автоматически, иначе их нужно запускать принудительно!
29# Если bus_voltage_enabled в Истина, то измерения входного НАПРЯЖЕНИЯ производятся! Иначе не производятся!
30# Если shunt_voltage_enabled в Истина, то измерения входного ТОКА производятся! Иначе не производятся!
31ina219_operation_mode = namedtuple("ina219_operation_mode", "continuous bus_voltage_enabled shunt_voltage_enabled")32# имена полей регистра конфигурации
33# Бит Имя Описание
34# 13 BRNG Диапазон напряжения для АЦП напряжения на шине (входное напряжение)
35# 11..12 PGA Диапазоны напряжения для АЦП токового шунта
36# 7..10 BADC Разрешение/Усреднение АЦП шины
37# 3..6 SADC Разрешение/Усреднение АЦП токового шунта
38# 2 CNTNS Непрерывный режим работы(1)/однократный режим работы(0)
39# 1 BADC_EN АЦП напряжения на шине (входное напряжение) включен (1)
40# 0 SADC_EN АЦП напряжения на токовом шунте включен (1)
41config_ina219 = namedtuple("config_ina219", "BRNG PGA BADC SADC CNTNS BADC_EN SADC_EN")42# для метода get_voltage
43voltage_ina219 = namedtuple("voltage_ina219", "bus_voltage data_ready overflow")44
45def _get_conv_time(value: int) -> int:46"""Возвращает время из полей SADC, BADC в микросекундах"""47_conv_time = 84, 148, 276, 53248if value < 8:49value &= 0x3 # 0..350return _conv_time[value]51# 0x8..0xF. Усреднение по 2, 4, 8, 16, 32, 64, 128 отсчетам52value -= 0x08 # 0..753coefficient = 2 ** value54return 532 * coefficient55
56class InaBase(BaseSensorEx):57"""Base class for INA current/voltage monitor"""58
59def __init__(self, adapter: bus_service.BusAdapter, address):60""""""61check_value(address, range(0x40, 0x50), f"Неверный адрес устройства: {address}")62super().__init__(adapter, address, True)63
64def _get_16bit_reg(self, address: int, format_char: str) -> int:65_raw = self.read_reg(address, 2)66return self.unpack(format_char, _raw)[0]67
68# BaseSensor69def _set_raw_cfg(self, value: int) -> int:70"""Установить сырую конфигурацию в регистре. Set raw configuration in register."""71return self.write_reg(0x00, value, 2)72
73def _get_raw_cfg(self) -> int:74"""Get raw configuration from register"""75return self._get_16bit_reg(0x00, "H")76
77# BaseSensorEx78def soft_reset(self):79self._set_raw_cfg(0b11100110011111)80
81
82class INA219Simple(InaBase):83"""Класс для работы с датчиком TI INA219 без какой либо настройки!84Диапазон измерения входного напряжения: 0..26 Вольт. Рекомендую 0..24 Вольта,
85дополнительно защита от выбросов напряжения!!!
86Диапазон измерения напряжения на токоизмерительном шунте: ±320 милливольт.
87Никаких настроек нет!
88---------------------
89A class for working with a TI INA219 sensor without any configuration!
90Input voltage measurement range: 0-26 Volts.
91Voltage measurement range on the current measuring shunt: ±320 millivolts.
92There are no settings!"""
93
94# для вычислений95# предельное напряжение на шунте: 0.32768 В. lsb = желаемое предельное напряжение на шунте поделить на 2 ** 1596_lsb_shunt_voltage = 1E-5 # 10 uV97_lsb_bus_voltage = 4E-3 # 4 mV98
99@staticmethod100def get_shunt_adc_lsb()->float:101"""Возвращает цену младшего разряда АЦП токового шунта. Не изменяется при изменении разрядности, что странно!"""102return INA219Simple._lsb_shunt_voltage103
104@staticmethod105def get_bus_adc_lsb()->float:106"""Возвращает цену младшего разряда АЦП напряжения на шине. Не изменяется при изменении разрядности, что странно!"""107return INA219Simple._lsb_bus_voltage108
109def __init__(self, adapter: bus_service.BusAdapter, address=0x40):110super().__init__(adapter, address)111# 0x399F настройка по умолчанию, простое считывание двух напряжений (входное-на шине и токового шунта).112# Входного напряжения и напряжения на токовом шунте. Непрерывное произведение измерений.113# Bus Voltage Range: 32 V ("Senses Bus Voltages from 0 to 26 V". From page 1 of datasheet.)114# Shunt Voltage Range: ±320 mV115# Bus ADC Resolution: 12 bit116# Shunt ADC Resolution: 12 bit117# Conversion Time: 532 us118# Mode: Shunt and bus, continuous119self._set_raw_cfg(0b0011_1001_1001_1111)120
121def get_conversion_cycle_time(self) -> int:122"""Возвращает время в мкс(!) преобразования сигнала в цифровой код и готовности его для чтения по шине!123Для текущих настроек датчика. При изменении настроек следует заново вызвать этот метод!"""
124return 532125
126def get_shunt_voltage(self) -> float:127"""Возвращает напряжение на токовом(!) шунте в Вольтах. Чтобы вычислить ток через шунт,128нужно это напряжение поделить на сопротивление шунта в Омах!!!
129Returns the shunt voltage in Volts. To calculate the current through the shunt, you need to divide this voltage
130by the resistance of the shunt in ohms !!!"""
131# DC ACCURACY. ADC basic resolution: 12 bit;:132# Shunt voltage, 1 LSB step size: 10 μV133# Bus voltage, 1 LSB step size: 4 mV134_raw = self._get_16bit_reg(0x01, "h")135_lsb = self.get_shunt_adc_lsb()136# print(f"DBG:get_shunt_voltage. _raw: {_raw}\t{_lsb} ")137return _lsb * _raw138
139def get_voltage(self) -> voltage_ina219:140"""Возвращает кортеж из входного измеряемого напряжения, флага готовности данных, флага математического переполнения (OVF).141Флаг математического переполнения (OVF) устанавливается, когда расчеты мощности или тока выходят за допустимые
142пределы. Это указывает на то, что данные о токе и мощности могут быть бессмысленными!
143------------------------------------------------------------------------------
144Хотя данные последнего преобразования могут быть прочитаны в любое время, бит готовности к преобразованию указывает,
145когда доступны данные преобразования в регистрах вывода данных. Бит готовности данных устанавливается после завершения всех(!) преобразований,
146усреднения и умножения. Он сбрасывается при следующих событиях:
1471) Запись нового режима в биты режима работы в регистре конфигурации (за исключением отключения или отключения питания).
1482) Чтение регистра мощности
149
150Бит готовности (CNVR) к преобразованию устанавливается после завершения всех(!) операций преобразования, усреднения и умножения!
151------------------------------------------------------------------------------
152Returns a tuple of input measured voltage, data ready flag, math overflow flag (OVF).
153The Math Overflow Flag (OVF) is set when power or current calculations are out of range.
154This indicates that current and power data may be meaningless!"""
155# DC ACCURACY: ADC basic resolution: 12 bit; Bus voltage, 1 LSB step size: 4 mV156_raw = self._get_16bit_reg(0x02, "H")157# return self.get_bus_adc_lsb() * (_raw >> 3), bool(_raw & 0x02), bool(_raw & 0x01)158return voltage_ina219(bus_voltage=self.get_bus_adc_lsb() * (_raw >> 3), data_ready=bool(_raw & 0x02),159overflow=bool(_raw & 0x01))160
161
162class INA219(INA219Simple, BaseSensorEx, IBaseSensorEx, Iterator):163"""Class for work with TI INA219 sensor"""164
165# предел напряжения на шунте из документации, Вольт166# shunt voltage limit, Volt167_shunt_voltage_limit = 0.32768168# Предел измеряемого напряжения! И неважно, что чип измеряет до 32 Вольт!169# В документации 26. Минус 1 вольт для запаса (Senses Bus Voltages from 0 to 26 V).170# "Senses Bus Voltages from 0 to 26 V"171# Measured voltage limit! And it doesn't matter that the chip measures up to 32 volts!172# In the documentation 26. Minus 1 volt for a margin (Senses Bus Voltages from 0 to 26 V)173_vbus_max = 25174# разрешенные значения для полей BADC, SADC175_vval = tuple(i for i in range(0x10) if i not in range(4, 8))176# описание регистра конфигурации177_config_reg_ina219 = (bit_field_info(name='RST', position=range(15, 16), valid_values=None, description="Сбрасывает все регистры в значениям по умолчанию."), # Reset Bit178# Bus Voltage Range, 0 - 16 V; 1 - 32 V179bit_field_info(name='BRNG', position=range(13, 14), valid_values=None, description="Переключатель диапазонов измеряемого напряжения на шине."),180# PGA (Current Shunt Voltage Only). 0 - +/-40 mV; 1 - +/-80 mV; 2 - +/-160 mV; 3 - +/-320 mV;181bit_field_info(name='PGA', position=range(11, 13), valid_values=range(4), description="Переключатель диапазонов напряжения на токовом шунте."),182# Bus ADC Resolution/Averaging. These bits adjust the Bus ADC resolution (9-, 10-, 11-, or 12-bit) or set the number of samples used when averaging results for the Bus Voltage Register (02h).183bit_field_info(name='BADC', position=range(7, 11), valid_values=_vval, description="Биты регулируют разрешение АЦП шины или устанавливают количество выборок для усреднении результатов."),184# Shunt ADC Resolution/Averaging. These bits adjust the Shunt ADC resolution (9-, 10-, 11-, or 12-bit) or set the number of samples used when averaging results for the Shunt Voltage Register (01h).185bit_field_info(name='SADC', position=range(3, 7), valid_values=_vval, description="Биты регулируют разрешение АЦП токового шунта или устанавливают количество выборок для усреднения результатов."),186# Operating Mode. Selects continuous, triggered, or power-down mode of operation. These bits default to continuous shunt and bus measurement mode.187# bit_field_info(name='MODE', position=range(3), valid_values=tuple(i for i in range(8) if 4 != i), description="Непрерывный, однократный режим работы или режим пониженного энергопотребления."),188bit_field_info(name='CNTNS', position=range(2, 3), valid_values=None, description='1 - Непрерывный режим работы датчика, 0 - по запросу'),189# Внимание хотя бы один(!) АЦП должен быть ВКЛЮЧЕН в непрерывном режиме измерений! Смотри "Table 6. Mode Settings"190bit_field_info(name='BADC_EN', position=range(1, 2), valid_values=None, description='1 - АЦП напряжения на шине включен, 0 - выключен'),191bit_field_info(name='SADC_EN', position=range(0, 1), valid_values=None, description='1 - АЦП напряжения на токовом шунте включен, 0 - выключен'),192)193
194def __init__(self, adapter: bus_service.BusAdapter, address=0x40, max_expected_curr: float = 3.2, shunt_resistance: float = 0.1):195"""shunt_resistance - сопротивление шунта, [Ом].196max_expected_curr - предельный ток через шунт, [A]"""
197super().__init__(adapter, address)198# для удобства работы с настройками199self._bit_fields = BitFields(fields_info=INA219._config_reg_ina219)200# сопротивление токового шунта в Омах!201self.shunt_resistance = shunt_resistance202self.max_expected_current = max_expected_curr203self._current_lsb = INA219Simple._lsb_shunt_voltage204self._power_lsb = None # для метода calibrate205
206@staticmethod207def shunt_voltage_range_to_volt(index: int) -> float:208"""Преобразует индекс диапазона напряжения токового шунта в напряжение, Вольт.209index = 0 +/- 40 mV, 1 +/- 80 mV, 2 +/- 160 mV, 3 +/- 320 mV"""
210check_value(index, range(4),f"Неверный индекс диапазона напряжения токового шунта: {index}")211return 0.040 * (2 ** index)212
213def _get_pwr_reg(self) -> int:214"""Возвращает содержимое регистра мощности"""215return self._get_16bit_reg(0x03, 'H')216
217def _get_curr_reg(self) -> int:218"""Возвращает содержимое регистра тока"""219return self._get_16bit_reg(0x04, 'h')220
221@staticmethod222def _get_curr_lsb(max_expected_cur: float) -> float:223"""Возвращает цену наименьшего разряда токового регистра в Амперах [A]. Ток max_expected_cur в [А]"""224return max_expected_cur / 2 ** 15225
226@staticmethod227def _get_shunt_v_rng_cr(max_expected_current: float, shunt_resistance: float) -> int:228"""Возвращает 0..3, сырой диапазон напряжений на шунте по макс. току и сопротивлению шунта."""229_max_v_shunt = abs(max_expected_current * shunt_resistance)230for index in range(4):231_v = INA219.shunt_voltage_range_to_volt(index)232if _v >= _max_v_shunt:233return index234raise ValueError("Не удалось подобрать диапазона напряжения шунта!")235
236def calibrate(self, max_expected_current: float, shunt_resistance: float) -> int:237"""Производит вычисления и запись в регистр калибровки значения, которое и возвращает, как результат.238max_expected_current - предельный долговременный ожидаемый ток, Ампер.
239shunt_resistance - сопротивление шунта, Ом."""
240_mx = 0.32768 # больше микросхема не вывозит! Производитель ИМС (TI) указывает 0.32, но у меня свое мнение!241_max_shunt_voltage = max_expected_current * shunt_resistance242if _max_shunt_voltage > _mx or _max_shunt_voltage <= 0 or max_expected_current <= 0:243raise ValueError(f"Неверная комбинация входных параметров! {max_expected_current}\t{shunt_resistance}")244#245self._current_lsb = INA219._get_curr_lsb(max_expected_current)246self._power_lsb = 20 * self._current_lsb247_cal = int(0.04096 // (self._current_lsb * shunt_resistance)) # волшебная формула из документации248self.current_shunt_voltage_range = INA219._get_shunt_v_rng_cr(max_expected_current, shunt_resistance)249# запись в регистр калибровки. младший бит недоступен для записи!250self.write_reg(0x05, _cal, 2)251return _cal252
253def get_config(self, return_value: bool = True) -> [config_ina219, None]:254"""Считывает настройками датчика по шине"""255raw_config = self._get_raw_cfg()256#257bf = self._bit_fields258# запоминаю считанную конфигурацию259bf.source = raw_config260#261if return_value:262return config_ina219(BRNG=self.bus_voltage_range, PGA=self.current_shunt_voltage_range,263BADC=self.bus_adc_resolution, SADC=self.shunt_adc_resolution,264CNTNS=self.continuous, BADC_EN=self.bus_adc_enabled,265SADC_EN=self.shunt_adc_enabled,266)267
268def is_single_shot_mode(self) -> bool:269"""Возвращает Истина, когда датчик находится в режиме однократных измерений,270каждое из которых запускается методом start_measurement."""
271return not self.is_continuously_mode()272
273def is_continuously_mode(self) -> bool:274"""Возвращает Истина, когда датчик находится в режиме многократных измерений,275производимых автоматически. Процесс запускается методом start_measurement."""
276return self._bit_fields['CNTNS']277
278@property279def continuous(self) -> bool:280return self.is_continuously_mode()281
282@continuous.setter # qqq283def continuous(self, value: bool):284if value and not (self.bus_adc_enabled or self.shunt_adc_enabled):285raise ValueError('В непрерывном режиме измерения хотя бы один АЦП должен быть включен!')286self._bit_fields['CNTNS'] = value287
288def get_conversion_cycle_time(self) -> int:289"""Возвращает время в мкс(!) преобразования сигнала в цифровой код и готовности его для чтения по шине!290Для текущих настроек датчика. При изменении настроек следует заново вызвать этот метод!"""
291_t0, _t1 = 0, 0292# bf = self._bit_fields293if self.shunt_adc_enabled:294adc_field = self.shunt_adc_resolution # bf['SADC'] # выделяю поле SADC (токовый шунт)295_t0 = _get_conv_time(adc_field)296# print(f"DBG:get_conversion_cycle_time SADC: {adc_field}")297if self.bus_adc_enabled:298adc_field = self.bus_adc_resolution # bf['BADC'] # выделяю поле BADC (напряжение на шине)299_t1 = _get_conv_time(adc_field)300# print(f"DBG:get_conversion_cycle_time BADC: {adc_field}")301# возвращаю наибольшее значение, поскольку измерения производятся параллельно, как утверждает документация302return max(_t0, _t1)303
304
305def start_measurement(self, continuous: bool = True, enable_calibration: bool = True,306enable_shunt_adc: bool = True, enable_bus_adc: bool = True):307"""Настраивает параметры датчика и запускает процесс измерения.308continuous - если Истина, то новое измерение запускается автоматически после завершения предидущего;
309enable_calibration - если Истина, то происходит калибловка под заданное сопротивление шунта и ток в нагрузке;
310enable_shunt_adc - включить измерение напряжения на токовом шунте;
311enable_bus_adc - включить измерение напряжения на шине;"""
312self.bus_adc_enabled = enable_bus_adc313self.shunt_adc_enabled = enable_shunt_adc314self.continuous = continuous # устанавливайте этот бит после bus_adc_enabled и shunt_adc_enabled315if enable_calibration:316self.calibrate(self.max_expected_current, self.shunt_resistance)317# print(f"DBG: calibrate value: 0x{clbr:X}")318cfg = self.set_config()319# print(f"DBG: set_config return: 0x{cfg:X}")320
321@property322def bus_voltage_range(self) -> bool:323"""Возвращает измеряемый диапазон напряжений на шине. Если Истина то диапазон 0..25 Вольт, иначе 0..16 Вольт."""324return self._bit_fields['BRNG']325
326@bus_voltage_range.setter327def bus_voltage_range(self, value: bool):328self._bit_fields['BRNG'] = value329
330@property331def shunt_resistance(self) -> float:332"""Возвращает сопротивление токового шунта в Омах."""333return self._shunt_res334
335@shunt_resistance.setter336def shunt_resistance(self, value: float):337"""Метод устанавливает сопротивление шунта в пределах 0.01..10 Ом"""338if .001 <= value <= 10:339self._shunt_res = value340return341raise ValueError(f"Неверное значение сопротивления шунта: {value}")342
343@property344def max_expected_current(self) -> float:345"""Возвращает максимальный ожидаемый ток в Амперах"""346return self._max_expected_curr347
348@max_expected_current.setter349def max_expected_current(self, value: float):350if .1 < value < 10:351self._max_expected_curr = value352return353raise ValueError(f"Неверное значение тока: {value}")354
355@property356def current_shunt_voltage_range(self) -> int:357"""Возвращает установленный диапазон напряжения на шунте."""358return self._bit_fields['PGA']359
360@current_shunt_voltage_range.setter361def current_shunt_voltage_range(self, value):362"""Устанавливает диапазон напряжения на шунте 0..3.363# value range, mV
364# 0 ±40 mV
365# 1 ±80 mV
366# 2 ±160 mV
367# 3 ±320 mV"""
368self._bit_fields['PGA'] = value369
370def set_config(self) -> int:371"""Настраивает датчик в соответствии с настройками. Возвращает значение настроек в сыром(!) виде"""372bf = self._bit_fields373_cfg = bf.source374# print(f"DBG: _set_raw_cfg: 0x{_cfg:X}")375self._set_raw_cfg(_cfg)376#377return _cfg378
379@property380def shunt_adc_enabled(self) -> bool:381"""Если Истина, то АЦП напряжения на токовом шунте включен!"""382return self._bit_fields['SADC_EN']383
384@shunt_adc_enabled.setter385def shunt_adc_enabled(self, value: bool):386self._bit_fields['SADC_EN'] = value387
388@property389def bus_adc_enabled(self) -> bool:390"""Если Истина, то АЦП напряжения на шине включен!"""391return self._bit_fields['BADC_EN']392
393@bus_adc_enabled.setter394def bus_adc_enabled(self, value: bool):395self._bit_fields['BADC_EN'] = value396
397@property398def bus_adc_resolution(self) -> int:399"""Разрешение АЦП на шине в сыром виде.4000 - 9 бит
4011 - 10 бит
4022 - 11 бит
4033 - 12 бит
4048 - 12 бит
4059..15 - количество отсчетов, которое используется для усреднения результата. 9 - 2 отсчета; 15 - 128 отсчетов,
406смотри 'Table 5. ADC Settings'"""
407return self._bit_fields['BADC']408
409@bus_adc_resolution.setter410def bus_adc_resolution(self, value: int):411self._bit_fields['BADC'] = value412
413@property414def shunt_adc_resolution(self) -> int:415"""Разрешение АЦП напряжения на токовом шунте.4160 - 9 бит
4171 - 10 бит
4182 - 11 бит
4193 - 12 бит
4204, 8 - 12 бит
4219..15 - количество отсчетов, которое используется для усреднения результата. 9 - 2 отсчета; 15 - 128 отсчетов,
422смотри 'Table 5. ADC Settings'"""
423return self._bit_fields['SADC']424
425@shunt_adc_resolution.setter426def shunt_adc_resolution(self, value: int):427self._bit_fields['SADC'] = value428
429def get_power(self) -> float:430"""Возвращает мощность в Ваттах в нагрузке"""431return self._power_lsb * self._get_pwr_reg()432
433def get_current(self) -> float:434"""Возвращает ток в нагрузке в Амперах"""435return self._current_lsb * self._get_curr_reg()436
437def __iter__(self):438return self439
440def __next__(self) -> tuple:441"""Возвращает измеренные значения. кортеж, число."""442_shunt, _bus = None, None443if self.shunt_adc_enabled:444_shunt = self.get_shunt_voltage()445if self.bus_adc_enabled:446_bus = self.get_voltage()447
448return _shunt, _bus449