mirror of
				https://github.com/KevinMidboe/python-gpiozero.git
				synced 2025-10-29 17:50:37 +00:00 
			
		
		
		
	Added SPI tests, simplified the shared SPI software bus implementation, and fixed several protocol errors in our MCP3xxx classes (the x2 and x1 protocols were wrong)
		
			
				
	
	
		
			518 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			518 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| from __future__ import (
 | |
|     unicode_literals,
 | |
|     print_function,
 | |
|     absolute_import,
 | |
|     division,
 | |
|     )
 | |
| str = type('')
 | |
| 
 | |
| 
 | |
| from math import log, ceil
 | |
| from operator import or_
 | |
| try:
 | |
|     from functools import reduce
 | |
| except ImportError:
 | |
|     pass # py2's reduce is built-in
 | |
| 
 | |
| from .exc import DeviceClosed, SPIBadChannel
 | |
| from .devices import Device
 | |
| from .spi import SPI
 | |
| 
 | |
| 
 | |
| class SPIDevice(Device):
 | |
|     """
 | |
|     Extends :class:`Device`. Represents a device that communicates via the SPI
 | |
|     protocol.
 | |
| 
 | |
|     See :ref:`spi_args` for information on the keyword arguments that can be
 | |
|     specified with the constructor.
 | |
|     """
 | |
|     def __init__(self, **spi_args):
 | |
|         self._spi = SPI(**spi_args)
 | |
| 
 | |
|     def close(self):
 | |
|         if self._spi:
 | |
|             s = self._spi
 | |
|             self._spi = None
 | |
|             s.close()
 | |
|         super(SPIDevice, self).close()
 | |
| 
 | |
|     @property
 | |
|     def closed(self):
 | |
|         return self._spi is None
 | |
| 
 | |
|     def _int_to_words(self, pattern):
 | |
|         """
 | |
|         Given a bit-pattern expressed an integer number, return a sequence of
 | |
|         the individual words that make up the pattern. The number of bits per
 | |
|         word will be obtained from the internal SPI interface.
 | |
|         """
 | |
|         try:
 | |
|             bits_required = int(ceil(log(pattern, 2))) + 1
 | |
|         except ValueError:
 | |
|             # pattern == 0 (technically speaking, no bits are required to
 | |
|             # transmit the value zero ;)
 | |
|             bits_required = 1
 | |
|         shifts = range(0, bits_required, self._spi.bits_per_word)[::-1]
 | |
|         mask = 2 ** self._spi.bits_per_word - 1
 | |
|         return [(pattern >> shift) & mask for shift in shifts]
 | |
| 
 | |
|     def _words_to_int(self, words, expected_bits=None):
 | |
|         """
 | |
|         Given a sequence of words which each fit in the internal SPI
 | |
|         interface's number of bits per word, returns the value obtained by
 | |
|         concatenating each word into a single bit-string.
 | |
| 
 | |
|         If *expected_bits* is specified, it limits the size of the output to
 | |
|         the specified number of bits (by masking off bits above the expected
 | |
|         number). If unspecified, no limit will be applied.
 | |
|         """
 | |
|         if expected_bits is None:
 | |
|             expected_bits = len(words) * self._spi.bits_per_word
 | |
|         shifts = range(0, expected_bits, self._spi.bits_per_word)[::-1]
 | |
|         mask = 2 ** expected_bits - 1
 | |
|         return reduce(or_, (word << shift for word, shift in zip(words, shifts))) & mask
 | |
| 
 | |
|     def __repr__(self):
 | |
|         try:
 | |
|             self._check_open()
 | |
|             return "<gpiozero.%s object using %r>" % (self.__class__.__name__, self._spi)
 | |
|         except DeviceClosed:
 | |
|             return "<gpiozero.%s object closed>" % self.__class__.__name__
 | |
| 
 | |
| 
 | |
| class AnalogInputDevice(SPIDevice):
 | |
