Initial commit.
This commit is contained in:
parent
b63b4b22df
commit
82bb79962d
9 changed files with 1145 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
*.pyc
|
||||||
160
Adafruit_GPIO/GPIO.py
Normal file
160
Adafruit_GPIO/GPIO.py
Normal file
|
|
@ -0,0 +1,160 @@
|
||||||
|
# Copyright (c) 2014 Adafruit Industries
|
||||||
|
# Author: Tony DiCola
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
|
import platform
|
||||||
|
|
||||||
|
|
||||||
|
OUT = 0
|
||||||
|
IN = 1
|
||||||
|
HIGH = True
|
||||||
|
LOW = False
|
||||||
|
|
||||||
|
|
||||||
|
class BaseGPIO(object):
|
||||||
|
"""Base class for implementing simple digital IO for a platform.
|
||||||
|
Implementors are expected to subclass from this and provide an implementation
|
||||||
|
of the setup, output, and input functions."""
|
||||||
|
|
||||||
|
def setup(self, pin, mode):
|
||||||
|
"""Set the input or output mode for a specified pin. Mode should be
|
||||||
|
either OUT or IN."""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def output(self, pin, value):
|
||||||
|
"""Set the specified pin the provided high/low value. Value should be
|
||||||
|
either HIGH/LOW or a boolean (true = high)."""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def input(self, pin):
|
||||||
|
"""Read the specified pin and return HIGH/true if the pin is pulled high,
|
||||||
|
or LOW/false if pulled low."""
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def set_high(self, pin):
|
||||||
|
"""Set the specified pin HIGH."""
|
||||||
|
self.output(pin, HIGH)
|
||||||
|
|
||||||
|
def set_low(self, pin):
|
||||||
|
"""Set the specified pin LOW."""
|
||||||
|
self.output(pin, LOW)
|
||||||
|
|
||||||
|
def is_high(self, pin):
|
||||||
|
"""Return true if the specified pin is pulled high."""
|
||||||
|
return self.input(pin) == HIGH
|
||||||
|
|
||||||
|
def is_low(self, pin):
|
||||||
|
"""Return true if the specified pin is pulled low."""
|
||||||
|
return self.input(pin) == LOW
|
||||||
|
|
||||||
|
|
||||||
|
class RPiGPIOAdapter(BaseGPIO):
|
||||||
|
"""GPIO implementation for the Raspberry Pi using the RPi.GPIO library."""
|
||||||
|
|
||||||
|
def __init__(self, rpi_gpio, mode=None):
|
||||||
|
self.rpi_gpio = rpi_gpio
|
||||||
|
# Suppress warnings about GPIO in use.
|
||||||
|
rpi_gpio.setwarnings(False)
|
||||||
|
if mode == rpi_gpio.BOARD or mode == rpi_gpio.BCM:
|
||||||
|
rpi_gpio.setmode(mode)
|
||||||
|
elif mode is not None:
|
||||||
|
raise ValueError('Unexpected value for mode. Must be BOARD or BCM.')
|
||||||
|
else:
|
||||||
|
# Default to BCM numbering if not told otherwise.
|
||||||
|
rpi_gpio.setmode(rpi_gpio.BCM)
|
||||||
|
|
||||||
|
def setup(self, pin, mode):
|
||||||
|
"""Set the input or output mode for a specified pin. Mode should be
|
||||||
|
either OUTPUT or INPUT.
|
||||||
|
"""
|
||||||
|
self.rpi_gpio.setup(pin, self.rpi_gpio.IN if mode == IN else \
|
||||||
|
self.rpi_gpio.OUT)
|
||||||
|
|
||||||
|
def output(self, pin, value):
|
||||||
|
"""Set the specified pin the provided high/low value. Value should be
|
||||||
|
either HIGH/LOW or a boolean (true = high).
|
||||||
|
"""
|
||||||
|
self.rpi_gpio.output(pin, value)
|
||||||
|
|
||||||
|
def input(self, pin):
|
||||||
|
"""Read the specified pin and return HIGH/true if the pin is pulled high,
|
||||||
|
or LOW/false if pulled low.
|
||||||
|
"""
|
||||||
|
return self.rpi_gpio.input(pin)
|
||||||
|
|
||||||
|
|
||||||
|
class AdafruitBBIOAdapter(BaseGPIO):
|
||||||
|
"""GPIO implementation for the Beaglebone Black using the Adafruit_BBIO
|
||||||
|
library.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, bbio_gpio):
|
||||||
|
self.bbio_gpio = bbio_gpio
|
||||||
|
|
||||||
|
def setup(self, pin, mode):
|
||||||
|
"""Set the input or output mode for a specified pin. Mode should be
|
||||||
|
either OUTPUT or INPUT.
|
||||||
|
"""
|
||||||
|
self.bbio_gpio.setup(pin, self.bbio_gpio.IN if mode == IN else \
|
||||||
|
self.bbio_gpio.OUT)
|
||||||
|
|
||||||
|
def output(self, pin, value):
|
||||||
|
"""Set the specified pin the provided high/low value. Value should be
|
||||||
|
either HIGH/LOW or a boolean (true = high).
|
||||||
|
"""
|
||||||
|
self.bbio_gpio.output(pin, value)
|
||||||
|
|
||||||
|
def input(self, pin):
|
||||||
|
"""Read the specified pin and return HIGH/true if the pin is pulled high,
|
||||||
|
or LOW/false if pulled low.
|
||||||
|
"""
|
||||||
|
return self.bbio_gpio.input(pin)
|
||||||
|
|
||||||
|
|
||||||
|
def get_platform_gpio(plat=platform.platform(), **keywords):
|
||||||
|
"""Attempt to return a GPIO instance for the platform which the code is being
|
||||||
|
executed on. Currently supports only the Raspberry Pi using the RPi.GPIO
|
||||||
|
library and Beaglebone Black using the Adafruit_BBIO library. Will throw an
|
||||||
|
exception if a GPIO instance can't be created for the current platform. The
|
||||||
|
returned GPIO object is an instance of BaseGPIO.
|
||||||
|
"""
|
||||||
|
if plat is None:
|
||||||
|
raise RuntimeError('Could not determine platform type.')
|
||||||
|
|
||||||
|
# TODO: Is there a better way to check if running on BBB or Pi? Relying on
|
||||||
|
# the architecture name is brittle because new boards running armv6 or armv7
|
||||||
|
# might come along and conflict with this simple identification scheme.
|
||||||
|
|
||||||
|
# Handle Raspberry Pi
|
||||||
|
# Platform output on Raspbian testing/jessie ~May 2014:
|
||||||
|
# Linux-3.10.25+-armv6l-with-debian-7.4
|
||||||
|
if plat.lower().find('armv6l-with-debian') > -1:
|
||||||
|
import RPi.GPIO
|
||||||
|
return RPiGPIOAdapter(RPi.GPIO, **keywords)
|
||||||
|
|
||||||
|
# Handle Beaglebone Black
|
||||||
|
# Platform output on Debian ~May 2014:
|
||||||
|
# Linux-3.8.13-bone47-armv7l-with-debian-7.4
|
||||||
|
if plat.lower().find('armv7l-with-debian') > -1:
|
||||||
|
import Adafruit_BBIO.GPIO
|
||||||
|
return AdafruitBBIOAdapter(Adafruit_BBIO.GPIO, **keywords)
|
||||||
|
|
||||||
|
# Couldn't determine platform, raise error.
|
||||||
|
raise RuntimeError('Unsupported platform: {0}'.format(plat))
|
||||||
283
Adafruit_GPIO/SPI.py
Normal file
283
Adafruit_GPIO/SPI.py
Normal file
|
|
@ -0,0 +1,283 @@
|
||||||
|
# Copyright (c) 2014 Adafruit Industries
|
||||||
|
# Author: Tony DiCola
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
|
import operator
|
||||||
|
import time
|
||||||
|
|
||||||
|
import spidev
|
||||||
|
|
||||||
|
import Adafruit_GPIO as GPIO
|
||||||
|
|
||||||
|
|
||||||
|
MSBFIRST = 0
|
||||||
|
LSBFIRST = 1
|
||||||
|
|
||||||
|
|
||||||
|
class SpiDev(object):
|
||||||
|
"""Hardware-based SPI implementation using the spidev interface."""
|
||||||
|
|
||||||
|
def __init__(self, port, device, max_speed_hz=500000):
|
||||||
|
"""Initialize an SPI device using the SPIdev interface. Port and device
|
||||||
|
identify the device, for example the device /dev/spidev1.0 would be port
|
||||||
|
1 and device 0.
|
||||||
|
"""
|
||||||
|
self._device = spidev.SpiDev()
|
||||||
|
self._device.open(port, device)
|
||||||
|
self._device.max_speed_hz=max_speed_hz
|
||||||
|
|
||||||
|
def set_clock_hz(self, hz):
|
||||||
|
"""Set the speed of the SPI clock in hertz. Note that not all speeds
|
||||||
|
are supported and a lower speed might be chosen by the hardware.
|
||||||
|
"""
|
||||||
|
self._device.max_speed_hz=hz
|
||||||
|
|
||||||
|
def set_mode(self, mode):
|
||||||
|
"""Set SPI mode which controls clock polarity and phase. Should be a
|
||||||
|
numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning:
|
||||||
|
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
|
||||||
|
"""
|
||||||
|
if mode < 0 or mode > 3:
|
||||||
|
raise ValueError('Mode must be a value 0, 1, 2, or 3.')
|
||||||
|
self._device.mode = mode
|
||||||
|
|
||||||
|
def set_bit_order(self, order):
|
||||||
|
"""Set order of bits to be read/written over serial lines. Should be
|
||||||
|
either MSBFIRST for most-significant first, or LSBFIRST for
|
||||||
|
least-signifcant first.
|
||||||
|
"""
|
||||||
|
if order == MSBFIRST:
|
||||||
|
self._device.lsbfirst = False
|
||||||
|
elif order == LSBFIRST:
|
||||||
|
self._device.lsbfirst = True
|
||||||
|
else:
|
||||||
|
raise ValueError('Order must be MSBFIRST or LSBFIRST.')
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""Close communication with the SPI device."""
|
||||||
|
self._device.close()
|
||||||
|
|
||||||
|
def write(self, data):
|
||||||
|
"""Half-duplex SPI write. The specified array of bytes will be clocked
|
||||||
|
out the MOSI line.
|
||||||
|
"""
|
||||||
|
self._device.writebytes(data)
|
||||||
|
|
||||||
|
def read(self, length):
|
||||||
|
"""Half-duplex SPI read. The specified length of bytes will be clocked
|
||||||
|
in the MISO line and returned as a bytearray object.
|
||||||
|
"""
|
||||||
|
return bytearray(self._device.readbytes(length))
|
||||||
|
|
||||||
|
def transfer(self, data):
|
||||||
|
"""Full-duplex SPI read and write. The specified array of bytes will be
|
||||||
|
clocked out the MOSI line, while simultaneously bytes will be read from
|
||||||
|
the MISO line. Read bytes will be returned as a bytearray object.
|
||||||
|
"""
|
||||||
|
return bytearray(self._device.xfer2(data))
|
||||||
|
|
||||||
|
|
||||||
|
class BitBang(object):
|
||||||
|
"""Software-based implementation of the SPI protocol over GPIO pins."""
|
||||||
|
|
||||||
|
def __init__(self, gpio, sclk, mosi, miso, ss):
|
||||||
|
"""Initialize bit bang (or software) based SPI. Must provide a BaseGPIO
|
||||||
|
class, the SPI clock, and optionally MOSI, MISO, and SS (slave select)
|
||||||
|
pin numbers. If MOSI is set to None then writes will be disabled and fail
|
||||||
|
with an error, likewise for MISO reads will be disabled. If SS is set to
|
||||||
|
None then SS will not be asserted high/low by the library when
|
||||||
|
transfering data.
|
||||||
|
"""
|
||||||
|
self._gpio = gpio
|
||||||
|
self._sclk = sclk
|
||||||
|
self._mosi = mosi
|
||||||
|
self._miso = miso
|
||||||
|
self._ss = ss
|
||||||
|
# Set pins as outputs/inputs.
|
||||||
|
gpio.setup(sclk, GPIO.OUT)
|
||||||
|
if mosi is not None:
|
||||||
|
gpio.setup(mosi, GPIO.OUT)
|
||||||
|
if miso is not None:
|
||||||
|
gpio.setup(miso, GPIO.IN)
|
||||||
|
if ss is not None:
|
||||||
|
gpio.setup(ss, GPIO.OUT)
|
||||||
|
# Assert SS high to start with device communication off.
|
||||||
|
gpio.set_high(ss)
|
||||||
|
# Assume mode 0.
|
||||||
|
self.set_mode(0)
|
||||||
|
# Assume most significant bit first order.
|
||||||
|
self.set_bit_order(MSBFIRST)
|
||||||
|
|
||||||
|
def set_clock_hz(self, hz):
|
||||||
|
"""Set the speed of the SPI clock. This is unsupported with the bit
|
||||||
|
bang SPI class and will be ignored.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def set_mode(self, mode):
|
||||||
|
"""Set SPI mode which controls clock polarity and phase. Should be a
|
||||||
|
numeric value 0, 1, 2, or 3. See wikipedia page for details on meaning:
|
||||||
|
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
|
||||||
|
"""
|
||||||
|
if mode < 0 or mode > 3:
|
||||||
|
raise ValueError('Mode must be a value 0, 1, 2, or 3.')
|
||||||
|
if mode & 0x02:
|
||||||
|
# Clock is normally high in mode 2 and 3.
|
||||||
|
self._clock_base = GPIO.HIGH
|
||||||
|
else:
|
||||||
|
# Clock is normally low in mode 0 and 1.
|
||||||
|
self._clock_base = GPIO.LOW
|
||||||
|
if mode & 0x01:
|
||||||
|
# Read on trailing edge in mode 1 and 3.
|
||||||
|
self._read_leading = False
|
||||||
|
else:
|
||||||
|
# Read on leading edge in mode 0 and 2.
|
||||||
|
self._read_leading = True
|
||||||
|
# Put clock into its base state.
|
||||||
|
self._gpio.output(self._sclk, self._clock_base)
|
||||||
|
|
||||||
|
def set_bit_order(self, order):
|
||||||
|
"""Set order of bits to be read/written over serial lines. Should be
|
||||||
|
either MSBFIRST for most-significant first, or LSBFIRST for
|
||||||
|
least-signifcant first.
|
||||||
|
"""
|
||||||
|
# Set self._mask to the bitmask which points at the appropriate bit to
|
||||||
|
# read or write, and appropriate left/right shift operator function for
|
||||||
|
# reading/writing.
|
||||||
|
if order == MSBFIRST:
|
||||||
|
self._mask = 0x80
|
||||||
|
self._write_shift = operator.lshift
|
||||||
|
self._read_shift = operator.rshift
|
||||||
|
elif order == LSBFIRST:
|
||||||
|
self._mask = 0x01
|
||||||
|
self._write_shift = operator.rshift
|
||||||
|
self._read_shift = operator.lshift
|
||||||
|
else:
|
||||||
|
raise ValueError('Order must be MSBFIRST or LSBFIRST.')
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
"""Close the SPI connection. Unused in the bit bang implementation."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def write(self, data, assert_ss=True, deassert_ss=True):
|
||||||
|
"""Half-duplex SPI write. If assert_ss is True, the SS line will be
|
||||||
|
asserted low, the specified bytes will be clocked out the MOSI line, and
|
||||||
|
if deassert_ss is True the SS line be put back high.
|
||||||
|
"""
|
||||||
|
# Fail MOSI is not specified.
|
||||||
|
if self._mosi is None:
|
||||||
|
raise RuntimeError('Write attempted with no MOSI pin specified.')
|
||||||
|
if assert_ss and self._ss is not None:
|
||||||
|
self._gpio.set_low(self._ss)
|
||||||
|
for byte in data:
|
||||||
|
for i in range(8):
|
||||||
|
# Write bit to MOSI.
|
||||||
|
if self._write_shift(byte, i) & self._mask:
|
||||||
|
self._gpio.set_high(self._mosi)
|
||||||
|
else:
|
||||||
|
self._gpio.set_low(self._mosi)
|
||||||
|
# Flip clock off base.
|
||||||
|
self._gpio.output(self._sclk, not self._clock_base)
|
||||||
|
# Return clock to base.
|
||||||
|
self._gpio.output(self._sclk, self._clock_base)
|
||||||
|
if deassert_ss and self._ss is not None:
|
||||||
|
self._gpio.set_high(self._ss)
|
||||||
|
|
||||||
|
def read(self, length, assert_ss=True, deassert_ss=True):
|
||||||
|
"""Half-duplex SPI read. If assert_ss is true, the SS line will be
|
||||||
|
asserted low, the specified length of bytes will be clocked in the MISO
|
||||||
|
line, and if deassert_ss is true the SS line will be put back high.
|
||||||
|
Bytes which are read will be returned as a bytearray object.
|
||||||
|
"""
|
||||||
|
if self._mosi is None:
|
||||||
|
raise RuntimeError('Read attempted with no MISO pin specified.')
|
||||||
|
if assert_ss and self._ss is not None:
|
||||||
|
self._gpio.set_low(self._ss)
|
||||||
|
result = bytearray(length)
|
||||||
|
for i in range(length):
|
||||||
|
for j in range(8):
|
||||||
|
# Flip clock off base.
|
||||||
|
self._gpio.output(self._sclk, not self._clock_base)
|
||||||
|
# Handle read on leading edge of clock.
|
||||||
|
if self._read_leading:
|
||||||
|
if self._gpio.is_high(self._miso):
|
||||||
|
# Set bit to 1 at appropriate location.
|
||||||
|
result[i] |= self._read_shift(self._mask, j)
|
||||||
|
else:
|
||||||
|
# Set bit to 0 at appropriate location.
|
||||||
|
result[i] &= ~self._read_shift(self._mask, j)
|
||||||
|
# Return clock to base.
|
||||||
|
self._gpio.output(self._sclk, self._clock_base)
|
||||||
|
# Handle read on trailing edge of clock.
|
||||||
|
if not self._read_leading:
|
||||||
|
if self._gpio.is_high(self._miso):
|
||||||
|
# Set bit to 1 at appropriate location.
|
||||||
|
result[i] |= self._read_shift(self._mask, j)
|
||||||
|
else:
|
||||||
|
# Set bit to 0 at appropriate location.
|
||||||
|
result[i] &= ~self._read_shift(self._mask, j)
|
||||||
|
if deassert_ss and self._ss is not None:
|
||||||
|
self._gpio.set_high(self._ss)
|
||||||
|
return result
|
||||||
|
|
||||||
|
def transfer(self, data, assert_ss=True, deassert_ss=True):
|
||||||
|
"""Full-duplex SPI read and write. If assert_ss is true, the SS line
|
||||||
|
will be asserted low, the specified bytes will be clocked out the MOSI
|
||||||
|
line while bytes will also be read from the MISO line, and if
|
||||||
|
deassert_ss is true the SS line will be put back high. Bytes which are
|
||||||
|
read will be returned as a bytearray object.
|
||||||
|
"""
|
||||||
|
if self._mosi is None:
|
||||||
|
raise RuntimeError('Write attempted with no MOSI pin specified.')
|
||||||
|
if self._mosi is None:
|
||||||
|
raise RuntimeError('Read attempted with no MISO pin specified.')
|
||||||
|
if assert_ss and self._ss is not None:
|
||||||
|
self._gpio.set_low(self._ss)
|
||||||
|
result = bytearray(len(data))
|
||||||
|
for i in range(len(data)):
|
||||||
|
for j in range(8):
|
||||||
|
# Write bit to MOSI.
|
||||||
|
if self._write_shift(data[i], j) & self._mask:
|
||||||
|
self._gpio.set_high(self._mosi)
|
||||||
|
else:
|
||||||
|
self._gpio.set_low(self._mosi)
|
||||||
|
# Flip clock off base.
|
||||||
|
self._gpio.output(self._sclk, not self._clock_base)
|
||||||
|
# Handle read on leading edge of clock.
|
||||||
|
if self._read_leading:
|
||||||
|
if self._gpio.is_high(self._miso):
|
||||||
|
# Set bit to 1 at appropriate location.
|
||||||
|
result[i] |= self._read_shift(self._mask, j)
|
||||||
|
else:
|
||||||
|
# Set bit to 0 at appropriate location.
|
||||||
|
result[i] &= ~self._read_shift(self._mask, j)
|
||||||
|
# Return clock to base.
|
||||||
|
self._gpio.output(self._sclk, self._clock_base)
|
||||||
|
# Handle read on trailing edge of clock.
|
||||||
|
if not self._read_leading:
|
||||||
|
if self._gpio.is_high(self._miso):
|
||||||
|
# Set bit to 1 at appropriate location.
|
||||||
|
result[i] |= self._read_shift(self._mask, j)
|
||||||
|
else:
|
||||||
|
# Set bit to 0 at appropriate location.
|
||||||
|
result[i] &= ~self._read_shift(self._mask, j)
|
||||||
|
if deassert_ss and self._ss is not None:
|
||||||
|
self._gpio.set_high(self._ss)
|
||||||
|
return result
|
||||||
1
Adafruit_GPIO/__init__.py
Normal file
1
Adafruit_GPIO/__init__.py
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
from GPIO import *
|
||||||
332
ez_setup.py
Normal file
332
ez_setup.py
Normal file
|
|
@ -0,0 +1,332 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""Bootstrap setuptools installation
|
||||||
|
|
||||||
|
To use setuptools in your package's setup.py, include this
|
||||||
|
file in the same directory and add this to the top of your setup.py::
|
||||||
|
|
||||||
|
from ez_setup import use_setuptools
|
||||||
|
use_setuptools()
|
||||||
|
|
||||||
|
To require a specific version of setuptools, set a download
|
||||||
|
mirror, or use an alternate download directory, simply supply
|
||||||
|
the appropriate options to ``use_setuptools()``.
|
||||||
|
|
||||||
|
This file can also be run as a script to install or upgrade setuptools.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import zipfile
|
||||||
|
import optparse
|
||||||
|
import subprocess
|
||||||
|
import platform
|
||||||
|
import textwrap
|
||||||
|
import contextlib
|
||||||
|
|
||||||
|
from distutils import log
|
||||||
|
|
||||||
|
try:
|
||||||
|
from site import USER_SITE
|
||||||
|
except ImportError:
|
||||||
|
USER_SITE = None
|
||||||
|
|
||||||
|
DEFAULT_VERSION = "3.5.1"
|
||||||
|
DEFAULT_URL = "https://pypi.python.org/packages/source/s/setuptools/"
|
||||||
|
|
||||||
|
def _python_cmd(*args):
|
||||||
|
"""
|
||||||
|
Return True if the command succeeded.
|
||||||
|
"""
|
||||||
|
args = (sys.executable,) + args
|
||||||
|
return subprocess.call(args) == 0
|
||||||
|
|
||||||
|
|
||||||
|
def _install(archive_filename, install_args=()):
|
||||||
|
with archive_context(archive_filename):
|
||||||
|
# installing
|
||||||
|
log.warn('Installing Setuptools')
|
||||||
|
if not _python_cmd('setup.py', 'install', *install_args):
|
||||||
|
log.warn('Something went wrong during the installation.')
|
||||||
|
log.warn('See the error message above.')
|
||||||
|
# exitcode will be 2
|
||||||
|
return 2
|
||||||
|
|
||||||
|
|
||||||
|
def _build_egg(egg, archive_filename, to_dir):
|
||||||
|
with archive_context(archive_filename):
|
||||||
|
# building an egg
|
||||||
|
log.warn('Building a Setuptools egg in %s', to_dir)
|
||||||
|
_python_cmd('setup.py', '-q', 'bdist_egg', '--dist-dir', to_dir)
|
||||||
|
# returning the result
|
||||||
|
log.warn(egg)
|
||||||
|
if not os.path.exists(egg):
|
||||||
|
raise IOError('Could not build the egg.')
|
||||||
|
|
||||||
|
|
||||||
|
def get_zip_class():
|
||||||
|
"""
|
||||||
|
Supplement ZipFile class to support context manager for Python 2.6
|
||||||
|
"""
|
||||||
|
class ContextualZipFile(zipfile.ZipFile):
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
def __exit__(self, type, value, traceback):
|
||||||
|
self.close
|
||||||
|
return zipfile.ZipFile if hasattr(zipfile.ZipFile, '__exit__') else \
|
||||||
|
ContextualZipFile
|
||||||
|
|
||||||
|
|
||||||
|
@contextlib.contextmanager
|
||||||
|
def archive_context(filename):
|
||||||
|
# extracting the archive
|
||||||
|
tmpdir = tempfile.mkdtemp()
|
||||||
|
log.warn('Extracting in %s', tmpdir)
|
||||||
|
old_wd = os.getcwd()
|
||||||
|
try:
|
||||||
|
os.chdir(tmpdir)
|
||||||
|
with get_zip_class()(filename) as archive:
|
||||||
|
archive.extractall()
|
||||||
|
|
||||||
|
# going in the directory
|
||||||
|
subdir = os.path.join(tmpdir, os.listdir(tmpdir)[0])
|
||||||
|
os.chdir(subdir)
|
||||||
|
log.warn('Now working in %s', subdir)
|
||||||
|
yield
|
||||||
|
|
||||||
|
finally:
|
||||||
|
os.chdir(old_wd)
|
||||||
|
shutil.rmtree(tmpdir)
|
||||||
|
|
||||||
|
|
||||||
|
def _do_download(version, download_base, to_dir, download_delay):
|
||||||
|
egg = os.path.join(to_dir, 'setuptools-%s-py%d.%d.egg'
|
||||||
|
% (version, sys.version_info[0], sys.version_info[1]))
|
||||||
|
if not os.path.exists(egg):
|
||||||
|
archive = download_setuptools(version, download_base,
|
||||||
|
to_dir, download_delay)
|
||||||
|
_build_egg(egg, archive, to_dir)
|
||||||
|
sys.path.insert(0, egg)
|
||||||
|
|
||||||
|
# Remove previously-imported pkg_resources if present (see
|
||||||
|
# https://bitbucket.org/pypa/setuptools/pull-request/7/ for details).
|
||||||
|
if 'pkg_resources' in sys.modules:
|
||||||
|
del sys.modules['pkg_resources']
|
||||||
|
|
||||||
|
import setuptools
|
||||||
|
setuptools.bootstrap_install_from = egg
|
||||||
|
|
||||||
|
|
||||||
|
def use_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||||
|
to_dir=os.curdir, download_delay=15):
|
||||||
|
to_dir = os.path.abspath(to_dir)
|
||||||
|
rep_modules = 'pkg_resources', 'setuptools'
|
||||||
|
imported = set(sys.modules).intersection(rep_modules)
|
||||||
|
try:
|
||||||
|
import pkg_resources
|
||||||
|
except ImportError:
|
||||||
|
return _do_download(version, download_base, to_dir, download_delay)
|
||||||
|
try:
|
||||||
|
pkg_resources.require("setuptools>=" + version)
|
||||||
|
return
|
||||||
|
except pkg_resources.DistributionNotFound:
|
||||||
|
return _do_download(version, download_base, to_dir, download_delay)
|
||||||
|
except pkg_resources.VersionConflict as VC_err:
|
||||||
|
if imported:
|
||||||
|
msg = textwrap.dedent("""
|
||||||
|
The required version of setuptools (>={version}) is not available,
|
||||||
|
and can't be installed while this script is running. Please
|
||||||
|
install a more recent version first, using
|
||||||
|
'easy_install -U setuptools'.
|
||||||
|
|
||||||
|
(Currently using {VC_err.args[0]!r})
|
||||||
|
""").format(VC_err=VC_err, version=version)
|
||||||
|
sys.stderr.write(msg)
|
||||||
|
sys.exit(2)
|
||||||
|
|
||||||
|
# otherwise, reload ok
|
||||||
|
del pkg_resources, sys.modules['pkg_resources']
|
||||||
|
return _do_download(version, download_base, to_dir, download_delay)
|
||||||
|
|
||||||
|
def _clean_check(cmd, target):
|
||||||
|
"""
|
||||||
|
Run the command to download target. If the command fails, clean up before
|
||||||
|
re-raising the error.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
subprocess.check_call(cmd)
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
if os.access(target, os.F_OK):
|
||||||
|
os.unlink(target)
|
||||||
|
raise
|
||||||
|
|
||||||
|
def download_file_powershell(url, target):
|
||||||
|
"""
|
||||||
|
Download the file at url to target using Powershell (which will validate
|
||||||
|
trust). Raise an exception if the command cannot complete.
|
||||||
|
"""
|
||||||
|
target = os.path.abspath(target)
|
||||||
|
cmd = [
|
||||||
|
'powershell',
|
||||||
|
'-Command',
|
||||||
|
"(new-object System.Net.WebClient).DownloadFile(%(url)r, %(target)r)" % vars(),
|
||||||
|
]
|
||||||
|
_clean_check(cmd, target)
|
||||||
|
|
||||||
|
def has_powershell():
|
||||||
|
if platform.system() != 'Windows':
|
||||||
|
return False
|
||||||
|
cmd = ['powershell', '-Command', 'echo test']
|
||||||
|
devnull = open(os.path.devnull, 'wb')
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
devnull.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
download_file_powershell.viable = has_powershell
|
||||||
|
|
||||||
|
def download_file_curl(url, target):
|
||||||
|
cmd = ['curl', url, '--silent', '--output', target]
|
||||||
|
_clean_check(cmd, target)
|
||||||
|
|
||||||
|
def has_curl():
|
||||||
|
cmd = ['curl', '--version']
|
||||||
|
devnull = open(os.path.devnull, 'wb')
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
devnull.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
download_file_curl.viable = has_curl
|
||||||
|
|
||||||
|
def download_file_wget(url, target):
|
||||||
|
cmd = ['wget', url, '--quiet', '--output-document', target]
|
||||||
|
_clean_check(cmd, target)
|
||||||
|
|
||||||
|
def has_wget():
|
||||||
|
cmd = ['wget', '--version']
|
||||||
|
devnull = open(os.path.devnull, 'wb')
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
subprocess.check_call(cmd, stdout=devnull, stderr=devnull)
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
finally:
|
||||||
|
devnull.close()
|
||||||
|
return True
|
||||||
|
|
||||||
|
download_file_wget.viable = has_wget
|
||||||
|
|
||||||
|
def download_file_insecure(url, target):
|
||||||
|
"""
|
||||||
|
Use Python to download the file, even though it cannot authenticate the
|
||||||
|
connection.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from urllib.request import urlopen
|
||||||
|
except ImportError:
|
||||||
|
from urllib2 import urlopen
|
||||||
|
src = dst = None
|
||||||
|
try:
|
||||||
|
src = urlopen(url)
|
||||||
|
# Read/write all in one block, so we don't create a corrupt file
|
||||||
|
# if the download is interrupted.
|
||||||
|
data = src.read()
|
||||||
|
dst = open(target, "wb")
|
||||||
|
dst.write(data)
|
||||||
|
finally:
|
||||||
|
if src:
|
||||||
|
src.close()
|
||||||
|
if dst:
|
||||||
|
dst.close()
|
||||||
|
|
||||||
|
download_file_insecure.viable = lambda: True
|
||||||
|
|
||||||
|
def get_best_downloader():
|
||||||
|
downloaders = [
|
||||||
|
download_file_powershell,
|
||||||
|
download_file_curl,
|
||||||
|
download_file_wget,
|
||||||
|
download_file_insecure,
|
||||||
|
]
|
||||||
|
|
||||||
|
for dl in downloaders:
|
||||||
|
if dl.viable():
|
||||||
|
return dl
|
||||||
|
|
||||||
|
def download_setuptools(version=DEFAULT_VERSION, download_base=DEFAULT_URL,
|
||||||
|
to_dir=os.curdir, delay=15, downloader_factory=get_best_downloader):
|
||||||
|
"""
|
||||||
|
Download setuptools from a specified location and return its filename
|
||||||
|
|
||||||
|
`version` should be a valid setuptools version number that is available
|
||||||
|
as an egg for download under the `download_base` URL (which should end
|
||||||
|
with a '/'). `to_dir` is the directory where the egg will be downloaded.
|
||||||
|
`delay` is the number of seconds to pause before an actual download
|
||||||
|
attempt.
|
||||||
|
|
||||||
|
``downloader_factory`` should be a function taking no arguments and
|
||||||
|
returning a function for downloading a URL to a target.
|
||||||
|
"""
|
||||||
|
# making sure we use the absolute path
|
||||||
|
to_dir = os.path.abspath(to_dir)
|
||||||
|
zip_name = "setuptools-%s.zip" % version
|
||||||
|
url = download_base + zip_name
|
||||||
|
saveto = os.path.join(to_dir, zip_name)
|
||||||
|
if not os.path.exists(saveto): # Avoid repeated downloads
|
||||||
|
log.warn("Downloading %s", url)
|
||||||
|
downloader = downloader_factory()
|
||||||
|
downloader(url, saveto)
|
||||||
|
return os.path.realpath(saveto)
|
||||||
|
|
||||||
|
def _build_install_args(options):
|
||||||
|
"""
|
||||||
|
Build the arguments to 'python setup.py install' on the setuptools package
|
||||||
|
"""
|
||||||
|
return ['--user'] if options.user_install else []
|
||||||
|
|
||||||
|
def _parse_args():
|
||||||
|
"""
|
||||||
|
Parse the command line for options
|
||||||
|
"""
|
||||||
|
parser = optparse.OptionParser()
|
||||||
|
parser.add_option(
|
||||||
|
'--user', dest='user_install', action='store_true', default=False,
|
||||||
|
help='install in user site package (requires Python 2.6 or later)')
|
||||||
|
parser.add_option(
|
||||||
|
'--download-base', dest='download_base', metavar="URL",
|
||||||
|
default=DEFAULT_URL,
|
||||||
|
help='alternative URL from where to download the setuptools package')
|
||||||
|
parser.add_option(
|
||||||
|
'--insecure', dest='downloader_factory', action='store_const',
|
||||||
|
const=lambda: download_file_insecure, default=get_best_downloader,
|
||||||
|
help='Use internal, non-validating downloader'
|
||||||
|
)
|
||||||
|
parser.add_option(
|
||||||
|
'--version', help="Specify which version to download",
|
||||||
|
default=DEFAULT_VERSION,
|
||||||
|
)
|
||||||
|
options, args = parser.parse_args()
|
||||||
|
# positional arguments are ignored
|
||||||
|
return options
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Install or upgrade setuptools and EasyInstall"""
|
||||||
|
options = _parse_args()
|
||||||
|
archive = download_setuptools(
|
||||||
|
version=options.version,
|
||||||
|
download_base=options.download_base,
|
||||||
|
downloader_factory=options.downloader_factory,
|
||||||
|
)
|
||||||
|
return _install(archive, _build_install_args(options))
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
sys.exit(main())
|
||||||
13
setup.py
Normal file
13
setup.py
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
from ez_setup import use_setuptools
|
||||||
|
use_setuptools()
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
setup(name = 'Adafruit_GPIO',
|
||||||
|
version = '0.1.0',
|
||||||
|
author = 'Tony DiCola',
|
||||||
|
author_email = 'tdicola@adafruit.com',
|
||||||
|
description = 'Library to provide a cross-platform GPIO interface on the Raspberry Pi and Beaglebone Black using the RPi.GPIO and Adafruit_BBIO libraries.',
|
||||||
|
license = 'MIT',
|
||||||
|
url = 'https://github.com/adafruit/Adafruit_Python_GPIO/',
|
||||||
|
install_requires = ['spidev'],
|
||||||
|
packages = find_packages())
|
||||||
40
test/MockGPIO.py
Normal file
40
test/MockGPIO.py
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Copyright (c) 2014 Adafruit Industries
|
||||||
|
# Author: Tony DiCola
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
|
import Adafruit_GPIO as GPIO
|
||||||
|
|
||||||
|
|
||||||
|
class MockGPIO(GPIO.BaseGPIO):
|
||||||
|
def __init__(self):
|
||||||
|
self.pin_mode = {}
|
||||||
|
self.pin_written = {}
|
||||||
|
self.pin_read = {}
|
||||||
|
|
||||||
|
def setup(self, pin, mode):
|
||||||
|
self.pin_mode[pin] = mode
|
||||||
|
|
||||||
|
def output(self, pin, bit):
|
||||||
|
self.pin_written.setdefault(pin, []).append(1 if bit else 0)
|
||||||
|
|
||||||
|
def input(self, pin):
|
||||||
|
if pin not in self.pin_read:
|
||||||
|
raise RuntimeError('No mock GPIO data to read for pin {0}'.format(pin))
|
||||||
|
return self.pin_read[pin].pop(0) == 1
|
||||||
123
test/test_GPIO.py
Normal file
123
test/test_GPIO.py
Normal file
|
|
@ -0,0 +1,123 @@
|
||||||
|
# Copyright (c) 2014 Adafruit Industries
|
||||||
|
# Author: Tony DiCola
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from mock import Mock, patch
|
||||||
|
|
||||||
|
import Adafruit_GPIO as GPIO
|
||||||
|
import Adafruit_GPIO.SPI as SPI
|
||||||
|
|
||||||
|
from MockGPIO import MockGPIO
|
||||||
|
|
||||||
|
|
||||||
|
class TestBaseGPIO(unittest.TestCase):
|
||||||
|
def test_set_high_and_set_low(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
gpio.set_high(1)
|
||||||
|
gpio.set_low(1)
|
||||||
|
self.assertDictEqual(gpio.pin_written, {1: [1, 0]})
|
||||||
|
|
||||||
|
def test_is_high_and_is_low(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
gpio.pin_read[1] = [0, 0, 1, 1]
|
||||||
|
self.assertTrue(gpio.is_low(1))
|
||||||
|
self.assertFalse(gpio.is_high(1))
|
||||||
|
self.assertFalse(gpio.is_low(1))
|
||||||
|
self.assertTrue(gpio.is_high(1))
|
||||||
|
|
||||||
|
|
||||||
|
class TestRPiGPIOAdapter(unittest.TestCase):
|
||||||
|
def test_setup(self):
|
||||||
|
rpi_gpio = Mock()
|
||||||
|
adapter = GPIO.RPiGPIOAdapter(rpi_gpio)
|
||||||
|
adapter.setup(1, GPIO.OUT)
|
||||||
|
rpi_gpio.setup.assert_called_with(1, rpi_gpio.OUT)
|
||||||
|
adapter.setup(1, GPIO.IN)
|
||||||
|
rpi_gpio.setup.assert_called_with(1, rpi_gpio.IN)
|
||||||
|
|
||||||
|
def test_output(self):
|
||||||
|
rpi_gpio = Mock()
|
||||||
|
adapter = GPIO.RPiGPIOAdapter(rpi_gpio)
|
||||||
|
adapter.output(1, True)
|
||||||
|
rpi_gpio.output.assert_called_with(1, True)
|
||||||
|
adapter.output(1, False)
|
||||||
|
rpi_gpio.output.assert_called_with(1, False)
|
||||||
|
|
||||||
|
def test_input(self):
|
||||||
|
rpi_gpio = Mock()
|
||||||
|
adapter = GPIO.RPiGPIOAdapter(rpi_gpio)
|
||||||
|
rpi_gpio.input = Mock(return_value=True)
|
||||||
|
val = adapter.input(1)
|
||||||
|
self.assertTrue(val)
|
||||||
|
rpi_gpio.input.assert_called_with(1)
|
||||||
|
|
||||||
|
def test_setmode(self):
|
||||||
|
rpi_gpio = Mock()
|
||||||
|
adapter = GPIO.RPiGPIOAdapter(rpi_gpio, mode=rpi_gpio.BCM)
|
||||||
|
rpi_gpio.setmode.assert_called_with(rpi_gpio.BCM)
|
||||||
|
adapter = GPIO.RPiGPIOAdapter(rpi_gpio, mode=rpi_gpio.BOARD)
|
||||||
|
rpi_gpio.setmode.assert_called_with(rpi_gpio.BOARD)
|
||||||
|
adapter = GPIO.RPiGPIOAdapter(rpi_gpio)
|
||||||
|
rpi_gpio.setmode.assert_called_with(rpi_gpio.BCM)
|
||||||
|
|
||||||
|
|
||||||
|
class TestAdafruitBBIOAdapter(unittest.TestCase):
|
||||||
|
def test_setup(self):
|
||||||
|
bbio_gpio = Mock()
|
||||||
|
adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio)
|
||||||
|
adapter.setup(1, GPIO.OUT)
|
||||||
|
bbio_gpio.setup.assert_called_with(1, bbio_gpio.OUT)
|
||||||
|
adapter.setup(1, GPIO.IN)
|
||||||
|
bbio_gpio.setup.assert_called_with(1, bbio_gpio.IN)
|
||||||
|
|
||||||
|
def test_output(self):
|
||||||
|
bbio_gpio = Mock()
|
||||||
|
adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio)
|
||||||
|
adapter.output(1, True)
|
||||||
|
bbio_gpio.output.assert_called_with(1, True)
|
||||||
|
adapter.output(1, False)
|
||||||
|
bbio_gpio.output.assert_called_with(1, False)
|
||||||
|
|
||||||
|
def test_input(self):
|
||||||
|
bbio_gpio = Mock()
|
||||||
|
adapter = GPIO.AdafruitBBIOAdapter(bbio_gpio)
|
||||||
|
bbio_gpio.input = Mock(return_value=True)
|
||||||
|
val = adapter.input(1)
|
||||||
|
self.assertTrue(val)
|
||||||
|
bbio_gpio.input.assert_called_with(1)
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetPlatformGPIO(unittest.TestCase):
|
||||||
|
def test_raspberrypi(self):
|
||||||
|
rpi = Mock()
|
||||||
|
with patch.dict('sys.modules', {'RPi': rpi, 'RPi.GPIO': rpi.gpio}):
|
||||||
|
gpio = GPIO.get_platform_gpio(plat='Linux-3.10.25+-armv6l-with-debian-7.4')
|
||||||
|
self.assertIsInstance(gpio, GPIO.RPiGPIOAdapter)
|
||||||
|
|
||||||
|
def test_beagleboneblack(self):
|
||||||
|
bbio = Mock()
|
||||||
|
with patch.dict('sys.modules', {'Adafruit_BBIO': bbio, 'Adafruit_BBIO.GPIO': bbio.gpio}):
|
||||||
|
gpio = GPIO.get_platform_gpio(plat='Linux-3.8.13-bone47-armv7l-with-debian-7.4')
|
||||||
|
self.assertIsInstance(gpio, GPIO.AdafruitBBIOAdapter)
|
||||||
|
|
||||||
|
def test_otherplatform(self):
|
||||||
|
self.assertRaises(RuntimeError, GPIO.get_platform_gpio, plat='foo-bar')
|
||||||
192
test/test_SPI.py
Normal file
192
test/test_SPI.py
Normal file
|
|
@ -0,0 +1,192 @@
|
||||||
|
# Copyright (c) 2014 Adafruit Industries
|
||||||
|
# Author: Tony DiCola
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in
|
||||||
|
# all copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
# THE SOFTWARE.
|
||||||
|
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
import Adafruit_GPIO as GPIO
|
||||||
|
import Adafruit_GPIO.SPI as SPI
|
||||||
|
|
||||||
|
from MockGPIO import MockGPIO
|
||||||
|
|
||||||
|
|
||||||
|
class TestBitBangSPI(unittest.TestCase):
|
||||||
|
def test_pin_modes_set_correctly(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
device = SPI.BitBang(gpio, 1, 2, 3, 4)
|
||||||
|
self.assertDictEqual(gpio.pin_mode, { 1: GPIO.OUT,
|
||||||
|
2: GPIO.OUT,
|
||||||
|
3: GPIO.IN,
|
||||||
|
4: GPIO.OUT })
|
||||||
|
|
||||||
|
def test_ss_set_high_after_initialization(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
device = SPI.BitBang(gpio, 1, 2, 3, 4)
|
||||||
|
self.assertListEqual(gpio.pin_written[4], [1])
|
||||||
|
|
||||||
|
def test_mode_0_write(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
device = SPI.BitBang(gpio, 1, 2, 3, 4)
|
||||||
|
device.write([0x1F])
|
||||||
|
# Verify clock
|
||||||
|
self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1, 0])
|
||||||
|
# Verify MOSI
|
||||||
|
self.assertListEqual(gpio.pin_written[2], [0, 0, 0, 1, 1, 1, 1, 1])
|
||||||
|
# Verify MISO
|
||||||
|
self.assertNotIn(3, gpio.pin_written)
|
||||||
|
# Verify SS
|
||||||
|
self.assertListEqual(gpio.pin_written[4], [1, 0, 1])
|
||||||
|
|
||||||
|
def test_write_assert_deassert_ss_false(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
device = SPI.BitBang(gpio, 1, 2, 3, 4)
|
||||||
|
device.write([0x1F], assert_ss=False, deassert_ss=False)
|
||||||
|
self.assertListEqual(gpio.pin_written[4], [1])
|
||||||
|
|
||||||
|
def test_write_lsbfirst(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
device = SPI.BitBang(gpio, 1, 2, 3, 4)
|
||||||
|
device.set_bit_order(SPI.LSBFIRST)
|
||||||
|
device.write([0x1F])
|
||||||
|
self.assertListEqual(gpio.pin_written[2], [1, 1, 1, 1, 1, 0, 0, 0])
|
||||||
|
|
||||||
|
def test_invalid_mode_fails(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
device = SPI.BitBang(gpio, 1, 2, 3, 4)
|
||||||
|
self.assertRaises(ValueError, device.set_mode, -1)
|
||||||
|
self.assertRaises(ValueError, device.set_mode, 4)
|
||||||
|
|
||||||
|
def test_invalid_bit_order_fails(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
device = SPI.BitBang(gpio, 1, 2, 3, 4)
|
||||||
|
self.assertRaises(ValueError, device.set_bit_order, -1)
|
||||||
|
self.assertRaises(ValueError, device.set_bit_order, 2)
|
||||||
|
|
||||||
|
def test_mode_0_read(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
device = SPI.BitBang(gpio, 1, 2, 3, 4)
|
||||||
|
gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1]
|
||||||
|
result = device.read(1)
|
||||||
|
# Verify clock
|
||||||
|
self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1, 0])
|
||||||
|
# Verify MOSI
|
||||||
|
self.assertNotIn(2, gpio.pin_written)
|
||||||
|
# Verify MISO
|
||||||
|
self.assertNotIn(3, gpio.pin_written)
|
||||||
|
# Verify SS
|
||||||
|
self.assertListEqual(gpio.pin_written[4], [1, 0, 1])
|
||||||
|
# Verify result
|
||||||
|
self.assertEqual(result, bytearray([0x1F]))
|
||||||
|
|
||||||
|
def test_read_assert_deassert_ss_false(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
device = SPI.BitBang(gpio, 1, 2, 3, 4)
|
||||||
|
gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1]
|
||||||
|
result = device.read(1, assert_ss=False, deassert_ss=False)
|
||||||
|
self.assertListEqual(gpio.pin_written[4], [1])
|
||||||
|
|
||||||
|
def test_read_multiple_bytes(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
device = SPI.BitBang(gpio, 1, 2, 3, 4)
|
||||||
|
gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||||
|
0, 0, 0, 1, 1, 1, 1, 1]
|
||||||
|
result = device.read(3)
|
||||||
|
# Verify clock
|
||||||
|
self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1, 0])
|
||||||
|
# Verify MOSI
|
||||||
|
self.assertNotIn(2, gpio.pin_written)
|
||||||
|
# Verify MISO
|
||||||
|
self.assertNotIn(3, gpio.pin_written)
|
||||||
|
# Verify SS
|
||||||
|
self.assertListEqual(gpio.pin_written[4], [1, 0, 1])
|
||||||
|
# Verify result
|
||||||
|
self.assertEqual(result, bytearray([0x1F, 0xF8, 0x1F]))
|
||||||
|
|
||||||
|
def test_write_multiple_bytes(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
device = SPI.BitBang(gpio, 1, 2, 3, 4)
|
||||||
|
device.write([0x1F, 0xF8, 0x1F])
|
||||||
|
# Verify clock
|
||||||
|
self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1, 0])
|
||||||
|
# Verify MOSI
|
||||||
|
self.assertListEqual(gpio.pin_written[2], [0, 0, 0, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 0, 0, 0,
|
||||||
|
0, 0, 0, 1, 1, 1, 1, 1])
|
||||||
|
# Verify MISO
|
||||||
|
self.assertNotIn(3, gpio.pin_written)
|
||||||
|
# Verify SS
|
||||||
|
self.assertListEqual(gpio.pin_written[4], [1, 0, 1])
|
||||||
|
|
||||||
|
def test_mode_0_transfer(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
device = SPI.BitBang(gpio, 1, 2, 3, 4)
|
||||||
|
gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1]
|
||||||
|
result = device.transfer([0xF8])
|
||||||
|
# Verify clock
|
||||||
|
self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1, 0])
|
||||||
|
# Verify MOSI
|
||||||
|
self.assertListEqual(gpio.pin_written[2], [1, 1, 1, 1, 1, 0, 0, 0])
|
||||||
|
# Verify MISO
|
||||||
|
self.assertNotIn(3, gpio.pin_written)
|
||||||
|
# Verify SS
|
||||||
|
self.assertListEqual(gpio.pin_written[4], [1, 0, 1])
|
||||||
|
# Verify result
|
||||||
|
self.assertEqual(result, bytearray([0x1F]))
|
||||||
|
|
||||||
|
def test_transfer_multiple_bytes(self):
|
||||||
|
gpio = MockGPIO()
|
||||||
|
device = SPI.BitBang(gpio, 1, 2, 3, 4)
|
||||||
|
gpio.pin_read[3] = [0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0,
|
||||||
|
0, 0, 0, 1, 1, 1, 1, 1]
|
||||||
|
result = device.transfer([0xF8, 0x1F, 0xF8])
|
||||||
|
# Verify clock
|
||||||
|
self.assertListEqual(gpio.pin_written[1], [0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1,
|
||||||
|
0, 1, 0, 1, 0, 1, 0, 1, 0])
|
||||||
|
# Verify MOSI
|
||||||
|
self.assertListEqual(gpio.pin_written[2], [1, 1, 1, 1, 1, 0, 0, 0,
|
||||||
|
0, 0, 0, 1, 1, 1, 1, 1,
|
||||||
|
1, 1, 1, 1, 1, 0, 0, 0])
|
||||||
|
# Verify MISO
|
||||||
|
self.assertNotIn(3, gpio.pin_written)
|
||||||
|
# Verify SS
|
||||||
|
self.assertListEqual(gpio.pin_written[4], [1, 0, 1])
|
||||||
|
# Verify result
|
||||||
|
self.assertEqual(result, bytearray([0x1F, 0xF8, 0x1F]))
|
||||||
|
|
||||||
|
#TODO: Test mode 1, 2, 3
|
||||||
|
|
||||||
|
#TODO: Test null MOSI, MISO, SS
|
||||||
Loading…
Reference in a new issue