/ adafruit_tlc5947.py
adafruit_tlc5947.py
1 # The MIT License (MIT) 2 # 3 # Copyright (c) 2017 Tony DiCola 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_tlc5947` 24 ==================================================== 25 26 CircuitPython module for the TLC5947 12-bit 24 channel LED PWM driver. See 27 examples/simpletest.py for a demo of the usage. 28 29 * Author(s): Tony DiCola, Walter Haschka 30 31 Implementation Notes 32 -------------------- 33 34 **Hardware:** 35 36 * Adafruit `24-Channel 12-bit PWM LED Driver - SPI Interface - TLC5947 37 <https://www.adafruit.com/product/1429>`_ (Product ID: 1429) 38 39 **Software and Dependencies:** 40 41 * Adafruit CircuitPython firmware for the ESP8622 and M0-based boards: 42 https://github.com/adafruit/circuitpython/releases 43 """ 44 __version__ = "0.0.0-auto.0" 45 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_TLC5947.git" 46 47 48 # Globally disable protected access. Ppylint can't figure out the 49 # context for using internal decorate classes below. In these cases protectected 50 # access is by design for the internal class. 51 # pylint: disable=protected-access 52 53 _CHANNELS = 24 54 _STOREBYTES = _CHANNELS + _CHANNELS // 2 55 56 57 class TLC5947: 58 """TLC5947 12-bit 24 channel LED PWM driver. Create an instance of this by 59 passing in at least the following parameters: 60 61 :param spi: The SPI bus connected to the chip (only the SCK and MOSI lines are 62 used, there is no MISO/input). 63 :param latch: A DigitalInOut instance connected to the chip's latch line. 64 65 Optionally you can specify: 66 67 :param auto_write: This is a boolean that defaults to True and will automatically 68 write out all the channel values to the chip as soon as a 69 single one is updated. If you set to False to disable then 70 you MUST call write after every channel update or when you 71 deem necessary to update the chip state. 72 73 :param num_drivers: This is an integer that defaults to 1. It stands for the 74 number of chained LED driver boards (DOUT of one board has 75 to be connected to DIN of the next). For each board added, 76 36 bytes of RAM memory will be taken. The channel numbers 77 on the driver directly connected to the controller are 0 to 78 23, and for each driver add 24 to the port number printed. 79 The more drivers are chained, the more viable it is to set 80 auto_write=False, and call write explicitly after updating 81 all the channels. 82 """ 83 84 class PWMOut: 85 """Internal PWMOut class that mimics the behavior of CircuitPython's 86 PWMOut class but is associated with a channel on the TLC5947. You can 87 get and set the instance's duty_cycle property as a 16-bit PWM value 88 (note there will be quantization errors as the TLC5947 is a 12-bit PWM 89 chip, instead use the TLC5947 class item accessor notation for direct 90 12-bit raw PWM channel access). Note you cannot change the frequency 91 as it is fixed by the TLC5947 to ~2.4-5.6 mhz. 92 """ 93 94 def __init__(self, tlc5947, channel): 95 self._tlc5947 = tlc5947 96 self._channel = channel 97 98 @property 99 def duty_cycle(self): 100 """Get and set the 16-bit PWM duty cycle value for this channel. 101 """ 102 raw_value = self._tlc5947._get_gs_value(self._channel) 103 # Convert to 16-bit value from 12-bits and return it. 104 return (raw_value << 4) & 0xFFFF 105 106 @duty_cycle.setter 107 def duty_cycle(self, val): 108 if val < 0 or val > 65535: 109 raise ValueError( 110 "PWM intensity {0} outside supported range [0;65535]".format(val) 111 ) 112 # Convert to 12-bit value (quantization error will occur!). 113 val = (val >> 4) & 0xFFF 114 self._tlc5947._set_gs_value(self._channel, val) 115 116 @property 117 def frequency(self): 118 """Frequency of the PWM channel, note you cannot change this and 119 cannot read its exact value (it varies from 2.4-5.6 mhz, see the 120 TLC5947 datasheet). 121 """ 122 return 0 123 124 # pylint bug misidentifies the following as a regular function instead 125 # of the associated setter: https://github.com/PyCQA/pylint/issues/870 126 # Must disable a few checks to make pylint happy (ugh). 127 # pylint: disable=no-self-use,unused-argument 128 @frequency.setter 129 def frequency(self, val): 130 raise RuntimeError("Cannot set TLC5947 PWM frequency!") 131 132 # pylint: enable=no-self-use,unused-argument 133 134 def __init__(self, spi, latch, *, auto_write=True, num_drivers=1): 135 if num_drivers < 1: 136 raise ValueError( 137 "Need at least one driver; {0} is not supported.".format(num_drivers) 138 ) 139 self._spi = spi 140 self._latch = latch 141 self._latch.switch_to_output(value=False) 142 # This device is just a big 36*n byte long shift register. There's no 143 # fancy protocol or other commands to send, just write out all 288*n 144 # bits every time the state is updated. 145 self._n = num_drivers 146 self._shift_reg = bytearray(_STOREBYTES * self._n) 147 # Save auto_write state (i.e. push out shift register values on 148 # any channel value change). 149 self.auto_write = auto_write 150 151 def write(self): 152 """Write out the current channel PWM values to the chip. This is only 153 necessary to call if you disabled auto_write in the initializer, 154 otherwise write is automatically called on any channel update. 155 """ 156 # Write out the current state to the shift register. 157 try: 158 # Lock the SPI bus and configure it for the shift register. 159 while not self._spi.try_lock(): 160 pass 161 self._spi.configure(baudrate=1000000, polarity=0, phase=0, bits=8) 162 # First ensure latch is low. 163 self._latch.value = False 164 # Write out the bits. 165 self._spi.write(self._shift_reg, start=0, end=_STOREBYTES * self._n + 1) 166 # Then toggle latch high and low to set the value. 167 self._latch.value = True 168 self._latch.value = False 169 finally: 170 # Ensure the SPI bus is unlocked. 171 self._spi.unlock() 172 173 def _get_gs_value(self, channel): 174 # pylint: disable=no-else-return 175 # Disable should be removed when refactor can be tested 176 if channel < 0 or channel >= _CHANNELS * self._n: 177 raise ValueError( 178 "Channel {0} not available with {1} board(s).".format(channel, self._n) 179 ) 180 # Invert channel position as the last channel needs to be written first. 181 # I.e. is in the first position of the shift registr. 182 channel = _CHANNELS * self._n - 1 - channel 183 # Calculate exact bit position within the shift register. 184 bit_offset = channel * 12 185 # Now calculate the byte that this position falls within and any offset 186 # from the left inside that byte. 187 byte_start = bit_offset // 8 188 start_offset = bit_offset % 8 189 # Grab the high and low bytes. 190 high_byte = self._shift_reg[byte_start] 191 low_byte = self._shift_reg[byte_start + 1] 192 if start_offset == 4: 193 # Value starts in the lower 4 bits of the high bit so you can 194 # just concat high with low byte and return the 12-bit value. 195 return ((high_byte << 8) | low_byte) & 0xFFF 196 elif start_offset == 0: 197 # Value starts in the entire high byte and spills into upper 198 # 4 bits of low byte. Shift low byte and concat values. 199 return ((high_byte << 4) | (low_byte >> 4)) & 0xFFF 200 else: 201 raise RuntimeError("Unsupported bit offset!") 202 203 def _set_gs_value(self, channel, val): 204 if channel < 0 or channel >= _CHANNELS * self._n: 205 raise ValueError( 206 "Channel {0} not available with {1} board(s).".format(channel, self._n) 207 ) 208 if val < 0 or val > 4095: 209 raise ValueError( 210 "PWM intensity {0} outside supported range [0;4095]".format(val) 211 ) 212 213 # Invert channel position as the last channel needs to be written first. 214 # I.e. is in the first position of the shift registr. 215 channel = _CHANNELS * self._n - 1 - channel 216 # Calculate exact bit position within the shift register. 217 bit_offset = channel * 12 218 # Now calculate the byte that this position falls within and any offset 219 # from the left inside that byte. 220 byte_start = bit_offset // 8 221 start_offset = bit_offset % 8 222 # Grab the high and low bytes. 223 high_byte = self._shift_reg[byte_start] 224 low_byte = self._shift_reg[byte_start + 1] 225 if start_offset == 4: 226 # Value starts in the lower 4 bits of the high bit. 227 high_byte &= 0b11110000 228 high_byte |= val >> 8 229 low_byte = val & 0xFF 230 elif start_offset == 0: 231 # Value starts in the entire high byte and spills into upper 232 # 4 bits of low byte. 233 high_byte = (val >> 4) & 0xFF 234 low_byte &= 0b00001111 235 low_byte |= (val << 4) & 0xFF 236 else: 237 raise RuntimeError("Unsupported bit offset!") 238 self._shift_reg[byte_start] = high_byte 239 self._shift_reg[byte_start + 1] = low_byte 240 # Write the updated shift register values if required. 241 if self.auto_write: 242 self.write() 243 244 def create_pwm_out(self, channel): 245 """Create an instance of a PWMOut-like class that mimics the built-in 246 CircuitPython PWMOut class but is associated with the TLC5947 channel 247 that is specified. This PWMOut class has a duty_cycle property which 248 you can read and write with a 16-bit value to control the channel. 249 Note there will be quantization error as the chip only supports 12-bit 250 PWM, if this is problematic use the item accessor approach to update 251 the raw 12-bit channel values. 252 """ 253 return self.PWMOut(self, channel) 254 255 # Define index and length properties to set and get each channel's raw 256 # 12-bit value (useful for changing channels without quantization error 257 # like when using the PWMOut mock class). 258 def __len__(self): 259 """Retrieve the total number of PWM channels available.""" 260 return _CHANNELS * self._n # number channels times number chips. 261 262 def __getitem__(self, key): 263 """Retrieve the 12-bit PWM value for the specified channel (0-max). 264 max depends on the number of boards. 265 """ 266 if key < 0: # allow reverse adressing with negative index 267 key = key + _CHANNELS * self._n 268 return self._get_gs_value(key) # does parameter checking 269 270 def __setitem__(self, key, val): 271 """Set the 12-bit PWM value (0-4095) for the specified channel (0-max). 272 max depends on the number of boards. 273 If auto_write is enabled (the default) then the chip PWM state will 274 immediately be updated too, otherwise you must call write to update 275 the chip with the new PWM state. 276 """ 277 if key < 0: # allow reverse adressing with negative index 278 key = key + _CHANNELS * self._n 279 self._set_gs_value(key, val) # does parameter checking