mcp3421
242 строки · 13.3 Кб
1# micropython
2# MIT license
3# Copyright (c) 2022 Roman Shevchik goctaprog@gmail.com
4"""MicroPython модуль для работы с шинами ввода/вывода"""
5
6import math7from machine import I2C, SPI, Pin8
9
10def mpy_bl(value: int) -> int:11"""Возвращает место, занимаемое значением value в битах.12Аналог int.bit_length(), которая есть в Python, но отсутствует в MicroPython!"""
13if 0 == value:14return 015return 1 + int(math.log2(abs(value)))16
17
18class BusAdapter:19"""Посредник между шиной ввода/вывода и классом ввода/вывода устройства"""20def __init__(self, bus: [I2C, SPI]):21self.bus = bus22
23def get_bus_type(self) -> type:24"""Возвращает тип шины"""25return type(self.bus)26
27def read_register(self, device_addr: [int, Pin], reg_addr: int, bytes_count: int) -> bytes:28"""считывает из регистра датчика значение.29device_addr - адрес датчика на шине. Для шины SPI это физический вывод MCU!
30reg_addr - адрес регистра в адресном пространстве датчика.
31bytes_count - размер значения в байтах."""
32raise NotImplementedError33
34def write_register(self, device_addr: [int, Pin], reg_addr: int, value: [int, bytes, bytearray],35bytes_count: int, byte_order: str):36"""записывает данные value в датчик, по адресу reg_addr.37bytes_count - кол-во записываемых байт из value.
38byte_order - порядок расположения байт в записываемом значении."""
39raise NotImplementedError40
41def read(self, device_addr: [int, Pin], n_bytes: int) -> bytes:42"""Читает из устройства на шине с адресом device_addr, n_bytes байт.43Возвращает экземпляр класса типа bytes"""
44raise NotImplementedError45
46def read_to_buf(self, device_addr: [int, Pin], buf: bytearray) -> bytes:47"""Читает из устройства на шине, с адресом device_addr, кол-во байт, равное длине буфера buf.48Возвращает ссылку на buf"""
49raise NotImplementedError50
51def write(self, device_addr: [int, Pin], buf: bytes):52"""Записывает в устройство на шине все байты из буфера buf"""53raise NotImplementedError54
55def write_const(self, device_addr: [int, Pin], val: int, count: int):56"""Отправляет пакет байт со значение val количеством count на шину.57Часто, при работе с дисплеями или памятью, требуется заполнение экрана/области
58постоянным значением. Для этого и предназначен этот метод!
59Вызов его для сравнительно медленных шин - плохая идея!"""
60if 0 == count:61return # нет ничего62# bl = val.bit_length() # bit_length() отсутствует в MicroPython63bl = mpy_bl(val)64if bl > 8:65raise ValueError(f"The value must take no more than 8 bits! Current: {bl}")66_max = 1667if count < _max:68_max = count69# вычисляю кол-во повторений тела цикла70repeats = count // _max # количество итераций71b = bytearray([val for _ in range(_max)])72for _ in range(repeats):73self.write(device_addr, b)74# вычисляю остаток75remainder = count - _max * repeats76if remainder:77b = bytearray([val for _ in range(remainder)])78self.write(device_addr, b)79
80def read_buf_from_memory(self, device_addr: [int, Pin], mem_addr, buf, address_size: int):81"""Читает из устройства с адресом device_addr в буфер buf, начиная с адреса в устройстве mem_addr.82Количество считываемых байт определяется длинной буфера buf.
83address_size - определяет размер адреса в байтах. (в ESP8266 этот аргумент не
84распознается и размер адреса всегда равен 1 (8 бит))."""
85raise NotImplementedError86
87def write_buf_to_memory(self, device_addr: [int, Pin], mem_addr, buf):88raise NotImplementedError89
90
91class I2cAdapter(BusAdapter):92"""Адаптер шины I2C"""93def __init__(self, bus: I2C):94super().__init__(bus)95
96def write_register(self, device_addr: int, reg_addr: int, value: [int, bytes, bytearray],97bytes_count: int, byte_order: str):98"""записывает данные value в датчик, по адресу reg_addr.99bytes_count - кол-во записываемых данных
100value - должно быть типов int, bytes, bytearray"""
101buf = None102if isinstance(value, int):103buf = value.to_bytes(bytes_count, byte_order)104if isinstance(value, (bytes, bytearray)):105buf = value106
107return self.bus.writeto_mem(device_addr, reg_addr, buf)108
109def read_register(self, device_addr: int, reg_addr: int, bytes_count: int) -> bytes:110"""считывает из регистра датчика значение.111bytes_count - размер значения в байтах"""
112return self.bus.readfrom_mem(device_addr, reg_addr, bytes_count)113
114def read(self, device_addr: int, n_bytes: int) -> bytes:115return self.bus.readfrom(device_addr, n_bytes)116
117def read_to_buf(self, device_addr: int, buf: bytearray) -> bytes:118"""Читает из устройства на шине с адресом device_addr в буфер buf количество байт, равное длине(len) буфера!"""119self.bus.readfrom_into(device_addr, buf)120return buf121
122def write(self, device_addr: int, buf: bytes):123return self.bus.writeto(device_addr, buf)124
125def read_buf_from_memory(self, device_addr: int, mem_addr, buf, address_size: int = 1):126"""Читает из устройства с адресом device_addr в буфер buf, начиная с адреса в устройстве mem_addr.127Количество считываемых байт определяется длинной буфера buf.
128address_size - определяет размер адреса в байтах. (в ESP8266 этот аргумент не распознается и размер адреса
129всегда равен 1 (8 бит)).
130Расширение возможностей базового класса."""
131self.bus.readfrom_mem_into(device_addr, mem_addr, buf)132return buf133
134def write_buf_to_memory(self, device_addr: int, mem_addr, buf):135"""Записывает в устройство с адресом device_addr все байты из буфера buf.136Запись начинается с адреса в устройстве: mem_addr.
137Расширение возможностей базового класса."""
138return self.bus.writeto_mem(device_addr, mem_addr, buf)139
140
141class SpiAdapter(BusAdapter):142"""Адаптер шины SPI"""143def __init__(self, bus: SPI, data_mode: Pin = None):144"""Параметр data_mode представляет собой вывод MCU, который используется для установки флага,145что посылка является данными (high) или командой (low). Например это необходимо при обмене с ILI9481."""
146super().__init__(bus)147# вывод MCU для режима данных148self.data_mode_pin = data_mode149# использовать ли вывод MCU для режима данных (Истина) или команд (Ложь)150self.use_data_mode_pin = False151# флаг для методов write.. . Если Истина, то data_mode (Pin) будет установлена в Истина, иначе в Ложь!152# flag for write.. methods. If True, then data_mode (Pin) will be set to True, otherwise to False!153self.data_packet = False154# индекс/номер байта в пересылаемом устройству по шину буферу, в котором находится адрес регистра устройства!155self._address_index = 0156# ссылка на функцию подготовки содержимого буфера перед его пересылкой в устройство!157# вида prepare(buf:bytearray, address_index:int) -> bytes: ...158# или None159self._prepare_before_send_ref = None160
161@property162def prepare_func(self):163"""Возвращает ссылку на функцию обработки буфера перед отправкой его по шине"""164return self._prepare_before_send_ref165
166@prepare_func.setter167def prepare_func(self, value):168"""Устанавливает ссылку на функцию обработки буфера перед отправкой его по шине"""169self._prepare_before_send_ref = value170
171def _call_prepare(self, buf: bytearray):172ref = self._prepare_before_send_ref173if ref is not None:174ref(buf, self._address_index)175
176def read(self, device_addr: Pin, n_bytes: int) -> bytes:177"""Read a number of bytes specified by n_bytes while continuously writing the single byte given by write.178Returns a bytes object with the data that was read."""
179try:180device_addr.low()181return self.bus.read(n_bytes)182finally:183device_addr.high()184
185def read_to_buf(self, device_addr: Pin, buf) -> bytes:186"""Читает из устройства на шине с адресом device_addr в буфер buf количество байт, равное длине(len) буфера!"""187try:188device_addr.low()189self.bus.readinto(buf, 0x00)190return buf191finally:192device_addr.high()193
194def write(self, device_addr: Pin, buf: bytes):195"""Параметр data_packet представляет собой признак того, что посылка является данными (high) или командой (low).196Например это необходимо при обмене ILI9481.
197Write the bytes contained in buf. Returns None.
198The data_packet parameter is an indication that the package is data (high) or command (low).
199For example, this is necessary when exchanging ILI9481."""
200try:201device_addr.low() # chip select202if self.use_data_mode_pin and self.data_mode_pin:203self.data_mode_pin.value(self.data_packet)204return self.bus.write(buf)205finally:206device_addr.high()207
208def write_and_read(self, device_addr: Pin, wr_buf: bytes, rd_buf: bytes):209"""Параметр data_packet представляет собой признак того, что посылка является данными (high) или командой (low).210Например это необходимо при обмене ILI9481.
211Расширение возможностей базового класса.
212Write the bytes from write_buf while reading into read_buf. The buffers can be the same or different,
213but both buffers must have the same length. Returns None.
214The data_packet parameter is an indication that the package is data (high) or command (low).
215For example, this is necessary when exchanging ILI9481."""
216try:217device_addr.low() # chip select218if self.use_data_mode_pin and self.data_mode_pin:219self.data_mode_pin.value(self.data_packet)220return self.bus.write_readinto(wr_buf, rd_buf)221finally:222device_addr.high()223
224def read_buf_from_memory(self, device_addr: Pin, mem_addr, buf):225"""Читает из устройства с адресом device_addr в буфер buf, начиная с адреса в устройстве mem_addr.226Количество считываемых байт определяется длинной буфера buf."""
227try:228device_addr.low() # chip select229# пока нет реализации!!!230raise NotImplementedError231finally:232device_addr.high()233
234def write_buf_to_memory(self, device_addr: Pin, mem_addr, buf):235try:236device_addr.low() # chip select237# подготовка буфера к пересылке238self._call_prepare(buf)239# пока нет реализации!!!240raise NotImplementedError241finally:242device_addr.high()243