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