summaryrefslogtreecommitdiff
path: root/Adafruit_Python_GPIO/Adafruit_GPIO/SPI.py
diff options
context:
space:
mode:
Diffstat (limited to 'Adafruit_Python_GPIO/Adafruit_GPIO/SPI.py')
-rw-r--r--Adafruit_Python_GPIO/Adafruit_GPIO/SPI.py328
1 files changed, 328 insertions, 0 deletions
diff --git a/Adafruit_Python_GPIO/Adafruit_GPIO/SPI.py b/Adafruit_Python_GPIO/Adafruit_GPIO/SPI.py
new file mode 100644
index 0000000..c20a32c
--- /dev/null
+++ b/Adafruit_Python_GPIO/Adafruit_GPIO/SPI.py
@@ -0,0 +1,328 @@
+# 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 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.
+ """
+ import spidev
+ self._device = spidev.SpiDev()
+ self._device.open(port, device)
+ self._device.max_speed_hz=max_speed_hz
+ # Default to mode 0, and make sure CS is active low.
+ self._device.mode = 0
+ self._device.cshigh = False
+
+ 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 SpiDevMraa(object):
+ """Hardware SPI implementation with the mraa library on Minnowboard"""
+ def __init__(self, port, device, max_speed_hz=500000):
+ import mraa
+ self._device = mraa.Spi(0)
+ self._device.mode(0)
+
+ 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.frequency(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.lsbmode(False)
+ elif order == LSBFIRST:
+ self._device.lsbmode(True)
+ else:
+ raise ValueError('Order must be MSBFIRST or LSBFIRST.')
+
+ def close(self):
+ """Close communication with the SPI device."""
+ self._device.Spi()
+
+ def write(self, data):
+ """Half-duplex SPI write. The specified array of bytes will be clocked
+ out the MOSI line.
+ """
+ self._device.write(bytearray(data))
+
+class BitBang(object):
+ """Software-based implementation of the SPI protocol over GPIO pins."""
+
+ def __init__(self, gpio, sclk, mosi=None, miso=None, ss=None):
+ """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._miso 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