/ adafruit_pca9685.py
adafruit_pca9685.py
  1  # The MIT License (MIT)
  2  #
  3  # Copyright (c) 2016 Radomir Dopieralski, written for Adafruit Industries
  4  # Copyright (c) 2017 Scott Shawcroft for Adafruit Industries LLC
  5  #
  6  # Permission is hereby granted, free of charge, to any person obtaining a copy
  7  # of this software and associated documentation files (the "Software"), to deal
  8  # in the Software without restriction, including without limitation the rights
  9  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 10  # copies of the Software, and to permit persons to whom the Software is
 11  # furnished to do so, subject to the following conditions:
 12  #
 13  # The above copyright notice and this permission notice shall be included in
 14  # all copies or substantial portions of the Software.
 15  #
 16  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 17  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 18  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 19  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 20  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 21  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 22  # THE SOFTWARE.
 23  """
 24  `adafruit_pca9685`
 25  ====================================================
 26  
 27  Driver for the PCA9685 PWM control IC. Its commonly used to control servos, leds and motors.
 28  
 29  .. seealso:: The `Adafruit CircuitPython Motor library
 30      <https://github.com/adafruit/Adafruit_CircuitPython_Motor>`_ can be used to control the PWM
 31      outputs for specific uses instead of generic duty_cycle adjustments.
 32  
 33  * Author(s): Scott Shawcroft
 34  
 35  Implementation Notes
 36  --------------------
 37  
 38  **Hardware:**
 39  
 40  * Adafruit `16-Channel 12-bit PWM/Servo Driver - I2C interface - PCA9685
 41    <https://www.adafruit.com/product/815>`_ (Product ID: 815)
 42  
 43  **Software and Dependencies:**
 44  
 45  * Adafruit CircuitPython firmware for the ESP8622 and M0-based boards:
 46    https://github.com/adafruit/circuitpython/releases
 47  * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice
 48  * Adafruit's Register library: https://github.com/adafruit/Adafruit_CircuitPython_Register
 49  """
 50  
 51  __version__ = "0.0.0-auto.0"
 52  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_PCA9685.git"
 53  
 54  import time
 55  
 56  from adafruit_register.i2c_struct import UnaryStruct
 57  from adafruit_register.i2c_struct_array import StructArray
 58  from adafruit_bus_device import i2c_device
 59  
 60  
 61  class PWMChannel:
 62      """A single PCA9685 channel that matches the :py:class:`~pulseio.PWMOut` API."""
 63  
 64      def __init__(self, pca, index):
 65          self._pca = pca
 66          self._index = index
 67  
 68      @property
 69      def frequency(self):
 70          """The overall PWM frequency in Hertz (read-only).
 71          A PWMChannel's frequency cannot be set individually.
 72          All channels share a common frequency, set by PCA9685.frequency."""
 73          return self._pca.frequency
 74  
 75      @frequency.setter
 76      def frequency(self, _):
 77          raise NotImplementedError("frequency cannot be set on individual channels")
 78  
 79      @property
 80      def duty_cycle(self):
 81          """16 bit value that dictates how much of one cycle is high (1) versus low (0). 0xffff will
 82             always be high, 0 will always be low and 0x7fff will be half high and then half low."""
 83          pwm = self._pca.pwm_regs[self._index]
 84          if pwm[0] == 0x1000:
 85              return 0xFFFF
 86          return pwm[1] << 4
 87  
 88      @duty_cycle.setter
 89      def duty_cycle(self, value):
 90          if not 0 <= value <= 0xFFFF:
 91              raise ValueError("Out of range")
 92  
 93          if value == 0xFFFF:
 94              self._pca.pwm_regs[self._index] = (0x1000, 0)
 95          else:
 96              # Shift our value by four because the PCA9685 is only 12 bits but our value is 16
 97              value = (value + 1) >> 4
 98              self._pca.pwm_regs[self._index] = (0, value)
 99  
100  
101  class PCAChannels:  # pylint: disable=too-few-public-methods
102      """Lazily creates and caches channel objects as needed. Treat it like a sequence."""
103  
104      def __init__(self, pca):
105          self._pca = pca
106          self._channels = [None] * len(self)
107  
108      def __len__(self):
109          return 16
110  
111      def __getitem__(self, index):
112          if not self._channels[index]:
113              self._channels[index] = PWMChannel(self._pca, index)
114          return self._channels[index]
115  
116  
117  class PCA9685:
118      """
119      Initialise the PCA9685 chip at ``address`` on ``i2c_bus``.
120  
121      The internal reference clock is 25mhz but may vary slightly with environmental conditions and
122      manufacturing variances. Providing a more precise ``reference_clock_speed`` can improve the
123      accuracy of the frequency and duty_cycle computations. See the ``calibration.py`` example for
124      how to derive this value by measuring the resulting pulse widths.
125  
126      :param ~busio.I2C i2c_bus: The I2C bus which the PCA9685 is connected to.
127      :param int address: The I2C address of the PCA9685.
128      :param int reference_clock_speed: The frequency of the internal reference clock in Hertz.
129      """
130  
131      # Registers:
132      mode1_reg = UnaryStruct(0x00, "<B")
133      prescale_reg = UnaryStruct(0xFE, "<B")
134      pwm_regs = StructArray(0x06, "<HH", 16)
135  
136      def __init__(self, i2c_bus, *, address=0x40, reference_clock_speed=25000000):
137          self.i2c_device = i2c_device.I2CDevice(i2c_bus, address)
138          self.channels = PCAChannels(self)
139          """Sequence of 16 `PWMChannel` objects. One for each channel."""
140          self.reference_clock_speed = reference_clock_speed
141          """The reference clock speed in Hz."""
142          self.reset()
143  
144      def reset(self):
145          """Reset the chip."""
146          self.mode1_reg = 0x00  # Mode1
147  
148      @property
149      def frequency(self):
150          """The overall PWM frequency in Hertz."""
151          return self.reference_clock_speed / 4096 / self.prescale_reg
152  
153      @frequency.setter
154      def frequency(self, freq):
155          prescale = int(self.reference_clock_speed / 4096.0 / freq + 0.5)
156          if prescale < 3:
157              raise ValueError("PCA9685 cannot output at the given frequency")
158          old_mode = self.mode1_reg  # Mode 1
159          self.mode1_reg = (old_mode & 0x7F) | 0x10  # Mode 1, sleep
160          self.prescale_reg = prescale  # Prescale
161          self.mode1_reg = old_mode  # Mode 1
162          time.sleep(0.005)
163          self.mode1_reg = old_mode | 0xA1  # Mode 1, autoincrement on
164  
165      def __enter__(self):
166          return self
167  
168      def __exit__(self, exception_type, exception_value, traceback):
169          self.deinit()
170  
171      def deinit(self):
172          """Stop using the pca9685."""
173          self.reset()