mcp3421
/
mcp3421mod.py
201 строка · 12.5 Кб
1# micropython
2# mail: goctaprog@gmail.com
3# MIT license
4# import struct
5
6from sensor_pack_2 import bus_service
7from sensor_pack_2.base_sensor import DeviceEx, Iterator, check_value, get_error_str # all_none
8from sensor_pack_2.adcmod import ADC, adc_init_props # , raw_value_ex
9# import micropython
10# from micropython import const
11from collections import namedtuple
12from sensor_pack_2.bitfield import bit_field_info
13from sensor_pack_2.bitfield import BitFields
14
15_model_3421 = 'mcp3421'
16_model_3422 = 'mcp3422'
17_model_3424 = 'mcp3424'
18
19
20def get_init_props(model: str) -> adc_init_props:
21"""Возвращает параметры для инициализации АЦП в виде именованного кортежа по имени модели АЦП."""
22if _model_3421 == model.lower():
23return adc_init_props(reference_voltage=2.048, max_resolution=18, channels=0,
24differential_channels=1, differential_mode=True)
25if _model_3422 == model.lower():
26return adc_init_props(reference_voltage=2.048, max_resolution=18, channels=0,
27differential_channels=2, differential_mode=True)
28if _model_3424 == model.lower():
29return adc_init_props(reference_voltage=2.048, max_resolution=18, channels=0,
30differential_channels=4, differential_mode=True)
31raise ValueError(f"Неизвестная модель АЦП!")
32
33
34class Mcp342X(DeviceEx, ADC, Iterator):
35"""18-битный аналого-цифровой преобразователь с интерфейсом I2C и встроенным ИОН.
3618-Bit Analog-to-Digital Converter with I2C Interface and On-Board Reference"""
37
38_config_reg_mcp3421 = (bit_field_info(name='RDY', position=range(7, 8), valid_values=None), # Этот бит является флагом готовности данных. В режиме чтения этот бит указывает, был ли выходной регистр обновлен новым преобразованием. В режиме однократного преобразования запись этого бита в «1» инициирует новое преобразование.
39bit_field_info(name='CH', position=range(5, 7), valid_values=None), # (channel) Это биты выбора канала, но они не используются в MCP3421.
40bit_field_info(name='CCM', position=range(4, 5), valid_values=range(6)), # (continue conversion mode) Бит режима преобразования. 1 - режим непрерывного преобразования. 0 - режим однократного преобразования.
41bit_field_info(name='SampleRate', position=range(2, 4), valid_values=None), # Бит выбора частоты дискретизации. 0 - 240 SPS (12 bit); 1 - 60 SPS (14 bit); 2 - 15 SPS (16 bit); 3 - 3.75 SPS (18 bit)
42bit_field_info(name='PGA', position=range(2), valid_values=None), # Биты выбора усиления PGA. 0 - 1; 1 - 1/2; 2 - 1/4; 3 - 1/8
43)
44# ответ от АЦП
45_mcp3421_raw_data = namedtuple("_mcp3421_raw_data", "b0 b1 b2 config")
46
47def get_resolution(self, raw_data_rate: int) -> int:
48"""Преобразует сырое значение частоты обновления данных в кол-во бит в отсчете АЦП.
49У многих АЦП кол-во бит в отсчете зависит(!) от частоты преобразования."""
50return 12 + 2 * raw_data_rate
51
52def __init__(self, adapter: bus_service.BusAdapter, model: str = 'mcp3421', address=0x68):
53# MCP3421 имеет фиксированный адрес 0x68, но АЦП MCP342Х имеют адреса в диапазоне 0x68..0x6F
54check_value(address, range(0x68, 0x70), f"Неверное значение адреса I2C устройства: 0x{address:x}")
55DeviceEx.__init__(self, adapter, address, True)
56ADC.__init__(self, get_init_props(model), model=model)
57# print("DBG:__init__")
58# для удобства работы с настройками АЦП
59self._bit_fields = BitFields(fields_info=Mcp342X._config_reg_mcp3421)
60# буфер на 4 байта
61self._buf_4 = bytearray((0 for _ in range(4)))
62# последнее считанное из АЦП значение
63self._last_raw_value = None
64self._differential_mode = True # дифференциальный АЦП. для get_lsb
65# Этот бит является флагом готовности данных. В режиме чтения этот бит указывает, был ли выходной регистр
66# обновлен новым преобразованием (0).
67# В режиме однократного преобразования запись этого бита в «1» инициирует новое преобразование.
68self._data_ready = None
69# Внимание, важный вызов(!)
70# читаю config АЦП и обновляю поля класса
71_raw_cfg = self.get_raw_config()
72self.raw_config_to_adc_properties(_raw_cfg)
73
74# def _read_raw_data(self) -> _mcp3421_raw_data:
75# """Считывает из АЦП информацию о результате преобразования и текущие 'сырые' настройки"""
76# buf = self._buf_4
77# self.read_to_buf(buf)
78# b0, b1, b2, cfg = self.unpack(fmt_char=len(buf)*"B", source=buf)
79# return Mcp3421._mcp3421_raw_data(b0=b0, b1=b1, b2=b2, config=cfg)
80
81def get_raw_config(self) -> int:
82"""Возвращает(считывает) текущие настройки датчика из регистров(конфигурации) в виде числа."""
83# raw = self._read_raw_data()
84buf = self._buf_4
85self.read_to_buf(buf)
86# print(f"DBG:get_raw_config: 0x{buf[-1]:x}")
87return buf[-1]
88
89def set_raw_config(self, value: int):
90"""Записывает настройки(value) во внутреннюю память/регистр датчика."""
91self.write(value.to_bytes(1, 'big'))
92
93def raw_config_to_adc_properties(self, raw_config: int):
94"""Возвращает текущие настройки датчика из числа, возвращенного get_raw_config(!), в поля(!) класса.
95raw_config -> adc_properties"""
96# вызывать только после вызова get_raw_config!!!
97bf = self._bit_fields
98bf.source = raw_config
99bf.field_name = 'RDY' # инверсное значение, читай bit 7, RDY: Ready Bit
100# 0 - в бите DRY, означает, что данные были обновлены АЦП
101self._data_ready = not bf.get_field_value()
102bf.field_name = 'CH'
103self._curr_channel = bf.get_field_value()
104bf.field_name = 'CCM'
105self._single_shot_mode = not bf.get_field_value()
106bf.field_name = 'PGA'
107self._curr_raw_gain = bf.get_field_value()
108bf.field_name = 'SampleRate'
109self._curr_raw_data_rate = bf.get_field_value()
110
111def get_raw_value(self) -> int:
112"""Возвращает 'сырое' значение отсчета АЦП. Переопределяется в классах - наследниках!"""
113# вызывать только после вызова get_raw_config и raw_config_to_adc_properties!!!
114# print("DBG:get_raw_value")
115cfg = self.get_raw_config()
116# print(f"DBG:get_raw_value. config: 0x{cfg:x}")
117self.raw_config_to_adc_properties(raw_config=cfg)
118if self.data_ready:
119if self._curr_raw_data_rate < 3:
120# два байта на отсчет, 12, 14, 16 бит
121return self.unpack(fmt_char='h', source=self._buf_4)[0]
122b0, b1, b2 = self.unpack(fmt_char='bBB', source=self._buf_4) # 18 бит на отсчет
123return 65536*b0 + 256*b1 + b2
124# print(f"DBG:get_raw_value. data not ready! config: 0x{cfg:x}")
125
126def raw_sample_rate_to_real(self, raw_sample_rate: int) -> float:
127"""Преобразует сырое значение частоты преобразования в частоту [Гц]."""
128sps = 240, 60, 15, 3.75
129return sps[raw_sample_rate]
130
131def gain_raw_to_real(self, raw_gain: int) -> float:
132"""Преобразует 'сырое' значение усиления в 'настоящее'"""
133return 2 ** raw_gain
134
135def get_conversion_cycle_time(self) -> int:
136"""возвращает время преобразования в [мкс] аналогового значения в цифровое в зависимости от
137текущих настроек АЦП. Переопредели для каждого АЦП!"""
138# вызывать только после вызова get_raw_config и raw_config_to_adc_properties!!!
139return 1 + int(1_000_000 / self.sample_rate)
140
141def check_gain_raw(self, gain_raw: int) -> int:
142"""Проверяет сырое усиление на правильность. В случае ошибки выброси исключение!
143Возвращает значение gain_raw в случае успеха! Для переопределения в классе-наследнике."""
144r4 = range(4)
145return check_value(gain_raw, r4, get_error_str("gain_raw", gain_raw, r4))
146
147def check_data_rate_raw(self, data_rate_raw: int) -> int:
148"""Проверяет сырое data_rate на правильность. В случае ошибки выброси исключение!
149Возвращает data_rate_raw в случае успеха! Для переопределения в классе-наследнике."""
150r4 = range(4)
151return check_value(data_rate_raw, r4, get_error_str("data_rate_raw", data_rate_raw, r4))
152
153def adc_properties_to_raw_config(self) -> int:
154"""Преобразует свойства АЦП из полей класса в 'сырую' конфигурацию АЦП.
155adc_properties -> raw_config"""
156# print("DBG:adc_properties_to_raw_config")
157_cfg = self.get_raw_config()
158bf = self._bit_fields
159bf.source = _cfg
160#
161bf.field_name = 'CH'
162bf.set_field_value(value=not self._curr_channel)
163bf.field_name = 'CCM'
164bf.set_field_value(value=not self.single_shot_mode)
165bf.field_name = 'RDY'
166bf.set_field_value(value=self.single_shot_mode)
167bf.field_name = 'SampleRate'
168bf.set_field_value(value=self.current_sample_rate)
169bf.field_name = 'PGA'
170bf.set_field_value(value=self.current_raw_gain)
171#
172# print(f"DBG:adc_properties_to_raw_config: 0x{bf.source:x}")
173return bf.source
174
175@property
176def data_ready(self) -> bool:
177if self.single_shot_mode:
178return self._data_ready
179else: # автоматический режим измерений
180if 3 == self.current_sample_rate:
181return self._data_ready
182# В автоматическом режиме измерений, при data_rate меньше трех, не выставлялся бит готовности данных.
183# Причину не нашел! Пришлось делать это!
184# Вот что сказано в документации:
185# The MCP3421 device performs a Continuous Conversion if the O/C bit is set to logic “high”. Once the
186# conversion is completed, the result is placed at the output data register. The device immediately begins
187# another conversion and overwrites the output data register with the most recent data.
188#
189# The device also clears the data ready flag (RDY bit = 0) when the conversion is completed. The device sets the
190# ready flag bit (RDY bit = 1), if the latest conversion result has been read by the Master.
191return True
192
193# Iterator
194def __iter__(self):
195return self
196
197def __next__(self) -> [int, None]:
198if not self.single_shot_mode:
199# режим непрерывного преобразования!
200return self.value
201return None
202