mirror of
				https://github.com/KevinMidboe/python-gpiozero.git
				synced 2025-10-29 17:50:37 +00:00 
			
		
		
		
	Fix #459 - properly support remote SPI with pigpio
Sorry! Dave's messing around with the pin implementations again. Hopefully the last time. The pin_factory is now really a factory object which can be asked to produce individual pins or pin-based interfaces like SPI (which can be supported properly via pigpio).
This commit is contained in:
		| @@ -1,3 +1,5 @@ | ||||
| # vim: set fileencoding=utf-8: | ||||
|  | ||||
| from __future__ import ( | ||||
|     unicode_literals, | ||||
|     absolute_import, | ||||
| @@ -6,32 +8,124 @@ from __future__ import ( | ||||
|     ) | ||||
| str = type('') | ||||
|  | ||||
| import io | ||||
|  | ||||
| from .data import pi_info | ||||
| from ..exc import ( | ||||
|     PinInvalidFunction, | ||||
|     PinSetInput, | ||||
|     PinFixedPull, | ||||
|     PinSPIUnsupported, | ||||
|     PinPWMUnsupported, | ||||
|     PinEdgeDetectUnsupported, | ||||
|     SPIFixedClockMode, | ||||
|     SPIFixedBitOrder, | ||||
|     SPIFixedSelect, | ||||
|     SPIFixedWordSize, | ||||
|     ) | ||||
|  | ||||
|  | ||||
| PINS_CLEANUP = [] | ||||
| def _pins_shutdown(): | ||||
|     for routine in PINS_CLEANUP: | ||||
|         routine() | ||||
| class Factory(object): | ||||
|     """ | ||||
|     Generates pins, SPI, and I2C interfaces for devices. This is an abstract | ||||
|     base class for pin factories. Descendents must override: | ||||
|  | ||||
|     * :meth:`_get_address` | ||||
|     * :meth:`pin_address` | ||||
|  | ||||
|     Descendents may override: | ||||
|  | ||||
|     * :meth:`close` | ||||
|     * :meth:`pin` | ||||
|     * :meth:`spi` | ||||
|     * :meth:`_get_pi_info` | ||||
|     """ | ||||
|  | ||||
|     def close(self): | ||||
|         """ | ||||
|         Closes the pin factory. This is expected to clean up all resources | ||||
|         manipulated by the factory. It it typically called at script | ||||
|         termination. | ||||
|         """ | ||||
|         pass | ||||
|  | ||||
|     def pin(self, spec): | ||||
|         """ | ||||
|         Creates an instance of a :class:`Pin` descendent representing the | ||||
|         specified pin. | ||||
|  | ||||
|         .. warning:: | ||||
|  | ||||
|             Descendents must ensure that pin instances representing the same | ||||
|             hardware are identical; i.e. two separate invocations of | ||||
|             :meth:`pin` for the same pin specification must return the same | ||||
|             object. | ||||
|         """ | ||||
|         raise PinGPIOUnsupported("GPIO not supported by this pin factory") | ||||
|  | ||||
|     def pin_address(self, spec): | ||||
|         """ | ||||
|         Returns the address that a pin *would* have if constructed from the | ||||
|         given *spec*. | ||||
|  | ||||
|         This unusual method is used by the pin reservation system to check | ||||
|         for conflicts *prior* to pin construction; with most implementations, | ||||
|         pin construction implicitly alters the state of the pin (e.g. setting | ||||
|         it to an input). This allows pin reservation to take place without | ||||
|         affecting the state of other components. | ||||
|         """ | ||||
|         raise NotImplementedError | ||||
|  | ||||
|     def spi(self, **spi_args): | ||||
|         """ | ||||
|         Returns an instance of an :class:`SPI` interface, for the specified SPI | ||||
|         *port* and *device*, or for the specified pins (*clock_pin*, | ||||
|         *mosi_pin*, *miso_pin*, and *select_pin*).  Only one of the schemes can | ||||
|         be used; attempting to mix *port* and *device* with pin numbers will | ||||
|         raise :exc:`SPIBadArgs`. | ||||
|         """ | ||||
|         raise PinSPIUnsupported('SPI not supported by this pin factory') | ||||
|  | ||||
|     def _get_address(self): | ||||
|         raise NotImplementedError | ||||
|  | ||||
|     address = property( | ||||
|         lambda self: self._get_address(), | ||||
|         doc="""\ | ||||
|         Returns a tuple of strings representing the address of the factory. | ||||
|         For the Pi itself this is a tuple of one string representing the Pi's | ||||
|         address (e.g. "localhost"). Expander chips can return a tuple appending | ||||
|         whatever string they require to uniquely identify the expander chip | ||||
|         amongst all factories in the system. | ||||
|  | ||||
|         .. note:: | ||||
|  | ||||
|             This property *must* return an immutable object capable of being | ||||
|             used as a dictionary key. | ||||
|         """) | ||||
|  | ||||
|     def _get_pi_info(self): | ||||
|         return None | ||||
|  | ||||
|     pi_info = property( | ||||
|         lambda self: self._get_pi_info(), | ||||
|         doc="""\ | ||||
|         Returns a :class:`PiBoardInfo` instance representing the Pi that | ||||
|         instances generated by this factory will be attached to. | ||||
|  | ||||
|         If the pins represented by this class are not *directly* attached to a | ||||
|         Pi (e.g. the pin is attached to a board attached to the Pi, or the pins | ||||
|         are not on a Pi at all), this may return ``None``. | ||||
|         """) | ||||
|  | ||||
|  | ||||
| class Pin(object): | ||||
|     """ | ||||
|     Abstract base class representing a GPIO pin or a pin from an IO extender. | ||||
|     Abstract base class representing a pin attached to some form of controller, | ||||
|     be it GPIO, SPI, ADC, etc. | ||||
|  | ||||
|     Descendents should override property getters and setters to accurately | ||||
|     represent the capabilities of pins. The following functions *must* be | ||||
|     overridden: | ||||
|  | ||||
|     * :meth:`_get_address` | ||||
|     * :meth:`_get_function` | ||||
|     * :meth:`_set_function` | ||||
|     * :meth:`_get_state` | ||||
| @@ -39,6 +133,8 @@ class Pin(object): | ||||
|     The following functions *may* be overridden if applicable: | ||||
|  | ||||
|     * :meth:`close` | ||||
|     * :meth:`output_with_state` | ||||
|     * :meth:`input_with_pull` | ||||
|     * :meth:`_set_state` | ||||
|     * :meth:`_get_frequency` | ||||
|     * :meth:`_set_frequency` | ||||
| @@ -50,20 +146,10 @@ class Pin(object): | ||||
|     * :meth:`_set_edges` | ||||
|     * :meth:`_get_when_changed` | ||||
|     * :meth:`_set_when_changed` | ||||
|     * :meth:`pi_info` | ||||
|     * :meth:`output_with_state` | ||||
|     * :meth:`input_with_pull` | ||||
|  | ||||
|     .. warning:: | ||||
|  | ||||
|         Descendents must ensure that pin instances representing the same | ||||
|         physical hardware are identical, right down to object identity. The | ||||
|         framework relies on this to correctly clean up resources at interpreter | ||||
|         shutdown. | ||||
|     """ | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return "Abstract pin" | ||||
|         return self.address[-1] | ||||
|  | ||||
|     def close(self): | ||||
|         """ | ||||
| @@ -105,6 +191,18 @@ class Pin(object): | ||||
|         self.function = 'input' | ||||
|         self.pull = pull | ||||
|  | ||||
|     def _get_address(self): | ||||
|         raise NotImplementedError | ||||
|  | ||||
|     address = property( | ||||
|         lambda self: self._get_address(), | ||||
|         doc="""\ | ||||
|         The address of the pin. This property is a tuple of strings constructed | ||||
|         from the owning factory's address with the unique address of the pin | ||||
|         appended to it. The tuple as a whole uniquely identifies the pin | ||||
|         amongst all pins attached to the system. | ||||
|         """) | ||||
|  | ||||
|     def _get_function(self): | ||||
|         return "input" | ||||
|  | ||||
| @@ -140,10 +238,19 @@ class Pin(object): | ||||
|         doc="""\ | ||||
|         The state of the pin. This is 0 for low, and 1 for high. As a low level | ||||
|         view of the pin, no swapping is performed in the case of pull ups (see | ||||
|         :attr:`pull` for more information). | ||||
|         :attr:`pull` for more information): | ||||
|  | ||||
|         If PWM is currently active (when :attr:`frequency` is not ``None``), | ||||
|         this represents the PWM duty cycle as a value between 0.0 and 1.0. | ||||
|         .. code-block:: text | ||||
|  | ||||
|             HIGH - - - - >       ,---------------------- | ||||
|                                  | | ||||
|                                  | | ||||
|             LOW  ----------------' | ||||
|  | ||||
|         Descendents which implement analog, or analog-like capabilities can | ||||
|         return values between 0 and 1. For example, pins implementing PWM | ||||
|         (where :attr:`frequency` is not ``None``) return a value between 0.0 | ||||
|         and 1.0 representing the current PWM duty cycle. | ||||
|  | ||||
|         If a pin is currently configured for input, and an attempt is made to | ||||
|         set this attribute, :exc:`PinSetInput` will be raised. If an invalid | ||||
| @@ -205,6 +312,26 @@ class Pin(object): | ||||
|         detection, measured in seconds. If bounce detection is not currently in | ||||
|         use, this is ``None``. | ||||
|  | ||||
|         For example, if :attr:`edge` is currently "rising", :attr:`bounce` is | ||||
|         currently 5/1000 (5ms), then the waveform below will only fire | ||||
|         :attr:`when_changed` on two occasions despite there being three rising | ||||
|         edges: | ||||
|  | ||||
|         .. code-block:: text | ||||
|  | ||||
|             TIME 0...1...2...3...4...5...6...7...8...9...10..11..12 ms | ||||
|  | ||||
|             bounce elimination   |===================| |============== | ||||
|  | ||||
|             HIGH - - - - >       ,--. ,--------------. ,--. | ||||
|                                  |  | |              | |  | | ||||
|                                  |  | |              | |  | | ||||
|             LOW  ----------------'  `-'              `-'  `----------- | ||||
|                                  :                     : | ||||
|                                  :                     : | ||||
|                            when_changed          when_changed | ||||
|                                fires                 fires | ||||
|  | ||||
|         If the pin does not support edge detection, attempts to set this | ||||
|         property will raise :exc:`PinEdgeDetectUnsupported`. If the pin | ||||
|         supports edge detection, the class must implement bounce detection, | ||||
| @@ -223,7 +350,18 @@ class Pin(object): | ||||
|         doc="""\ | ||||
|         The edge that will trigger execution of the function or bound method | ||||
|         assigned to :attr:`when_changed`. This can be one of the strings | ||||
|         "both" (the default), "rising", "falling", or "none". | ||||
|         "both" (the default), "rising", "falling", or "none": | ||||
|  | ||||
|         .. code-block:: text | ||||
|  | ||||
|             HIGH - - - - >           ,--------------. | ||||
|                                      |              | | ||||
|                                      |              | | ||||
|             LOW  --------------------'              `-------------- | ||||
|                                      :              : | ||||
|                                      :              : | ||||
|             Fires when_changed     "both"         "both" | ||||
|             when edges is ...     "rising"       "falling" | ||||
|  | ||||
|         If the pin does not support edge detection, attempts to set this | ||||
|         property will raise :exc:`PinEdgeDetectUnsupported`. | ||||
| @@ -247,48 +385,300 @@ class Pin(object): | ||||
|         property will raise :exc:`PinEdgeDetectUnsupported`. | ||||
|         """) | ||||
|  | ||||
|     @classmethod | ||||
|     def pi_info(cls): | ||||
|         """ | ||||
|         Returns a :class:`PiBoardInfo` instance representing the Pi that | ||||
|         instances of this pin class will be attached to. | ||||
|  | ||||
|         If the pins represented by this class are not *directly* attached to a | ||||
|         Pi (e.g. the pin is attached to a board attached to the Pi, or the pins | ||||
|         are not on a Pi at all), this may return ``None``. | ||||
|         """ | ||||
|         return None | ||||
|  | ||||
|  | ||||
| class LocalPin(Pin): | ||||
| class SPI(object): | ||||
|     """ | ||||
|     Abstract base class representing pins attached locally to a Pi. This forms | ||||
|     the base class for local-only pin interfaces (:class:`RPiGPIOPin`, | ||||
|     :class:`RPIOPin`, and :class:`NativePin`). | ||||
|     Abstract interface for `Serial Peripheral Interface`_ (SPI) implementations. | ||||
|     Descendents *must* override the following: | ||||
|  | ||||
|     * :meth:`transfer` | ||||
|     * :meth:`_get_clock_mode` | ||||
|  | ||||
|     Descendents *may* override the following methods: | ||||
|  | ||||
|     * :meth:`read` | ||||
|     * :meth:`write` | ||||
|     * :meth:`_set_clock_mode` | ||||
|     * :meth:`_get_lsb_first` | ||||
|     * :meth:`_set_lsb_first` | ||||
|     * :meth:`_get_select_high` | ||||
|     * :meth:`_set_select_high` | ||||
|     * :meth:`_get_bits_per_word` | ||||
|     * :meth:`_set_bits_per_word` | ||||
|  | ||||
|     .. _Serial Peripheral Interface: https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus | ||||
|     """ | ||||
|     _PI_REVISION = None | ||||
|  | ||||
|     @classmethod | ||||
|     def pi_info(cls): | ||||
|     def read(self, n): | ||||
|         """ | ||||
|         Returns a :class:`PiBoardInfo` instance representing the local Pi. | ||||
|         The Pi's revision is determined by reading :file:`/proc/cpuinfo`. If | ||||
|         no valid revision is found, returns ``None``. | ||||
|         """ | ||||
|         # Cache the result as we can reasonably assume it won't change during | ||||
|         # runtime (this is LocalPin after all; descendents that deal with | ||||
|         # remote Pis should inherit from Pin instead) | ||||
|         if cls._PI_REVISION is None: | ||||
|             with io.open('/proc/cpuinfo', 'r') as f: | ||||
|                 for line in f: | ||||
|                     if line.startswith('Revision'): | ||||
|                         revision = line.split(':')[1].strip().lower() | ||||
|                         overvolted = revision.startswith('100') | ||||
|                         if overvolted: | ||||
|                             revision = revision[-4:] | ||||
|                         cls._PI_REVISION = revision | ||||
|                         break | ||||
|                 if cls._PI_REVISION is None: | ||||
|                     return None # something weird going on | ||||
|         return pi_info(cls._PI_REVISION) | ||||
|         Read *n* words of data from the SPI interface, returning them as a | ||||
|         sequence of unsigned ints, each no larger than the configured | ||||
|         :attr:`bits_per_word` of the interface. | ||||
|  | ||||
|         This method is typically used with read-only devices that feature | ||||
|         half-duplex communication. See :meth:`transfer` for full duplex | ||||
|         communication. | ||||
|         """ | ||||
|         return self.transfer((0,) * n) | ||||
|  | ||||
|     def write(self, data): | ||||
|         """ | ||||
|         Write *data* to the SPI interface. *data* must be a sequence of | ||||
|         unsigned integer words each of which will fit within the configured | ||||
|         :attr:`bits_per_word` of the interface. The method returns the number | ||||
|         of words written to the interface (which may be less than or equal to | ||||
|         the length of *data*). | ||||
|  | ||||
|         This method is typically used with write-only devices that feature | ||||
|         half-duplex communication. See :meth:`transfer` for full duplex | ||||
|         communication. | ||||
|         """ | ||||
|         return len(self.transfer(data)) | ||||
|  | ||||
|     def transfer(self, data): | ||||
|         """ | ||||
|         Write *data* to the SPI interface. *data* must be a sequence of | ||||
|         unsigned integer words each of which will fit within the configured | ||||
|         :attr:`bits_per_word` of the interface. The method returns the sequence | ||||
|         of words read from the interface while writing occurred (full duplex | ||||
|         communication). | ||||
|  | ||||
|         The length of the sequence returned dictates the number of words of | ||||
|         *data* written to the interface. Each word in the returned sequence | ||||
|         will be an unsigned integer no larger than the configured | ||||
|         :attr:`bits_per_word` of the interface. | ||||
|         """ | ||||
|         raise NotImplementedError | ||||
|  | ||||
|     @property | ||||
|     def clock_polarity(self): | ||||
|         """ | ||||
|         The polarity of the SPI clock pin. If this is ``False`` (the default), | ||||
|         the clock pin will idle low, and pulse high. Setting this to ``True`` | ||||
|         will cause the clock pin to idle high, and pulse low. On many data | ||||
|         sheets this is documented as the CPOL value. | ||||
|  | ||||
|         The following diagram illustrates the waveform when | ||||
|         :attr:`clock_polarity` is ``False`` (the default), equivalent to CPOL | ||||
|         0: | ||||
|  | ||||
|         .. code-block:: text | ||||
|  | ||||
|                    on      on      on      on      on      on      on | ||||
|                   ,---.   ,---.   ,---.   ,---.   ,---.   ,---.   ,---. | ||||
|             CLK   |   |   |   |   |   |   |   |   |   |   |   |   |   | | ||||
|                   |   |   |   |   |   |   |   |   |   |   |   |   |   | | ||||
|             ------'   `---'   `---'   `---'   `---'   `---'   `---'   `------ | ||||
|             idle       off     off     off     off     off     off       idle | ||||
|  | ||||
|         The following diagram illustrates the waveform when | ||||
|         :attr:`clock_polarity` is ``True``, equivalent to CPOL 1: | ||||
|  | ||||
|         .. code-block:: text | ||||
|  | ||||
|             idle       off     off     off     off     off     off       idle | ||||
|             ------.   ,---.   ,---.   ,---.   ,---.   ,---.   ,---.   ,------ | ||||
|                   |   |   |   |   |   |   |   |   |   |   |   |   |   | | ||||
|             CLK   |   |   |   |   |   |   |   |   |   |   |   |   |   | | ||||
|                   `---'   `---'   `---'   `---'   `---'   `---'   `---' | ||||
|                    on      on      on      on      on      on      on | ||||
|         """ | ||||
|         return bool(self.clock_mode & 2) | ||||
|  | ||||
|     @clock_polarity.setter | ||||
|     def clock_polarity(self, value): | ||||
|         self.clock_mode = self.clock_mode & (~2) | (bool(value) << 1) | ||||
|  | ||||
|     @property | ||||
|     def clock_phase(self): | ||||
|         """ | ||||
|         The phase of the SPI clock pin. If this is ``False`` (the default), | ||||
|         data will be read from the MISO pin when the clock pin activates. | ||||
|         Setting this to ``True`` will cause data to be read from the MISO pin | ||||
|         when the clock pin deactivates. On many data sheets this is documented | ||||
|         as the CPHA value. Whether the clock edge is rising or falling when the | ||||
|         clock is considered activated is controlled by the | ||||
|         :attr:`clock_polarity` attribute (corresponding to CPOL). | ||||
|  | ||||
|         The following diagram indicates when data is read when | ||||
|         :attr:`clock_polarity` is ``False``, and :attr:`clock_phase` is | ||||
|         ``False`` (the default), equivalent to CPHA 0: | ||||
|  | ||||
|         .. code-block:: text | ||||
|  | ||||
|                 ,---.   ,---.   ,---.   ,---.   ,---.   ,---.   ,---. | ||||
|             CLK |   |   |   |   |   |   |   |   |   |   |   |   |   | | ||||
|                 |   |   |   |   |   |   |   |   |   |   |   |   |   | | ||||
|             ----'   `---'   `---'   `---'   `---'   `---'   `---'   `------- | ||||
|                 :       :       :       :       :       :       : | ||||
|             MISO---.   ,---.   ,---.   ,---.   ,---.   ,---.   ,---. | ||||
|               /     \ /     \ /     \ /     \ /     \ /     \ /     \\ | ||||
|             -{  Bit  X  Bit  X  Bit  X  Bit  X  Bit  X  Bit  X  Bit  }------ | ||||
|               \     / \     / \     / \     / \     / \     / \     / | ||||
|                `---'   `---'   `---'   `---'   `---'   `---'   `---' | ||||
|  | ||||
|         The following diagram indicates when data is read when | ||||
|         :attr:`clock_polarity` is ``False``, but :attr:`clock_phase` is | ||||
|         ``True``, equivalent to CPHA 1: | ||||
|  | ||||
|         .. code-block:: text | ||||
|  | ||||
|                 ,---.   ,---.   ,---.   ,---.   ,---.   ,---.   ,---. | ||||
|             CLK |   |   |   |   |   |   |   |   |   |   |   |   |   | | ||||
|                 |   |   |   |   |   |   |   |   |   |   |   |   |   | | ||||
|             ----'   `---'   `---'   `---'   `---'   `---'   `---'   `------- | ||||
|                     :       :       :       :       :       :       : | ||||
|             MISO   ,---.   ,---.   ,---.   ,---.   ,---.   ,---.   ,---. | ||||
|                   /     \ /     \ /     \ /     \ /     \ /     \ /     \\ | ||||
|             -----{  Bit  X  Bit  X  Bit  X  Bit  X  Bit  X  Bit  X  Bit  }-- | ||||
|                   \     / \     / \     / \     / \     / \     / \     / | ||||
|                    `---'   `---'   `---'   `---'   `---'   `---'   `---' | ||||
|         """ | ||||
|         return bool(self.clock_mode & 1) | ||||
|  | ||||
|     @clock_phase.setter | ||||
|     def clock_phase(self, value): | ||||
|         self.clock_mode = self.clock_mode & (~1) | bool(value) | ||||
|  | ||||
|     def _get_clock_mode(self): | ||||
|         raise NotImplementedError | ||||
|  | ||||
|     def _set_clock_mode(self, value): | ||||
|         raise SPIFixedClockMode("clock_mode cannot be changed on %r" % self) | ||||
|  | ||||
|     clock_mode = property( | ||||
|         lambda self: self._get_clock_mode(), | ||||
|         lambda self, value: self._set_clock_mode(value), | ||||
|         doc="""\ | ||||
|         Presents a value representing the :attr:`clock_polarity` and | ||||
|         :attr:`clock_phase` attributes combined according to the following | ||||
|         table: | ||||
|  | ||||
|         +------+-----------------+--------------+ | ||||
|         | mode | polarity (CPOL) | phase (CPHA) | | ||||
|         +======+=================+==============+ | ||||
|         | 0    | False           | False        | | ||||
|         | 1    | False           | True         | | ||||
|         | 2    | True            | False        | | ||||
|         | 3    | True            | True         | | ||||
|         +------+-----------------+--------------+ | ||||
|  | ||||
|         Adjusting this value adjusts both the :attr:`clock_polarity` and | ||||
|         :attr:`clock_phase` attributes simultaneously. | ||||
|         """) | ||||
|  | ||||
|     def _get_lsb_first(self): | ||||
|         return False | ||||
|  | ||||
|     def _set_lsb_first(self, value): | ||||
|         raise SPIFixedBitOrder("lsb_first cannot be changed on %r" % self) | ||||
|  | ||||
|     lsb_first = property( | ||||
|         lambda self: self._get_lsb_first(), | ||||
|         lambda self, value: self._set_lsb_first(value), | ||||
|         doc="""\ | ||||
|         Controls whether words are read and written LSB in (Least Significant | ||||
|         Bit first) order. The default is ``False`` indicating that words are | ||||
|         read and written in MSB (Most Significant Bit first) order. | ||||
|         Effectively, this controls the `Bit endianness`_ of the connection. | ||||
|  | ||||
|         The following diagram shows the a word containing the number 5 (binary | ||||
|         0101) transmitted on MISO with :attr:`bits_per_word` set to 4, and | ||||
|         :attr:`clock_mode` set to 0, when :attr:`lsb_first` is ``False`` (the | ||||
|         default): | ||||
|  | ||||
|         .. code-block:: text | ||||
|  | ||||
|                 ,---.   ,---.   ,---.   ,---. | ||||
|             CLK |   |   |   |   |   |   |   | | ||||
|                 |   |   |   |   |   |   |   | | ||||
|             ----'   `---'   `---'   `---'   `----- | ||||
|                 :     ,-------. :     ,-------. | ||||
|             MISO:     | :     | :     | :     | | ||||
|                 :     | :     | :     | :     | | ||||
|             ----------' :     `-------' :     `---- | ||||
|                 :       :       :       : | ||||
|                MSB                     LSB | ||||
|  | ||||
|         And now with :attr:`lsb_first` set to ``True`` (and all other | ||||
|         parameters the same): | ||||
|  | ||||
|         .. code-block:: text | ||||
|  | ||||
|                 ,---.   ,---.   ,---.   ,---. | ||||
|             CLK |   |   |   |   |   |   |   | | ||||
|                 |   |   |   |   |   |   |   | | ||||
|             ----'   `---'   `---'   `---'   `----- | ||||
|               ,-------. :     ,-------. : | ||||
|             MISO:     | :     | :     | : | ||||
|               | :     | :     | :     | : | ||||
|             --' :     `-------' :     `----------- | ||||
|                 :       :       :       : | ||||
|                LSB                     MSB | ||||
|  | ||||
|         .. _Bit endianness: https://en.wikipedia.org/wiki/Endianness#Bit_endianness | ||||
|         """) | ||||
|  | ||||
|     def _get_select_high(self): | ||||
|         return False | ||||
|  | ||||
|     def _set_select_high(self, value): | ||||
|         raise SPIFixedSelect("select_high cannot be changed on %r" % self) | ||||
|  | ||||
|     select_high = property( | ||||
|         lambda self: self._get_select_high(), | ||||
|         lambda self, value: self._set_select_high(value), | ||||
|         doc="""\ | ||||
|         If ``False`` (the default), the chip select line is considered active | ||||
|         when it is pulled low. When set to ``True``, the chip select line is | ||||
|         considered active when it is driven high. | ||||
|  | ||||
|         The following diagram shows the waveform of the chip select line, and | ||||
|         the clock when :attr:`clock_polarity` is ``False``, and | ||||
|         :attr:`select_high` is ``False`` (the default): | ||||
|  | ||||
|         .. code-block:: text | ||||
|  | ||||
|             ---.                                                     ,------ | ||||
|             __ |                                                     | | ||||
|             CS |      chip is selected, and will react to clock      |  idle | ||||
|                `-----------------------------------------------------' | ||||
|  | ||||
|                 ,---.   ,---.   ,---.   ,---.   ,---.   ,---.   ,---. | ||||
|             CLK |   |   |   |   |   |   |   |   |   |   |   |   |   | | ||||
|                 |   |   |   |   |   |   |   |   |   |   |   |   |   | | ||||
|             ----'   `---'   `---'   `---'   `---'   `---'   `---'   `------- | ||||
|  | ||||
|         And when :attr:`select_high` is ``True``: | ||||
|  | ||||
|         .. code-block:: text | ||||
|  | ||||
|                ,-----------------------------------------------------. | ||||
|             CS |      chip is selected, and will react to clock      |  idle | ||||
|                |                                                     | | ||||
|             ---'                                                     `------ | ||||
|  | ||||
|                 ,---.   ,---.   ,---.   ,---.   ,---.   ,---.   ,---. | ||||
|             CLK |   |   |   |   |   |   |   |   |   |   |   |   |   | | ||||
|                 |   |   |   |   |   |   |   |   |   |   |   |   |   | | ||||
|             ----'   `---'   `---'   `---'   `---'   `---'   `---'   `------- | ||||
|         """) | ||||
|  | ||||
|     def _get_bits_per_word(self): | ||||
|         return 8 | ||||
|  | ||||
|     def _set_bits_per_word(self, value): | ||||
|         raise SPIFixedWordSize("bits_per_word cannot be changed on %r" % self) | ||||
|  | ||||
|     bits_per_word = property( | ||||
|         lambda self: self._get_bits_per_word(), | ||||
|         lambda self, value: self._set_bits_per_word(value), | ||||
|         doc="""\ | ||||
|         Controls the number of bits that make up a word, and thus where the | ||||
|         word boundaries appear in the data stream, and the maximum value of a | ||||
|         word. Defaults to 8 meaning that words are effectively bytes. | ||||
|  | ||||
|         Several implementations do not support non-byte-sized words. | ||||
|         """) | ||||
|  | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user