|     """
 | |
|     Represents an analog input device connected to SPI (serial interface).
 | |
| 
 | |
|     Typical analog input devices are `analog to digital converters`_ (ADCs).
 | |
|     Several classes are provided for specific ADC chips, including
 | |
|     :class:`MCP3004`, :class:`MCP3008`, :class:`MCP3204`, and :class:`MCP3208`.
 | |
| 
 | |
|     The following code demonstrates reading the first channel of an MCP3008
 | |
|     chip attached to the Pi's SPI pins::
 | |
| 
 | |
|         from gpiozero import MCP3008
 | |
| 
 | |
|         pot = MCP3008(0)
 | |
|         print(pot.value)
 | |
| 
 | |
|     The :attr:`value` attribute is normalized such that its value is always
 | |
|     between 0.0 and 1.0 (or in special cases, such as differential sampling,
 | |
|     -1 to +1). Hence, you can use an analog input to control the brightness of
 | |
|     a :class:`PWMLED` like so::
 | |
| 
 | |
|         from gpiozero import MCP3008, PWMLED
 | |
| 
 | |
|         pot = MCP3008(0)
 | |
|         led = PWMLED(17)
 | |
|         led.source = pot.values
 | |
| 
 | |
|     .. _analog to digital converters: https://en.wikipedia.org/wiki/Analog-to-digital_converter
 | |
|     """
 | |
| 
 | |
|     def __init__(self, bits, **spi_args):
 | |
|         self._bits = bits
 | |
|         self._min_value = -(2 ** bits)
 | |
|         self._range = 2 ** (bits + 1) - 1
 | |
|         super(AnalogInputDevice, self).__init__(shared=True, **spi_args)
 | |
| 
 | |
|     @property
 | |
|     def bits(self):
 | |
|         """
 | |
|         The bit-resolution of the device/channel.
 | |
|         """
 | |
|         return self._bits
 | |
| 
 | |
|     def _read(self):
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     @property
 | |
|     def value(self):
 | |
|         """
 | |
|         The current value read from the device, scaled to a value between 0 and
 | |
|         1 (or -1 to +1 for certain devices operating in differential mode).
 | |
|         """
 | |
|         return (2 * (self._read() - self._min_value) / self._range) - 1
 | |
| 
 | |
|     @property
 | |
|     def raw_value(self):
 | |
|         """
 | |
|         The raw value as read from the device.
 | |
|         """
 | |
|         return self._read()
 | |
| 
 | |
| 
 | |
| class MCP3xxx(AnalogInputDevice):
 | |
|     """
 | |
|     Extends :class:`AnalogInputDevice` to implement an interface for all ADC
 | |
|     chips with a protocol similar to the Microchip MCP3xxx series of devices.
 | |
|     """
 | |
| 
 | |
|     def __init__(self, channel=0, bits=10, differential=False, **spi_args):
 | |
|         self._channel = channel
 | |
|         self._differential = bool(differential)
 | |
|         super(MCP3xxx, self).__init__(bits, **spi_args)
 | |
| 
 | |
|     @property
 | |
|     def channel(self):
 | |
|         """
 | |
|         The channel to read data from. The MCP3008/3208/3304 have 8 channels
 | |
|         (0-7), while the MCP3004/3204/3302 have 4 channels (0-3), the
 | |
|         MCP3002/3202 have 2 channels (0-1), and the MCP3001/3201/3301 only
 | |
|         have 1 channel.
 | |
|         """
 | |
|         return self._channel
 | |
| 
 | |
|     @property
 | |
|     def differential(self):
 | |
|         """
 | |
|         If ``True``, the device is operated in differential mode. In this mode
 | |
|         one channel (specified by the channel attribute) is read relative to
 | |
|         the value of a second channel (implied by the chip's design).
 | |
| 
 | |
|         Please refer to the device data-sheet to determine which channel is
 | |
|         used as the relative base value (for example, when using an
 | |
|         :class:`MCP3008` in differential mode, channel 0 is read relative to
 | |
|         channel 1).
 | |
|         """
 | |
|         return self._differential
 | |
| 
 | |
|     def _read(self):
 | |
|         return self._words_to_int(
 | |
|             self._spi.transfer(self._send())[-2:], self.bits
 | |
|             )
 | |
| 
 | |
|     def _send(self):
 | |
|         # MCP3004/08 protocol looks like the following:
 | |
