From 7ddc4287fba795de444e8d0c234cbd449fe06cd0 Mon Sep 17 00:00:00 2001 From: Nat Pryce Date: Fri, 25 Jan 2013 15:43:46 +0000 Subject: [PATCH 01/10] quick2wire.gpio Pin classes now follow same API conventions as those in the mcp23x17 module. GPIO pins now have open() and close() methods and act as their own context managers. They export the pin to userspace on open() and reset the pin state and unexport it on close(). THIS IS A BREAKING API CHANGE --- examples/blink | 8 +- examples/button-blink | 9 +- examples/counter | 17 +-- examples/gpio-speed | 21 ++-- examples/pullup | 18 ++-- examples/selector-button-blink | 6 +- quick2wire/gpio.py | 173 ++++++++++++++++--------------- quick2wire/test_gpio.py | 143 +++++++++++++------------ quick2wire/test_gpio_loopback.py | 2 +- 9 files changed, 206 insertions(+), 191 deletions(-) diff --git a/examples/blink b/examples/blink index 748ac90..339c25a 100755 --- a/examples/blink +++ b/examples/blink @@ -5,13 +5,9 @@ from time import sleep from quick2wire.gpio import GPIOPin, Out pin = GPIOPin(int(sys.argv[1]) if len(sys.argv) > 1 else 1, direction=Out) - - -try: +with pin: pin.value = 1 while True: sleep(1) pin.value = 1 - pin.value -except KeyboardInterrupt: - pin.value = 0 - pin.unexport() + diff --git a/examples/button-blink b/examples/button-blink index 53cd6af..7f6768e 100755 --- a/examples/button-blink +++ b/examples/button-blink @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 import sys from time import sleep @@ -7,6 +7,7 @@ from quick2wire.gpio import GPIOPin, In, Out led = GPIOPin(1, direction=Out) button = GPIOPin(0, direction=In) -while True: - led.value = button.value - sleep(0.1) +with led, button: + while True: + led.value = button.value + sleep(0.1) diff --git a/examples/counter b/examples/counter index 1b6b246..d432f4a 100755 --- a/examples/counter +++ b/examples/counter @@ -1,19 +1,20 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 from itertools import cycle from time import sleep -from quick2wire.gpio import Pin +from quick2wire.gpio import GPIOPin, Out -pins = [Pin(n, Pin.Out) for n in [7, 22, 18, 16, 15, 13, 12, 11]] +pins = [GPIOPin(n, Out) for n in range(8)] try: + for p in pins: + p.open() + for count in cycle(range(256)): bitset = [int(count & (1< 0) for n in range(8)] for (pin, value) in zip(pins, bitset): pin.value = value sleep(0.5) - -except KeyboardInterrupt: - for pin in pins: - pin.value = 0 - pin.unexport() +finally: + for p in pins: + p.close() diff --git a/examples/gpio-speed b/examples/gpio-speed index 04c879f..3e410a7 100755 --- a/examples/gpio-speed +++ b/examples/gpio-speed @@ -1,12 +1,9 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 from datetime import datetime from quick2wire.gpio import Pin from timeit import Timer -outpin = Pin(12, Pin.Out) -inpin = Pin(11, Pin.In) -iterations = 10000 def nothin(): pass @@ -18,12 +15,16 @@ def onepass_toggle(): outpin.value = 1 outpin.value = 0 -overhead = Timer(nothin).timeit(iterations) -readresult = Timer(onepass_read).timeit(iterations) -toggleresult = Timer(onepass_toggle).timeit(iterations) +iterations = 10000 -print("The time to do nothing %d times is %4.3fsec" % (iterations, overhead)) -print("The time to read %d times is %4.3fsec" % (iterations, readresult)) -print("The time to toggle %d times is %4.3fsec" % (iterations, toggleresult)) +outpin = Pin(12, Pin.Out) +inpin = Pin(11, Pin.In) +with inpin, outpin: + overhead = Timer(nothin).timeit(iterations) + readresult = Timer(onepass_read).timeit(iterations) + toggleresult = Timer(onepass_toggle).timeit(iterations) + print("The time to do nothing %d times is %4.3fsec" % (iterations, overhead)) + print("The time to read %d times is %4.3fsec" % (iterations, readresult)) + print("The time to toggle %d times is %4.3fsec" % (iterations, toggleresult)) diff --git a/examples/pullup b/examples/pullup index d6997bd..f786ac1 100755 --- a/examples/pullup +++ b/examples/pullup @@ -5,16 +5,14 @@ # the Pi's GPIO pins. # -from quick2wire.gpio import Pin +from quick2wire.gpio import GPIOPin, In, PullUp, PullDown -pin = Pin(11, Pin.In) -print( "Pull up/down is unspecified, pin is now", pin.value) -pin.unexport() +with GPIOPin(0, In) as pin: + print("Pull up/down is unspecified, pin is now", pin.value) -pin = Pin(11, Pin.In, pull=Pin.PullUp) -print( "Pin is set with pull up, pin in is now", pin.value) -pin.unexport() +with GPIOPin(0, In, pull=PullUp) as pin: + print("Pin is set with pull up, pin in is now", pin.value) + +with GPIOPin(0, In, pull=PullDown) as pin: + print("Pin is set with pull down, pin is now", pin.value) -pin = Pin(11, Pin.In, pull=Pin.PullDown) -print( "Pin is set with pull down, pin is now", pin.value) -pin.unexport() diff --git a/examples/selector-button-blink b/examples/selector-button-blink index 599d5d9..9b0957d 100755 --- a/examples/selector-button-blink +++ b/examples/selector-button-blink @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 import sys import os @@ -8,8 +8,8 @@ from quick2wire.selector import Selector, Timer blink_rate=2.0 -with exported(GPIOPin(0, direction=In, interrupt=Both)) as button_pin, \ - exported(GPIOPin(1, direction=Out)) as led_pin, \ +with GPIOPin(0, direction=In, interrupt=Both) as button_pin, \ + GPIOPin(1, direction=Out) as led_pin, \ Timer(interval=1/blink_rate) as timer, \ Selector() as selector: diff --git a/quick2wire/gpio.py b/quick2wire/gpio.py index fe05461..210c6ba 100644 --- a/quick2wire/gpio.py +++ b/quick2wire/gpio.py @@ -68,19 +68,6 @@ def gpio_admin(subcommand, pin, pull=None): -def _pin_file(name, parser, doc): - def _read(self): - self._ensure_exported() - with open(self._pin_path(name), "r") as f: - return parser(f.read()) - - def _write(self, value): - self._ensure_exported() - with open(self._pin_path(name), "w") as f: - f.write(str(value)) - - return property(_read, _write, doc=doc) - class _IOPin(object): """Controls a GPIO pin.""" @@ -97,7 +84,7 @@ class _IOPin(object): __trigger__ = EDGE - def __init__(self, user_pin_number, soc_pin_number, direction=None, interrupt=None, pull=None): + def __init__(self, user_pin_number, soc_pin_number, direction=In, interrupt=None, pull=None): """Creates a pin If the direction is specified, the pin is exported if @@ -109,7 +96,8 @@ def __init__(self, user_pin_number, soc_pin_number, direction=None, interrupt=No 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) + interrupt -- (optional) + pull -- (optional) Raises: IOError -- could not export the pin (if direction is given) @@ -117,47 +105,35 @@ def __init__(self, user_pin_number, soc_pin_number, direction=None, interrupt=No self.index = user_pin_number self.soc_pin_number = soc_pin_number self._file = None - self.pull = pull - if direction is not None: - self.direction = direction - if interrupt is not None: - self.interrupt = interrupt + self._direction = direction + self._interrupt = interrupt + self._pull = pull - def __repr__(self): - return self.__module__ + "." + str(self) + 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) + if self._direction == In: + self._write("edge", self._interrupt if self._interrupt is not None else "none") + + def close(self): + if not self.closed: + if self.direction == Out: + self.value = 0 + self._file.close() + self._file = None + self._write("direction", In) + self._write("edge", "none") + gpio_admin("unexport", self.soc_pin_number) - def __str__(self): - return "%s(%i)"%(self.__class__.__name__, self.index) + def __enter__(self): + self.open() + return self - @property - def is_exported(self): - """Has the pin been exported to user space?""" - return os.path.exists(self._pin_path()) - - def export(self): - """Export the pin to user space, making its control files visible in the filesystem. - - Raises: - IOError -- could not export the pin. - - - """ - gpio_admin("export", self.soc_pin_number, self.pull) - - def unexport(self): - """Unexport the pin, removing its control files from the filesystem. - - Raises: - IOError -- could not unexport the pin. - - """ - self._maybe_close() - gpio_admin("unexport", self.soc_pin_number) - - def _ensure_exported(self): - if not self.is_exported: - self.export() + def __exit__(self, *exc): + self.close() + return False @property def value(self): @@ -168,57 +144,88 @@ def value(self): Raises: IOError -- could not read or write the pin's value. - """ - f = self._lazyopen() - f.seek(0) - v = f.read() + self._check_open() + self._file.seek(0) + v = self._file.read() return int(v) if v else 0 @value.setter def value(self, new_value): - f = self._lazyopen() - f.seek(0) - f.write(str(int(new_value))) - f.flush() + self._check_open() + if self._direction != Out: + raise ValueError("not an output pin") + self._file.seek(0) + self._file.write(str(int(new_value))) + self._file.flush() - direction = _pin_file("direction", str.strip, + + @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 read or set the pin's direction. + 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 + def interrupt(self): + """The interrupt property specifies what event (if any) will raise an interrupt. - """) - - interrupt = _pin_file("edge", str.strip, - """The interrupt property specifies what event (if any) will trigger an interrupt. - - Raises: - IOError -- could not read or set the pin's interrupt trigger + 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): + self._write("edge", new_value) + self._interrupt = new_value - """) + @property + def pull(self): + return self._pull def fileno(self): - """ - Return the underlying fileno. Useful for calling select - """ - return self._lazyopen().fileno() - - def _lazyopen(self): - if self._file is None: - self._file = open(self._pin_path("value"), "r+") - return self._file + """Return the underlying file descriptor. Useful for select, epoll, etc.""" + return self._file.fileno() - def _maybe_close(self): - if self._file is not None: - self._file.close() - self._file = None + @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) + def __repr__(self): + return self.__module__ + "." + str(self) + + def __str__(self): + return "%s(%i)"%(self.__class__.__name__, self.index) + class HeaderPin(_IOPin): diff --git a/quick2wire/test_gpio.py b/quick2wire/test_gpio.py index 2c028a1..5365a4f 100644 --- a/quick2wire/test_gpio.py +++ b/quick2wire/test_gpio.py @@ -1,85 +1,96 @@ -from quick2wire.gpio import GPIOPin, exported, In, Out +import os +from quick2wire.gpio import GPIOPin, In, Out, PullDown, gpio_admin import pytest @pytest.mark.gpio @pytest.mark.loopback -class TestPin: - def setup_method(self, method): - self.pin = GPIOPin(0) - - def teardown_method(self, method): - if self.pin.is_exported: - self.pin.unexport() - - def test_pin_must_be_exported_before_use(self): - with pytest.raises(IOError): - self.pin.value - self.pin.export() - self.pin.value +class TestGPIOPin: + def test_pin_must_be_opened_before_use_and_is_unusable_after_being_closed(self): + pin = GPIOPin(0) - def test_pin_can_be_unexported_and_made_unusable(self): - self.pin.export() - self.pin.unexport() with pytest.raises(IOError): - self.pin.value - - def test_can_set_and_query_direction_of_pin(self): - self.pin.export() - - self.pin.direction = Out - assert self.pin.direction == Out - - self.pin.direction = In - assert self.pin.direction == In - + pin.value - def test_can_set_value_of_output_pin(self): - self.pin.export() + pin.open() + try: + pin.value + finally: + pin.close() - self.pin.direction = Out + with pytest.raises(IOError): + pin.value + + + def test_opens_and_closes_itself_when_used_as_a_context_manager(self): + pin = GPIOPin(0) - self.pin.value = 1 - assert self.pin.value == 1 + with pin: + pin.value - self.pin.value = 0 - assert self.pin.value == 0 + with pytest.raises(IOError): + pin.value + + + def test_exports_gpio_device_to_userspace_when_opened_and_unexports_when_closed(self): + with GPIOPin(0) as pin: + assert os.path.exists('/sys/class/gpio/gpio17/value') - def test_can_export_pin_and_set_direction_on_construction(self): - p = GPIOPin(0, Out) + assert not os.path.exists('/sys/class/gpio/gpio17/value') + + + def test_can_set_and_query_direction_of_pin_when_open(self): + with GPIOPin(0) as pin: + pin.direction = Out + assert pin.direction == Out + + assert content_of("/sys/class/gpio/gpio17/direction") == "out\n" + + pin.direction = In + assert pin.direction == In + + assert content_of("/sys/class/gpio/gpio17/direction") == "in\n" + + + def test_can_set_direction_on_construction(self): + pin = GPIOPin(0, Out) - assert p.is_exported - assert p.direction == Out - - def test_can_read_after_write(self): - outpin = GPIOPin(0, Out) + assert pin.direction == Out + assert not os.path.exists("/sys/class/gpio/gpio17/direction") - outpin.value = 1 - assert self.pin.value == 1 - with open('/sys/class/gpio/gpio17/value', 'r+') as f: - assert f.read() == '1\n' + with pin: + assert content_of("/sys/class/gpio/gpio17/direction") == "out\n" + assert pin.direction == Out - - -@pytest.mark.gpio -@pytest.mark.loopback -class TestExportedContextManager: - def test_can_automatically_unexport_pin_with_context_manager(self): - with exported(GPIOPin(0)) as p: - assert p.is_exported - - p = GPIOPin(0) - assert not p.is_exported - def test_can_use_context_manager_with_pin_exported_by_constructor(self): - with exported(GPIOPin(0, Out)) as p: - assert p.is_exported - - p = GPIOPin(0) - assert not p.is_exported + def test_setting_value_of_output_pin_writes_to_device_file(self): + with GPIOPin(0) as pin: + pin.direction = Out + + pin.value = 1 + assert pin.value == 1 + assert content_of('/sys/class/gpio/gpio17/value') == '1\n' + + pin.value = 0 + assert pin.value == 0 + assert content_of('/sys/class/gpio/gpio17/value') == '0\n' + - def test_can_use_context_manager_with_pin_already_exported(self): - GPIOPin(0).export() - self.test_can_automatically_unexport_pin_with_context_manager() + def test_direction_and_value_of_pin_is_reset_when_closed(self): + with GPIOPin(0, Out) as pin: + pin.value = 1 + gpio_admin("export", 17, PullDown) + try: + assert content_of('/sys/class/gpio/gpio17/value') == '0\n' + assert content_of('/sys/class/gpio/gpio17/direction') == 'in\n' + finally: + gpio_admin("unexport", 17) + + + +def content_of(filename): + with open(filename, 'r') as f: + return f.read() + diff --git a/quick2wire/test_gpio_loopback.py b/quick2wire/test_gpio_loopback.py index 92f1107..13e8409 100644 --- a/quick2wire/test_gpio_loopback.py +++ b/quick2wire/test_gpio_loopback.py @@ -33,7 +33,7 @@ def assert_outputs_seen_at_corresponding_inputs(pin_type, topology): def assert_output_seen_at_input(pin_type, op, ip): - with exported(pin_type(op, direction=Out)) as output_pin, exported(pin_type(ip, direction=In)) as input_pin: + with pin_type(op, direction=Out) as output_pin, pin_type(ip, direction=In) as input_pin: for value in [1, 0, 1, 0]: output_pin.value = value assert input_pin.value == value From 9df41a667ce3a10b5a76ed089cc9edafae72e0f0 Mon Sep 17 00:00:00 2001 From: Nat Pryce Date: Sat, 26 Jan 2013 16:04:31 +0000 Subject: [PATCH 02/10] Removing imports and calls to exported() which are no longer necessary. --- examples/selector-button-blink | 2 +- quick2wire/gpio.py | 21 ++++----------------- quick2wire/test_gpio_loopback.py | 6 +----- 3 files changed, 6 insertions(+), 23 deletions(-) diff --git a/examples/selector-button-blink b/examples/selector-button-blink index 9b0957d..232938e 100755 --- a/examples/selector-button-blink +++ b/examples/selector-button-blink @@ -3,7 +3,7 @@ import sys import os from contextlib import closing -from quick2wire.gpio import GPIOPin, Both, In, Out, exported +from quick2wire.gpio import GPIOPin, Both, In, Out from quick2wire.selector import Selector, Timer blink_rate=2.0 diff --git a/quick2wire/gpio.py b/quick2wire/gpio.py index 210c6ba..dd22440 100644 --- a/quick2wire/gpio.py +++ b/quick2wire/gpio.py @@ -87,11 +87,6 @@ class _IOPin(object): def __init__(self, user_pin_number, soc_pin_number, direction=In, interrupt=None, pull=None): """Creates a pin - If the direction is specified, the pin is exported if - necessary and its direction is set. If the direction is not - specified, the pin is not exported and you must call export() - before you start using it. - 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. @@ -137,8 +132,7 @@ def __exit__(self, *exc): @property def value(self): - """The current value of the pin: 1 if the pin is high or 0 if - the pin is low. + """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. @@ -255,19 +249,12 @@ def __init__(self, gpio_pin_number, *args, **kwargs): @contextmanager def exported(pin): - """A context manager that automatically exports a pin if necessary - and unexports it at the end of the block. + """Obsolete. Pins are their own context managers. Example:: - with exported(Pin(15)) as pin: + with GPIOPin(0) as pin: print(pin.value) """ - - if not pin.is_exported: - pin.export() - try: - yield pin - finally: - pin.unexport() + return pin diff --git a/quick2wire/test_gpio_loopback.py b/quick2wire/test_gpio_loopback.py index 13e8409..b6f9863 100644 --- a/quick2wire/test_gpio_loopback.py +++ b/quick2wire/test_gpio_loopback.py @@ -1,5 +1,5 @@ -from quick2wire.gpio import HeaderPin, GPIOPin, In, Out, exported +from quick2wire.gpio import HeaderPin, GPIOPin, In, Out from time import sleep import pytest @@ -37,7 +37,3 @@ def assert_output_seen_at_input(pin_type, op, ip): for value in [1, 0, 1, 0]: output_pin.value = value assert input_pin.value == value - print(op, "->", ip,": ok!") - - - From 497b1bc94d0db27d23487626cb15219664a21a6f Mon Sep 17 00:00:00 2001 From: Nat Pryce Date: Sat, 26 Jan 2013 16:04:54 +0000 Subject: [PATCH 03/10] Updated getting-started doc to reflect new API --- doc/getting-started-with-gpio.md | 44 ++++++++++++-------------------- 1 file changed, 16 insertions(+), 28 deletions(-) diff --git a/doc/getting-started-with-gpio.md b/doc/getting-started-with-gpio.md index 9c200ec..555cfb1 100644 --- a/doc/getting-started-with-gpio.md +++ b/doc/getting-started-with-gpio.md @@ -35,50 +35,38 @@ output. in_pin = Pin(0, direction=In) out_pin = Pin(1, direction=Out) -When you have a Pin instance you can read or write its value. A value -of 1 is high, a value of 0 is low. - - out_pin.value = 1 - print(in_pin.value) - -When you have finished using the pin, you must unexport it: +You must open a pin before you can read or write its value and close +the pin when you no longer need it. The most convenient way to do +this is to use Python's `with` statement, which will open the pins at +the start of the statement and close them when the body of the +statement has finished running, even if the user kills the program or +a bad piece of code throws an exception. + + with in_pin, out_pin: + out_pin.value = 1 + print(in_pin.value) - out_pin.unexport() - in_pin.unexport() +A pin has a value of 1 when high, a value of 0 when low. Putting it all together into a single program: - from quick2wire.gpio import Pin + from quick2wire.gpio import Pin, In, Out in_pin = Pin(0, direction=In) out_pin = Pin(1, direction=Out) - out_pin.value = 1 - print(in_pin.value) - - out_pin.unexport() - in_pin.unexport() - -To make sure you always unexport any pins you've exported, you can wrap the Pin objects -with `exported()`, a Python [context manager](http://docs.python.org/reference/datamodel.html#context-managers), -as part of a [with](http://docs.python.org/reference/compound_stmts.html#with) statement: - - from quick2wire.gpio import Pin, exported - - with exported(GPIOPin(0, direction=In)) as in_pin, exported(GPIOPin(1, direction=Out)) as out_pin: + with in_pin, out_pin: out_pin.value = 1 - print(in_pin.value) + print(in_pin.value) -This will unexport the pins when the program leaves the `with` statement, even -if the user kills the program or a bad piece of code throws an exception. Here's a slightly more complicated example that blinks an LED attached to pin 1. This will loop forever until the user stops it with a Control-C. from time import sleep - from quick2wire.gpio import GPIOPin, Out, exported + from quick2wire.gpio import GPIOPin, Out - with exported(GPIOPin(1, direction=Out)) as pin: + with GPIOPin(1, direction=Out) as pin: while True: pin.value = 1 - pin.value sleep(1) From f7b005d637fa20fa256ae9f94f62ef34d7bef843 Mon Sep 17 00:00:00 2001 From: Nat Pryce Date: Sat, 26 Jan 2013 22:40:16 +0000 Subject: [PATCH 04/10] Renamed _IOPin to Pin. Pass function to __init__ that maps index to SoC pin number. This is fist step to introducing pin banks for on-board GPIO --- quick2wire/gpio.py | 77 ++++++++++++++++++++-------------------------- 1 file changed, 33 insertions(+), 44 deletions(-) diff --git a/quick2wire/gpio.py b/quick2wire/gpio.py index dd22440..174d50b 100644 --- a/quick2wire/gpio.py +++ b/quick2wire/gpio.py @@ -68,23 +68,23 @@ def gpio_admin(subcommand, pin, pull=None): - -class _IOPin(object): - """Controls a GPIO pin.""" +Out = "out" +In = "in" - Out = "out" - In = "in" +Rising = "rising" +Falling = "falling" +Both = "both" - Rising = "rising" - Falling = "falling" - Both = "both" +PullDown = "pulldown" +PullUp = "pullup" - PullDown = "pulldown" - PullUp = "pullup" + +class Pin(object): + """Controls a GPIO pin.""" __trigger__ = EDGE - def __init__(self, user_pin_number, soc_pin_number, direction=In, interrupt=None, pull=None): + def __init__(self, index_to_soc_pin_number, index, direction=In, interrupt=None, pull=None): """Creates a pin Parameters: @@ -97,14 +97,22 @@ def __init__(self, user_pin_number, soc_pin_number, direction=In, interrupt=None Raises: IOError -- could not export the pin (if direction is given) """ - self.index = user_pin_number - self.soc_pin_number = soc_pin_number + self._index_to_soc_pin_number = index_to_soc_pin_number + self._index = index self._file = None self._direction = direction self._interrupt = interrupt self._pull = pull + @property + def index(self): + return self._index + + @property + def soc_pin_number(self): + return self._index_to_soc_pin_number(self._index) + def open(self): gpio_admin("export", self.soc_pin_number, self._pull) self._file = open(self._pin_path("value"), "r+") @@ -218,43 +226,24 @@ def __repr__(self): return self.__module__ + "." + str(self) def __str__(self): - return "%s(%i)"%(self.__class__.__name__, self.index) + return "%s(%s,%i)"%(self.__class__.__name__, self._index_to_soc_pin_number.__name__, self.index) +def pi_broadcom_soc(index): + return index -class HeaderPin(_IOPin): - def __init__(self, header_pin_number, *args, **kwargs): - return super(HeaderPin, self).__init__(header_pin_number, header_to_soc(header_pin_number), *args, **kwargs) - -class GPIOPin(_IOPin): - def __init__(self, gpio_pin_number, *args, **kwargs): - return super(GPIOPin, self).__init__(gpio_pin_number, gpio_to_soc(gpio_pin_number), *args, **kwargs) +def pi_header(index): + return header_to_soc(index) - - -Out = _IOPin.Out -In = _IOPin.In - -Rising = _IOPin.Rising -Falling = _IOPin.Falling -Both = _IOPin.Both - -PullDown = _IOPin.PullDown -PullUp = _IOPin.PullUp +def gpio_breakout(index): + return gpio_to_soc(index) # Backwards compatability -Pin = HeaderPin -@contextmanager -def exported(pin): - """Obsolete. Pins are their own context managers. - - Example:: - - with GPIOPin(0) as pin: - print(pin.value) - - """ - return pin +def HeaderPin(header_pin_number, *args, **kwargs): + return Pin(pi_header, header_pin_number, *args, **kwargs) + +def GPIOPin(gpio_pin_number, *args, **kwargs): + return Pin(gpio_breakout, gpio_pin_number, *args, **kwargs) From c0b02653c5e3cca9d1be50afc3725094052f499e Mon Sep 17 00:00:00 2001 From: Nat Pryce Date: Sun, 27 Jan 2013 23:16:59 +0000 Subject: [PATCH 05/10] PinBank abstraction for on-board GPIO. Different pin-banks are exported from the quick2wire.gpio module: gpio - GPIO pins numbered 0..8 pi_header_1 - the pins on the Pi's header 1, except power and those labeled do-not-use, numbered 1..26 pi_broadcom_soc - pins identified by Broadcom SoC pin number --- examples/analogue-bar | 6 +- examples/blink | 4 +- examples/button-blink | 6 +- examples/counter | 4 +- examples/gpio-speed | 6 +- examples/pullup | 8 +- examples/selector-button-blink | 6 +- quick2wire/board_revision.py | 1 + quick2wire/gpio.py | 206 ++++++++++++++++++------------- quick2wire/parts/mcp23x17.py | 17 +-- quick2wire/test_gpio.py | 26 ++-- quick2wire/test_gpio_loopback.py | 20 +-- 12 files changed, 173 insertions(+), 137 deletions(-) diff --git a/examples/analogue-bar b/examples/analogue-bar index 4ead8da..3c32d96 100755 --- a/examples/analogue-bar +++ b/examples/analogue-bar @@ -1,19 +1,19 @@ #!/usr/bin/python3 from contextlib import closing -from quick2wire.gpio import Pin +from quick2wire.gpio import gpio from quick2wire.helpers.display import AnalogueDisplay import quick2wire.i2c as i2c from quick2wire.parts.pcf8591 import PCF8591 with closing(i2c.I2CMaster()) as master: pcf = PCF8591(master) - pins = [Pin(header_pin_number, Pin.Out) for header_pin_number in [11, 12, 13, 15, 16, 18, 22, 7]] + pins = [gpio.pin(i, Out) for i in range(8)] display = AnalogueDisplay(64, *pins) pin = pcf.begin_analogue_read(0) try: while(True): display.display(pin.read()) - except KeyboardInterrupt: + except KeyboardInterrupt: display.display(63) diff --git a/examples/blink b/examples/blink index 339c25a..4d5a969 100755 --- a/examples/blink +++ b/examples/blink @@ -2,9 +2,9 @@ import sys from time import sleep -from quick2wire.gpio import GPIOPin, Out +from quick2wire.gpio import gpio, Out -pin = GPIOPin(int(sys.argv[1]) if len(sys.argv) > 1 else 1, direction=Out) +pin = gpio.pin(int(sys.argv[1]) if len(sys.argv) > 1 else 1, direction=Out) with pin: pin.value = 1 while True: diff --git a/examples/button-blink b/examples/button-blink index 7f6768e..ab652cb 100755 --- a/examples/button-blink +++ b/examples/button-blink @@ -2,10 +2,10 @@ import sys from time import sleep -from quick2wire.gpio import GPIOPin, In, Out +from quick2wire.gpio import gpio, In, Out -led = GPIOPin(1, direction=Out) -button = GPIOPin(0, direction=In) +led = gpio.pin(1, direction=Out) +button = gpio.pin(0, direction=In) with led, button: while True: diff --git a/examples/counter b/examples/counter index d432f4a..63a1ec4 100755 --- a/examples/counter +++ b/examples/counter @@ -2,9 +2,9 @@ from itertools import cycle from time import sleep -from quick2wire.gpio import GPIOPin, Out +from quick2wire.gpio import gpio, Out -pins = [GPIOPin(n, Out) for n in range(8)] +pins = [gpio.pin(i, Out) for i in range(8)] try: for p in pins: diff --git a/examples/gpio-speed b/examples/gpio-speed index 3e410a7..63afb44 100755 --- a/examples/gpio-speed +++ b/examples/gpio-speed @@ -1,7 +1,7 @@ #!/usr/bin/env python3 from datetime import datetime -from quick2wire.gpio import Pin +from quick2wire.gpio import gpio, In, Out from timeit import Timer @@ -17,8 +17,8 @@ def onepass_toggle(): iterations = 10000 -outpin = Pin(12, Pin.Out) -inpin = Pin(11, Pin.In) +outpin = gpio.pin(0, Out) +inpin = gpio.pin(1, In) with inpin, outpin: overhead = Timer(nothin).timeit(iterations) diff --git a/examples/pullup b/examples/pullup index f786ac1..87c1755 100755 --- a/examples/pullup +++ b/examples/pullup @@ -5,14 +5,14 @@ # the Pi's GPIO pins. # -from quick2wire.gpio import GPIOPin, In, PullUp, PullDown +from quick2wire.gpio import gpio, In, PullUp, PullDown -with GPIOPin(0, In) as pin: +with gpio.pin(0, In) as pin: print("Pull up/down is unspecified, pin is now", pin.value) -with GPIOPin(0, In, pull=PullUp) as pin: +with gpio.pin(0, In, pull=PullUp) as pin: print("Pin is set with pull up, pin in is now", pin.value) -with GPIOPin(0, In, pull=PullDown) as pin: +with gpio.pin(0, In, pull=PullDown) as pin: print("Pin is set with pull down, pin is now", pin.value) diff --git a/examples/selector-button-blink b/examples/selector-button-blink index 232938e..ec4cdf9 100755 --- a/examples/selector-button-blink +++ b/examples/selector-button-blink @@ -3,13 +3,13 @@ import sys import os from contextlib import closing -from quick2wire.gpio import GPIOPin, Both, In, Out +from quick2wire.gpio import gpio, Both, In, Out from quick2wire.selector import Selector, Timer blink_rate=2.0 -with GPIOPin(0, direction=In, interrupt=Both) as button_pin, \ - GPIOPin(1, direction=Out) as led_pin, \ +with gpio.pin(0, direction=In, interrupt=Both) as button_pin, \ + gpio.pin(1, direction=Out) as led_pin, \ Timer(interval=1/blink_rate) as timer, \ Selector() as selector: diff --git a/quick2wire/board_revision.py b/quick2wire/board_revision.py index 2319ea6..ea5c623 100644 --- a/quick2wire/board_revision.py +++ b/quick2wire/board_revision.py @@ -8,3 +8,4 @@ def revision(): return 0 except: return 0 + diff --git a/quick2wire/gpio.py b/quick2wire/gpio.py index 174d50b..dbd7fe2 100644 --- a/quick2wire/gpio.py +++ b/quick2wire/gpio.py @@ -8,57 +8,6 @@ from quick2wire.board_revision import revision from quick2wire.selector import EDGE -# 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 -# - the Pi documentation identifies some header pins as GPIO0, -# GPIO1, etc., but these are not the same as the SoC GPIO -# numbers. -# -# Todo - different factory functions for creating Pins by SoC id, -# header id and Pi GPIO id. - -RaspberryPi_HeaderToSOC = { - 3: 0, - 5: 1, - 7: 4, - 8: 14, - 10: 15, - 11: 17, - 12: 18, - 13: 21, - 15: 22, - 16: 23, - 18: 24, - 19: 10, - 21: 9, - 22: 25, - 23: 11, - 24: 8, - 26: 7 -} - -if revision() > 1: - RaspberryPi_HeaderToSOC[3] = 2 - RaspberryPi_HeaderToSOC[5] = 3 - RaspberryPi_HeaderToSOC[13] = 27 - - -RaspberryPi_GPIOToHeader = [11, 12, 13, 15, 16, 18, 22, 7] - -def gpio_to_soc(gpio_pin_number): - if 0 <= gpio_pin_number < 8: - return header_to_soc(RaspberryPi_GPIOToHeader[gpio_pin_number]) - else: - raise ValueError(str(gpio_pin_number)+" is not a valid GPIO pin") - - -def header_to_soc(header_pin_number): - if header_pin_number in RaspberryPi_HeaderToSOC: - return RaspberryPi_HeaderToSOC[header_pin_number] - else: - raise ValueError(str(header_pin_number)+" is not a valid IO header pin") def gpio_admin(subcommand, pin, pull=None): if pull: @@ -67,7 +16,6 @@ def gpio_admin(subcommand, pin, pull=None): subprocess.check_call(["gpio-admin", subcommand, str(pin)]) - Out = "out" In = "in" @@ -77,14 +25,54 @@ def gpio_admin(subcommand, pin, pull=None): PullDown = "pulldown" PullUp = "pullup" + + + +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), + 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 + -class Pin(object): + +class Pin(PinAPI): """Controls a GPIO pin.""" __trigger__ = EDGE - def __init__(self, index_to_soc_pin_number, index, direction=In, interrupt=None, pull=None): + def __init__(self, bank, index, soc_pin_number, direction=In, interrupt=None, pull=None): """Creates a pin Parameters: @@ -97,21 +85,17 @@ def __init__(self, index_to_soc_pin_number, index, direction=In, interrupt=None, Raises: IOError -- could not export the pin (if direction is given) """ - self._index_to_soc_pin_number = index_to_soc_pin_number - self._index = index + super(Pin,self).__init__(None, index) + self._soc_pin_number = soc_pin_number self._file = None self._direction = direction self._interrupt = interrupt self._pull = pull - @property - def index(self): - return self._index - @property def soc_pin_number(self): - return self._index_to_soc_pin_number(self._index) + return self._soc_pin_number def open(self): gpio_admin("export", self.soc_pin_number, self._pull) @@ -130,16 +114,7 @@ def close(self): self._write("edge", "none") gpio_admin("unexport", self.soc_pin_number) - def __enter__(self): - self.open() - return self - - def __exit__(self, *exc): - self.close() - return False - - @property - def value(self): + 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. @@ -152,8 +127,7 @@ def value(self): v = self._file.read() return int(v) if v else 0 - @value.setter - def value(self, new_value): + def set(self, new_value): self._check_open() if self._direction != Out: raise ValueError("not an output pin") @@ -161,7 +135,6 @@ def value(self, new_value): self._file.write(str(int(new_value))) self._file.flush() - @property def direction(self): """The direction of the pin: either In or Out. @@ -226,24 +199,85 @@ def __repr__(self): return self.__module__ + "." + str(self) def __str__(self): - return "%s(%s,%i)"%(self.__class__.__name__, self._index_to_soc_pin_number.__name__, self.index) + return "{type}({index})".format( + type=self.__class__.__name__, + index=self.index) + + + + + +class PinBank(PinBankAPI): + 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 + else: + raise TypeError(self.__class__.__name__ + " has no len") + + +_pi_revision = revision() -def pi_broadcom_soc(index): - return index +def by_revision(d): + return d[_pi_revision] -def pi_header(index): - return header_to_soc(index) -def gpio_breakout(index): - return gpio_to_soc(index) +# 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 +# - 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, + 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) -# Backwards compatability -def HeaderPin(header_pin_number, *args, **kwargs): - return Pin(pi_header, header_pin_number, *args, **kwargs) +pi_broadcom_soc = PinBank(lambda p: p) +pi_header_1 = PinBank(map_with(_pi_header_1_pins)) +gpio = PinBank(map_with(_pi_gpio_pins), len(_pi_gpio_pins)) -def GPIOPin(gpio_pin_number, *args, **kwargs): - return Pin(gpio_breakout, gpio_pin_number, *args, **kwargs) diff --git a/quick2wire/parts/mcp23x17.py b/quick2wire/parts/mcp23x17.py index 9ffd96e..fe55e53 100644 --- a/quick2wire/parts/mcp23x17.py +++ b/quick2wire/parts/mcp23x17.py @@ -9,6 +9,7 @@ import contextlib from warnings import warn +from quick2wire.gpio import PinAPI, PinBankAPI # TODO - import from GPIO or common definitions module In = "in" @@ -208,7 +209,7 @@ def immediate_write(f): f() -class PinBank(object): +class PinBank(PinBankAPI): """A bank of 8 GPIO pins""" def __init__(self, chip, bank_id): @@ -320,13 +321,12 @@ def _write(self, value): return property(_read, _write, doc=doc) -class Pin(object): +class Pin(PinAPI): """A digital Pin that can be used for input or output.""" def __init__(self, bank, index): """Called by the PinBank. Not used by application code.""" - self.bank = bank - self.index = index + super(Pin,self).__init__(bank,index) self._is_claimed = False def open(self): @@ -341,13 +341,6 @@ def open(self): def close(self): self._is_claimed = False - def __enter__(self): - self.open() - return self - - def __exit__(self, exc_type, exc_value, traceback): - self.close() - def get(self): """Returns the value of the pin. @@ -361,8 +354,6 @@ def set(self, new_value): The same as pin.value, but a method so that it can easily be passed around as a function. """ self._set_register_bit(OLAT, new_value) - - value = property(get, set, doc="""The value of the pin: 1 if the pin is high, 0 if the pin is low.""") direction = _register_bit(IODIR, high_value=In, low_value=Out, doc="""The direction of the pin: In if the pin is used for input, Out if it is used for output.""") diff --git a/quick2wire/test_gpio.py b/quick2wire/test_gpio.py index 5365a4f..f645430 100644 --- a/quick2wire/test_gpio.py +++ b/quick2wire/test_gpio.py @@ -1,14 +1,14 @@ import os -from quick2wire.gpio import GPIOPin, In, Out, PullDown, gpio_admin +from quick2wire.gpio import gpio, In, Out, PullDown, gpio_admin import pytest @pytest.mark.gpio @pytest.mark.loopback -class TestGPIOPin: +class TestGPIO: def test_pin_must_be_opened_before_use_and_is_unusable_after_being_closed(self): - pin = GPIOPin(0) + pin = gpio.pin(0) with pytest.raises(IOError): pin.value @@ -24,7 +24,7 @@ def test_pin_must_be_opened_before_use_and_is_unusable_after_being_closed(self): def test_opens_and_closes_itself_when_used_as_a_context_manager(self): - pin = GPIOPin(0) + pin = gpio.pin(0) with pin: pin.value @@ -34,14 +34,14 @@ def test_opens_and_closes_itself_when_used_as_a_context_manager(self): def test_exports_gpio_device_to_userspace_when_opened_and_unexports_when_closed(self): - with GPIOPin(0) as pin: + with gpio.pin(0) as pin: assert os.path.exists('/sys/class/gpio/gpio17/value') assert not os.path.exists('/sys/class/gpio/gpio17/value') def test_can_set_and_query_direction_of_pin_when_open(self): - with GPIOPin(0) as pin: + with gpio.pin(0) as pin: pin.direction = Out assert pin.direction == Out @@ -54,7 +54,7 @@ def test_can_set_and_query_direction_of_pin_when_open(self): def test_can_set_direction_on_construction(self): - pin = GPIOPin(0, Out) + pin = gpio.pin(0, Out) assert pin.direction == Out assert not os.path.exists("/sys/class/gpio/gpio17/direction") @@ -65,7 +65,7 @@ def test_can_set_direction_on_construction(self): def test_setting_value_of_output_pin_writes_to_device_file(self): - with GPIOPin(0) as pin: + with gpio.pin(0) as pin: pin.direction = Out pin.value = 1 @@ -78,7 +78,7 @@ def test_setting_value_of_output_pin_writes_to_device_file(self): def test_direction_and_value_of_pin_is_reset_when_closed(self): - with GPIOPin(0, Out) as pin: + with gpio.pin(0, Out) as pin: pin.value = 1 gpio_admin("export", 17, PullDown) @@ -88,8 +88,14 @@ def test_direction_and_value_of_pin_is_reset_when_closed(self): finally: gpio_admin("unexport", 17) - + def test_cannot_get_a_pin_with_an_invalid_index(self): + with pytest.raises(IndexError): + gpio.pin(-1) + + with pytest.raises(IndexError): + gpio.pin(len(gpio)) + def content_of(filename): with open(filename, 'r') as f: return f.read() diff --git a/quick2wire/test_gpio_loopback.py b/quick2wire/test_gpio_loopback.py index b6f9863..50624e8 100644 --- a/quick2wire/test_gpio_loopback.py +++ b/quick2wire/test_gpio_loopback.py @@ -1,5 +1,5 @@ -from quick2wire.gpio import HeaderPin, GPIOPin, In, Out +from quick2wire.gpio import pi_header_1, gpio, In, Out from time import sleep import pytest @@ -12,7 +12,7 @@ def inverse(topology): @pytest.mark.gpio def test_gpio_loopback(): assert_outputs_seen_at_corresponding_inputs( - GPIOPin, + gpio, [(i,i+4) for i in range(4)]) @@ -20,20 +20,24 @@ def test_gpio_loopback(): @pytest.mark.gpio def test_gpio_loopback_by_header_pin(): assert_outputs_seen_at_corresponding_inputs( - HeaderPin, + pi_header_1, [(11,16), (12,18), (13,22), (15,7)]) -def assert_outputs_seen_at_corresponding_inputs(pin_type, topology): + +def assert_outputs_seen_at_corresponding_inputs(pin_bank, topology): for (op, ip) in topology: - assert_output_seen_at_input(pin_type, op, ip) + assert_output_seen_at_input(pin_bank, op, ip) for (op, ip) in inverse(topology): - assert_output_seen_at_input(pin_type, op, ip) + assert_output_seen_at_input(pin_bank, op, ip) -def assert_output_seen_at_input(pin_type, op, ip): - with pin_type(op, direction=Out) as output_pin, pin_type(ip, direction=In) as input_pin: +def assert_output_seen_at_input(pin_bank, op, ip): + output_pin = pin_bank.pin(op, direction=Out) + input_pin = pin_bank.pin(ip, direction=In) + + with output_pin, input_pin: for value in [1, 0, 1, 0]: output_pin.value = value assert input_pin.value == value From 760255d0f473e233d11bacf9ccecf8f1dd9ab6f0 Mon Sep 17 00:00:00 2001 From: Nat Pryce Date: Sun, 27 Jan 2013 23:34:08 +0000 Subject: [PATCH 06/10] updating the getting started with GPIO doc --- doc/getting-started-with-gpio.md | 36 +++++++++++++++++++------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/doc/getting-started-with-gpio.md b/doc/getting-started-with-gpio.md index 555cfb1..c9850f3 100644 --- a/doc/getting-started-with-gpio.md +++ b/doc/getting-started-with-gpio.md @@ -22,25 +22,31 @@ group membership. Now Let's Write Some Code! -------------------------- -The GPIO pins are controlled by GPIOPin objects. Python program must -import the GPIOPin class from the quick2wire.gpio module, along with -constants to configure the pin: +The GPIO pins are controlled by Pin objects, and those Pin objects are +managed by a "pin bank". The simplest pin bank to use is called +`gpio` and gives access to the pins named GPIO0 to GPIO8 on the +Raspberry Pi's header number 1. (There's also a bank called +pi_header_1 that gives access to all the header pins, but we don't +need that for this example.) - from quick2wire.gpio import GPIOPin, In, Out +Python program must import the `gpio` pin bank from the +`quick2wire.gpio` module, along with constants to configure the pin: -Then you can create a Pin. The Pin's constructor takes two arguments: -the header pin number and whether the pin is to be used for input or -output. + from quick2wire.gpio import gpio, In, Out - in_pin = Pin(0, direction=In) - out_pin = Pin(1, direction=Out) +Then you can get a Pin by calling the pin bank's `pin` method. This +takes two arguments: the pin number and whether the pin is to be used +for input or output. + + in_pin = gpio.pin(0, direction=In) + out_pin = gpio.pin(1, direction=Out) You must open a pin before you can read or write its value and close the pin when you no longer need it. The most convenient way to do this is to use Python's `with` statement, which will open the pins at the start of the statement and close them when the body of the statement has finished running, even if the user kills the program or -a bad piece of code throws an exception. +failure makes the code throw an exception. with in_pin, out_pin: out_pin.value = 1 @@ -50,10 +56,10 @@ A pin has a value of 1 when high, a value of 0 when low. Putting it all together into a single program: - from quick2wire.gpio import Pin, In, Out + from quick2wire.gpio import gpio, In, Out - in_pin = Pin(0, direction=In) - out_pin = Pin(1, direction=Out) + in_pin = gpio.pin(0, direction=In) + out_pin = gpio.pin(1, direction=Out) with in_pin, out_pin: out_pin.value = 1 @@ -64,9 +70,9 @@ Here's a slightly more complicated example that blinks an LED attached to pin 1. loop forever until the user stops it with a Control-C. from time import sleep - from quick2wire.gpio import GPIOPin, Out + from quick2wire.gpio import gpio, Out - with GPIOPin(1, direction=Out) as pin: + with gpio.pin(1, direction=Out) as pin: while True: pin.value = 1 - pin.value sleep(1) From 1f249464ea768cba4fd4bf18b42968c313963287 Mon Sep 17 00:00:00 2001 From: Nat Pryce Date: Wed, 30 Jan 2013 23:22:31 +0000 Subject: [PATCH 07/10] renamed quick2wire.gpio.gpio to gpio_breakout --- examples/blink | 4 ++-- examples/button-blink | 6 +++--- examples/gpio-speed | 6 +++--- examples/selector-button-blink | 6 +++--- quick2wire/gpio.py | 2 +- quick2wire/test_gpio.py | 20 ++++++++++---------- quick2wire/test_gpio_loopback.py | 4 ++-- 7 files changed, 24 insertions(+), 24 deletions(-) diff --git a/examples/blink b/examples/blink index 4d5a969..b3b07bb 100755 --- a/examples/blink +++ b/examples/blink @@ -2,9 +2,9 @@ import sys from time import sleep -from quick2wire.gpio import gpio, Out +from quick2wire.gpio import gpio_breakout, Out -pin = gpio.pin(int(sys.argv[1]) if len(sys.argv) > 1 else 1, direction=Out) +pin = gpio_breakout.pin(int(sys.argv[1]) if len(sys.argv) > 1 else 1, direction=Out) with pin: pin.value = 1 while True: diff --git a/examples/button-blink b/examples/button-blink index ab652cb..3b3bbf3 100755 --- a/examples/button-blink +++ b/examples/button-blink @@ -2,10 +2,10 @@ import sys from time import sleep -from quick2wire.gpio import gpio, In, Out +from quick2wire.gpio import gpio_breakout, In, Out -led = gpio.pin(1, direction=Out) -button = gpio.pin(0, direction=In) +led = gpio_breakout.pin(1, direction=Out) +button = gpio_breakout.pin(0, direction=In) with led, button: while True: diff --git a/examples/gpio-speed b/examples/gpio-speed index 63afb44..46f0c1c 100755 --- a/examples/gpio-speed +++ b/examples/gpio-speed @@ -1,7 +1,7 @@ #!/usr/bin/env python3 from datetime import datetime -from quick2wire.gpio import gpio, In, Out +from quick2wire.gpio import gpio_breakout, In, Out from timeit import Timer @@ -17,8 +17,8 @@ def onepass_toggle(): iterations = 10000 -outpin = gpio.pin(0, Out) -inpin = gpio.pin(1, In) +outpin = gpio_breakout.pin(0, Out) +inpin = gpio_breakout.pin(1, In) with inpin, outpin: overhead = Timer(nothin).timeit(iterations) diff --git a/examples/selector-button-blink b/examples/selector-button-blink index ec4cdf9..e9ffba3 100755 --- a/examples/selector-button-blink +++ b/examples/selector-button-blink @@ -3,13 +3,13 @@ import sys import os from contextlib import closing -from quick2wire.gpio import gpio, Both, In, Out +from quick2wire.gpio import gpio_breakout, Both, In, Out from quick2wire.selector import Selector, Timer blink_rate=2.0 -with gpio.pin(0, direction=In, interrupt=Both) as button_pin, \ - gpio.pin(1, direction=Out) as led_pin, \ +with gpio_breakout.pin(0, direction=In, interrupt=Both) as button_pin, \ + gpio_breakout.pin(1, direction=Out) as led_pin, \ Timer(interval=1/blink_rate) as timer, \ Selector() as selector: diff --git a/quick2wire/gpio.py b/quick2wire/gpio.py index dbd7fe2..671214d 100644 --- a/quick2wire/gpio.py +++ b/quick2wire/gpio.py @@ -279,5 +279,5 @@ def map_with(pin_mapping): pi_broadcom_soc = PinBank(lambda p: p) pi_header_1 = PinBank(map_with(_pi_header_1_pins)) -gpio = PinBank(map_with(_pi_gpio_pins), len(_pi_gpio_pins)) +gpio_breakout = PinBank(map_with(_pi_gpio_pins), len(_pi_gpio_pins)) diff --git a/quick2wire/test_gpio.py b/quick2wire/test_gpio.py index f645430..52020e5 100644 --- a/quick2wire/test_gpio.py +++ b/quick2wire/test_gpio.py @@ -1,6 +1,6 @@ import os -from quick2wire.gpio import gpio, In, Out, PullDown, gpio_admin +from quick2wire.gpio import gpio_breakout, In, Out, PullDown, gpio_admin import pytest @@ -8,7 +8,7 @@ @pytest.mark.loopback class TestGPIO: def test_pin_must_be_opened_before_use_and_is_unusable_after_being_closed(self): - pin = gpio.pin(0) + pin = gpio_breakout.pin(0) with pytest.raises(IOError): pin.value @@ -24,7 +24,7 @@ def test_pin_must_be_opened_before_use_and_is_unusable_after_being_closed(self): def test_opens_and_closes_itself_when_used_as_a_context_manager(self): - pin = gpio.pin(0) + pin = gpio_breakout.pin(0) with pin: pin.value @@ -34,14 +34,14 @@ def test_opens_and_closes_itself_when_used_as_a_context_manager(self): def test_exports_gpio_device_to_userspace_when_opened_and_unexports_when_closed(self): - with gpio.pin(0) as pin: + with gpio_breakout.pin(0) as pin: assert os.path.exists('/sys/class/gpio/gpio17/value') assert not os.path.exists('/sys/class/gpio/gpio17/value') def test_can_set_and_query_direction_of_pin_when_open(self): - with gpio.pin(0) as pin: + with gpio_breakout.pin(0) as pin: pin.direction = Out assert pin.direction == Out @@ -54,7 +54,7 @@ def test_can_set_and_query_direction_of_pin_when_open(self): def test_can_set_direction_on_construction(self): - pin = gpio.pin(0, Out) + pin = gpio_breakout.pin(0, Out) assert pin.direction == Out assert not os.path.exists("/sys/class/gpio/gpio17/direction") @@ -65,7 +65,7 @@ def test_can_set_direction_on_construction(self): def test_setting_value_of_output_pin_writes_to_device_file(self): - with gpio.pin(0) as pin: + with gpio_breakout.pin(0) as pin: pin.direction = Out pin.value = 1 @@ -78,7 +78,7 @@ def test_setting_value_of_output_pin_writes_to_device_file(self): def test_direction_and_value_of_pin_is_reset_when_closed(self): - with gpio.pin(0, Out) as pin: + with gpio_breakout.pin(0, Out) as pin: pin.value = 1 gpio_admin("export", 17, PullDown) @@ -90,10 +90,10 @@ def test_direction_and_value_of_pin_is_reset_when_closed(self): def test_cannot_get_a_pin_with_an_invalid_index(self): with pytest.raises(IndexError): - gpio.pin(-1) + gpio_breakout.pin(-1) with pytest.raises(IndexError): - gpio.pin(len(gpio)) + gpio_breakout.pin(len(gpio_breakout)) def content_of(filename): diff --git a/quick2wire/test_gpio_loopback.py b/quick2wire/test_gpio_loopback.py index 50624e8..6232a62 100644 --- a/quick2wire/test_gpio_loopback.py +++ b/quick2wire/test_gpio_loopback.py @@ -1,5 +1,5 @@ -from quick2wire.gpio import pi_header_1, gpio, In, Out +from quick2wire.gpio import pi_header_1, gpio_breakout, In, Out from time import sleep import pytest @@ -12,7 +12,7 @@ def inverse(topology): @pytest.mark.gpio def test_gpio_loopback(): assert_outputs_seen_at_corresponding_inputs( - gpio, + gpio_breakout, [(i,i+4) for i in range(4)]) From 50984adff8576608af650a10d5d398009c3eda48 Mon Sep 17 00:00:00 2001 From: Nat Pryce Date: Wed, 30 Jan 2013 23:24:41 +0000 Subject: [PATCH 08/10] updating getting started doc to reflect new name for gpio_breakout --- doc/getting-started-with-gpio.md | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/doc/getting-started-with-gpio.md b/doc/getting-started-with-gpio.md index c9850f3..25d2903 100644 --- a/doc/getting-started-with-gpio.md +++ b/doc/getting-started-with-gpio.md @@ -24,22 +24,23 @@ Now Let's Write Some Code! The GPIO pins are controlled by Pin objects, and those Pin objects are managed by a "pin bank". The simplest pin bank to use is called -`gpio` and gives access to the pins named GPIO0 to GPIO8 on the -Raspberry Pi's header number 1. (There's also a bank called -pi_header_1 that gives access to all the header pins, but we don't -need that for this example.) +`gpio_breakout` and gives access to the pins labelled P0 to P7 on the +Quick2Wire interface board (or named GPIO0 to GPIO7 on the Raspberry +Pi's header number 1). There's also a bank called pi_header_1 that +gives access to all the header pins, but we don't need that for this +example. -Python program must import the `gpio` pin bank from the +Python programs must import the `gpio` pin bank from the `quick2wire.gpio` module, along with constants to configure the pin: - from quick2wire.gpio import gpio, In, Out + from quick2wire.gpio import gpio_breakout, In, Out Then you can get a Pin by calling the pin bank's `pin` method. This takes two arguments: the pin number and whether the pin is to be used for input or output. - in_pin = gpio.pin(0, direction=In) - out_pin = gpio.pin(1, direction=Out) + in_pin = gpio_breakout.pin(0, direction=In) + out_pin = gpio_breakout.pin(1, direction=Out) You must open a pin before you can read or write its value and close the pin when you no longer need it. The most convenient way to do @@ -56,10 +57,10 @@ A pin has a value of 1 when high, a value of 0 when low. Putting it all together into a single program: - from quick2wire.gpio import gpio, In, Out + from quick2wire.gpio import gpio_breakout, In, Out - in_pin = gpio.pin(0, direction=In) - out_pin = gpio.pin(1, direction=Out) + in_pin = gpio_breakout.pin(0, direction=In) + out_pin = gpio_breakout.pin(1, direction=Out) with in_pin, out_pin: out_pin.value = 1 @@ -70,9 +71,9 @@ Here's a slightly more complicated example that blinks an LED attached to pin 1. loop forever until the user stops it with a Control-C. from time import sleep - from quick2wire.gpio import gpio, Out + from quick2wire.gpio import gpio_breakout, Out - with gpio.pin(1, direction=Out) as pin: + with gpio_breakout.pin(1, direction=Out) as pin: while True: pin.value = 1 - pin.value sleep(1) From 337845885c88d7f9e70fe8cc9a6dda87017edcaf Mon Sep 17 00:00:00 2001 From: Nat Pryce Date: Sat, 2 Feb 2013 22:40:52 +0000 Subject: [PATCH 09/10] Renamed gpio_breakout -> pins --- examples/analogue-bar | 19 ------------------- examples/blink | 4 ++-- examples/button-blink | 6 +++--- examples/gpio-speed | 6 +++--- examples/pullup | 8 ++++---- examples/selector-button-blink | 6 +++--- quick2wire/gpio.py | 2 +- quick2wire/test_gpio.py | 20 ++++++++++---------- quick2wire/test_gpio_loopback.py | 4 ++-- 9 files changed, 28 insertions(+), 47 deletions(-) delete mode 100755 examples/analogue-bar diff --git a/examples/analogue-bar b/examples/analogue-bar deleted file mode 100755 index 3c32d96..0000000 --- a/examples/analogue-bar +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/python3 - -from contextlib import closing -from quick2wire.gpio import gpio -from quick2wire.helpers.display import AnalogueDisplay -import quick2wire.i2c as i2c -from quick2wire.parts.pcf8591 import PCF8591 - -with closing(i2c.I2CMaster()) as master: - pcf = PCF8591(master) - pins = [gpio.pin(i, Out) for i in range(8)] - display = AnalogueDisplay(64, *pins) - pin = pcf.begin_analogue_read(0) - try: - while(True): - display.display(pin.read()) - except KeyboardInterrupt: - display.display(63) - diff --git a/examples/blink b/examples/blink index b3b07bb..ba3b8e5 100755 --- a/examples/blink +++ b/examples/blink @@ -2,9 +2,9 @@ import sys from time import sleep -from quick2wire.gpio import gpio_breakout, Out +from quick2wire.gpio import pins, Out -pin = gpio_breakout.pin(int(sys.argv[1]) if len(sys.argv) > 1 else 1, direction=Out) +pin = pins.pin(int(sys.argv[1]) if len(sys.argv) > 1 else 1, direction=Out) with pin: pin.value = 1 while True: diff --git a/examples/button-blink b/examples/button-blink index 3b3bbf3..22fcdd2 100755 --- a/examples/button-blink +++ b/examples/button-blink @@ -2,10 +2,10 @@ import sys from time import sleep -from quick2wire.gpio import gpio_breakout, In, Out +from quick2wire.gpio import pins, In, Out -led = gpio_breakout.pin(1, direction=Out) -button = gpio_breakout.pin(0, direction=In) +led = pins.pin(1, direction=Out) +button = pins.pin(0, direction=In) with led, button: while True: diff --git a/examples/gpio-speed b/examples/gpio-speed index 46f0c1c..bb7c06b 100755 --- a/examples/gpio-speed +++ b/examples/gpio-speed @@ -1,7 +1,7 @@ #!/usr/bin/env python3 from datetime import datetime -from quick2wire.gpio import gpio_breakout, In, Out +from quick2wire.gpio import pins, In, Out from timeit import Timer @@ -17,8 +17,8 @@ def onepass_toggle(): iterations = 10000 -outpin = gpio_breakout.pin(0, Out) -inpin = gpio_breakout.pin(1, In) +outpin = pins.pin(0, Out) +inpin = pins.pin(1, In) with inpin, outpin: overhead = Timer(nothin).timeit(iterations) diff --git a/examples/pullup b/examples/pullup index 87c1755..3822076 100755 --- a/examples/pullup +++ b/examples/pullup @@ -5,14 +5,14 @@ # the Pi's GPIO pins. # -from quick2wire.gpio import gpio, In, PullUp, PullDown +from quick2wire.gpio import pins, In, PullUp, PullDown -with gpio.pin(0, In) as pin: +with pins.pin(0, In) as pin: print("Pull up/down is unspecified, pin is now", pin.value) -with gpio.pin(0, In, pull=PullUp) as pin: +with pins.pin(0, In, pull=PullUp) as pin: print("Pin is set with pull up, pin in is now", pin.value) -with gpio.pin(0, In, pull=PullDown) as pin: +with pins.pin(0, In, pull=PullDown) as pin: print("Pin is set with pull down, pin is now", pin.value) diff --git a/examples/selector-button-blink b/examples/selector-button-blink index e9ffba3..4fe184c 100755 --- a/examples/selector-button-blink +++ b/examples/selector-button-blink @@ -3,13 +3,13 @@ import sys import os from contextlib import closing -from quick2wire.gpio import gpio_breakout, Both, In, Out +from quick2wire.gpio import pins, Both, In, Out from quick2wire.selector import Selector, Timer blink_rate=2.0 -with gpio_breakout.pin(0, direction=In, interrupt=Both) as button_pin, \ - gpio_breakout.pin(1, direction=Out) as led_pin, \ +with pins.pin(0, direction=In, interrupt=Both) as button_pin, \ + pins.pin(1, direction=Out) as led_pin, \ Timer(interval=1/blink_rate) as timer, \ Selector() as selector: diff --git a/quick2wire/gpio.py b/quick2wire/gpio.py index 671214d..89d7aaf 100644 --- a/quick2wire/gpio.py +++ b/quick2wire/gpio.py @@ -279,5 +279,5 @@ def map_with(pin_mapping): pi_broadcom_soc = PinBank(lambda p: p) pi_header_1 = PinBank(map_with(_pi_header_1_pins)) -gpio_breakout = PinBank(map_with(_pi_gpio_pins), len(_pi_gpio_pins)) +pins = PinBank(map_with(_pi_gpio_pins), len(_pi_gpio_pins)) diff --git a/quick2wire/test_gpio.py b/quick2wire/test_gpio.py index 52020e5..7901928 100644 --- a/quick2wire/test_gpio.py +++ b/quick2wire/test_gpio.py @@ -1,6 +1,6 @@ import os -from quick2wire.gpio import gpio_breakout, In, Out, PullDown, gpio_admin +from quick2wire.gpio import pins, In, Out, PullDown, gpio_admin import pytest @@ -8,7 +8,7 @@ @pytest.mark.loopback class TestGPIO: def test_pin_must_be_opened_before_use_and_is_unusable_after_being_closed(self): - pin = gpio_breakout.pin(0) + pin = pins.pin(0) with pytest.raises(IOError): pin.value @@ -24,7 +24,7 @@ def test_pin_must_be_opened_before_use_and_is_unusable_after_being_closed(self): def test_opens_and_closes_itself_when_used_as_a_context_manager(self): - pin = gpio_breakout.pin(0) + pin = pins.pin(0) with pin: pin.value @@ -34,14 +34,14 @@ def test_opens_and_closes_itself_when_used_as_a_context_manager(self): def test_exports_gpio_device_to_userspace_when_opened_and_unexports_when_closed(self): - with gpio_breakout.pin(0) as pin: + with pins.pin(0) as pin: assert os.path.exists('/sys/class/gpio/gpio17/value') assert not os.path.exists('/sys/class/gpio/gpio17/value') def test_can_set_and_query_direction_of_pin_when_open(self): - with gpio_breakout.pin(0) as pin: + with pins.pin(0) as pin: pin.direction = Out assert pin.direction == Out @@ -54,7 +54,7 @@ def test_can_set_and_query_direction_of_pin_when_open(self): def test_can_set_direction_on_construction(self): - pin = gpio_breakout.pin(0, Out) + pin = pins.pin(0, Out) assert pin.direction == Out assert not os.path.exists("/sys/class/gpio/gpio17/direction") @@ -65,7 +65,7 @@ def test_can_set_direction_on_construction(self): def test_setting_value_of_output_pin_writes_to_device_file(self): - with gpio_breakout.pin(0) as pin: + with pins.pin(0) as pin: pin.direction = Out pin.value = 1 @@ -78,7 +78,7 @@ def test_setting_value_of_output_pin_writes_to_device_file(self): def test_direction_and_value_of_pin_is_reset_when_closed(self): - with gpio_breakout.pin(0, Out) as pin: + with pins.pin(0, Out) as pin: pin.value = 1 gpio_admin("export", 17, PullDown) @@ -90,10 +90,10 @@ def test_direction_and_value_of_pin_is_reset_when_closed(self): def test_cannot_get_a_pin_with_an_invalid_index(self): with pytest.raises(IndexError): - gpio_breakout.pin(-1) + pins.pin(-1) with pytest.raises(IndexError): - gpio_breakout.pin(len(gpio_breakout)) + pins.pin(len(pins)) def content_of(filename): diff --git a/quick2wire/test_gpio_loopback.py b/quick2wire/test_gpio_loopback.py index 6232a62..9593fe2 100644 --- a/quick2wire/test_gpio_loopback.py +++ b/quick2wire/test_gpio_loopback.py @@ -1,5 +1,5 @@ -from quick2wire.gpio import pi_header_1, gpio_breakout, In, Out +from quick2wire.gpio import pins, pi_header_1, In, Out from time import sleep import pytest @@ -12,7 +12,7 @@ def inverse(topology): @pytest.mark.gpio def test_gpio_loopback(): assert_outputs_seen_at_corresponding_inputs( - gpio_breakout, + pins, [(i,i+4) for i in range(4)]) From 812f13e2ba189d9e96ca3b074ff8cfa7951dfd4f Mon Sep 17 00:00:00 2001 From: Nat Pryce Date: Sat, 2 Feb 2013 22:48:57 +0000 Subject: [PATCH 10/10] updated docs to match rename --- doc/getting-started-with-gpio.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/doc/getting-started-with-gpio.md b/doc/getting-started-with-gpio.md index 25d2903..67e7c25 100644 --- a/doc/getting-started-with-gpio.md +++ b/doc/getting-started-with-gpio.md @@ -24,23 +24,23 @@ Now Let's Write Some Code! The GPIO pins are controlled by Pin objects, and those Pin objects are managed by a "pin bank". The simplest pin bank to use is called -`gpio_breakout` and gives access to the pins labelled P0 to P7 on the +`pins` and gives access to the pins labelled P0 to P7 on the Quick2Wire interface board (or named GPIO0 to GPIO7 on the Raspberry Pi's header number 1). There's also a bank called pi_header_1 that gives access to all the header pins, but we don't need that for this example. -Python programs must import the `gpio` pin bank from the +Python programs must import the `pins` pin bank from the `quick2wire.gpio` module, along with constants to configure the pin: - from quick2wire.gpio import gpio_breakout, In, Out + from quick2wire.gpio import pins, In, Out Then you can get a Pin by calling the pin bank's `pin` method. This takes two arguments: the pin number and whether the pin is to be used for input or output. - in_pin = gpio_breakout.pin(0, direction=In) - out_pin = gpio_breakout.pin(1, direction=Out) + in_pin = pins.pin(0, direction=In) + out_pin = pins.pin(1, direction=Out) You must open a pin before you can read or write its value and close the pin when you no longer need it. The most convenient way to do @@ -57,10 +57,10 @@ A pin has a value of 1 when high, a value of 0 when low. Putting it all together into a single program: - from quick2wire.gpio import gpio_breakout, In, Out + from quick2wire.gpio import pins, In, Out - in_pin = gpio_breakout.pin(0, direction=In) - out_pin = gpio_breakout.pin(1, direction=Out) + in_pin = pins.pin(0, direction=In) + out_pin = pins.pin(1, direction=Out) with in_pin, out_pin: out_pin.value = 1 @@ -71,9 +71,9 @@ Here's a slightly more complicated example that blinks an LED attached to pin 1. loop forever until the user stops it with a Control-C. from time import sleep - from quick2wire.gpio import gpio_breakout, Out + from quick2wire.gpio import pins, Out - with gpio_breakout.pin(1, direction=Out) as pin: + with pins.pin(1, direction=Out) as pin: while True: pin.value = 1 - pin.value sleep(1)