/ adafruit_ds2413.py
adafruit_ds2413.py
  1  # The MIT License (MIT)
  2  #
  3  # Copyright (c) 2017 Carter Nelson for adafruit
  4  #
  5  # Permission is hereby granted, free of charge, to any person obtaining a copy
  6  # of this software and associated documentation files (the "Software"), to deal
  7  # in the Software without restriction, including without limitation the rights
  8  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9  # copies of the Software, and to permit persons to whom the Software is
 10  # furnished to do so, subject to the following conditions:
 11  #
 12  # The above copyright notice and this permission notice shall be included in
 13  # all copies or substantial portions of the Software.
 14  #
 15  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 21  # THE SOFTWARE.
 22  # pylint: disable=C0103
 23  """
 24  `adafruit_ds2413`
 25  ====================================================
 26  
 27   CircuitPython driver for the DS2413 one wire 2 channel GPIO breakout.
 28  
 29  * Author(s): Carter Nelson
 30  """
 31  
 32  __version__ = "0.0.0-auto.0"
 33  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DS2413.git"
 34  
 35  from micropython import const
 36  from adafruit_onewire.device import OneWireDevice
 37  
 38  _DS2413_ACCESS_READ = b"\xF5"
 39  _DS2413_ACCESS_WRITE = b"\x5A"
 40  _DS2413_ACK_SUCCESS = b"\xAA"
 41  _DS2413_ACK_ERROR = b"\xFF"
 42  INPUT = const(0)
 43  OUTPUT = const(1)
 44  
 45  
 46  class DS2413Pin:
 47      """Class which provides interface to single DS2413 GPIO pin."""
 48  
 49      def __init__(self, number, host, direction=OUTPUT):
 50          if number not in (0, 1):
 51              raise ValueError("Incorrect pin number.")
 52          self._number = number
 53          self._host = host
 54          self._mask = 1 << (number * 2)
 55          self._direction = None  # create it, and then...
 56          self.direction = direction  # set it through setter
 57  
 58      @property
 59      def direction(self):
 60          """The direction of the pin, either INPUT or OUTPUT."""
 61          return self._direction
 62  
 63      @direction.setter
 64      def direction(self, direction):
 65          if direction not in (INPUT, OUTPUT):
 66              raise ValueError("Incorrect direction setting.")
 67          self._direction = OUTPUT
 68          self.value = False
 69          self._direction = direction
 70  
 71      @property
 72      def value(self):
 73          """The pin state if configured as INPUT. The output latch state
 74          if configured as OUTPUT. True is HIGH/ON, False is LOW/OFF."""
 75          # return Pin State if configured for INPUT
 76          # return Latch State if configured for OUTPUT
 77          # NOTE: logic is re-inverted to make it more normally
 78          return not self._host.pio_state & (self._mask << self._direction)
 79  
 80      @value.setter
 81      def value(self, state):
 82          # This only makes sense if the pin is configured for OUTPUT.
 83          if self._direction == INPUT:
 84              raise RuntimeError("Can't set value when pin is set to input.")
 85          # We jump through some hoops in order to only set/clear the bit
 86          # for the channel associated with this pin object.
 87          current = self._host.pio_state
 88          #       PIOB Output Latch       PIOA Output Latch
 89          new = (current >> 2 & 0x02) | (current >> 1 & 0x01)
 90          # To switch the output transistor on, the corresponding bit value is 0.
 91          # To switch the output transistor off (non-conducting) the bit must be 1.
 92          if state:
 93              # clear it (transistor = ON)
 94              new &= ~(1 << self._number)
 95          else:
 96              # set it (transistor = OFF)
 97              new |= 1 << self._number
 98          self._host.pio_state = new
 99  
100  
101  class DS2413:
102      """Class which provides interface to DS2413 GPIO breakout."""
103  
104      def __init__(self, bus, address):
105          if address.family_code == 0x3A:
106              self._address = address
107              self._device = OneWireDevice(bus, address)
108              self._buf = bytearray(3)
109              self._IOA = None
110              self._IOB = None
111          else:
112              raise RuntimeError("Incorrect family code in device address.")
113  
114      @property
115      def IOA(self):
116          """The pin object for channel A."""
117          if self._IOA is None:
118              self._IOA = DS2413Pin(0, self)
119          return self._IOA
120  
121      @property
122      def IOB(self):
123          """The pin object for channel B."""
124          if self._IOB is None:
125              self._IOB = DS2413Pin(1, self)
126          return self._IOB
127  
128      @property
129      def pio_state(self):
130          """The state of both PIO channels."""
131          return self._read_status()
132  
133      @pio_state.setter
134      def pio_state(self, value):
135          return self._write_latches(value)
136  
137      def _read_status(self):
138          with self._device as dev:
139              dev.write(_DS2413_ACCESS_READ)
140              dev.readinto(self._buf, end=1)
141          return self._buf[0]
142  
143      def _write_latches(self, value):
144          # top six bits must be 1
145          value |= 0xFC
146          self._buf[0] = value
147          self._buf[1] = ~value & 0xFF
148          with self._device as dev:
149              dev.write(_DS2413_ACCESS_WRITE)
150              dev.write(self._buf, end=2)
151              dev.readinto(self._buf, end=1)
152          if not self._buf[0] == ord(_DS2413_ACK_SUCCESS):
153              raise RuntimeError("ACK failure.")