|         #
 | |
|         #     Byte        0        1        2
 | |
|         #     ==== ======== ======== ========
 | |
|         #     Tx   00000001 MCCCxxxx xxxxxxxx
 | |
|         #     Rx   xxxxxxxx xxxxx0RR RRRRRRRR
 | |
|         #
 | |
|         # MCP3204/08 protocol looks like the following:
 | |
|         #
 | |
|         #     Byte        0        1        2
 | |
|         #     ==== ======== ======== ========
 | |
|         #     Tx   000001MC CCxxxxxx xxxxxxxx
 | |
|         #     Rx   xxxxxxxx xxx0RRRR RRRRRRRR
 | |
|         #
 | |
|         # The transmit bits start with several preamble "0" bits, the number
 | |
|         # of which is determined by the amount required to align the last byte
 | |
|         # of the result with the final byte of output. A start "1" bit is then
 | |
|         # transmitted, followed by the single/differential bit (M); 1 for
 | |
|         # single-ended read, 0 for differential read. Next comes three bits for
 | |
|         # channel (C).
 | |
|         #
 | |
|         # Read-out begins with a don't care bit (x), then a null bit (0)
 | |
|         # followed by the result bits (R). All other bits are don't care (x).
 | |
|         #
 | |
|         # The 3x01 variant of the chips always operates in differential mode
 | |
|         # and effectively only has one channel (composed of an IN+ and IN-). As
 | |
|         # such it requires no input, just output.
 | |
|         return self._int_to_words(
 | |
|             (0b10000 | (not self.differential) << 3 | self.channel) << (self.bits + 2)
 | |
|             )
 | |
| 
 | |
| 
 | |
| class MCP3xx2(MCP3xxx):
 | |
|     def _send(self):
 | |
|         # MCP3002 protocol looks like the following:
 | |
|         #
 | |
|         #     Byte        0        1
 | |
|         #     ==== ======== ========
 | |
|         #     Tx   01MCLxxx xxxxxxxx
 | |
|         #     Rx   xxxxx0RR RRRRRRRR for the 3002
 | |
|         #
 | |
|         # MCP3202 protocol looks like the following:
 | |
|         #
 | |
|         #     Byte        0        1        2
 | |
|         #     ==== ======== ======== ========
 | |
|         #     Tx   00000001 MCLxxxxx xxxxxxxx
 | |
|         #     Rx   xxxxxxxx xxx0RRRR RRRRRRRR
 | |
|         #
 | |
|         # The transmit bits start with several preamble "0" bits, the number of
 | |
|         # which is determined by the amount required to align the last byte of
 | |
|         # the result with the final byte of output. A start "1" bit is then
 | |
|         # transmitted, followed by the single/differential bit (M); 1 for
 | |
|         # single-ended read, 0 for differential read. Next comes a single bit
 | |
|         # for channel (M) then the MSBF bit (L) which selects whether the data
 | |
|         # will be read out in MSB form only (1) or whether LSB read-out will
 | |
|         # occur after MSB read-out (0).
 | |
|         #
 | |
|         # Read-out begins with a null bit (0) followed by the result bits (R).
 | |
|         # All other bits are don't care (x).
 | |
|         return self._int_to_words(
 | |
|             (0b1001 | (not self.differential) << 2 | self.channel << 1) << (self.bits + 1)
 | |
|             )
 | |
| 
 | |
| 
 | |
| class MCP30xx(MCP3xxx):
 | |
|     """
 | |
|     Extends :class:`MCP3xxx` to implement an interface for all ADC
 | |
|     chips with a protocol similar to the Microchip MCP30xx series of devices.
 | |
|     """
 | |
| 
 | |
|     def __init__(self, channel=0, differential=False, **spi_args):
 | |
|         super(MCP30xx, self).__init__(channel, 10, differential, **spi_args)
 | |
| 
 | |
| 
 | |
| class MCP32xx(MCP3xxx):
 | |
|     """
 | |
|     Extends :class:`MCP3xxx` to implement an interface for all ADC
 | |
|     chips with a protocol similar to the Microchip MCP32xx series of devices.
 | |
|     """
 | |
| 
 | |
