class MCP3008(object): """ MCP3008 ADC (Analogue-to-Digital converter). """ def __init__(self, bus=0, device=0, channel=0): self.bus = bus self.device = device self.channel = channel self.spi = SpiDev() def __enter__(self): self.open() return self def open(self): self.spi.open(self.bus, self.device) def read(self): adc = self.spi.xfer2([1, (8 + self.channel) << 4, 0]) data = ((adc[1] & 3) << 8) + adc[2] return data def __exit__(self, type, value, traceback): self.close() def close(self): self.spi.close()
class SPIDataLink(DataLink): """Clase que gestiona un enlace Serial Peripheral Interface (SPI). :param max_speed: máxima velocidad en herzios del enlace de datos. :param bus: Identificador del bus SPI que se usa para el enlace de datos. :param device: Línea de selección de chip SPI activa en el enlace de datos. Ejemplo de uso para pedir una medida del primer canal analógico de un conversor ADC MCP3202 conectado a la línea de selección de chip 0 de Raspberry Pi: >>> from pida.links import SPIDataLink >>> link = SPIDataLink(1000000, 0, 0) >>> link.open() >>> request = [1, 2 << 6, 0] >>> response = link.transfer(request) >>> link.close() """ def __init__(self, max_speed, bus, device): DataLink.__init__(self, max_speed) self._bus = bus self._device = device self._spi = SpiDev() @property def bus(self): """Identificador del bus SPI que se usa para el enlace de datos. .. note:: Raspberry Pi ofrece a través de su puerto GPIO un único bus SPI cuyo identificador es 0. Es una propiedad de sólo lectura. """ return self._bus @property def device(self): """Línea de selección de chip SPI activa en el enlace de datos. .. note:: El bus SPI 0 de Raspberry Pi puede, a través del puerto GPIO, activar dos líneas de selección de chip SPI: 0 y 1. Es una propiedad de sólo lectura. """ return self._device def open(self): self._spi.open(self._bus, self._device) self._spi.max_speed_hz = self.max_speed def close(self): self._spi.close() def transfer(self, data): return self._spi.xfer2(data)
class MCP3008(object): """Class for MCP3008 ADC""" def __init__(self, port=0, device=0): self.spi = SpiDev() # connect spi object to specified spi device self.spi.open(port, device) def readValueChannel(self, channel=0): """ read SPI data from MCP3008 on channel -> digital value spi.xfer2() send three bytes to the device the first byte is 1 -> 00000001 the second byte is 8 + channel and left shift with 4 bits the third byte is 0 -> 00000000 the device return 3 bytes as responce """ # perform spi transaction adc = self.spi.xfer2([1, (8 + channel) <<4, 0]) # extract value from data bytes data = ((adc[1] & 3) << 8) + adc[2] return data def readVoltChannel(self, channel=0, vmax=3.3, places=5): """ read the digital data from MCP3008 and convert it to voltage MCP3008: 10bit ADC -> value in number range 0-1023 spi value -> voltage 0 -> 0v 1023 -> vmax """ # read spi digital value adc = self.spi.xfer2([1, (8 + channel) <<4, 0]) data = ((adc[1] & 3) << 8) + adc[2] # convert it to voltage volts = (data * vmax) / float(1023) # round to specified number of decimal places volts = round(volts, places) return volts
class SpiDevice(object): def __init__(self, bus, device): self.spi = SpiDev() self.bus = bus self.device = device def init(self): self.spi.open(self.bus, self.device) def transfer(self, data): return self.spi.xfer2(data) def close(self): self.spi.close()
class MCP3008: def __init__(self, bus = 0, device = 0): self.bus, self.device = bus, device self.spi = SpiDev() self.open() def open(self): self.spi.open(self.bus, self.device) def read(self, channel = 0): adc = self.spi.xfer2([1, (8 + channel) << 4, 0]) data = ((adc[1] & 3) << 8) + adc[2] return data def close(self): self.spi.close()
class MCP3008: def __init__(self, bus = 0, device = 0, channel = 0): self.bus, self.device, self.channel = bus, device, channel self.spi = SpiDev() def __enter__(self): self.open() return self def open(self): self.spi.open(self.bus, self.device) def read(self): adc = self.spi.xfer2([1, (8 + self.channel) << 4, 0]) data = ((adc[1] & 3) << 8) + adc[2] return data def __exit__(self, type, value, traceback): self.close() def close(self): self.spi.close()
class MCP3008: def __init__(self, bus, device, max_speed_hz=1000000): self.bus = bus self.device = device self.spi = SpiDev() self.open() self.spi.max_speed_hz = max_speed_hz def open(self, max_speed_hz=1000000): self.spi.open(self.bus, self.device) self.spi.max_speed_hz = max_speed_hz def read(self, channel): cmd1 = 4 | 2 | ((channel & 4) >> 2) cmd2 = (channel & 3) << 6 adc = self.spi.xfer2([cmd1, cmd2, 0]) data = ((adc[1] & 15) << 8) + adc[2] return data def close(self): self.spi.close()
class ADC: """Analog joystick attached to a MCP3008 A/D converter.""" def __init__( self, bus=1, channel=0, device=1, ): """Define which SPI device is the A/D Converter on.""" self.bus = bus self.device = device self.channel = channel self.spi = SpiDev() def open(self): """Claim the SPI channel when asked.""" self.spi.open(self.bus, self.device) self.spi.max_speed_hz = 1000000 self.spi.mode = 0b00 def read(self): """Ask the device for a reading on a channel.""" adc = self.spi.xfer2([1, (8 + self.channel) << 4, 0]) data = ((adc[1] & 3) << 8) + adc[2] return data def close_spi(self): """Close the SPI channel when asked.""" self.spi.close() def __enter__(self): """Magic function if you use 'with'.""" self.open() return self def __exit__(self, type, value, traceback): """Magic function if you use 'with'.""" self.close_spi()
class MCP3008: def __init__(self, bus=0, device=0): self.bus, self.device = bus, device self.spi = SpiDev() self.open() def open(self): self.spi.open(self.bus, self.device) self.spi.max_speed_hz = 1000000 # ab Raspbian-Version "Buster" erforderlich! def read(self, channel=0): adc = self.spi.xfer2([1, (8 + channel) << 4, 0]) if 0 <= adc[1] <= 3: data = ((adc[1] & 3) << 8) + adc[2] per = ( 918 - data ) / 918 * 100 # maximalen Wert testen und 2x eintragen, zB 918; Wert kann nie mehr als 1023 sein! return per else: return 0 def close(self): self.spi.close()
class Temperature(object): def __init__(self, major=0, minor=0): self.spi = SpiDev() self.spi.open(major, minor) def rawread(self): return self.spi.xfer2([0, 0]) def read(self): return self.calc_temp(self.rawread()) @staticmethod def calc_temp(buf): return (((buf[0] << 8) | buf[1]) >> 3) * 0.0625 def cleanup(self): self.spi.close() def __enter__(self): return self def __exit__(self, type_, value, traceback): self.cleanup()
class MCP3201: def __init__(self): self.bus = SpiDev() self.bus.open(0, 0) self._average = 50 def close(self): self.bus.close() def __enter__(self): return self def __exit__(self, *args): self.close() def __str__(self): return '<Device class {0}>'.format(self.__class__) @property def average(self): return self._average @average.setter def average(self, value): self._average = value def get_analog_value(self): result = [] for i in range(self._average): adc = self.bus.xfer2([0, 0]) adc = ((adc[0] & 0x1F) << 7) | (adc[1] >> 1) result.append(adc) return sum(result) / self._average def get_voltage(self, aref): return (self.get_analog_value() * aref / 4096)
class MCP3008: def __init__(self, bus = 0, device = 0): self.bus, self.device = bus, device self.spi = SpiDev() self.open() self.spi.max_speed_hz = 1000000 # 1MHz def open(self): self.spi.open(self.bus, self.device) self.spi.max_speed_hz = 1000000 # 1MHz def read(self, channel = 0): #cmd1 = 4 | 2 | (( channel & 4) >> 2) #cmd2 = (channel & 3) << 6 #adc = self.spi.xfer2([cmd1, cmd2, 0]) #data = ((adc[1] & 15) << 8) + adc[2] adc = self.spi.xfer2([1,(8+channel)<<4,0]) data = ((adc[1]&3) << 8) + adc[2] return data def close(self): self.spi.close()
class LocalPiHardwareSPI(SPI): def __init__(self, clock_pin, mosi_pin, miso_pin, select_pin, pin_factory): self._port, self._device = spi_port_device(clock_pin, mosi_pin, miso_pin, select_pin) self._bus = None if SpiDev is None: raise ImportError('failed to import spidev') super().__init__(pin_factory=pin_factory) to_reserve = {clock_pin, select_pin} if mosi_pin is not None: to_reserve.add(mosi_pin) if miso_pin is not None: to_reserve.add(miso_pin) self.pin_factory.reserve_pins(self, *to_reserve) self._bus = SpiDev() self._bus.open(self._port, self._device) self._bus.max_speed_hz = 500000 def close(self): if self._bus is not None: self._bus.close() self._bus = None self.pin_factory.release_all(self) super().close() @property def closed(self): return self._bus is None def __repr__(self): try: self._check_open() return 'SPI(port=%d, device=%d)' % (self._port, self._device) except DeviceClosed: return 'SPI(closed)' def transfer(self, data): """ Writes data (a list of integer words where each word is assumed to have :attr:`bits_per_word` bits or less) to the SPI interface, and reads an equivalent number of words, returning them as a list of integers. """ return self._bus.xfer2(data) def _get_clock_mode(self): return self._bus.mode def _set_clock_mode(self, value): self._bus.mode = value def _get_lsb_first(self): return self._bus.lsbfirst def _set_lsb_first(self, value): self._bus.lsbfirst = bool(value) def _get_select_high(self): return self._bus.cshigh def _set_select_high(self, value): self._bus.cshigh = bool(value) def _get_bits_per_word(self): return self._bus.bits_per_word def _set_bits_per_word(self, value): self._bus.bits_per_word = value def _get_rate(self): return self._bus.max_speed_hz def _set_rate(self, value): self._bus.max_speed_hz = int(value)
class CC2500(object): REG_MARCSTATE = 0xC0 | 0x35 CMD_SRES = 0x30 CMD_SFSTXON = 0x31 CMD_SXOFF = 0x32 CMD_XCAL = 0x33 CMD_SRX = 0x34 CMD_STX = 0x35 CMD_SIDLE = 0x36 CMD_SWOR = 0x38 CMD_SPWD = 0x39 CMD_SFRX = 0x3A CMD_SFTX = 0x3B CMD_SWORRST = 0x3C CMD_SNOP = 0x3D CMD_PATABLE = 0x3E CMD_TXFIFO = 0x3F CMD_SINGLE_WRITE = 0x00 CMD_BRUST_WRITE = 0x40 CMD_SINGLE_READ = 0x80 CMD_BRUST_READ = 0xC0 # get Main Radio Control State Machine State def __init__(self, bus=0, channel_select=1): self.bus = SpiDev() self.bus.open(bus, channel_select) self.reset() ## ## COMMAND ## def STX(self): self.bus.xfer2([self.CMD_STX]) def SRX(self): self.bus.xfer2([self.CMD_SRX]) def SIDLE(self): self.bus.xfer2([self.CMD_SIDLE]) def SFRX(self): self.bus.xfer2([self.CMD_SFRX]) def SFTX(self): self.bus.xfer2([self.CMD_SFTX]) def SRES(self): self.bus.xfer2([self.CMD_SRES]) ## Access REG def get_STATE(self): return self.bus.xfer2([0xC0 | 0x35, 0x00])[1] def get_RXBYTES(self): return self.bus.xfer2([0xC0 | 0x3B, 0x00])[1] def write_TXFIFO(self, package): tmp = [] if type(package) == str: tmp = [ord(i) for i in package] else: tmp = list(package) tmp = [self.CMD_TXFIFO | self.CMD_BRUST_WRITE, len(tmp)] + tmp self.bus.xfer2(tmp) # write package to fifo buffer (max 64byte) def read_RXFIFO(self): len_FIFO = self.get_RXBYTES() return self.bus.xfer2( [self.CMD_BRUST_READ | 0x3F for i in range(len_FIFO)]) def reset(self): self.SRES() reg_config = [ 0x29, 0x2E, 0x06, 0x07, 0xD3, 0x91, 0x61, 0x04, 0x45, 0x00, 0x00, 0x09, 0x00, 0x5E, 0xC4, 0xEC, 0x2C, 0x22, 0x73, 0x22, 0xF8, 0x01, 0x07, 0x00, 0x18, 0x1D, 0x1C, 0xC7, 0x00, 0xB2, 0x87, 0x6B, 0xF8, 0xB6, 0x10, 0xEB, 0x0B, 0x1D, 0x11, 0x41, 0x00, 0x59, 0x7F, 0x3C, 0x88, 0x31, 0x0B ] for reg, val in enumerate(reg_config): self.bus.xfer2([reg, val]) self.SIDLE() self.SFRX() self.SFTX() def read_config(self): reg_config = [] for reg in range(49): val = self.bus.xfer2([0x80 | reg, 0x00])[1] reg_config.append(val) return reg_config ## USER API def write(self, package): self.write_TXFIFO(package) if (self.get_STATE() == 22): self.SFTX() self.SIDLE() return -1 self.STX() return 0 def read(self): self.reset() self.SRX() while (True): sleep(0.1) state = self.get_STATE() if (state in [13, 14, 15]): continue elif (state == 1): res = self.read_RXFIFO() return res elif (state == 17): self.SFRX() self.SIDLE() return -1
class SPIHardwareInterface(Device): def __init__(self, port, device): self._device = None super(SPIHardwareInterface, self).__init__() # XXX How can we detect conflicts with existing GPIO instances? This # isn't ideal ... in fact, it's downright crap and doesn't guard # against conflicts created *after* this instance, but it's all I can # come up with right now ... conflicts = (11, 10, 9, (8, 7)[device]) with _PINS_LOCK: for pin in _PINS: if pin.number in conflicts: raise GPIOPinInUse( 'pin %r is already in use by another gpiozero object' % pin ) self._device_num = device self._device = SpiDev() self._device.open(port, device) self._device.max_speed_hz = 500000 def close(self): if self._device: try: self._device.close() finally: self._device = None super(SPIHardwareInterface, self).close() @property def closed(self): return self._device is None def __repr__(self): try: self._check_open() return ( "hardware SPI on clock_pin=11, mosi_pin=10, miso_pin=9, " "select_pin=%d" % ( 8 if self._device_num == 0 else 7)) except DeviceClosed: return "hardware SPI closed" def read(self, n): return self.transfer((0,) * n) def write(self, data): return len(self.transfer(data)) def transfer(self, data): """ Writes data (a list of integer words where each word is assumed to have :attr:`bits_per_word` bits or less) to the SPI interface, and reads an equivalent number of words, returning them as a list of integers. """ return self._device.xfer2(data) def _get_clock_mode(self): return self._device.mode def _set_clock_mode(self, value): self._device.mode = value def _get_clock_polarity(self): return bool(self.mode & 2) def _set_clock_polarity(self, value): self.mode = self.mode & (~2) | (bool(value) << 1) def _get_clock_phase(self): return bool(self.mode & 1) def _set_clock_phase(self, value): self.mode = self.mode & (~1) | bool(value) def _get_lsb_first(self): return self._device.lsbfirst def _set_lsb_first(self, value): self._device.lsbfirst = bool(value) def _get_select_high(self): return self._device.cshigh def _set_select_high(self, value): self._device.cshigh = bool(value) def _get_bits_per_word(self): return self._device.bits_per_word def _set_bits_per_word(self, value): self._device.bits_per_word = value clock_polarity = property(_get_clock_polarity, _set_clock_polarity) clock_phase = property(_get_clock_phase, _set_clock_phase) clock_mode = property(_get_clock_mode, _set_clock_mode) lsb_first = property(_get_lsb_first, _set_lsb_first) select_high = property(_get_select_high, _set_select_high) bits_per_word = property(_get_bits_per_word, _set_bits_per_word)
class Lights: """ A collection of APA102 LEDs. """ def __init__(self, led_indexes): """ Create the LED controller. :param led_indexes: physical -> logical LED mapping: led_indexes[n] = LED number """ self.spi = SpiDev() self.spi.open(0, 0) self.spi.max_speed_hz = 1000000 self.pixels = [Pixel(n) for n in range(len(led_indexes))] self.mapping = led_indexes atexit.register(self._on_exit) def set_pixel(self, index, r, g, b, brightness=None): """ Set pixel colour. :param index: logical index of the LED :param r: Red :param g: Green :param b: Blue :param brightness: Optional brightness value """ pixel = self.get_pixel(index) pixel.set(r, g, b, brightness) def set_brightness(self, brightness, index=None): """ Set global brightness. :param brightness: Global brightness value. :param index: Optional LED to address. If None, sets brightness for the whole string. """ b = round(brightness) if index is None: for p in self.pixels: p.set_brightness(b) else: self.get_pixel(index).set_brightness(b) def get_pixel(self, index): """ Gets the pixel at a given index. :param index: The index of the pixel. :return: The pixel. """ return self.pixels[self.mapping[index]] def clear(self): """ Blank all LEDs. """ for p in self.pixels: p.set(0, 0, 0) def show(self): """ Send the current pixel data to the LEDs """ buf = [0x00 for _ in range(8)] for p in self.pixels: buf += p.raw() buf += [0xFF for _ in range(8)] self.spi.xfer2(buf) def _on_exit(self): self.clear() self.show()
class SPIDataLink(FullDuplexDataLink): """Clase que gestiona un enlace Serial Peripheral Interface (SPI). :param bus: Identificador del bus SPI que se usa para el enlace de datos. :param device: Línea de selección de chip SPI activa en el enlace de datos. :param configuration: Configuración del enlace de datos Ejemplo de uso: >>> from pida.links import SPIDataLinkConfiguration, SPIDataLink >>> configuration = SPIDataLinkConfiguration(mode=0, max_speed_hz=32000000) >>> with SPIDataLink(0, 0, configuration) as link: request = [0x00, 0x01, 0xFF] response = link.transfer(request) >>> response [0, 1, 255] """ def __init__(self, bus, device, configuration): self._bus = bus self._device = device self._configuration = configuration self._spi = SpiDev() def _apply_configuration(self): self._spi.mode = self._configuration.mode self._spi.max_speed_hz = self._configuration.max_speed_hz @property def bus(self): """Identificador del bus SPI que se usa para el enlace de datos. .. note:: Raspberry Pi ofrece a través de su puerto GPIO un único bus SPI cuyo identificador es 0. Es una propiedad de sólo lectura. """ return self._bus @property def device(self): """Línea de selección de chip SPI activa en el enlace de datos. .. note:: El bus SPI 0 de Raspberry Pi puede, a través del puerto GPIO, activar dos líneas de selección de chip SPI: 0 y 1. Es una propiedad de sólo lectura. """ return self._device def open(self): self._spi.open(self._bus, self._device) self._apply_configuration() def close(self): self._spi.close() def transfer(self, data): return self._spi.xfer2(data) def __enter__(self): self.open() return self def __exit__(self, exc_type, exc_val, exc_tb): self.close() @property def max_speed_hz(self): return self._configuration.max_speed_hz @property def mode(self): return self._configuration.mode
class LocalPiHardwareSPI(SPI, Device): def __init__(self, factory, port, device): self._port = port self._device = device self._interface = None if SpiDev is None: raise ImportError('failed to import spidev') super(LocalPiHardwareSPI, self).__init__() pins = SPI_HARDWARE_PINS[port] self.pin_factory.reserve_pins( self, pins['clock'], pins['mosi'], pins['miso'], pins['select'][device] ) self._interface = SpiDev() self._interface.open(port, device) self._interface.max_speed_hz = 500000 def close(self): if getattr(self, '_interface', None): self._interface.close() self._interface = None self.pin_factory.release_all(self) super(LocalPiHardwareSPI, self).close() @property def closed(self): return self._interface is None def __repr__(self): try: self._check_open() return 'SPI(port=%d, device=%d)' % (self._port, self._device) except DeviceClosed: return 'SPI(closed)' def transfer(self, data): """ Writes data (a list of integer words where each word is assumed to have :attr:`bits_per_word` bits or less) to the SPI interface, and reads an equivalent number of words, returning them as a list of integers. """ return self._interface.xfer2(data) def _get_clock_mode(self): return self._interface.mode def _set_clock_mode(self, value): self._interface.mode = value def _get_lsb_first(self): return self._interface.lsbfirst def _set_lsb_first(self, value): self._interface.lsbfirst = bool(value) def _get_select_high(self): return self._interface.cshigh def _set_select_high(self, value): self._interface.cshigh = bool(value) def _get_bits_per_word(self): return self._interface.bits_per_word def _set_bits_per_word(self, value): self._interface.bits_per_word = value
class SipmSPI: def __init__(self, spinum, sipmpins, chippins): self.spi = SpiDev(spinum, 0) self.spi.mode = 3 self.spi.max_speed_hz = 1000000 self.sipmpins = ['P9_%i' % num for num in sipmpins] self.chippins = ['P9_%i' % num for num in chippins] for pin in self.sipmpins + self.chippins: GPIO.setup(pin, GPIO.OUT) GPIO.output(pin, GPIO.LOW) def select_sipm(self, sipm_number): for idx, pin_name in enumerate(self.sipmpins): if (sipm_number >> idx & 0x1): GPIO.output(pin_name, GPIO.HIGH) else: GPIO.output(pin_name, GPIO.LOW) #print("sipm %d selected" % sipm_number) def chip_select(self, key): chip_num = CHIP_MAP[key] for idx, pin_name in enumerate(self.chippins): if (chip_num >> idx & 0x1): GPIO.output(pin_name, GPIO.HIGH) else: GPIO.output(pin_name, GPIO.LOW) def reset_temperature(self): self.spi.xfer2([0xff, 0xff, 0xff, 0xff]) #self.spi.xfer2([0x00, 0x00, 0x00, 0x00, 0x00, 0x00]) #time.sleep(0.2) res = self.spi.xfer2([0x40, 0x00]) # read status register print("status: 0x%02x" % res[1]) res = self.spi.xfer2([0x48, 0x00]) # read config register print("status: 0x%02x" % res[1]) res = self.spi.xfer2([0x58, 0x00]) # read ID register print("status: 0x%02x" % res[1]) def setup_temperature(self): self.spi.xfer2([0x08, 0x80]) # time.sleep(0.2) def read_temperature(self): res = self.spi.xfer2([0x50, 0x00, 0x00]) #temp = (res[1] << 5 | res[2] >> 3) / 16.0 temp = (res[1] << 8 | res[2]) / 128.0 #print("temp: %f deg C" % temp) return str(temp) def read_gain(self): res = self.spi.xfer2([0x83, 0x00]) gain_read = res[1] #print("old gain readout: %d = %f dB" % (gain_read, 26 - gain_read / 4.0)) return str(gain_read) def set_gain(self, gain_value): res = self.spi.xfer2([0x03, gain_value]) res = self.spi.xfer2([0x83, 0x00]) gain_read = res[1] return str(gain_read) def check_eeprom_status(self): res = self.spi.xfer2([0x05, 0x00]) print("eeprom status 0x%02x\n" % res[1]) def read_eeprom_page(self, page): page <<= 4 cmd = [0x03, page] + [0 for i in range(16)] res = self.spi.xfer2(cmd) int_array = res[2:] #utf8 safety chr_array = [' ' if i == 255 else chr(i) for i in int_array] return ''.join(chr_array) def write_eeprom_page(self, page, msg): page <<= 4 #enable write latch self.spi.xfer2([0x06]) self.check_eeprom_status() #write page cmd = [0x02, page] + msg res = self.spi.xfer2(cmd) # print(res) self.check_eeprom_status() return res
class ADS1293: """SpiDev Wrapper for TI ADS1293 """ def __init__(self, bus=0, device=0): """initializes the SPI bus for the ADS1293 """ self.bus, self.device = bus, device self.spi = SpiDev() self.open() self.inax_max = [0, 0, 0] self.inax_min = [0, 0, 0] self.inax_zero = [0, 0, 0] def open(self): """opens the SPI bus for the ADS1293 """ self.spi.open(self.bus, self.device) self.spi.max_speed_hz = F_SCLK def close(self): self.spi.close() def read(self, register, num_bytes=1): """reads a number of bytes from the ADS1293 """ data = [0] * (num_bytes + 1) data[0] = READ_BIT | register self.spi.xfer2(data) return data[1:] def write(self, register, value): """writes the value value to register register """ data = [WRITE_BIT & register, value] self.spi.xfer2(data) def go_to_standby(self): """sends the ADS1293 to standby """ self.write(CONFIG, STANDBY) def test(self): # initial setup count = 100 self.write(OSC_CN, 0x04) # set external oscillator self.write(R2_RATE, 0x02) # set R2 decimation rate as 5 for i in range(3): # for all three Channels # shut down unused circuitry shdn = 0 for j in range(3): if i != j: shdn = shdn | AFE_SHDN_INA_CHx[j] | AFE_SHDN_SDM_CHx[j] self.write(AFE_SHDN_CN, shdn) # initialize channel self.write(R3_RATE_CHx[i], 0x02) # set R3 decimation rate as 6 self.write(CH_CNFG, Ex_EN[i]) # enable loopback reading # positive test voltage self.write(FLEX_CHx_CN[i], TST_POS) # connect channel to pos test signal self.write(CONFIG, START_CON) # start data conversion results = self.stat_values(count, Ex_DRDY[i]) self.write(CONFIG, STANDBY) # send chip to standby self.inax_max[i] = results[0] logging.info("Channel %d positive test:\n\t" "Expected: %.1f\n\t" "Received: %.1f avg [%.1f]\n\t\t" " (%.1f min, %.1f max, %.1f stddev)" % (i + 1, ADC_OUT_POS, results[0], results[0] - ADC_OUT_POS, results[1], results[2], results[3])) # negative test voltage self.write(FLEX_CHx_CN[i], TST_NEG) # connect channel to neg test signal self.write(CONFIG, START_CON) # start data conversion results = self.stat_values(count, Ex_DRDY[i]) self.write(CONFIG, STANDBY) # send chip to standby self.inax_min[i] = results[0] logging.info("Channel %d negative test:\n\t" "Expected: %.1f\n\t" "Received: %.1f avg [%.1f]\n\t\t" " (%.1f min, %.1f max, %.1f stddev)" % (i + 1, ADC_OUT_NEG, results[0], results[0] - ADC_OUT_NEG, results[1], results[2], results[3])) # zero test voltage self.write(FLEX_CHx_CN[i], TST_ZER) self.write(CONFIG, START_CON) # start data conversion results = self.stat_values(count, Ex_DRDY[i]) self.write(CONFIG, STANDBY) # send chip to standby self.inax_zero[i] = results[0] logging.info("Channel %d zero test:\n\t" "Expected: %.1f\n\t" "Received: %.1f avg [%.1f]\n\t\t" " (%.1f min, %.1f max, %.1f stddev)" % (i + 1, ADC_OUT_ZERO, results[0], results[0] - ADC_OUT_ZERO, results[1], results[2], results[3])) # deinitialize channel self.write(FLEX_CHx_CN[i], 0x00) # disconnect the channel self.write(R3_RATE_CHx[i], 0x00) # reset R3 decimation rate # reset the self self.reset() def reset(self): self.deinit_three_lead_ecg() self.deinit_five_lead_ecg() def check_ready(self, datareadybit=E1_DRDY): result = self.read(DATA_STATUS) return result[0] & datareadybit def stat_values(self, count=100, channel=E1_DRDY): """returns a list of avg, min, max, stdev """ results = [] i = 0 while i < count: if self.check_ready(channel): data = self.read(DATA_LOOP, NUM_ECG_DATA_REGISTERS * 1) results.append(data[0] << 16 | data[1] << 8 | data[2]) i += 1 return [ sum(results) / len(results), min(results), max(results), statistics.stdev(results) ] def init_three_lead_ecg(self): """Initializes the ADS1293 to the 3-Lead ECG application from the datasheet, chapter 9.2.1 """ # 1. Set address 0x01 = 0x11: Connect channel 1’s INP to IN2 and INN to IN1. self.write(FLEX_CH1_CN, POS_IN2 | NEG_IN1) # 2. Set address 0x02 = 0x19: Connect channel 2’s INP to IN3 and INN to IN1. self.write(FLEX_CH2_CN, POS_IN3 | NEG_IN1) # 3. Set address 0x0A = 0x07: Enable the common-mode detector on input pins # IN1, IN2 and IN3. self.write(CMDET_EN, 0x07) # 4. Set address 0x0C = 0x04: Connect the output of the RLD amplifier # internally to pin IN4. self.write(RLD_CN, 0x04) # 5. Set address 0x12 = 0x04: Use external crystal and feed the internal # oscillator's output to the digital. self.write(OSC_CN, 0x04) # 6. Set address 0x14 = 0x24: Shuts down unused channel 3’s signal path. self.write(AFE_SHDN_CN, 0x24) # 7. Set address 0x21 = 0x02: Configures the R2 decimation rate as 5 for all # channels. self.write(R2_RATE, 0x02) # 8. Set address 0x22 = 0x02: Configures the R3 decimation rate as 6 for # channel 1. self.write(R3_RATE_CH1, 0x02) # 9. Set address 0x23 = 0x02: Configures the R3 decimation rate as 6 for # channel 2. self.write(R3_RATE_CH2, 0x02) # 10. Set address 0x27 = 0x08: Configures the DRDYB source to channel 1 ECG # (or fastest channel). self.write(DRDYB_SRC, 0x08) # 11. Set address 0x2F = 0x30: Enables channel 1 ECG and channel 2 ECG for # loop read-back mode. self.write(CH_CNFG, 0x30) # 12. Set address 0x00 = 0x01: Starts data conversion. self.write(CONFIG, START_CON) def deinit_three_lead_ecg(self): """Deinitializes the ADS1293. Effectively this sends the ADS1293 to standby and sets the initialized registers to their default values. """ # send ADS1293 to standby mode to save energy self.write(CONFIG, STANDBY) # reset registers to their default values self.write(CH_CNFG, 0x00) self.write(DRDYB_SRC, 0x00) self.write(R3_RATE_CH2, 0x00) self.write(R3_RATE_CH1, 0x00) self.write(R2_RATE, 0x00) self.write(AFE_SHDN_CN, 0x00) self.write(OSC_CN, 0x00) self.write(RLD_CN, 0x00) self.write(CMDET_EN, 0x00) self.write(FLEX_CH2_CN, 0x00) self.write(FLEX_CH1_CN, 0x00) def init_five_lead_ecg(self): """Initializes the ADS1293 to the 5-Lead ECG application from the datasheet, chapter 9.2.2 """ # 1. Set address 0x01 = 0x11: Connect channel 1’s INP to IN2 and INN to IN1. self.write(FLEX_CH1_CN, POS_IN2 | NEG_IN1) # 2. Set address 0x02 = 0x19: Connect channel 2’s INP to IN3 and INN to IN1. self.write(FLEX_CH2_CN, POS_IN3 | NEG_IN1) # 3. Set address 0x03 = 0x2E: Connect channel 3’s INP to IN5 and INN to IN6. self.write(FLEX_CH3_CN, POS_IN5 | NEG_IN6) # 4. Set address 0x0A = 0x07: Enable the common-mode detector on input pins IN1, IN2 and IN3. self.write(CMDET_EN, 0x07) # 5. Set address 0x0C = 0x04: Connect the output of the RLD amplifier internally to pin IN4. self.write(RLD_CN, 0x04) # 6. Set addresses 0x0D = 0x01, 0x0E = 0x02, 0x0F = 0x03: COnnects the first buffer of the Wilson reference to the IN1 pin, the second buffer to the IN2 pin, and the third buffer to the IN3 pin. self.write(WILSON_EN1, 0x01) self.write(WILSON_EN2, 0x02) self.write(WILSON_EN3, 0x03) # 7. Set address 0x10 = 0x01: Connects the output of the Wilson reference internally to IN6 self.write(WILSON_CN, 0x01) # 8. Set address 0x12 = 0x04: Uses external crystal and feeds the output of the internal oscillator module to the digital. self.write(OSC_CN, 0x04) # 9. Set address 0x21 = 0x02: Configures the R2 decimation rate as 5 for all channels. self.write(R2_RATE, 0x02) # 10. Set address 0x22 = 0x02: Configures the R3 decimation rate as 6 for channel 1. self.write(R3_RATE_CH1, 0x02) # 11. Set address 0x23 = 0x02: Configures the R3 decimation rate as 6 for channel 2. self.write(R3_RATE_CH2, 0x02) # 12. Set address 0x24 = 0x02: Configures the R3 decimation rate as 6 for channel 3. self.write(R3_RATE_CH3, 0x02) # 12. Set address 0x27 = 0x08: Configures the DRDYB source to channel 1 ECG (or fastest channel). self.write(DRDYB_SRC, 0x08) # 11. Set address 0x2F = 0x70: Enables ECG channel 1, ECG channel 2 and ECG channel 3 for loop read-back mode. self.write(CH_CNFG, 0x70) # 12. Set address 0x00 = 0x01: Starts data conversion. self.write(CONFIG, START_CON) def deinit_five_lead_ecg(self): """Deinitializes the ADS1293. Effectively this sends the ADS1293 to standby and sets the initialized registers to their default values. """ # send ADS1293 to standby mode to save energy self.write(CONFIG, STANDBY) # reset registers to their default values self.write(CH_CNFG, 0x00) self.write(DRDYB_SRC, 0x00) self.write(R3_RATE_CH3, 0x00) self.write(R3_RATE_CH2, 0x00) self.write(R3_RATE_CH1, 0x00) self.write(R2_RATE, 0x00) self.write(OSC_CN, 0x00) self.write(WILSON_CN, 0x00) self.write(WILSON_EN3, 0x00) self.write(WILSON_EN2, 0x00) self.write(WILSON_EN1, 0x00) self.write(RLD_CN, 0x00) self.write(CMDET_EN, 0x00) self.write(FLEX_CH3_CN, 0x00) self.write(FLEX_CH2_CN, 0x00) self.write(FLEX_CH1_CN, 0x00)
class cc2500: REG_MARCSTATE = 0xC0 | 0x35 CMD_SRES = 0x30 CMD_SFSTXON = 0x31 CMD_SXOFF = 0x32 CMD_XCAL = 0x33 CMD_SRX = 0x34 CMD_STX = 0x35 CMD_SIDLE = 0x36 CMD_SWOR = 0x38 CMD_SPWD = 0x39 CMD_SFRX = 0x3A CMD_SFTX = 0x3B CMD_SWORRST = 0x3C CMD_SNOP = 0x3D CMD_PATABLE = 0x3E CMD_TXFIFO = 0x3F CMD_SINGLE_WRITE = 0x00 CMD_BRUST_WRITE = 0x40 CMD_SINGLE_READ = 0x80 CMD_BRUST_READ = 0xC0 # get Main Radio Control State Machine State def __init__(self, bus = 0, channel_select = 1): self.bus = SpiDev() self.bus.open(bus,channel_select) self.reset() self.buff=[] self.run = True self.timestamp = time() def get_STATE(self): return self.bus.xfer2([self.REG_MARCSTATE,0x00])[1] def set_reg(self,reg, byte): return self.bus.xfer2([reg, byte]) def get_reg(self, reg): return self.bus.xfer2([0x80 | reg, 0x00]) def info(self): state = self.get_STATE() txbyte = self.get_TXBYTES() rxbyte = self.get_RXBYTES() print "state : %d , tx: %d , rx: %d " % (state, txbyte, rxbyte) ## ## COMMAND ## def STX(self): return self.bus.xfer2([self.CMD_STX]) def SRX(self): return self.bus.xfer2([self.CMD_SRX]) def SIDLE(self): return self.bus.xfer2([self.CMD_SIDLE]) def SFRX(self): return self.bus.xfer2([self.CMD_SFRX]) def SFTX(self): return self.bus.xfer2([self.CMD_SFTX]) def SRES(self): return self.bus.xfer2([self.CMD_SRES]) def reset(self): self.SRES() reg_config = [0x0B, 0x2E, 0x06, 0x07, 0xD3, 0x91, 0x61, 0x04, 0x45, 0x00, 0x00, 0x09, 0x00, 0x5D, 0x93, 0xB1, 0x2D, 0x3B, 0x73, 0x22, 0xF8, 0x01, 0x07, 0x00, 0x18, 0x1D, 0x1C, 0xC7, 0x00, 0xB2, 0x87, 0x6B, 0xF8, 0xB6, 0x10, 0xEA, 0x0A, 0x00, 0x11, 0x41, 0x00, 0x59, 0x7F, 0x3F, 0x88, 0x31, 0x0B ] for reg, val in enumerate(reg_config): self.bus.xfer2([reg, val]) self.SIDLE() self.SFRX() self.SFTX() ## FIFO buffer 64byte def send(self,package): tmp = [] if type(package) == str: tmp = [ord(i) for i in package] else: tmp = list(package) tmp = [self.CMD_TXFIFO | self.CMD_BRUST_WRITE ,len(tmp)] + tmp print "send" print tmp self.bus.xfer2(tmp) # write package to fifo buffer (max 64byte) if self.get_STATE() == 22: self.SFTX() self.SIDLE() return -1 self.STX() return 0 def get_package_RXFIFO(self): len_FIFO = self.get_RXBYTES() p_len = self.get_byte_RXFIFO() data = self.bus.xfer2([self.CMD_BRUST_READ | 0x3F for i in range(len_FIFO)]) self.buff.append(data) def get_byte_RXFIFO(self): return self.bus.xfer2( [ self.CMD_SINGLE_READ | 0x3F , 0x00])[1] def set_byte_TXFIFO(self, byte): return self.bus.xfer2( [ self.CMD_SINGLE_WRITE | 0x3F , byte])[1] def get_TXBYTES(self): return self.bus.xfer2([0xC0 | 0x3A, 0x00])[1] def get_RXBYTES(self): return self.bus.xfer2([0xC0 | 0x3B, 0x00])[1] def stop(self): self.run = False def start(self): self.thread = Thread(target=self.receive_loop, args=()) self.run = True self.thread.start() def receive_loop(self): while self.run : state = self.get_STATE() rxbytes = self.get_RXBYTES() txbytes = self.get_TXBYTES() if(state in [13,8,9,10,11]): #print state sleep(0.1) continue elif( (state == 1) and (rxbytes > 0)): #print state self.get_package_RXFIFO() self.SRX() elif(state == 1): #print state self.SIDLE() self.SRX() elif(state == 17): #print state self.SFRX() self.SIDLE() self.SRX() else: #print state self.reset() sleep(0.1) if time() > (self.timestamp + 10 ): print "reset" self.timestamp = time() self.reset() sleep(0.1) if len(self.buff) > 0: data = self.buff.pop(0)[1:-2] msg = ''.join([chr(i) for i in data]) print(msg)
class CC2500(object): REG_MARCSTATE = 0xC0 | 0x35 CMD_SRES = 0x30 CMD_SFSTXON = 0x31 CMD_SXOFF = 0x32 CMD_XCAL = 0x33 CMD_SRX = 0x34 CMD_STX = 0x35 CMD_SIDLE = 0x36 CMD_SWOR = 0x38 CMD_SPWD = 0x39 CMD_SFRX = 0x3A CMD_SFTX = 0x3B CMD_SWORRST = 0x3C CMD_SNOP = 0x3D CMD_PATABLE = 0x3E CMD_TXFIFO = 0x3F CMD_SINGLE_WRITE = 0x00 CMD_BRUST_WRITE = 0x40 CMD_SINGLE_READ = 0x80 CMD_BRUST_READ = 0xC0 # get Main Radio Control State Machine State def __init__(self, bus = 0, channel_select = 1): self.bus = SpiDev() self.bus.open(bus,channel_select) self.reset() ## ## COMMAND ## def STX(self): self.bus.xfer2([self.CMD_STX]) def SRX(self): self.bus.xfer2([self.CMD_SRX]) def SIDLE(self): self.bus.xfer2([self.CMD_SIDLE]) def SFRX(self): self.bus.xfer2([self.CMD_SFRX]) def SFTX(self): self.bus.xfer2([self.CMD_SFTX]) def SRES(self): self.bus.xfer2([self.CMD_SRES]) ## Access REG def get_STATE(self): return self.bus.xfer2([0xC0 | 0x35, 0x00])[1] def get_RXBYTES(self): return self.bus.xfer2([0xC0 | 0x3B, 0x00])[1] def write_TXFIFO(self,package): tmp = [] if type(package) == str: tmp = [ord(i) for i in package] else: tmp = list(package) tmp = [self.CMD_TXFIFO | self.CMD_BRUST_WRITE ,len(tmp)] + tmp self.bus.xfer2(tmp) # write package to fifo buffer (max 64byte) def read_RXFIFO(self): len_FIFO = self.get_RXBYTES() return self.bus.xfer2([self.CMD_BRUST_READ | 0x3F for i in range(len_FIFO)]) def reset(self): self.SRES() reg_config = [ 0x29,0x2E,0x06,0x07,0xD3,0x91,0x61,0x04, 0x45,0x00,0x00,0x09,0x00,0x5E,0xC4,0xEC, 0x2C,0x22,0x73,0x22,0xF8,0x01,0x07,0x00, 0x18,0x1D,0x1C,0xC7,0x00,0xB2,0x87,0x6B, 0xF8,0xB6,0x10,0xEB,0x0B,0x1D,0x11,0x41, 0x00,0x59,0x7F,0x3C,0x88,0x31,0x0B ] for reg, val in enumerate(reg_config): self.bus.xfer2([reg, val]) self.SIDLE() self.SFRX() self.SFTX() def read_config(self): reg_config = [] for reg in range(49): val = self.bus.xfer2([0x80 | reg , 0x00])[1] reg_config.append(val) return reg_config ## USER API def write(self,package): self.write_TXFIFO(package) if(self.get_STATE() == 22): self.SFTX() self.SIDLE() return -1 self.STX() return 0 def read(self): self.reset() self.SRX() while(True): sleep(0.1) state = self.get_STATE() if(state in [13,14,15]): continue elif(state == 1): res = self.read_RXFIFO() return res elif(state == 17): self.SFRX() self.SIDLE() return -1
class AMIS30543_Controller(): def __init__(self, DO_DIRECTION_PIN, DO_RESET_PIN, DI_FAULT_PIN, ARGS): self.REG = { # AMIS-30543 Registers 'WR': 0x00, 'CR0': 0x01, 'CR1': 0x02, 'CR2': 0x03, 'CR3': 0x09, 'SR0': 0x04, 'SR1': 0x05, 'SR2': 0x06, 'SR3': 0x07, 'SR4': 0x0A } self.CMD = { # AMIS-30543 Command constants 'READ': 0x00, 'WRITE': 0x80 } self.dirctrl = ARGS[0] self.pwmf = ARGS[1] self.pwmj = ARGS[2] self.sm = ARGS[3] self.mult = ARGS[4] self.dist = ARGS[5] self.step = ARGS[6] self.VAL = { 'WR': 0b00000000, # no watchdog 'CR0': 0b00010111 | self.sm, # & 2.7 A current limit 'CR1': 0b00000000 | self.dirctrl | self.pwmf | self.pwmj, # & step on rising edge & fast slopes 'CR2': 0b00000000, # motor off & no sleep & SLA gain @ 0.5 & SLA no transparent 'CR3': 0b00000000 #, # no extended step mode #'dist': self.dist, #'step': self.step } # InitGPIO PWM.setup(5, 0) # 5 us pulse_incr, 0 delay_hw PWM.init_channel(0, 3000) # DMA channel 0, 3000 us subcycle time self.DO_RESET = gpiozero.DigitalOutputDevice(DO_RESET_PIN) self.DO_DIRECTION = gpiozero.DigitalOutputDevice(DO_DIRECTION_PIN) self.DI_NO_FAULT = gpiozero.DigitalInputDevice(DI_FAULT_PIN) self.spi = SpiDev() self.spi.open(0, 0) self.spi.max_speed_hz = 1000000 self.RegisterSet() def __del__(self): self.spi.close() def ResetStepper(self): self.DO_RESET.off() # must be off for AMIS to see reset time.sleep(0.11) self.DO_RESET.on() time.sleep(0.11) self.DO_RESET.off() def RegisterDump(self): # to check stepper status print("\nAMIS-30543 Registers:") resp = self.spi.xfer2([self.CMD['READ'] | self.REG['WR'], 0]) print(" WR = ", bin(resp[1]), " ", str(resp[1])) resp = self.spi.xfer2([self.CMD['READ'] | self.REG['CR0'], 0]) print("CR0 = ", bin(resp[1]), " ", str(resp[1])) resp = self.spi.xfer2([self.CMD['READ'] | self.REG['CR1'], 0]) print("CR1 = ", bin(resp[1]), " ", str(resp[1])) resp = self.spi.xfer2([self.CMD['READ'] | self.REG['CR2'], 0]) print("CR2 = ", bin(resp[1]), " ", str(resp[1])) resp = self.spi.xfer2([self.CMD['READ'] | self.REG['CR3'], 0]) print("CR3 = ", bin(resp[1]), " ", str(resp[1])) resp = self.spi.xfer2([self.CMD['READ'] | self.REG['SR0'], 0]) print("SR0 = ", bin(resp[1]), " ", str(resp[1])) resp = self.spi.xfer2([self.CMD['READ'] | self.REG['SR1'], 0]) print("SR1 = ", bin(resp[1]), " ", str(resp[1])) resp = self.spi.xfer2([self.CMD['READ'] | self.REG['SR2'], 0]) print("SR2 = ", bin(resp[1]), " ", str(resp[1])) resp = self.spi.xfer2([self.CMD['READ'] | self.REG['SR3'], 0]) print("SR3 = ", bin(resp[1]), " ", str(resp[1])) resp = self.spi.xfer2([self.CMD['READ'] | self.REG['SR4'], 0]) print("SR4 = ", bin(resp[1]), " ", str(resp[1])) print("") def RegisterSet(self): self.ResetStepper() self.spi.writebytes( [self.CMD['WRITE'] | self.REG['WR'], self.VAL['WR']]) self.spi.writebytes( [self.CMD['WRITE'] | self.REG['CR0'], self.VAL['CR0']]) self.spi.writebytes( [self.CMD['WRITE'] | self.REG['CR1'], self.VAL['CR1']]) self.spi.writebytes( [self.CMD['WRITE'] | self.REG['CR2'], self.VAL['CR2']]) self.spi.writebytes( [self.CMD['WRITE'] | self.REG['CR3'], self.VAL['CR3']]) if self.spi.xfer2([self.CMD['READ'] | self.REG['WR'], 0 ])[1] != self.VAL['WR']: print( "Writing or reading self.REG['WR'] failed; driver power might be off." ) return False if self.spi.xfer2([self.CMD['READ'] | self.REG['CR0'], 0 ])[1] != self.VAL['CR0']: print( "Writing or reading self.REG['CR0'] failed; driver power might be off." ) return False if self.spi.xfer2([self.CMD['READ'] | self.REG['CR1'], 0 ])[1] != self.VAL['CR1']: print( "Writing or reading self.REG['CR1'] failed; driver power might be off." ) return False if self.spi.xfer2([self.CMD['READ'] | self.REG['CR2'], 0 ])[1] != self.VAL['CR2']: print( "Writing or reading self.REG['CR2'] failed; driver power might be off." ) return False if self.spi.xfer2([self.CMD['READ'] | self.REG['CR3'], 0 ])[1] != self.VAL['CR3']: print( "Writing or reading self.REG['CR3'] failed; driver power might be off." ) return False #self.RegisterDump() #print("RegisterSet Ok\n") return True def SetMotorEnable(self): self.spi.writebytes([ self.CMD['WRITE'] | self.REG['CR2'], self.VAL['CR2'] | 0b10000000 ]) if self.spi.xfer2([self.CMD['READ'] | self.REG['CR2'], 0 ])[1] != self.VAL['CR2'] | 0b10000000: print( "Writing or reading self.REG['CR2'] failed; driver power might be off." ) return False def SetMotorDisable(self): self.spi.writebytes([ self.CMD['WRITE'] | self.REG['CR2'], self.VAL['CR2'] & 0b01111111 ]) if self.spi.xfer2([self.CMD['READ'] | self.REG['CR2'], 0 ])[1] != self.VAL['CR2'] & 0b01111111: print( "Writing or reading self.REG['CR2'] failed; driver power might be off." ) return False
from spidev import SpiDev import RPi.GPIO as GPIO # import RPi.GPIO module GPIO.setmode(GPIO.BOARD) # choose BCM or BOARD GPIO.setup(11, GPIO.OUT) # set a port/pin as an output channel = 0 bus = 0 bus, device = bus, device spi = SpiDev() spi.open(bus, device # xfer2(list of values[, speed_hz, delay_usec, bits_per_word]) #s adc = spi.xfer2([1, (8 + channel) << 4, 0]) voltages = [] powerSum = 0 GPIO.output(11, 1) # set port/pin value to 1/GPIO.HIGH/True for i in range(0, 10): if (i == 8): GPIO.output(11,0) voltages[i] = ((adc[1] & 3) << 8) + adc[2] power = math.pow(voltages[i]/1023.0 * 3.3, 2)* ((voltages[i]/1023.0 * 3.3)/100 powerSum += power print("power: %.2f" % power)
class SPIHandler: def __init__(self, state_instance): self._shutdown_ctr = Counter(2) PTLogger.debug("\t\tCreating SPI handler") self._state = state_instance self.spi = None PTLogger.debug("\t\t\tSetting up SPI") self._setup_spi() PTLogger.debug("\t\t\tGetting initial state data") self._get_state_from_hub(init=True) init_state = StateChange(SPIStateChangeType.init, True) self.queued_changes = [init_state] def _update_state_from_pending_state_change(self, state_change_to_send): # If state is to change, update appropriate bit(s) if state_change_to_send is not None: if state_change_to_send._type == SPIStateChangeType.screen: if state_change_to_send._operation == SPIScreenOperations.blank: self._state.set_screen_blanked() elif state_change_to_send._operation == SPIScreenOperations.unblank: self._state.set_screen_unblanked() else: msg = "Unrecognised screen state change" msg += " - unable to parse into bits. Ignoring..." PTLogger.info(msg) elif state_change_to_send._type == SPIStateChangeType.brightness: if _represents_int(state_change_to_send._operation): brightness_level = int(state_change_to_send._operation) if brightness_level >= 0 and brightness_level <= 10: self._state.set_brightness(brightness_level) def transceive_and_process(self): state_change_to_send = self.pop_from_queue() self._update_state_from_pending_state_change(state_change_to_send) # Should this be here? # Set bits to send according to state variables if state_change_to_send is not None: # Pi's current state bits_to_send = self._parse_state_to_bits() else: # Probe for hub's state bits_to_send = 255 hub_response_bstring = self._transceive_spi(bits_to_send) byte_type = self._determine_byte(hub_response_bstring) # Determine if received byte represents device ID or state if byte_type == SPIResponseType.device_id: PTLogger.debug("Valid response from hub - DEVICE ID") self._process_device_id(hub_response_bstring) elif byte_type == SPIResponseType.state: self._process_spi_resp(hub_response_bstring) # State update has been sent to hub: perform another transceive to sync states self._get_state_from_hub(process_state=False) else: PTLogger.warning("Invalid response from hub") return False return True def pop_from_queue(self): if len(self.queued_changes) > 0: state_change_to_send = self.queued_changes[0] self.queued_changes.remove(self.queued_changes[0]) else: state_change_to_send = None return state_change_to_send def _process_device_id(self, resp): device_id = resp[5:8] if device_id == "000": PTLogger.info("Hub reports it's a pi-top v1") self._state.set_device_id(DeviceID.pi_top) elif device_id == "001": PTLogger.info("Hub reports it's a CEED") self._state.set_device_id(DeviceID.pi_top_ceed) def _parity_of(self, int_type): ''' Calculates the parity of an integer, returning 0 if there are an even number of set bits, and 1 if there are an odd number ''' parity = 0 for bit in bin(int_type)[2:]: parity ^= int(bit) return parity def _parse_state_to_bits(self): br_parity_bits = str(self._parity_of(self._state._brightness)) scaled_screen_off = (2 * int(self._state._screen_blanked)) state_bits_val = scaled_screen_off + self._state._shutdown state_parity_bits = str(self._parity_of(state_bits_val)) # Determine new bits to send # bs = bitshifted # br = brightness # par = parity bs_br_par = (128 * int(br_parity_bits)) bs_br = (8 * self._state._brightness) bs_state_par = (4 * int(state_parity_bits)) bs_screen_off = (2 * int(self._state._screen_blanked)) bits_to_send = bs_br_par bits_to_send += bs_br bits_to_send += bs_state_par bits_to_send += bs_screen_off bits_to_send += self._state._shutdown # e.g. bits = "10101010" # brightness parity = 1 # brightness = 5 # state parity = 0 # screen_off = 1 # shutdown = 0 return bits_to_send def _setup_spi(self): if self.spi is None: from spidev import SpiDev self.spi = SpiDev() self.spi.open(0, 1) self.spi.max_speed_hz = 9600 self.spi.mode = 0b00 self.spi.bits_per_word = 8 self.spi.cshigh = True self.spi.lsbfirst = False def _determine_byte(self, resp): # Check parity bit parity_bit_brightness = resp[0] brightness = resp[1:5] if parity_bit_brightness == "0" and brightness == "1111": return SPIResponseType.device_id else: correct_parity_val = str(self._parity_of(int(resp[1:8], 2))) if parity_bit_brightness != correct_parity_val: PTLogger.info("Invalid parity bit") return SPIResponseType.invalid return SPIResponseType.state def _process_spi_resp_shutdown(self, spi_shutdown_bit_int): if spi_shutdown_bit_int == 1: # Increment shutdown counter self._shutdown_ctr.increment() PTLogger.info("Received shutdown indication from hub (" + str(self._shutdown_ctr.current) + " of " + str(self._shutdown_ctr.max) + ")") if self._shutdown_ctr.maxed(): self._shutdown_ctr.reset() self._state.set_shutdown(1) else: self._shutdown_ctr.reset() def _process_spi_resp(self, resp, init=False): # Message from hub bits: # 0 : check sum: set if odd number of set bits in rest of message # 1 - 4 : Brightness of screen backlight # 5 : Lid state, 1 if open, 0 if closed # 6 : Screen blank state, 0 if unblanked, 1 if blanked # 7 : Shutdown requested from hub, 1 if shutting down # If we're communicating, but we still haven't decided what device # we're on, then we must be on a CEED, as if we were on a pi-top v1, # we would have identified this via connecting to the battery on i2c. if int(resp) != 0 and self._state._device_id == DeviceID.unknown: PTLogger.info("Received comms from hub - assuming we're on a CEED") self._state.set_device_id(DeviceID.pi_top_ceed) # Check shutdown bit spi_shutdown_bit_int = int(resp[7]) self._process_spi_resp_shutdown(spi_shutdown_bit_int) spi_screen_off_state = int(resp[6]) if (spi_screen_off_state == 1): self._state.set_screen_blanked() else: self._state.set_screen_unblanked() spi_lid_state = int(resp[5]) if (spi_lid_state == 1): self._state.set_lid_open() else: self._state.set_lid_closed() spi_brightness_int = int(resp[1:5], 2) screen_is_blanked = (spi_screen_off_state == 1 and spi_brightness_int == 0) if init or not screen_is_blanked: self._state.set_brightness(spi_brightness_int, False) def _transceive_spi(self, bits_to_send): hex_str_to_send = '0x' + str(hex(bits_to_send))[2:].zfill(2) bin_str_to_send = '{0:b}'.format(int(hex_str_to_send[2:], 16)).zfill(8) log_brightness = str(int(bin_str_to_send[1:5], 2)) log_screen = "On" if bin_str_to_send[6] == "0" else "Off" log_shutdown = "Shutting down" if bin_str_to_send[7] == "1" else "No shutdown" if (bin_str_to_send == "11111111"): PTLogger.debug("Pi sending: " + bin_str_to_send + " [ fetch state from hub ]") else: PTLogger.debug("Pi sending: " + bin_str_to_send + " [" + log_brightness + ", " + log_screen + ", " + log_shutdown + "]") # Initiate receiving communication from hub self.spi.cshigh = False # Transfer data with hub resp = self.spi.xfer2([bits_to_send], self.spi.max_speed_hz) self.spi.cshigh = True resp_hex = hex(resp[0]) resp_hex_str = '0x' + str(resp_hex)[2:].zfill(2) resp_bin_str = '{0:b}'.format(int(resp_hex_str[2:], 16)).zfill(8) log_brightness = str(int(resp_bin_str[1:5], 2)) log_lid = "Open" if resp_bin_str[5] == "1" else "Closed" log_screen = "On" if resp_bin_str[6] == "0" else "Off" log_shutdown = "Shutting down" if resp_bin_str[7] == "1" else "No shutdown" PTLogger.debug("Hub responds: " + resp_bin_str + " [" + log_brightness + ", " + log_lid + ", " + log_screen + ", " + log_shutdown + "]") return resp_bin_str def _attempt_get_state(self): # Send 0xFF to get data from hub resp_bin_str = self._transceive_spi(255) byte_type = self._determine_byte(resp_bin_str) if byte_type == SPIResponseType.state: PTLogger.debug("Valid response from hub - STATE") valid = True else: if byte_type == SPIResponseType.device_id: PTLogger.debug("Valid response from hub - DEVICE ID") # Process SPI resp, store brightness signal for check next loop self._process_device_id(resp_bin_str) else: PTLogger.debug("Invalid response from hub") valid = False return valid, resp_bin_str def _get_state_from_hub(self, init=False, process_state=True): valid = False get_state_ctr = Counter(5) do_extra_read = init while not valid and not get_state_ctr.maxed(): valid, resp_bin_str = self._attempt_get_state() if do_extra_read and valid: valid = False do_extra_read = False if not valid: get_state_ctr.current += 1 sleep(_cycle_sleep_time) if valid: if process_state: self._process_spi_resp(resp_bin_str, init=init) else: PTLogger.error("Unable to communicate with hub. " + "init: " + str(init) + ", resp_bin_str: " + str(resp_bin_str)) return valid
class MCP2515(): DEFAULT_SPI_CLOCK_FREQUENCY = 4000000 # 4MHz DEFAULT_CLOCK_POLARITY = 0 DEFAULT_CLOCK_PHASE = 0 # SPI Command Bytes RESET_INSTRUCTION_BYTE = 0xC0 WRITE_INSTRUCTION_BYTE = 0x02 READ_INSTRUCTION_BYTE = 0x03 LOAD_TX_BUFFER_INSTRUCTION_BASE_BYTE = 0x40 READ_RX_BUFFER_INSTRUCTION_BASE_BYTE = 0x90 REQUEST_TO_SEND_INSTRUCTION_BYTE = 0x80 READ_STATUS_INSTRUCTION_BYTE = 0xA0 BIT_MODIFY_INSTRUCTION_BYTE = 0x05 # CAN RX Filter Number to Address Map CAN_BUFFER_NUMBER_TO_CR_ADDRESS = [0x60, 0x70] CAN_FILTER_NUMBER_TO_STARTING_ADDRESS = [ 0x00, 0x04, 0x08, 0x10, 0x14, 0x18 ] CAN_MASK_NUMBER_TO_STARTING_ADDRESS = [0x20, 0x24] BYTE_MASK = 0xFF def __init__(self, bus_num, device_num, max_speed_hz=DEFAULT_SPI_CLOCK_FREQUENCY, clock_polarity=DEFAULT_CLOCK_POLARITY, clock_phase=DEFAULT_CLOCK_PHASE): self.spi_instance = SpiDev() self.bus_num = bus_num self.device_num = device_num self.clk_speed = max_speed_hz self.clk_polarity = clock_polarity self.clk_phase = clock_phase def set_spi_max_speed_hz(self, max_speed_hz=DEFAULT_SPI_CLOCK_FREQUENCY): self.spi_instance.max_speed_hz = max_speed_hz def set_spi_mode(self, clock_polarity=DEFAULT_CLOCK_POLARITY, clock_phase=DEFAULT_CLOCK_PHASE): self.spi_instance.mode = (clock_polarity << 1) | clock_phase def open_can_connection(self): self.spi_instance.open(self.bus_num, self.device_num) # Cannot set these SPI parameters until SPI connection has been opened self.set_spi_max_speed_hz(self.clk_speed) self.set_spi_mode(self.clk_polarity, self.clk_phase) def close_can_connection(self): self.spi_instance.close() def write_bytes(self, address, data_bytes): if not isinstance(data_bytes, list): data_bytes = [data_bytes] bytes_to_write = [MCP2515.WRITE_INSTRUCTION_BYTE, address]; bytes_to_write.extend(data_bytes) self.spi_instance.xfer2(bytes_to_write) def read_bytes(self, address, num_bytes): bytes_to_write = [MCP2515.READ_INSTRUCTION_BYTE, address] bytes_to_write.extend([0] * num_bytes) bytes_read = self.spi_instance.xfer2(bytes_to_write) return bytes_read[2:] def reset(self): self.spi_instance.xfer2([MCP2515.RESET_INSTRUCTION_BYTE]) def load_tx_buffer(self, can_message_buffer, can_message_buffer_number): data_bytes_to_send = [] load_tx_buffer_command = MCP2515.LOAD_TX_BUFFER_INSTRUCTION_BASE_BYTE | (can_message_buffer_number << 1) data_bytes_to_send.append(load_tx_buffer_command) data_byte = (can_message_buffer.get_standard_id() & (MCP2515.BYTE_MASK << 3)) >> 3 data_bytes_to_send.append(data_byte) data_byte = (can_message_buffer.get_standard_id() & 0x7) << 5 data_byte |= (can_message_buffer.get_id_extension() << 3) if (can_message_buffer.get_id_extension()): data_byte |= ((can_message_buffer.get_extended_id() & (0x3 << 16)) >> 16) data_bytes_to_send.append(data_byte) data_byte = (can_message_buffer.get_extended_id() & (MCP2515.BYTE_MASK << 8)) >> 8 data_bytes_to_send.append(data_byte) data_byte = (can_message_buffer.get_extended_id() & MCP2515.BYTE_MASK) data_bytes_to_send.append(data_byte) else: data_bytes_to_send.append(data_byte) data_bytes_to_send.append(0x00) data_bytes_to_send.append(0x00) data_byte = can_message_buffer.get_remote_transmission() << 6 data_byte |= can_message_buffer.get_num_data_bytes() data_bytes_to_send.append(data_byte) for i in range(can_message_buffer.get_num_data_bytes()): data_bytes_to_send.append(can_message_buffer.get_data_bytes()[i]) self.spi_instance.xfer2(data_bytes_to_send) def send_tx_buffers(self, buffer0=False, buffer1=False, buffer2=False): buffer_bits = 0 if buffer0: buffer_bits += 1 if buffer1: buffer_bits += 2 if buffer2: buffer_bits += 4 byte_to_send = MCP2515.REQUEST_TO_SEND_INSTRUCTION_BYTE | buffer_bits print(f"RTS Byte = {byte_to_send}") self.spi_instance.xfer2([byte_to_send]) def send_tx_buffer_with_priority(self, buffer_number, priority): # Priority [0, 3] with 0 the lowest and 3 the highest write_address = self.get_tx_buffer_control_register_address(buffer_number) data_byte_to_send = (1 << 3) | priority self.write_bytes(write_address, data_byte_to_send) def get_tx_buffer_control_register_address(self, buffer_number): BASE_TX_BUFFER_ADDRESS = 0x30 NUM_BYTES_BETWEEN_TX_BUFFERS = 16 return (BASE_TX_BUFFER_ADDRESS + (buffer_number * NUM_BYTES_BETWEEN_TX_BUFFERS)) def get_status(self, num_consecutive_times=1): bytes_to_write = [MCP2515.READ_STATUS_INSTRUCTION_BYTE] bytes_to_write.extend([0] * num_consecutive_times) bytes_read = self.spi_instance.xfer2(bytes_to_write) return bytes_read[1:] def wait_until_tx_message_success(self, buffer_number): tx_message_success = False while (not tx_message_success): byte_read = self.get_status()[0] bit_mask = 1 << ((2 * buffer_number) + 2) tx_message_success = not (byte_read & bit_mask) return tx_message_success def configure_bit_timing(self, bit_timing_configuration): CONFIGURATION_REGISTER1_ADDRESS = 0x2A CONFIGURATION_REGISTER2_ADDRESS = 0x29 CONFIGURATION_REGISTER3_ADDRESS = 0x28 bytes_to_write = [] byte_to_write = bit_timing_configuration.get_phase_segment2_length() & 0x07 bytes_to_write.append(byte_to_write) byte_to_write = bit_timing_configuration.get_propagation_segment_length() & 0x07 byte_to_write |= ((bit_timing_configuration.get_phase_segment1_length() & 0x07) << 3) if (bit_timing_configuration.get_num_samples_per_bit() == 3): byte_to_write |= (1 << 6) byte_to_write |= (1 << 7) bytes_to_write.append(byte_to_write) byte_to_write = bit_timing_configuration.get_baud_rate_prescalar() & 0x3F byte_to_write |= (bit_timing_configuration.get_sjw() - 1) & 0x03 bytes_to_write.append(byte_to_write) self.write_bytes(CONFIGURATION_REGISTER3_ADDRESS, bytes_to_write) def switch_operation_modes(self, operating_mode): CAN_CONTROL_REGISTER_ADDRESS = 0x0F self.write_bytes(CAN_CONTROL_REGISTER_ADDRESS, operating_mode) def get_operation_mode(self): CAN_STATUS_REGISTER_ADDRESS = 0x0E read_byte = self.read_bytes(CAN_STATUS_REGISTER_ADDRESS, 1)[0] return OperationModes((read_byte & (0x7 << 5)) >> 5) def initialize(self, bit_timing_configuration): self.reset() time.sleep(.1) while (self.get_operation_mode() != OperationModes.CONFIGURATION): self.reset() time.sleep(.1) self.configure_bit_timing(bit_timing_configuration) while (self.get_operation_mode() != OperationModes.NORMAL): self.switch_operation_modes(OperationModes.NORMAL) time.sleep(.1) def set_can_rx_filter(self, can_filter_number, can_filter): # Filter Number = [0, 5] data_bytes_to_send = [] base_filter_address = MCP2515.CAN_FILTER_NUMBER_TO_STARTING_ADDRESS[can_filter_number] data_byte = (can_filter.get_standard_id() & (MCP2515.BYTE_MASK << 3)) >> 3 data_bytes_to_send.append(data_byte) data_byte = (can_filter.get_standard_id() & 0x7) << 5 data_byte |= (can_filter.get_id_extension() << 3) if (can_filter.get_id_extension()): data_byte |= ((can_filter.get_extended_id() & (0x3 << 16)) >> 16) data_bytes_to_send.append(data_byte) data_byte = (can_filter.get_extended_id() & (MCP2515.BYTE_MASK << 8)) >> 8 data_bytes_to_send.append(data_byte) data_byte = (can_filter.get_extended_id() & MCP2515.BYTE_MASK) data_bytes_to_send.append(data_byte) else: data_bytes_to_send.append(data_byte) data_byte = (can_filter.get_data_byte0() & MCP2515.BYTE_MASK) data_bytes_to_send.append(data_byte) data_byte = (can_filter.get_data_byte1() & MCP2515.BYTE_MASK) data_bytes_to_send.append(data_byte) self.write_bytes(base_filter_address, data_bytes_to_send) def set_can_rx_mask(self, can_mask_number, can_mask): # Mask Number = [0, 1] data_bytes_to_send = [] base_mask_address = MCP2515.CAN_MASK_NUMBER_TO_STARTING_ADDRESS[can_mask_number] data_byte = (can_mask.get_standard_id() & (MCP2515.BYTE_MASK << 3)) >> 3 data_bytes_to_send.append(data_byte) data_byte = (can_mask.get_standard_id() & 0x7) << 5 if (can_mask.get_id_extension()): data_byte |= ((can_mask.get_extended_id() & (0x3 << 16)) >> 16) data_bytes_to_send.append(data_byte) data_byte = (can_mask.get_extended_id() & (MCP2515.BYTE_MASK << 8)) >> 8 data_bytes_to_send.append(data_byte) data_byte = (can_mask.get_extended_id() & MCP2515.BYTE_MASK) data_bytes_to_send.append(data_byte) else: data_bytes_to_send.append(data_byte) data_byte = (can_mask.get_data_byte0() & MCP2515.BYTE_MASK) data_bytes_to_send.append(data_byte) data_byte = (can_mask.get_data_byte1() & MCP2515.BYTE_MASK) data_bytes_to_send.append(data_byte) self.write_bytes(base_mask_address, data_bytes_to_send) def read_rx_control_register(self, buffer_number): rx_control_register_address = 0x60 if (buffer_number == 0) else 0x70 return self.read_bytes(rx_control_register_address, num_bytes=1) def read_rx_buffer(self, buffer_number, expected_num_data_bytes=8): # RX Buffer Number = [0, 1] bytes_to_write = [] num_bytes_to_receive = 5 + expected_num_data_bytes # RXnIF Flag automatically cleared when using READ RX BUFFER Command read_rx_command = MCP2515.READ_RX_BUFFER_INSTRUCTION_BASE_BYTE | (buffer_number << 2) bytes_to_write.append(read_rx_command) bytes_to_write.extend([0] * num_bytes_to_receive) bytes_read = self.spi_instance.xfer2(bytes_to_write) return bytes_read[1:] def interpret_rx_buffer(self, received_buffer_bytes): can_rx_message = CANMessageBuffer() standard_id = (received_buffer_bytes[0] << 3) | ((received_buffer_bytes[1] & (0x07 << 5)) >> 5) can_rx_message.set_standard_id(standard_id) ide = (received_buffer_bytes[1] & (0x01 << 3)) >> 3 can_rx_message.set_id_extension(ide) if ide: extended_id = (received_buffer_bytes[1] & 0x03) << 16 extended_id |= (received_buffer_bytes[2] & MCP2515.BYTE_MASK) << 8 extended_id |= (received_buffer_bytes[3] & MCP2515.BYTE_MASK) can_rx_message.set_extended_id(extended_id) rtr = (received_buffer_bytes[4] & (0x01 << 6)) >> 6 can_rx_message.set_remote_transmission(rtr) else: can_rx_message.set_extended_id(0) rtr = (received_buffer_bytes[1] & (0x01 << 4)) >> 4 can_rx_message.set_remote_transmission(rtr) num_data_bytes = received_buffer_bytes[4] & 0x0F expected_num_data_bytes = len(received_buffer_bytes) - 5 num_data_bytes_to_read = min(num_data_bytes, expected_num_data_bytes) starting_data_index = 5 ending_data_index = starting_data_index + num_data_bytes_to_read can_rx_message.set_data_bytes(received_buffer_bytes[starting_data_index:ending_data_index]) return can_rx_message def wait_until_message_received(self, rx_buffer_number): rx_message_received = False while (not rx_message_received): byte_read = self.get_status()[0] bit_mask = (0x01 << rx_buffer_number) rx_message_received = True if (byte_read & bit_mask) else False return rx_message_received def configure_rx_buffer(self, rx_buffer_number, is_filters_enabled=True, buffer_rollover=False): # Only RX Buffer 0 has the Buffer Overflow Option # Add additional bit to bit mask to allow buffer rollover control if rx_buffer_number == 0: mask_byte = 0x64 else: mask_byte = 0x60 data_byte = 0x00 if is_filters_enabled else (0x03 << 5) data_byte |= (0x01 << 2) if buffer_rollover else 0x00 # Modify Individual Bits of Control Register (CR) bytes_to_write = [ MCP2515.BIT_MODIFY_INSTRUCTION_BYTE, MCP2515.CAN_BUFFER_NUMBER_TO_CR_ADDRESS[rx_buffer_number], mask_byte, data_byte ] self.spi_instance.xfer2(bytes_to_write)
class LocalPiHardwareSPI(SPI, Device): def __init__(self, factory, port, device): self._port = port self._device = device self._interface = None if SpiDev is None: raise ImportError('failed to import spidev') super(LocalPiHardwareSPI, self).__init__() pins = SPI_HARDWARE_PINS[port] self.pin_factory.reserve_pins(self, pins['clock'], pins['mosi'], pins['miso'], pins['select'][device]) self._interface = SpiDev() self._interface.open(port, device) self._interface.max_speed_hz = 500000 def close(self): if self._interface is not None: self._interface.close() self._interface = None self.pin_factory.release_all(self) super(LocalPiHardwareSPI, self).close() @property def closed(self): return self._interface is None def __repr__(self): try: self._check_open() return 'SPI(port=%d, device=%d)' % (self._port, self._device) except DeviceClosed: return 'SPI(closed)' def transfer(self, data): """ Writes data (a list of integer words where each word is assumed to have :attr:`bits_per_word` bits or less) to the SPI interface, and reads an equivalent number of words, returning them as a list of integers. """ return self._interface.xfer2(data) def _get_clock_mode(self): return self._interface.mode def _set_clock_mode(self, value): self._interface.mode = value def _get_lsb_first(self): return self._interface.lsbfirst def _set_lsb_first(self, value): self._interface.lsbfirst = bool(value) def _get_select_high(self): return self._interface.cshigh def _set_select_high(self, value): self._interface.cshigh = bool(value) def _get_bits_per_word(self): return self._interface.bits_per_word def _set_bits_per_word(self, value): self._interface.bits_per_word = value
class SPIimplementation(SPI): """SPI imlementation wrapping spidev library. Extends :class:`SPI`. Args: port (int): SPI port on raspberry pi. devive (int): SPI device of raspberry. Raises: ImportError: If spidev is not installed. """ def __init__(self, port, device): self._port = port self._device = device self._interface = None if SpiDev is None: raise ImportError('failed to import spidev') self._interface = SpiDev() self._interface.open(port, device) self._interface.max_speed_hz = 1000000 def read(self, n): """Read n words from spi Args: n (int): The number of bytes to read from spi. """ return self._interface.readbytes(n) # TODO: Check writebytes2 for large lists def write(self, data): """Write data to spi Args: data (list): A list with integers to be writter to the device. """ self._interface.writebytes2(data) def read_write(self, data): """ Writes data (a list of integer words where each word is assumed to have :attr:`bits_per_word` bits or less) to the SPI interface, and reads an equivalent number of words, returning them as a list of integers. """ return self._interface.xfer2(data) def close(self): if self._interface is not None: self._interface.close() self._interface = None def _get_clock_mode(self): return self._interface.mode def _set_clock_mode(self, value): self._interface.mode = value def _get_lsb_first(self): return self._interface.lsbfirst def _set_lsb_first(self, value): self._interface.lsbfirst = bool(value) def _get_select_high(self): return self._interface.cshigh def _set_select_high(self, value): self._interface.cshigh = bool(value) def _get_bits_per_word(self): return self._interface.bits_per_word def _set_bits_per_word(self, value): self._interface.bits_per_word = value