ads1115
295 строк · 19.4 Кб
1# MicroPython
2# mail: goctaprog@gmail.com
3# MIT license
4
5# from sensor_pack_2.bus_service import mpy_bl
6from collections import namedtuple
7from sensor_pack_2.base_sensor import check_value
8
9# разностный вход (bool, differential_input)
10# разрядность в битах (int, resolution)
11# опорное напряжение в вольтах (float, rev_voltage)
12# количество аналоговых входов (channels)
13adc_base_props = namedtuple("adc_props", "ref_voltage resolution channels differential_channels")
14# кортеж информации о канале АЦП: number(номер канала):int, is_differential(дифференциальный_режим):bool
15adc_channel_info = namedtuple("adc_channel_info", "number is_differential")
16# кортеж информации о количестве(!) каналов АЦП
17# channels - количество обычных(single ended) каналов
18# differential_channels - количество дифференциальных(differential) каналов
19adc_channels = namedtuple("adc_channels", "channels differential_channels")
20# основные свойства АЦП: опорное напряжение, Вольт; текущее кол-во значащих бит в отсчете,
21# предельное кол-во значащих бит в отсчете, текущий номер канала, количество обычных накалов(Vxx..GND),
22# предельное кол-во обычных каналов, предельное кол-во дифференциальных каналов,
23# текущая частота отсчетов(current sample rate), Гц
24adc_general_props = namedtuple("adc_general_props",
25"ref_voltage resolution max_resolution current_channel channels diff_channels")
26# основные 'сырые' настройки, характерные для всех(!) АЦП
27adc_general_raw_props = namedtuple("adc_general_raw_props", "sample_rate gain_amplifier single_shot_mode")
28
29# параметры для инициализации АЦП
30# reference_voltage - опорное напряжение, Вольт
31# max_resolution - предельное кол-во значащих бит в отсчете. Разрешение часто, параметр динамический(!).
32# Зависит от частоты преобразования _data_rate (Гц) аналогового сигнала в цифровой.
33# channels - количество обычных(single ended) каналов
34# differential_channels - количество дифференциальных(differential) каналов
35# differential_mode - Если истина, то это дифференциальный АЦП. для метода get_lsb.
36adc_init_props = namedtuple("adc_init_props",
37"reference_voltage max_resolution channels differential_channels differential_mode")
38# для метода get_raw_value_ex
39# value - значение АЦП, сырое(!)
40# если low_limit в Истина, то "стрелка" АЦП на нижнем крае шкалы (underflow)
41# если hi_limit в Истина, то "стрелка" АЦП верхнем крае шкалы (overflow)
42raw_value_ex = namedtuple("raw_value_ex", "value low_limit hi_limit")
43
44# Типовое содержимое регистра конфигурации (все значения сырые/raw):
45# gain; коэффициент усиления для PGA-programmable gain amplifier (усилитель с программируемым усилением)
46# channel; измеряющий канал
47# data_rate; частота отсчетов
48# resolution; количество бит в отсчете (для некоторых АЦП)
49# meas_mode; режим измерения (single shot or automatic)
50# raw_config_reg = namedtuple("raw_config_reg", "gain channel data_rate resolution meas_mode")
51
52
53# вычисляет 'сырые' предельные значения, считываемые из регистра АЦП
54def _get_reg_raw_limits(adc_resolution: int, differential: bool) -> raw_value_ex:
55if differential:
56# для дифференциальных АЦП
57_base = 2 ** (adc_resolution - 1)
58return raw_value_ex(value=0, low_limit=_base, hi_limit=_base - 1)
59# для обычных АЦП
60return raw_value_ex(value=0, low_limit=0, hi_limit=2 ** adc_resolution - 1)
61
62
63class ADC:
64def __init__(self, init_props: adc_init_props, model: str = None):
65"""reference_voltage - опорное напряжение в Вольтах;
66max_resolution - предельное разрешение АЦП в битах;
67channels - кол-во(!) входных аналоговых каналов;
68differential_channels - кол-во(!) входных аналоговых дифференциальных каналов;
69model - модель АЦП в виде строки"""
70self.init_props = init_props
71adc_ip = self.init_props
72if adc_ip.reference_voltage <= 0 or adc_ip.channels < 0 or adc_ip.differential_channels < 0:
73raise ValueError(f"Неверный параметр! Опорное напряжение, В: {adc_ip.reference_voltage}; Кол-во каналов: {adc_ip.channels}/{adc_ip.differential_channels}")
74# текущее количество выполняемых преобразований аналогового сигнала в цифровой! RAW, сырое значение!
75# для записи в регистр
76self._curr_raw_data_rate = None
77# текущее разрешение АЦП в битах
78self._curr_resolution = None
79# текущий номер канала. Диапазон 0..self._channels/self._diff_channels. Проверка на правильность в методе
80# check_channel_number
81self._curr_channel = None
82# если Истина, то self._curr_channel это дифференциальный(!) канал, иначе канал не дифференциальный(!)
83self._is_diff_channel = None
84# текущий коэффициент усиления (raw). Для записи в регистр АЦП
85self._curr_raw_gain = None # RAW!
86# действительный текущий коэффициент усиления.
87# присвойте его в классе - наследнике путем пересчета из self._curr_gain. Смотри метод Ads1115.get_correct_gain
88self._real_gain = None
89# режим преобразования. если истина, то АЦП выполняет преобразование по запросу,
90# иначе АЦП выполняет преобразования автоматически с определенной частотой (_curr_data_rate)
91self._single_shot_mode = None
92# режим пониженного энергопотребления
93self._low_pwr_mode = None
94# строковое имя модели АЦП
95self._model_name = model
96
97@property
98def model(self) -> str:
99"""Строковое имя модели АЦП"""
100return self._model_name
101
102def get_general_props(self) -> adc_general_props:
103"""Возвращает основные свойства АЦП"""
104ipr = self.init_props
105return adc_general_props(ipr.reference_voltage, self.current_resolution, ipr.max_resolution, self._curr_channel,
106ipr.channels, ipr.differential_channels)
107
108def get_general_raw_props(self) -> adc_general_raw_props:
109"""Возвращает основные 'сырые' свойства АЦП, которые считываются из регистра"""
110return adc_general_raw_props(sample_rate=self._curr_raw_data_rate, gain_amplifier=self._curr_raw_gain,
111single_shot_mode=self._single_shot_mode)
112
113def get_specific_props(self):
114"""Возвращает характерные для АЦП свойства, желательно в виде именованного кортежа.
115Для переопределения в классе-наследнике"""
116raise NotImplemented
117
118def check_channel_number(self, value: int, diff: bool) -> int:
119"""Проверяет номер входного аналогового канала(value) АЦП на правильность.
120Если diff в Истина, то канал дифференциальный(!).
121value должно быть в диапазоне 0..self._channels/self._diff_channels"""
122ipr = self.init_props
123_max = ipr.differential_channels if diff else ipr.channels
124check_value(value, range(_max),
125f"Неверный номер канала АЦП: {value}; дифф: {diff}. Допустимый диапазон: 0..{_max - 1}")
126return value
127
128def check_gain_raw(self, gain_raw: int) -> int:
129"""Проверяет сырое усиление на правильность. В случае ошибки выброси исключение!
130Возвращает значение gain_raw в случае успеха! Для переопределения в классе-наследнике."""
131raise NotImplemented
132
133def check_data_rate_raw(self, data_rate_raw: int) -> int:
134"""Проверяет сырое data_rate на правильность. В случае ошибки выброси исключение!
135Возвращает data_rate_raw в случае успеха! Для переопределения в классе-наследнике."""
136raise NotImplemented
137
138def get_lsb(self) -> float:
139"""Возвращает цену младшего разряда в Вольтах в зависимости от текущих настроек АЦП.
140gain - коэффициент усиления/ослабления входного делителя АЦП, должен быть больше нуля!"""
141ipr = self.init_props
142_k = 2 if ipr.differential_mode else 1
143return _k * ipr.reference_voltage / (self.gain * 2 ** self.current_resolution)
144
145def get_conversion_cycle_time(self) -> int:
146"""возвращает время преобразования в [мкc/мс] аналогового значения в цифровое в зависимости от
147текущих настроек АЦП. Переопредели для каждого АЦП!"""
148raise NotImplemented
149
150@property
151def general_properties(self) -> adc_general_props:
152return self.get_general_props()
153
154@property
155def value(self) -> float:
156"""Возвращает значение текущего канала в Вольтах"""
157return self.get_value(raw=False)
158
159def get_raw_value(self) -> int:
160"""Возвращает 'сырое' значение отсчета АЦП.
161Переопределяется в классах - наследниках!"""
162raise NotImplemented
163
164def get_raw_value_ex(self, delta: int = 5) -> raw_value_ex:
165"""Возвращает 'сырое' значение отсчета АЦП и флаги переполнения.
166Переопределяется в классах - наследниках!
167delta - 'зазор'"""
168raw = self.get_raw_value()
169limits = _get_reg_raw_limits(self.current_resolution, self.init_props.differential_mode)
170return raw_value_ex(value=raw, low_limit=raw in range(limits.low_limit, 1 + delta + limits.low_limit),
171hi_limit=raw in range(limits.hi_limit - delta, 1 + limits.hi_limit))
172
173def raw_value_to_real(self, raw_val: int) -> float:
174"""Преобразует 'сырое' значение из регистра АЦП в значение в Вольтах"""
175return raw_val * self.get_lsb()
176
177def gain_raw_to_real(self, raw_gain: int) -> float:
178"""Преобразует 'сырое' значение усиления в 'настоящее'.
179Переопределить в классе - наследнике!"""
180raise NotImplemented
181
182def get_value(self, raw: bool = True) -> float:
183"""Возвращает значение текущего канала в Вольтах, если raw в Ложь, в коде, если raw в Истина"""
184val = self.get_raw_value()
185if raw:
186return val
187return self.raw_value_to_real(val)
188
189def get_resolution(self, raw_data_rate: int) -> int:
190"""Возвращает кол-во бит в отсчете АЦП в зависимости от частоты взятия отсчетов (сырое значение!).
191Переопределить в классе - наследнике!"""
192raise NotImplemented
193
194def get_current_channel(self) -> adc_channel_info:
195"""Возвращает информацию о текущем активном канале АЦП"""
196return adc_channel_info(number=self._curr_channel, is_differential=self._is_diff_channel)
197
198@property
199def channel(self) -> adc_channel_info:
200"""Возвращает информацию о текущем канале"""
201return self.get_current_channel()
202
203def __len__(self) -> int:
204"""Возвращает количество аналоговых каналов АЦП в зависимости от типа текущего канала.
205Если текущий канал дифференциальный, то возвращается кол-во дифференциальных каналов, иначе
206возвращается кол-во обычных(single ended) каналов"""
207ipr = self.init_props
208return ipr.differential_channels if self._is_diff_channel else ipr.channels
209
210def start_measurement(self, single_shot: bool, data_rate_raw: int, gain_raw: int, channel: int,
211differential_channel: bool):
212"""Запуск однократного(single_shot в Истина) или многократного(single_shot в Ложь) измерения.
213data_rate_raw - частота получения выборок АЦП, отсчетов в сек., RAW-параметр, смотри в datasheet битовое поле!
214gain_raw - коэффициент усиления входного аналогового напряжения, RAW-параметр, смотри в datasheet битовое поле!
215channel - номер аналогового входа. От 0 до self._channels/self._diff_channels - 1
216differential_channel - если Истина, то канал с номером channel дифференциальный(!)
217Внимание! Последней строкой этого метода всегда вызывайте метод raw_config_to_adc_properties для
218записи значений в соответствующие поля класса!
219Скорее всего этот метод не потребуется переопределять, в крайнем случае и это можно сделать."""
220self.check_gain_raw(gain_raw=gain_raw) # проверка на правильность
221self.check_data_rate_raw(data_rate_raw=data_rate_raw) # проверка на правильность
222self.check_channel_number(channel, differential_channel) # проверка на правильность
223#
224self._single_shot_mode = single_shot
225self._curr_raw_data_rate = data_rate_raw
226self._curr_raw_gain = gain_raw
227self._curr_channel = channel
228self._curr_resolution = self.get_resolution(data_rate_raw)
229self._is_diff_channel = differential_channel
230# переопределяемые для каждого АЦП, методы
231_raw_cfg = self.adc_properties_to_raw_config()
232self.set_raw_config(_raw_cfg)
233# читаю config АЦП и обновляю поля класса
234_raw_cfg = self.get_raw_config() # читаю настройки АЦП
235self.raw_config_to_adc_properties(_raw_cfg) # обновляю поля экземпляра класса
236# пересчет в реальное усиление
237self._real_gain = self.gain_raw_to_real(self._curr_raw_gain)
238
239def raw_config_to_adc_properties(self, raw_config: int):
240"""Возвращает текущие настройки датчика из числа, возвращенного get_raw_config(!), в поля(!) класса.
241raw_config -> adc_properties.
242Переопределить в классе - наследнике!"""
243raise NotImplemented
244
245def adc_properties_to_raw_config(self) -> int:
246"""Преобразует свойства АЦП из полей класса в 'сырую' конфигурацию АЦП.
247adc_properties -> raw_config.
248Переопределить в классе - наследнике!"""
249raise NotImplemented
250
251def get_raw_config(self) -> int:
252"""Возвращает(считывает) текущие настройки датчика из регистров(конфигурации) в виде числа.
253Переопределить в классе - наследнике!"""
254raise NotImplemented
255
256def set_raw_config(self, value: int):
257"""Записывает настройки(value) во внутреннюю память/регистр датчика.
258Переопределить в классе - наследнике!"""
259raise NotImplemented
260
261def raw_sample_rate_to_real(self, raw_sample_rate: int) -> float:
262"""Преобразует сырое значение частоты преобразования в [Гц].
263Переопределить в классе - наследнике!"""
264raise NotImplemented
265
266@property
267def sample_rate(self) -> float:
268"""Возвращает текущее число отсчетов в секунду"""
269return self.raw_sample_rate_to_real(self.current_sample_rate)
270
271@property
272def current_sample_rate(self) -> int:
273"""Возвращает текущее сырое(!) количество отсчетов АЦП"""
274return self._curr_raw_data_rate
275
276@property
277def current_raw_gain(self) -> int:
278"""Возвращает текущий сырой(!) коэффициент усиления АЦП"""
279return self._curr_raw_gain
280
281@property
282def gain(self) -> float:
283"""Возвращает текущий реальный коэффициент усиления АЦП"""
284return self._real_gain
285
286@property
287def current_resolution(self) -> int:
288"""Возвращает текущее(!) кол-во бит в отсчете АЦП"""
289return self._curr_resolution
290
291@property
292def single_shot_mode(self) -> bool:
293"""Возвращает Истина, Если АЦП настроен на однократный(single shot conversion) режим работы,
294иначе на непрерывный (continuous conversion mode)"""
295return self._single_shot_mode
296