|     def __init__(self, channel=0, differential=False, **spi_args):
 | |
|         super(MCP32xx, self).__init__(channel, 12, differential, **spi_args)
 | |
| 
 | |
| 
 | |
| class MCP33xx(MCP3xxx):
 | |
|     """
 | |
|     Extends :class:`MCP3xxx` with functionality specific to the MCP33xx family
 | |
|     of ADCs; specifically this handles the full differential capability of
 | |
|     these chips supporting the full 13-bit signed range of output values.
 | |
|     """
 | |
| 
 | |
|     def __init__(self, channel=0, differential=False, **spi_args):
 | |
|         super(MCP33xx, self).__init__(channel, 12, differential, **spi_args)
 | |
| 
 | |
|     def _read(self):
 | |
|         if self.differential:
 | |
|             result = self._words_to_int(
 | |
|                 self._spi.transfer(self._send())[-2:], self.bits + 1)
 | |
|             # Account for the sign bit
 | |
|             if result > 4095:
 | |
|                 return -(8192 - result)
 | |
|             else:
 | |
|                 return result
 | |
|         else:
 | |
|             return super(MCP33xx, self)._read()
 | |
| 
 | |
|     def _send(self):
 | |
|         # MCP3302/04 protocol looks like the following:
 | |
|         #
 | |
|         #     Byte        0        1        2
 | |
|         #     ==== ======== ======== ========
 | |
|         #     Tx   00001MCC Cxxxxxxx xxxxxxxx
 | |
|         #     Rx   xxxxxxxx xx0SRRRR RRRRRRRR
 | |
|         #
 | |
|         # The transmit bits start with 4 preamble bits "0000", a start bit "1"
 | |
|         # followed by the single/differential bit (M) which is 1 for
 | |
|         # single-ended read, and 0 for differential read, followed by 3-bits
 | |
|         # for the channel (C). The remainder of the transmission are "don't
 | |
|         # care" bits (x).
 | |
|         #
 | |
|         # The first byte received and the top 2 bits of the second byte are
 | |
|         # don't care bits (x). These are followed by a null bit (0), then the
 | |
|         # sign bit (S), and then the 12 result bits (R).
 | |
|         #
 | |
|         # In single read mode (the default) the sign bit is always zero and the
 | |
|         # result is effectively 12-bits. In differential mode, the sign bit is
 | |
|         # significant and the result is a two's-complement 13-bit value.
 | |
|         #
 | |
|         # The MCP3301 variant operates similarly to the other MCP3x01 variants;
 | |
|         # no input, just output and always differential.
 | |
|         return self._int_to_words(
 | |
|             (0b10000 | (not self.differential) << 3 | self.channel) << (self.bits + 3)
 | |
|             )
 | |
| 
 | |
|     @property
 | |
|     def differential(self):
 | |
|         """
 | |
|         If ``True``, the device is operated in differential mode. In this mode
 | |
|         one channel (specified by the channel attribute) is read relative to
 | |
|         the value of a second channel (implied by the chip's design).
 | |
| 
 | |
|         Please refer to the device data-sheet to determine which channel is
 | |
|         used as the relative base value (for example, when using an
 | |
|         :class:`MCP3304` in differential mode, channel 0 is read relative to
 | |
|         channel 1).
 | |
|         """
 | |
|         return super(MCP33xx, self).differential
 | |
| 
 | |
|     @property
 | |
|     def value(self):
 | |
|         """
 | |
|         The current value read from the device, scaled to a value between 0 and
 | |
|         1 (or -1 to +1 for devices operating in differential mode).
 | |
|         """
 | |
|         return super(MCP33xx, self).value
 | |
| 
 | |
| 
 | |
| class MCP3001(MCP30xx):
 | |
|     """
 | |
|     The `MCP3001`_ is a 10-bit analog to digital converter with 1 channel.
 | |
|     Please note that the MCP3001 always operates in differential mode,
 | |
|     measuring the value of IN+ relative to IN-.
 | |
| 
 | |
|     .. _MCP3001: http://www.farnell.com/datasheets/630400.pdf
 | |
|     """
 | |
|     def __init__(self, **spi_args):
 | |
