Насколько правильно реализован класс для датчика в Python?
Есть датчик, который выдаёт и записывает значение по шине SPI при обращении к определённым номерам регистров. Мне хотелось бы попрактиковаться в ООП и написать небольшую библиотеку для этого датчика, чтобы пользователь, который будет ей пользоваться не беспокоился о том, что происходит внутри, и просто по одной команде получал или записывал значения в датчик. Это моя первая практика в ООП, поэтому хотелось бы узнать, насколько правильно написана программа, где допустил ошибки, что можно сделать лучше или по-другому.
import spidev
import RPi.GPIO as GPIO
import time
# ADIS 16490 USER self.regISTER MEMORY MAP
_PAGE_ID = 0x00 # Same for all pages
# PAGE 0x00
_TEMP_OUT = 0x0E
_Z_GYRO_LOW = 0x18
_Z_GYRO_OUT = 0x1A
_Z_ACCL_LOW = 0x24
_Z_ACCL_OUT = 0x26
_PROD_ID = 0x7E
# PAGE 0x01 Reserved
# PAGE 0x03
_DEC_RATE = 0x0C
IRQ = 6 # GPIO connect to data redy
spi = spidev.SpiDev() # Создаем объект SPI
spi.open(0, 0) # Выбор номера порта и номера устройства(CS) шины SPI
spi.max_speed_hz = 15000000 # Задаём максимальную скорость работы шины SPI
spi.mode = 3 # Выбор режима работы SPI (от 0 до 3)
GPIO.setmode(GPIO.BCM) # Выбор режима нумерации выводов GPIO
GPIO.setup(IRQ, GPIO.IN) # Инициализация GPIO6 на ввод
# Функция читающая данные по шине SPI
def _spi_read(spi, reg): # Функция считывания данных по SPI
send = [0]*2 # Создаём список из двух элементов
# В 0 ячейку списка записываем адрес, который указываем в параметре reg
send[0] = reg
spi.writebytes(send) # Отправляем байты по шине SPI
# Считываем 2 байта по шине SPI. В итоге получаем список из двух значений [X, Y]
resp = spi.readbytes(2)
# Сдвигаем 8 бит ячейки 0 влево, затем используем лог. сложение с ячейкой 1
resp = ((resp[0] << 8) | resp[1])
return resp
# Функция записывающая данные по шине SPI
def _spi_write(spi, reg, value): # Функция записи данных по SPI
send = [0]*2 # Создаём список из двух элементов
# В 0 ячейку списка записываем адрес, который указываем в параметре reg и с помощью лог.ИЛИ указываем старший бит на запись
send[0] = 0x80 | reg
send[1] = value # В 1 ячейку отправляем данные которые нужно записать
spi.writebytes(send) # Отправляем байты по шине SPI
# Дескриптор позволяющий получать и записывать значения в атрибуты класса
class Descriptor:
def __get__(self, instance, owner):
return instance.__dict__[self.name]
def __set__(self, instance, value):
# if value < 0:
# raise ValueError('Cannot be negative.')
instance.__dict__[self.name] = value
def __set_name__(self, owner, name):
self.name = name
# Основной класс датчика
class ADIS_16490:
temp_out = Descriptor()
z_gyro_32 = Descriptor()
z_accl_32 = Descriptor()
decrate = Descriptor()
def __init__(self):
"""Check the ADIS was found, read the coefficients and enable the sensor"""
# Check device ID.
GPIO.wait_for_edge(IRQ, GPIO.FALLING) # Ждём уровень спадающего фронта (по документации)
_spi_write(spi, _PAGE_ID, 0x00) # Переключаемся на 1 страницу
GPIO.wait_for_edge(IRQ, GPIO.FALLING)
# Считываем ID датчика, если не совпадает, то вызываем ошибку
ADIS_PROD_ID = _spi_read(spi, _PROD_ID)
if ADIS_PROD_ID != 16490:
raise RuntimeError(f"Failed to find ADIS 16490! Chip ID {ADIS_PROD_ID}")
# Метод для чтения данных по номеру адреса регистра
def _get(self, reg):
GPIO.wait_for_edge(IRQ, GPIO.FALLING)
self.value = _spi_read(spi, reg) # Считываем значение
return self.value
# Метод для записи данных по номеру адреса регистра
def _set(self, reg, value):
GPIO.wait_for_edge(IRQ, GPIO.FALLING)
_spi_write(spi, reg, value) # Записываем данные в регистр
# Метод для изменения номера страниц датчика
def _select_page(self, page):
GPIO.wait_for_edge(IRQ, GPIO.FALLING)
_spi_write(spi, _PAGE_ID, page) # Переключаемся на страницу
# Метод для объединения 16 битных чисел в 32 бита
def _unity(self, high, low):
bit32 = ((high << 16) | (low & 0xFFFF))
return bit32
# Метод проверки значения на знак
def _check(self, val, bits):
# Если отрицательное, то переводим в дополнительный код
if((val & (1 << (bits-1))) != 0):
val = val - (1 << bits)
return val
# Метод для вывода температуры
def temp(self):
self._select_page(0x00)
self.temp_out = self._get(_TEMP_OUT)
self.temp_out = self._check(self.temp_out, 16) # Проверим число на знак
return 0.01429 * self.temp_out + 25 # Применяем формулу расчёта температуры из datasheet
# Метод для вывода значений гироскопа оси Z
def z_gyro(self):
self._select_page(0x00)
self.z_gyro_low = self._get(_Z_GYRO_LOW)
self.z_gyro_out = self._get(_Z_GYRO_OUT)
self.z_gyro_32 = self._unity(self.z_gyro_out, self.z_gyro_low)
self.z_gyro_32 = self._check(self.z_gyro_32, 32) # Проверим число на знак
return self.z_gyro_32 * 0.005/65536
# Метод для вывода значений акселерометра оси Z
def z_accl(self):
self._select_page(0x00)
self.z_accl_low = self._get(_Z_ACCL_LOW)
self.z_accl_out = self._get(_Z_ACCL_OUT)
self.z_accl_32 = self._unity(self.z_accl_out, self.z_accl_low)
self.z_accl_32 = self._check(self.z_accl_32, 32) # Проверим число на знак
return self.z_accl_32 * 0.5/65536
# Метод для чтения параметра декрейта
# Количество значений в 1 секунду = 4250 / (dec_rate + 1)
def read_decrate(self):
self._select_page(0x03)
self.decrate = self._get(_DEC_RATE)
return self.decrate
# Метод для изменения параметра декрейта
def set_decrate(self, value):
#Необходимо полученное число разделить на 2 части по 8 бит каждое
decrate_low = value & 0xff
decrate_high = (value >> 8) & 0xff
self._select_page(0x03)
self._set(_DEC_RATE, decrate_low)
self._set(_DEC_RATE+1, decrate_high)
sensor = ADIS_16490() # Создание экземпляра класса
sensor.set_decrate(5) # Установка декрейта
# Вывод параметров в консоль
while True:
temp = sensor.temp()
z_gyro = sensor.z_gyro()
z_accl = sensor.z_accl()
decrate = sensor.read_decrate()
print(temp)
print(z_gyro)
print(z_accl)
print(decrate)
time.sleep(1)
На данном этапе программа работает и значения выводятся в консоль.