/ adafruit_ds1841.py
adafruit_ds1841.py
  1  # The MIT License (MIT)
  2  #
  3  # Copyright (c) 2020 Bryan Siepert for Adafruit Industries
  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  """
 23  `adafruit_ds1841`
 24  ================================================================================
 25  
 26  Library for the DS1841 I2C Logarithmic Resistor
 27  
 28  * Author(s): Bryan Siepert
 29  
 30  Implementation Notes
 31  --------------------
 32  
 33  **Hardware:**
 34  
 35  * Adafruit's DS1841 Breakout: https://www.adafruit.com/product/4570
 36  
 37  **Software and Dependencies:**
 38  
 39  * Adafruit CircuitPython firmware for the supported boards:
 40    https://circuitpython.org/downloads
 41  * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
 42  * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register"""
 43  
 44  __version__ = "0.0.0-auto.0"
 45  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DS1841.git"
 46  
 47  from time import sleep
 48  import adafruit_bus_device.i2c_device as i2c_device
 49  from adafruit_register.i2c_struct import UnaryStruct
 50  from adafruit_register.i2c_struct_array import StructArray
 51  from adafruit_register.i2c_bit import RWBit
 52  
 53  
 54  _DS1841_IVR = 0x00
 55  _DS1841_CR0 = 0x02
 56  _DS1841_CR1 = 0x03
 57  _DS1841_LUTAR = 0x08
 58  _DS1841_WR = 0x09
 59  _DS1841_CR2 = 0x0A
 60  _DS1841_TEMP = 0x0C
 61  _DS1841_VOLTAGE = 0x0E
 62  _DS1841_LUT = 0x80  # to C7h
 63  
 64  _DS1841_VCC_LSB = 25.6
 65  _DS1841_DEFAULT_ADDRESS = 0x28  # up to 0x2B
 66  
 67  
 68  class DS1841:
 69      """Driver for the DS3502 I2C Digital Potentiometer.
 70      :param ~busio.I2C i2c_bus: The I2C bus the DS3502 is connected to.
 71      :param address: The I2C device address for the sensor. Default is ``0x28``.
 72      """
 73  
 74      _lut_address = UnaryStruct(_DS1841_LUTAR, ">B")
 75      _wiper_register = UnaryStruct(_DS1841_WR, ">B")
 76  
 77      _temperature_register = UnaryStruct(_DS1841_TEMP, ">b")
 78      _voltage_register = UnaryStruct(_DS1841_VOLTAGE, ">B")
 79  
 80      # NV-capable settings
 81      _disable_save_to_eeprom = RWBit(_DS1841_CR0, 7)
 82      # Can be shadowed by EEPROM
 83      _initial_value_register = UnaryStruct(_DS1841_IVR, ">B")
 84      _adder_mode_bit = RWBit(_DS1841_CR1, 1)
 85      _update_mode = RWBit(_DS1841_CR1, 0)
 86  
 87      _manual_lut_address = RWBit(_DS1841_CR2, 1)
 88      _manual_wiper_value = RWBit(_DS1841_CR2, 2)
 89  
 90      _lut = StructArray(_DS1841_LUT, ">B", 72)
 91  
 92      def __init__(self, i2c_bus, address=_DS1841_DEFAULT_ADDRESS):
 93          self.i2c_device = i2c_device.I2CDevice(i2c_bus, address)
 94  
 95          self._disable_save_to_eeprom = True  # turn off eeprom updates to IV and CR0
 96          self._adder_mode_bit = False  # Don't add IV to WR
 97          # UPDATE MODE MUST BE FALSE FOR WIPER TO SHADOW IV
 98  
 99          self._manual_lut_address = True  #
100          self._manual_wiper_value = True  # update WR by I2C
101          self._lut_mode_enabled = False
102          self._update_mode = True
103  
104      @property
105      def wiper(self):
106          """The value of the potentionmeter's wiper.
107              :param wiper_value: The value from 0-127 to set the wiper to.
108          """
109          return self._wiper_register
110  
111      @wiper.setter
112      def wiper(self, value):
113          if value > 127:
114              raise AttributeError("wiper must be from 0-127")
115          self._wiper_register = value
116  
117      @property
118      def wiper_default(self):
119          """Sets the wiper's default value and current value to the given value
120              :param new_default: The value from 0-127 to set as the wiper's default.
121          """
122  
123          return self._initial_value_register
124  
125      @wiper_default.setter
126      def wiper_default(self, value):
127          if value > 127:
128              raise AttributeError("initial_value must be from 0-127")
129          self._disable_save_to_eeprom = False
130          # allows for IV to pass through to WR.
131          # this setting is also saved to EEPROM so IV will load into WR on boot
132          self._update_mode = False
133          sleep(0.2)
134          self._initial_value_register = value
135          sleep(0.2)
136          self._disable_save_to_eeprom = True
137          # Turn update mode back on so temp and voltage update
138          # and LUT usage works
139          self._update_mode = True
140  
141      @property
142      def temperature(self):
143          """The current temperature in degrees celcius"""
144          return self._temperature_register
145  
146      @property
147      def voltage(self):
148          """The current voltage between VCC and GND"""
149          return self._voltage_register * _DS1841_VCC_LSB
150  
151      ######## LUTS on LUTS on LUTS
152      @property
153      def lut_mode_enabled(self):
154          """Enables LUT mode. LUT mode takes sets the value of the Wiper based on the entry in a
155          72-entry Look Up Table. The LUT entry is selected using the `lut_selection`
156          property to set an index from 0-71
157          """
158          return self._lut_mode_enabled
159  
160      @lut_mode_enabled.setter
161      def lut_mode_enabled(self, value):
162          self._manual_lut_address = value
163          self._update_mode = True
164          self._manual_wiper_value = not value
165          self._lut_mode_enabled = value
166  
167      def set_lut(self, index, value):
168          """Set the value of an entry in the Look Up Table.
169              :param index: The index of the entry to set, from 0-71.
170              :param value: The value to set at the given index. The `wiper` will be set to this
171              value when the LUT entry is selected using `lut_selection`
172          """
173          if value > 127:
174              raise IndexError("set_lut value must be from 0-127")
175          lut_value_byte = bytearray([value])
176          self._lut[index] = lut_value_byte
177          sleep(0.020)
178  
179      @property
180      def lut_selection(self):
181          """Choose the entry in the Look Up Table to use to set the wiper.
182              :param index: The index of the entry to use, from 0-71.
183          """
184          if not self._lut_mode_enabled:
185              raise RuntimeError(
186                  "lut_mode_enabled must be equal to True to use lut_selection"
187              )
188          return self._lut_address - _DS1841_LUT
189  
190      @lut_selection.setter
191      def lut_selection(self, value):
192          if value > 71 or value < 0:
193              raise IndexError("lut_selection value must be from 0-71")
194          self._lut_address = value + _DS1841_LUT
195          sleep(0.020)