|         super(MCP3001, self).__init__(0, differential=True, **spi_args)
 | |
| 
 | |
|     def _read(self):
 | |
|         # MCP3001 protocol looks like the following:
 | |
|         #
 | |
|         #     Byte        0        1
 | |
|         #     ==== ======== ========
 | |
|         #     Rx   xx0RRRRR RRRRRxxx
 | |
|         return self._words_to_int(self._spi.read(2), 13) >> 3
 | |
| 
 | |
| 
 | |
| class MCP3002(MCP30xx, MCP3xx2):
 | |
|     """
 | |
|     The `MCP3002`_ is a 10-bit analog to digital converter with 2 channels
 | |
|     (0-1).
 | |
| 
 | |
|     .. _MCP3002: http://www.farnell.com/datasheets/1599363.pdf
 | |
|     """
 | |
|     def __init__(self, channel=0, differential=False, **spi_args):
 | |
|         if not 0 <= channel < 2:
 | |
|             raise SPIBadChannel('channel must be 0 or 1')
 | |
|         super(MCP3002, self).__init__(channel, differential, **spi_args)
 | |
| 
 | |
| 
 | |
| class MCP3004(MCP30xx):
 | |
|     """
 | |
|     The `MCP3004`_ is a 10-bit analog to digital converter with 4 channels
 | |
|     (0-3).
 | |
| 
 | |
|     .. _MCP3004: http://www.farnell.com/datasheets/808965.pdf
 | |
|     """
 | |
|     def __init__(self, channel=0, differential=False, **spi_args):
 | |
|         if not 0 <= channel < 4:
 | |
|             raise SPIBadChannel('channel must be between 0 and 3')
 | |
|         super(MCP3004, self).__init__(channel, differential, **spi_args)
 | |
| 
 | |
| 
 | |
| class MCP3008(MCP30xx):
 | |
|     """
 | |
|     The `MCP3008`_ is a 10-bit analog to digital converter with 8 channels
 | |
|     (0-7).
 | |
| 
 | |
|     .. _MCP3008: http://www.farnell.com/datasheets/808965.pdf
 | |
|     """
 | |
|     def __init__(self, channel=0, differential=False, **spi_args):
 | |
|         if not 0 <= channel < 8:
 | |
|             raise SPIBadChannel('channel must be between 0 and 7')
 | |
|         super(MCP3008, self).__init__(channel, differential, **spi_args)
 | |
| 
 | |
| 
 | |
| class MCP3201(MCP32xx):
 | |
|     """
 | |
|     The `MCP3201`_ is a 12-bit analog to digital converter with 1 channel.
 | |
|     Please note that the MCP3201 always operates in differential mode,
 | |
|     measuring the value of IN+ relative to IN-.
 | |
| 
 | |
|     .. _MCP3201: http://www.farnell.com/datasheets/1669366.pdf
 | |
|     """
 | |
|     def __init__(self, **spi_args):
 | |
|         super(MCP3201, self).__init__(0, differential=True, **spi_args)
 | |
| 
 | |
|     def _read(self):
 | |
|         # MCP3201 protocol looks like the following:
 | |
|         #
 | |
|         #     Byte        0        1
 | |
|         #     ==== ======== ========
 | |
|         #     Rx   xx0RRRRR RRRRRRRx
 | |
|         return self._words_to_int(self._spi.read(2), 13) >> 1
 | |
| 
 | |
| 
 | |
| class MCP3202(MCP32xx, MCP3xx2):
 | |
|     """
 | |
|     The `MCP3202`_ is a 12-bit analog to digital converter with 2 channels
 | |
|     (0-1).
 | |
| 
 | |
|     .. _MCP3202: http://www.farnell.com/datasheets/1669376.pdf
 | |
|     """
 | |
|     def __init__(self, channel=0, differential=False, **spi_args):
 | |
|         if not 0 <= channel < 2:
 | |
|             raise SPIBadChannel('channel must be 0 or 1')
 | |
|         super(MCP3202, self).__init__(channel, differential, **spi_args)
 | |
| 
 | |
| 
 | |
| class MCP3204(MCP32xx):
 | |
