/ 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()