diff --git a/quick2wire/gpio.py b/quick2wire/gpio.py index fe8e3a8..6d45cfa 100644 --- a/quick2wire/gpio.py +++ b/quick2wire/gpio.py @@ -8,6 +8,14 @@ from quick2wire.board_revision import revision from quick2wire.selector import EDGE +gpio_class_path = '/sys/class/gpio/' +gpio_virtual_path = "/sys/devices/virtual/gpio/" + +if os.path.exists(gpio_virtual_path): + gpio_dir_path = gpio_virtual_path +else: + gpio_dir_path = gpio_class_path + def gpio_admin(subcommand, pin, pull=None): if pull: @@ -15,14 +23,33 @@ def gpio_admin(subcommand, pin, pull=None): else: subprocess.check_call(["gpio-admin", subcommand, str(pin)]) +def gpio_admin_sysfs(subcommand, pin, pull=None): + if pull is not None: + raise RuntimeError('pull-ups and pull-downs are not supported') + + if subcommand in ('export', 'unexport'): + path = gpio_class_path + subcommand + open(path, 'wt').write('%d\n' % pin) + else: + raise RuntimeError('unsupported command %s' % subcommand) + + +try: + subprocess.check_call(["gpio-admin"]) +except OSError: + gpio_admin = gpio_admin_sysfs + + + + Out = "out" In = "in" - + Rising = "rising" Falling = "falling" Both = "both" - + PullDown = "pulldown" PullUp = "pullup" @@ -32,36 +59,36 @@ class PinAPI(object): def __init__(self, bank, index): self._bank = bank self._index = index - + @property def index(self): return self._index - + @property def bank(self): return self._bank - + def __enter__(self): self.open() return self - + def __exit__(self, exc_type, exc_value, traceback): self.close() - - value = property(lambda p: p.get(), - lambda p,v: p.set(v), + + value = property(lambda p: p.get(), + lambda p,v: p.set(v), doc="""The value of the pin: 1 if the pin is high, 0 if the pin is low.""") - + class PinBankAPI(object): def __getitem__(self, n): if 0 < n < len(self): raise ValueError("no pin index {n} out of range", n=n) return self.pin(n) - + def write(self): pass - + def read(self): pass @@ -69,19 +96,19 @@ def read(self): class Pin(PinAPI): """Controls a GPIO pin.""" - + __trigger__ = EDGE - + def __init__(self, bank, index, soc_pin_number, direction=In, interrupt=None, pull=None): """Creates a pin - + Parameters: user_pin_number -- the identity of the pin used to create the derived class. soc_pin_number -- the pin on the header to control, identified by the SoC pin number. direction -- (optional) the direction of the pin, either In or Out. interrupt -- (optional) pull -- (optional) - + Raises: IOError -- could not export the pin (if direction is given) """ @@ -91,19 +118,27 @@ def __init__(self, bank, index, soc_pin_number, direction=In, interrupt=None, pu self._direction = direction self._interrupt = interrupt self._pull = pull - - + + @property def soc_pin_number(self): return self._soc_pin_number - + def open(self): gpio_admin("export", self.soc_pin_number, self._pull) self._file = open(self._pin_path("value"), "r+") self._write("direction", self._direction) + self._interrupt_supported = os.path.exists(self._pin_path('edge')) + + if self._direction == In: - self._write("edge", self._interrupt if self._interrupt is not None else "none") - + + if self._interrupt_supported: + self._write("edge", self._interrupt if self._interrupt is not None else "none") + else: + if self._interrupt is not None: + raise RuntimeError("Interrupts are not supported for given GPIO") + def close(self): if not self.closed: if self.direction == Out: @@ -111,22 +146,23 @@ def close(self): self._file.close() self._file = None self._write("direction", In) - self._write("edge", "none") + if self.interrupt_supported: + self._write("edge", "none") gpio_admin("unexport", self.soc_pin_number) - + def get(self): """The current value of the pin: 1 if the pin is high or 0 if the pin is low. - + The value can only be set if the pin's direction is Out. - - Raises: + + Raises: IOError -- could not read or write the pin's value. """ self._check_open() self._file.seek(0) v = self._file.read() return int(v) if v else 0 - + def set(self, new_value): self._check_open() if self._direction != Out: @@ -134,73 +170,82 @@ def set(self, new_value): self._file.seek(0) self._file.write(str(int(new_value))) self._file.flush() - + @property def direction(self): """The direction of the pin: either In or Out. - + The value of the pin can only be set if its direction is Out. - + Raises: IOError -- could not set the pin's direction. """ return self._direction - + @direction.setter def direction(self, new_value): self._write("direction", new_value) self._direction = new_value - - @property + + @property + def interrupt_supported(self): + """ Returns whether interrupts are supported for given GPIO or not + """ + + return self._interrupt_supported + + @property def interrupt(self): """The interrupt property specifies what event (if any) will raise an interrupt. - - One of: + + One of: Rising -- voltage changing from low to high Falling -- voltage changing from high to low Both -- voltage changing in either direction None -- interrupts are not raised - + Raises: IOError -- could not read or set the pin's interrupt trigger """ return self._interrupt - + @interrupt.setter def interrupt(self, new_value): + if not self.interrupt_supported: + raise RuntimeError("Interrupts are not supported for given GPIO") self._write("edge", new_value) self._interrupt = new_value @property def pull(self): return self._pull - + def fileno(self): """Return the underlying file descriptor. Useful for select, epoll, etc.""" return self._file.fileno() - + @property def closed(self): """Returns if this pin is closed""" return self._file is None or self._file.closed - + def _check_open(self): if self.closed: raise IOError(str(self) + " is closed") - + def _write(self, filename, value): with open(self._pin_path(filename), "w+") as f: f.write(value) - + def _pin_path(self, filename=""): - return "/sys/devices/virtual/gpio/gpio%i/%s" % (self.soc_pin_number, filename) - + return "%sgpio%i/%s" % (gpio_dir_path, self.soc_pin_number, filename) + def __repr__(self): return self.__module__ + "." + str(self) - + def __str__(self): return "{type}({index})".format( - type=self.__class__.__name__, + type=self.__class__.__name__, index=self.index) @@ -212,14 +257,14 @@ def __init__(self, index_to_soc_fn, count=None): super(PinBank,self).__init__() self._index_to_soc = index_to_soc_fn self._count = count - + def pin(self, index, *args, **kwargs): return Pin(self, index, self._index_to_soc(index), *args, **kwargs) - + @property def has_len(self): return self._count is not None - + def __len__(self): if self._count is not None: return self._count @@ -233,12 +278,15 @@ def __len__(self): I2C_INTERRUPT = 7 +pins_linux = PinBank(lambda p: p) +pi_broadcom_soc = pins_linux + + _pi_revision = revision() if _pi_revision == 0: # Not running on the Raspberry Pi, so define no-op pin banks - pins = PinBank(lambda p: p) - pi_broadcom_soc = pins + pins = pins_linux pi_header_1 = pins else: @@ -249,49 +297,49 @@ def by_revision(d): # Maps header pin numbers to SoC GPIO numbers # See http://elinux.org/RPi_Low-level_peripherals # - # Note: - header pins are numbered from 1, SoC GPIO from zero + # Note: - header pins are numbered from 1, SoC GPIO from zero # - the Pi documentation identifies some header pins as GPIO0, # GPIO1, etc., but these are not the same as the SoC GPIO # numbers. - + _pi_header_1_pins = { - 3: by_revision({1:0, 2:2}), - 5: by_revision({1:1, 2:3}), - 7: 4, - 8: 14, - 10: 15, - 11: 17, - 12: 18, - 13: by_revision({1:21, 2:27}), - 15: 22, - 16: 23, - 18: 24, - 19: 10, - 21: 9, - 22: 25, - 23: 11, + 3: by_revision({1:0, 2:2}), + 5: by_revision({1:1, 2:3}), + 7: 4, + 8: 14, + 10: 15, + 11: 17, + 12: 18, + 13: by_revision({1:21, 2:27}), + 15: 22, + 16: 23, + 18: 24, + 19: 10, + 21: 9, + 22: 25, + 23: 11, 24: 8, 26: 7 } - + _pi_gpio_pins = [_pi_header_1_pins[i] for i in [11, 12, 13, 15, 16, 18, 22, 7]] - - + + def lookup(pin_mapping, i): try: if i >= 0: return pin_mapping[i] except LookupError: pass - + raise IndexError(str(i) + " is not a valid pin index") def map_with(pin_mapping): return lambda i: lookup(pin_mapping,i) - - - pi_broadcom_soc = PinBank(lambda p: p) + + + pi_broadcom_soc = pins_linux pi_header_1 = PinBank(map_with(_pi_header_1_pins)) pins = PinBank(map_with(_pi_gpio_pins), len(_pi_gpio_pins)) - +