|     """
 | |
|     The `MCP3204`_ is a 12-bit analog to digital converter with 4 channels
 | |
|     (0-3).
 | |
| 
 | |
|     .. _MCP3204: http://www.farnell.com/datasheets/808967.pdf
 | |
|     """
 | |
|     def __init__(self, channel=0, differential=False, **spi_args):
 | |
|         if not 0 <= channel < 4:
 | |
|             raise SPIBadChannel('channel must be between 0 and 3')
 | |
|         super(MCP3204, self).__init__(channel, differential, **spi_args)
 | |
| 
 | |
| 
 | |
| class MCP3208(MCP32xx):
 | |
|     """
 | |
|     The `MCP3208`_ is a 12-bit analog to digital converter with 8 channels
 | |
|     (0-7).
 | |
| 
 | |
|     .. _MCP3208: http://www.farnell.com/datasheets/808967.pdf
 | |
|     """
 | |
|     def __init__(self, channel=0, differential=False, **spi_args):
 | |
|         if not 0 <= channel < 8:
 | |
|             raise SPIBadChannel('channel must be between 0 and 7')
 | |
|         super(MCP3208, self).__init__(channel, differential, **spi_args)
 | |
| 
 | |
| 
 | |
| class MCP3301(MCP33xx):
 | |
|     """
 | |
|     The `MCP3301`_ is a signed 13-bit analog to digital converter.  Please note
 | |
|     that the MCP3301 always operates in differential mode measuring the
 | |
|     difference between IN+ and IN-. Its output value is scaled from -1 to +1.
 | |
| 
 | |
|     .. _MCP3301: http://www.farnell.com/datasheets/1669397.pdf
 | |
|     """
 | |
|     def __init__(self, **spi_args):
 | |
|         super(MCP3301, self).__init__(0, differential=True, **spi_args)
 | |
| 
 | |
|     def _read(self):
 | |
|         # MCP3301 protocol looks like the following:
 | |
|         #
 | |
|         #     Byte        0        1
 | |
|         #     ==== ======== ========
 | |
|         #     Rx   xx0SRRRR RRRRRRRR
 | |
|         result = self._words_to_int(self._spi.read(2), 13)
 | |
|         # Account for the sign bit
 | |
|         if result > 4095:
 | |
|             return -(8192 - result)
 | |
|         else:
 | |
|             return result
 | |
| 
 | |
| 
 | |
| class MCP3302(MCP33xx):
 | |
|     """
 | |
|     The `MCP3302`_ is a 12/13-bit analog to digital converter with 4 channels
 | |
|     (0-3). When operated in differential mode, the device outputs a signed
 | |
|     13-bit value which is scaled from -1 to +1. When operated in single-ended
 | |
|     mode (the default), the device outputs an unsigned 12-bit value scaled from
 | |
|     0 to 1.
 | |
| 
 | |
|     .. _MCP3302: http://www.farnell.com/datasheets/1486116.pdf
 | |
|     """
 | |
|     def __init__(self, channel=0, differential=False, **spi_args):
 | |
|         if not 0 <= channel < 4:
 | |
|             raise SPIBadChannel('channel must be between 0 and 4')
 | |
|         super(MCP3302, self).__init__(channel, differential, **spi_args)
 | |
| 
 | |
| 
 | |
| class MCP3304(MCP33xx):
 | |
|     """
 | |
|     The `MCP3304`_ is a 12/13-bit analog to digital converter with 8 channels
 | |
|     (0-7). When operated in differential mode, the device outputs a signed
 | |
|     13-bit value which is scaled from -1 to +1. When operated in single-ended
 | |
|     mode (the default), the device outputs an unsigned 12-bit value scaled from
 | |
|     0 to 1.
 | |
| 
 | |
|     .. _MCP3304: http://www.farnell.com/datasheets/1486116.pdf
 | |
|     """
 | |
|     def __init__(self, channel=0, differential=False, **spi_args):
 | |
|         if not 0 <= channel < 8:
 | |
|             raise SPIBadChannel('channel must be between 0 and 7')
 | |
|         super(MCP3304, self).__init__(channel, differential, **spi_args)
 | |
| 
 |