adafruit_tlc59711.py error - NameError: name '_CHIP_BUFFER_B

Play with it! Please tell us which board you're using.
For CircuitPython issues, ask in the Adafruit CircuitPython forum.

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
Pawelkosi
 
Posts: 18
Joined: Thu Jul 08, 2021 1:10 pm

adafruit_tlc59711.py error - NameError: name '_CHIP_BUFFER_B

Post by Pawelkosi »

Hi guys,

I followed the tutorial on how to use the TLC59711 LED controller - https://learn.adafruit.com/tlc5947-tlc5 ... cuitpython

I am running it off Jetson Nano, - I successfully used other Adafruit breakout boards on Nano, I have blinka / circuitpython installed and working. Never had an issue until today.

After running the provided example code:

Code: Select all

# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries
# SPDX-License-Identifier: MIT

# simple demo of the TLC59711 16-bit 12 channel LED PWM driver.
# Shows the minimal usage - how to set pixel values in a few ways.
# Author: Tony DiCola

import board
import busio

import adafruit_tlc59711

print("tlc59711_simpletest.py")

# Define SPI bus connected to chip.
# You only need the clock and MOSI (output) line to use this chip.
spi = busio.SPI(board.SCK, MOSI=board.MOSI)
pixels = adafruit_tlc59711.TLC59711(spi, pixel_count=16)

# examples how to set the pixels:
# range:
# 0 - 65535
# or
# 0.0 - 1.0
# every pixel needs a color -
# give it just a list or tuple with 3 integer values: R G B

# set all pixels to a very low level
pixels.set_pixel_all((10, 10, 10))

# every chip has 4 Pixels (=RGB-LEDs = 12 Channel)
pixels[0] = (100, 100, 100)
pixels[1] = (0, 0, 100)
pixels[2] = (0.01, 0.0, 0.01)
pixels[3] = (0.1, 0.01, 0.0)
# if you are ready to show your values you have to call
pixels.show()

# there are a bunch of other ways to set pixel.
# have a look at the other examples.
I get a following error:

Code: Select all

pawel@devxavier:~/Code$ python3 pwm_controller_TLC59711.py
tlc59711_simpletest.py
Traceback (most recent call last):
  File "pwm_controller_TLC59711.py", line 18, in <module>
    pixels = adafruit_tlc59711.TLC59711(spi, pixel_count=16)
  File "/usr/local/lib/python3.6/dist-packages/adafruit_tlc59711.py", line 292, in __init__
    self._buffer = bytearray(_CHIP_BUFFER_BYTE_COUNT * self.chip_count)
NameError: name '_CHIP_BUFFER_BYTE_COUNT' is not defined
Exiting...
Cleaning up pins
Could anyone shed any light on why am I getting this error and how to fix it? Much appreciated!

User avatar
mikeysklar
 
Posts: 13936
Joined: Mon Aug 01, 2016 8:10 pm

Re: adafruit_tlc59711.py error - NameError: name '_CHIP_BUFF

Post by mikeysklar »

Have you tried running some of our current examples for the TLC59711? It looks like the github repo got cleaned up 2 months ago.

https://github.com/adafruit/Adafruit_Ci ... n_TLC59711

https://github.com/adafruit/Adafruit_Ci ... n/examples

User avatar
Pawelkosi
 
Posts: 18
Joined: Thu Jul 08, 2021 1:10 pm

Re: adafruit_tlc59711.py error - NameError: name '_CHIP_BUFF

Post by Pawelkosi »

Hi Mikeysklar!

Thanks for the answer. I tried them, it's the same story. Error is the same across different examples.

It seems that the error is in adafruit library. I downloaded the code and opened it in VS code with pylint and I get the same error about that variable being not defined.

I do not want to edit your libs manually as I don't fully understand the code driver and because the changes will get edited anyway with next update.

EDIT

It might be important: I have tested the other library for the tlc5947 and that runs absolutely fine - I don't have the board yet (I am just about to order it) but it might be worth for you to know.

User avatar
Pawelkosi
 
Posts: 18
Joined: Thu Jul 08, 2021 1:10 pm

Re: adafruit_tlc59711.py error - NameError: name '_CHIP_BUFF

Post by Pawelkosi »

Sorry for double post but I cannot edit the earlier reply.

It seems that the defined variables with const values are out of scope for the functions below, simply moving the definitions above the class definition fixed the error and I was able to successfully test the board.

Surely there is an issue somewhere else though, as whoever was cleaning up the code would notice that it does not run after the cleanup?

The code library that I was able to successfully run (with my edits):

Code: Select all

# SPDX-FileCopyrightText: 2017 Tony DiCola for Adafruit Industries
# SPDX-FileCopyrightText: 2018 Stefan Krüger s-light.eu
#
# SPDX-License-Identifier: MIT

"""
`adafruit_tlc59711`
====================================================

CircuitPython module for the
TLC59711 or TLC5971 16-bit 12 channel LED PWM driver.
See examples/tlc59711_simpletest.py for a demo of the usage.

* Author(s): Tony DiCola, Stefan Kruger

Implementation Notes
--------------------

**Hardware:**

* Adafruit `12-Channel 16-bit PWM LED Driver - SPI Interface - TLC59711
  <https://www.adafruit.com/product/1455>`_ (Product ID: 1455)
  or TLC5971

**Software and Dependencies:**

* The API is mostly compatible to the DotStar / NeoPixel Libraries
    and is therefore also compatible with FancyLED.

* Adafruit CircuitPython firmware for the supported boards:
  https://circuitpython.org/downloads
"""
__version__ = "0.0.0-auto.0"
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_TLC59711.git"


# pylint - globally disable
# 'invalid-name' check to allow for datasheet conform short channel and register names.
# pylint: disable=invalid-name
# 'undefined-variable' to allow for micropython const definitions
# pylint: disable=undefined-variable
# 'too-many-lines' with the extra class and the api backwards compatibel things i have ~1100 lines..
# and as it was wished to not alter the .pylint file to limit to 1100 i disable it here..
# and yes - this code is very detailed commented. but i think that this is a good thing -
# as hopefully this way it is easier to understand desicions &
# what is going on and learn the backgrounds..
# pylint: disable=too-many-lines


import struct

from micropython import const

_CHIP_BUFFER_BYTE_COUNT = const(28)

COLORS_PER_PIXEL = const(3)
PIXEL_PER_CHIP = const(4)
CHANNEL_PER_CHIP = const(COLORS_PER_PIXEL * PIXEL_PER_CHIP)

_BUFFER_BYTES_PER_COLOR = const(2)
_BUFFER_BYTES_PER_PIXEL = const(_BUFFER_BYTES_PER_COLOR * COLORS_PER_PIXEL)
_BC_CHIP_BUFFER_BIT_OFFSET = const(0)

_BC_BIT_COUNT = const(3 * 7)
_FC_CHIP_BUFFER_BIT_OFFSET = const(_BC_BIT_COUNT)
_FC_BIT_COUNT = const(5)

_WC_CHIP_BUFFER_BIT_OFFSET = const(_FC_BIT_COUNT + _BC_BIT_COUNT)
_WC_BIT_COUNT = const(6)

_CHIP_BUFFER_HEADER_BIT_COUNT = const(_WC_BIT_COUNT + _FC_BIT_COUNT + _BC_BIT_COUNT)
_CHIP_BUFFER_HEADER_BYTE_COUNT = const(_CHIP_BUFFER_HEADER_BIT_COUNT // 8)

class TLC59711:
    """TLC5971 & TLC59711 16-bit 12 channel LED PWM driver.

    The TLC59711 & TLC5971 chip is designed to drive 4 RGB LEDs with 16-bit PWM per Color.
    This Library can control 1..many chips.
    The class has an interface compatible with the FancyLED library -
    and the API is similar to the NeoPixel and DotStar Interfaces.

    :param ~busio.SPI spi: An instance of the SPI bus connected to the chip.
        The clock and MOSI/outout must be set, the MISO/input is unused.
        Maximal data clock frequence is:
        - TLC59711: 10MHz
        - TLC5971: 20MHz
    :param bool pixel_count: Number of RGB-LEDs (=Pixels) that are connected. (default=4)
    """

    # pylint: disable=too-many-instance-attributes
    # it just does make senes to have the chip params as attributes.

    # """
    # TLC5971 data / register structure
    #
    # some detailed information on the protocol based on
    # http://www.ti.com/lit/ds/symlink/tlc5971.pdf
    # 8.5.4 Register and Data Latch Configuration (page 23ff)
    # 9.2.2.3 How to Control the TLC5971 (page27)
    #
    # How to send:
    # the first data we send are received by the last device in chain.
    # Device Nth (244Bit = 28Byte)
    # Write Command (6Bit)
    #     WRCMD (fixed: 25h)
    # Function Control Data (5 x 1Bit = 5Bit)
    #     OUTTMG 1bit
    #         GS clock edge select
    #         1=rising edge, 0= falling edge
    #     EXTGCK 1bit
    #         GS reference clock select
    #         1=SCKI clock, 0=internal oscillator
    #     TMGRST 1bit
    #         display timing reset mode
    #         1=OUT forced of on latchpulse, 0=no forced reset
    #     DSPRPT 1bit
    #         display repeat mode
    #         1=auto repeate
    #         0=Out only turned on after Blank or internal latchpulse
    #     BLANK 1bit;
    #         1=blank (outputs off)
    #         0=Out on - controlled by GS-Data
    #         ic power on sets this to 1
    # BC-Data (3 x 7Bits = 21Bit)
    #     BCB 7bit;
    #     BCG 7bit;
    #     BCR 7bit;
    # GS-Data (12 x 16Bits = 192Bit)
    #     GSB3 16bit;
    #     GSG3 16bit;
    #     GSR3 16bit;
    #     GSB2 16bit;
    #     GSG2 16bit;
    #     GSR2 16bit;
    #     GSB1 16bit;
    #     GSG1 16bit;
    #     GSR1 16bit;
    #     GSB0 16bit;
    #     GSG0 16bit;
    #     GSR0 16bit;
    # Device Nth-1 (244Bit = 28Byte)
    #     Device ..
    #     Device 2
    #     Device 1
    #     short break of 8x period of clock (666ns .. 2.74ms)
    #     to generate latchpulse
    #     + 1.34uS
    #     than next update.
    # """

    ##########################################
    # helper
    ##########################################

    # pylama:ignore=E0602
    # ugly workaround for pylama not knowing of micropython const thing
    _CHIP_BUFFER_BYTE_COUNT = const(28)

    COLORS_PER_PIXEL = const(3)
    PIXEL_PER_CHIP = const(4)
    CHANNEL_PER_CHIP = const(COLORS_PER_PIXEL * PIXEL_PER_CHIP)

    _BUFFER_BYTES_PER_COLOR = const(2)
    _BUFFER_BYTES_PER_PIXEL = const(_BUFFER_BYTES_PER_COLOR * COLORS_PER_PIXEL)

    # @staticmethod
    # def set_bit_with_mask(v, mask, x):
    #     """Set bit with help of mask."""
    #     # clear
    #     v &= ~mask
    #     if x:
    #         # set
    #         v |= mask
    #     return v
    #
    # @staticmethod
    # def set_bit(v, index, x):
    #     """Set bit - return new value.
    #
    #     Set the index:th bit of v to 1 if x is truthy,
    #     else to 0, and return the new value.
    #     https://stackoverflow.com/a/12174051/574981
    #     """
    #     # Compute mask, an integer with just bit 'index' set.
    #     mask = 1 << index
    #     # Clear the bit indicated by the mask (if x is False)
    #     v &= ~mask
    #     if x:
    #         # If x was True, set the bit indicated by the mask.
    #         v |= mask
    #     # Return the result, we're done.
    #     return v

    ##########################################
    # class _BC():
    # BC-Data (3 x 7Bits = 21Bit).
    #
    # BCB 7bit;
    # BCG 7bit;
    # BCR 7bit;
    _BC_CHIP_BUFFER_BIT_OFFSET = const(0)
    _BC_BIT_COUNT = const(3 * 7)
    # this holds the chip offset and
    _BC_FIELDS = {
        "BCR": {
            "offset": 0,
            "length": 7,
            "mask": 0b01111111,
        },
        "BCG": {
            "offset": 7,
            "length": 7,
            "mask": 0b01111111,
        },
        "BCB": {
            "offset": 14,
            "length": 7,
            "mask": 0b01111111,
        },
    }

    ##########################################
    # class _FC():
    # """
    # Function Control Data (5 x 1Bit = 5Bit).
    #
    # OUTTMG 1bit
    #     GS clock edge select
    #     1 = rising edge
    #     0 = falling edge
    # EXTGCK 1bit
    #     GS reference clock select
    #     1 = SCKI clock
    #     0 = internal oscillator
    # TMGRST 1bit
    #     display timing reset mode
    #     1 = OUT forced of on latchpulse
    #     0 = no forced reset
    # DSPRPT 1bit
    #     display repeat mode
    #     1 = auto repeate
    #     0 = Out only turned on after Blank or internal latchpulse
    # BLANK 1bit;
    #     ic power on sets this to 1
    #     1 = blank (outputs off)
    #     0 = Out on - controlled by GS-Data
    # """

    _FC_CHIP_BUFFER_BIT_OFFSET = const(_BC_BIT_COUNT)
    _FC_BIT_COUNT = const(5)
    _FC_FIELDS = {
        "BLANK": {
            "offset": 0,
            "length": 1,
            "mask": 0b1,
        },
        "DSPRPT": {
            "offset": 1,
            "length": 1,
            "mask": 0b1,
        },
        "TMGRST": {
            "offset": 2,
            "length": 1,
            "mask": 0b1,
        },
        "EXTGCK": {
            "offset": 3,
            "length": 1,
            "mask": 0b1,
        },
        "OUTTMG": {
            "offset": 4,
            "length": 1,
            "mask": 0b1,
        },
    }

    ##########################################
    # class _WRITE_COMMAND():
    # """WRITE_COMMAND."""

    _WC_CHIP_BUFFER_BIT_OFFSET = const(_FC_BIT_COUNT + _BC_BIT_COUNT)
    _WC_BIT_COUNT = const(6)
    _WC_FIELDS = {
        "WRITE_COMMAND": {
            "offset": 0,
            "length": 6,
            "mask": 0b111111,
        },
    }
    WRITE_COMMAND = const(0b100101)
    ##########################################

    ########
    _CHIP_BUFFER_HEADER_BIT_COUNT = const(_WC_BIT_COUNT + _FC_BIT_COUNT + _BC_BIT_COUNT)
    _CHIP_BUFFER_HEADER_BYTE_COUNT = const(_CHIP_BUFFER_HEADER_BIT_COUNT // 8)

    ##########################################

    def __init__(self, spi, *, pixel_count=4):
        """Init."""
        self._spi = spi
        self.pixel_count = pixel_count
        self.channel_count = self.pixel_count * self.COLORS_PER_PIXEL
        # calculate how many chips are connected
        self.chip_count = self.pixel_count // 4

        # The chips are just a big 28 byte long shift register without any fancy update protocol.
        # Blast out all the bits to update, that's it! → create raw output data
        self._buffer = bytearray(_CHIP_BUFFER_BYTE_COUNT * self.chip_count)

        # Initialize the brightness channel values to max (these are 7-bit values).
        self.bcr = 127
        self.bcg = 127
        self.bcb = 127

        # Initialize external user-facing state for the function control bits of the chip.
        # These aren't commonly used but available and match the nomenclature from the datasheet.
        # you must manually call update_fc() after changing them
        # (reduces the need to make frivolous memory-hogging properties).
        # Default set: OUTTMG, TMGRST, and DSPRPT to on; like in Arduino library.
        # these are set for all chips the same
        self.outtmg = True
        self.extgck = False
        self.tmgrst = True
        self.dsprpt = True
        self.blank = False

        # preparation done → now initialize buffer to default values
        self._init_buffer()
        self._buffer_index_lookuptable = []
        self._init_lookuptable()

    def _init_buffer(self):
        for chip_index in range(self.chip_count):
            # set Write Command (6Bit) WRCMD (fixed: 25h)
            self.chip_set_BCData(chip_index, bcr=self.bcr, bcg=self.bcg, bcb=self.bcb)
            self._chip_set_FunctionControl(chip_index)
            self._chip_set_WriteCommand(chip_index)

    def set_chipheader_bits_in_buffer(
        self, *, chip_index=0, part_bit_offset=0, field=None, value=0
    ):
        """Set chip header bits in buffer."""
        if field is None:
            field = {"mask": 0, "length": 0, "offset": 0}
        offset = part_bit_offset + field["offset"]
        # restrict value
        value &= field["mask"]
        # move value to position
        value = value << offset
        # calculate header start
        header_start = chip_index * _CHIP_BUFFER_BYTE_COUNT
        # get chip header
        header = struct.unpack_from(">I", self._buffer, header_start)[0]
        # 0xFFFFFFFF == 0b11111111111111111111111111111111
        # create/move mask
        mask = field["mask"] << offset
        # clear
        header &= ~mask
        # set
        header |= value
        # write header back
        struct.pack_into(">I", self._buffer, header_start, header)

    ##########################################

    def chip_set_BCData(self, chip_index, bcr=127, bcg=127, bcb=127):
        """
        Set BC-Data.

        :param int chip_index: Index of Chip to set.
        :param int bcr: 7-bit value from 0-127 (default=127)
        :param int bcg: 7-bit value from 0-127 (default=127)
        :param int bcb: 7-bit value from 0-127 (default=127)
        """
        # set all bits
        self.set_chipheader_bits_in_buffer(
            chip_index=chip_index,
            part_bit_offset=_BC_CHIP_BUFFER_BIT_OFFSET,
            field=self._BC_FIELDS["BCR"],
            value=bcr,
        )
        self.set_chipheader_bits_in_buffer(
            chip_index=chip_index,
            part_bit_offset=_BC_CHIP_BUFFER_BIT_OFFSET,
            field=self._BC_FIELDS["BCG"],
            value=bcg,
        )
        self.set_chipheader_bits_in_buffer(
            chip_index=chip_index,
            part_bit_offset=_BC_CHIP_BUFFER_BIT_OFFSET,
            field=self._BC_FIELDS["BCB"],
            value=bcb,
        )

    def update_BCData(self):
        """
        Update BC-Data for all Chips in Buffer.

        need to be called after you changed on of the BC-Data Parameters. (bcr, bcg, bcb)
        """
        for chip_index in range(self.chip_count):
            self.chip_set_BCData(chip_index, bcr=self.bcr, bcg=self.bcg, bcb=self.bcb)

    def _chip_set_FunctionControl(self, chip_index):
        """
        Set Function Control Bits in Buffer.

        values from object global parameters are used.

        :param int chip_index: Index of Chip to set.
        """
        # set all bits
        self.set_chipheader_bits_in_buffer(
            chip_index=chip_index,
            part_bit_offset=_FC_CHIP_BUFFER_BIT_OFFSET,
            field=self._FC_FIELDS["OUTTMG"],
            value=self.outtmg,
        )
        self.set_chipheader_bits_in_buffer(
            chip_index=chip_index,
            part_bit_offset=_FC_CHIP_BUFFER_BIT_OFFSET,
            field=self._FC_FIELDS["EXTGCK"],
            value=self.extgck,
        )
        self.set_chipheader_bits_in_buffer(
            chip_index=chip_index,
            part_bit_offset=_FC_CHIP_BUFFER_BIT_OFFSET,
            field=self._FC_FIELDS["TMGRST"],
            value=self.tmgrst,
        )
        self.set_chipheader_bits_in_buffer(
            chip_index=chip_index,
            part_bit_offset=_FC_CHIP_BUFFER_BIT_OFFSET,
            field=self._FC_FIELDS["DSPRPT"],
            value=self.dsprpt,
        )
        self.set_chipheader_bits_in_buffer(
            chip_index=chip_index,
            part_bit_offset=_FC_CHIP_BUFFER_BIT_OFFSET,
            field=self._FC_FIELDS["BLANK"],
            value=self.blank,
        )

    def update_fc(self):
        """
        Update Function Control Bits for all Chips in Buffer.

        need to be called after you changed on of the Function Control Bit Parameters.
        (outtmg, extgck, tmgrst, dsprpt, blank)
        """
        for chip_index in range(self.chip_count):
            self._chip_set_FunctionControl(chip_index)

    def _chip_set_WriteCommand(self, chip_index):
        """Set WRITE_COMMAND."""
        # set all bits
        self.set_chipheader_bits_in_buffer(
            chip_index=chip_index,
            part_bit_offset=_WC_CHIP_BUFFER_BIT_OFFSET,
            field=self._WC_FIELDS["WRITE_COMMAND"],
            value=self.WRITE_COMMAND,
        )

    def _init_lookuptable(self):
        for channel_index in range(self.channel_count):
            buffer_index = (_CHIP_BUFFER_BYTE_COUNT // _BUFFER_BYTES_PER_COLOR) * (
                channel_index // self.CHANNEL_PER_CHIP
            ) + channel_index % self.CHANNEL_PER_CHIP
            buffer_index *= _BUFFER_BYTES_PER_COLOR
            buffer_index += _CHIP_BUFFER_HEADER_BYTE_COUNT
            self._buffer_index_lookuptable.append(buffer_index)

    ##########################################

    def _write(self):
        # Write out the current state to the shift register.
        try:
            # Lock the SPI bus and configure it for the shift register.
            while not self._spi.try_lock():
                pass
            self._spi.configure(baudrate=self._spi.frequency, polarity=0, phase=0)
            self._spi.write(self._buffer)
        finally:
            # Ensure the SPI bus is unlocked.
            self._spi.unlock()

    def show(self):
        """Write out the current LED PWM state to the chip."""
        self._write()

    ##########################################

    @staticmethod
    def calculate_Ioclmax(*, Riref=2.48):
        """
        Calculate Maximum Constant Sink Current Value.

        see:
        8.4.1 Maximum Constant Sink Current Setting
        http://www.ti.com/lit/ds/symlink/tlc5971.pdf#page=18&zoom=160,0,524

        Riref = (Viref / Ioclmax) * 41
        Ioclmax = (41 / Riref) * Viref

        :param float Riref: resistor value (kΩ) (default=20)
        :return tuple: Ioclmax (mA)
        """
        # Riref                 = (Viref / Ioclmax) * 41    | / 41
        # Riref / 41            = Viref / Ioclmax           | switch
        # 41 / Riref            = Ioclmax / Viref           | * Viref
        # (41 / Riref) * Viref  = Ioclmax
        if not 0.8 <= Riref <= 24.8:
            raise ValueError("Riref {} not in range: 0.8kΩ..25kΩ".format(Riref))
        Viref = 1.21
        Ioclmax = (41 / Riref) * Viref
        if not 2.0 <= Ioclmax <= 60.0:
            raise ValueError("Ioclmax {} not in range: 2mA..60mA".format(Ioclmax))
        return Ioclmax

    @staticmethod
    def calculate_Riref(*, Ioclmax=20):
        """
        Calculate Maximum Constant Sink Current Value.

        see:
        8.4.1 Maximum Constant Sink Current Setting
        http://www.ti.com/lit/ds/symlink/tlc5971.pdf#page=19&zoom=200,0,697

        Riref = (Viref / Ioclmax) * 41

        :param float Ioclmax: target max output current (mA) (default=20)
        :return tuple: Riref (kΩ)
        """
        if not 2.0 <= Ioclmax <= 60.0:
            raise ValueError("Ioclmax {} not in range: 2mA..60mA".format(Ioclmax))
        Viref = 1.21
        Riref = (Viref / Ioclmax) * 41
        if not 0.8 <= Riref <= 24.8:
            raise ValueError("Riref {} not in range: 0.8kΩ..25kΩ".format(Riref))
        return Riref

    @staticmethod
    def calculate_BCData(*, Ioclmax=18, IoutR=17, IoutG=15, IoutB=9):
        """
        Calculate Global Brightness Control Values.

        see:
        8.5.1 Global Brightness Control (BC) Function (Sink Current Control)
        http://www.ti.com/lit/ds/symlink/tlc5971.pdf#page=19&zoom=200,0,697

        Iout = Ioclmax * (BCX / 127)
        BCX = Iout / Ioclmax * 127

        :param float Ioclmax: max output current set by Riref (mA) (default=20)
        :param float IoutR: max output current for red color group (mA)
            (default=9)
        :param float IoutG: max output current for green color (mA)
            (default=15)
        :param float IoutB: max output current for blue color (mA)
            (default=17)
        :return tuple: (bcr, bcg, bcb)
        """
        # Iout                 =  Ioclmax * (BCX / 127)  | / Ioclmax
        # Iout / Ioclmax       =  BCX / 127              | * 127
        # Iout / Ioclmax * 127 =  BCX
        if not 2.0 <= Ioclmax <= 60.0:
            raise ValueError("Ioclmax {} not in range: 2mA..60mA" "".format(Ioclmax))
        if not 0.0 <= IoutR <= Ioclmax:
            raise ValueError("IoutR {} not in range: 2mA..{}mA".format(IoutR, Ioclmax))
        if not 0.0 <= IoutG <= Ioclmax:
            raise ValueError("IoutG {} not in range: 2mA..{}mA".format(IoutG, Ioclmax))
        if not 0.0 <= IoutB <= Ioclmax:
            raise ValueError("IoutB {} not in range: 2mA..{}mA".format(IoutB, Ioclmax))
        bcr = int((IoutR / Ioclmax) * 127)
        bcg = int((IoutG / Ioclmax) * 127)
        bcb = int((IoutB / Ioclmax) * 127)
        if not 0 <= bcr <= 127:
            raise ValueError("bcr {} not in range: 0..127".format(bcr))
        if not 0 <= bcg <= 127:
            raise ValueError("bcg {} not in range: 0..127".format(bcg))
        if not 0 <= bcb <= 127:
            raise ValueError("bcb {} not in range: 0..127".format(bcb))
        return (bcr, bcg, bcb)

    ##########################################

    @staticmethod
    def _convert_01_float_to_16bit_integer(value):
        """Convert 0..1 Float Value to 16bit (0..65535) Range."""
        # check if value is in range
        if not 0.0 <= value[0] <= 1.0:
            raise ValueError("value[0] {} not in range: 0..1".format(value[0]))
        # convert to 16bit value
        return int(value * 65535)

    @classmethod
    def _convert_if_float(cls, value):
        """Convert if value is Float."""
        if isinstance(value, float):
            value = cls._convert_01_float_to_16bit_integer(value)
        return value

    @staticmethod
    def _check_and_convert(value):
        # loop
        # mega slow
        # for i, val in enumerate(value):
        #     # check if we have float values
        #     if isinstance(val, float):
        #         # check if val is in range
        #         if not 0.0 <= val <= 1.0:
        #             raise ValueError(
        #                 "value[{}] {} not in range: 0..1".format(i, val))
        #         # convert to 16bit val
        #         value[i] = int(val * 65535)
        #     else:
        #         if not 0 <= val <= 65535:
        #             raise ValueError(
        #                 "value[{}] {} not in range: 0..65535".format(i, val))
        # discreet is way faster
        # (compared with tlc59711_multi_dev.py: pixels.set_all_black())
        # check if we have float values
        if isinstance(value[0], float):
            # check if value is in range
            if not 0.0 <= value[0] <= 1.0:
                raise ValueError("value[0] {} not in range: 0..1".format(value[0]))
            # convert to 16bit value
            value[0] = int(value[0] * 65535)
        else:
            if not 0 <= value[0] <= 65535:
                raise ValueError("value[0] {} not in range: 0..65535".format(value[0]))
        if isinstance(value[1], float):
            if not 0.0 <= value[1] <= 1.0:
                raise ValueError("value[1] {} not in range: 0..1".format(value[1]))
            value[1] = int(value[1] * 65535)
        else:
            if not 0 <= value[1] <= 65535:
                raise ValueError("value[1] {} not in range: 0..65535".format(value[1]))
        if isinstance(value[2], float):
            if not 0.0 <= value[2] <= 1.0:
                raise ValueError("value[2] {} not in range: 0..1".format(value[2]))
            value[2] = int(value[2] * 65535)
        else:
            if not 0 <= value[2] <= 65535:
                raise ValueError("value[2] {} not in range: 0..65535".format(value[2]))

    ##########################################

    def _get_channel_16bit_value(self, channel_index):
        buffer_start = self._buffer_index_lookuptable[channel_index]
        return struct.unpack_from(">H", self._buffer, buffer_start)[0]

    def set_pixel_16bit_value(self, pixel_index, value_r, value_g, value_b):
        """
        Set the value for pixel.

        This is a Fast UNPROTECTED function:
        no error / range checking is done.

        :param int pixel_index: 0..(pixel_count)
        :param int value_r: 0..65535
        :param int value_g: 0..65535
        :param int value_b: 0..65535
        """
        # optimized for speed.
        # the struct version leads to very slow runtime behaivor if you set
        # lots of pixels. that is the reason the discreet version is used.
        # you can check with the tlc59711_multi_dev.py file.
        # most prominent this is visible at the set_pixel_all_16bit_value func:
        #  struct 157ms to 16ms (@144pixel on ItsyBitsy M4)
        pixel_start = pixel_index * self.COLORS_PER_PIXEL
        buffer_start = self._buffer_index_lookuptable[pixel_start + 0]
        # struct.pack_into('>H', self._buffer, buffer_start, value_b)
        self._buffer[buffer_start + 0] = (value_b >> 8) & 0xFF
        self._buffer[buffer_start + 1] = value_b & 0xFF
        buffer_start = self._buffer_index_lookuptable[pixel_start + 1]
        # struct.pack_into('>H', self._buffer, buffer_start, value_g)
        self._buffer[buffer_start + 0] = (value_g >> 8) & 0xFF
        self._buffer[buffer_start + 1] = value_g & 0xFF
        buffer_start = self._buffer_index_lookuptable[pixel_start + 2]
        # struct.pack_into('>H', self._buffer, buffer_start, value_r)
        self._buffer[buffer_start + 0] = (value_r >> 8) & 0xFF
        self._buffer[buffer_start + 1] = value_r & 0xFF

    def set_pixel_float_value(self, pixel_index, value_r, value_g, value_b):
        """
        Set the value for pixel.

        This is a Fast UNPROTECTED function:
        no error / range checking is done.

        :param int pixel_index: 0..(pixel_count)
        :param int value_r: 0..1
        :param int value_g: 0..1
        :param int value_b: 0..1
        """
        # self.set_pixel_16bit_value(
        #     pixel_index,
        #     int(value_r * 65535),
        #     int(value_g * 65535),
        #     int(value_b * 65535)
        # )
        #  this is again a speed optimized version:
        #  this cuts down from 228ms to 22ms (@144pixel on ItsyBitsy M4)
        value_r = int(value_r * 65535)
        value_g = int(value_g * 65535)
        value_b = int(value_b * 65535)
        pixel_start = pixel_index * self.COLORS_PER_PIXEL
        buffer_start = self._buffer_index_lookuptable[pixel_start + 0]
        self._buffer[buffer_start + 0] = (value_b >> 8) & 0xFF
        self._buffer[buffer_start + 1] = value_b & 0xFF
        buffer_start = self._buffer_index_lookuptable[pixel_start + 1]
        self._buffer[buffer_start + 0] = (value_g >> 8) & 0xFF
        self._buffer[buffer_start + 1] = value_g & 0xFF
        buffer_start = self._buffer_index_lookuptable[pixel_start + 2]
        self._buffer[buffer_start + 0] = (value_r >> 8) & 0xFF
        self._buffer[buffer_start + 1] = value_r & 0xFF

    def set_pixel_16bit_color(self, pixel_index, color):
        """
        Set color for pixel.

        This is a Fast UNPROTECTED function:
        no error / range checking is done.
        its a little bit slower as `set_pixel_16bit_value`

        :param int pixel_index: 0..(pixel_count)
        :param int color: 3-tuple of R, G, B;  0..65535
        """
        # self.set_pixel_16bit_value(
        #     pixel_index,
        #     color[0],
        #     color[1],
        #     color[2]
        # )
        # speed optimization: 140ms to 24ms (@144pixel on ItsyBitsy M4)
        # the `color = list(color)` is the key here! (don't ask me why..)
        color = list(color)
        pixel_start = pixel_index * self.COLORS_PER_PIXEL
        buffer_start = self._buffer_index_lookuptable[pixel_start + 0]
        self._buffer[buffer_start + 0] = (color[2] >> 8) & 0xFF
        self._buffer[buffer_start + 1] = color[2] & 0xFF
        buffer_start = self._buffer_index_lookuptable[pixel_start + 1]
        self._buffer[buffer_start + 0] = (color[1] >> 8) & 0xFF
        self._buffer[buffer_start + 1] = color[1] & 0xFF
        buffer_start = self._buffer_index_lookuptable[pixel_start + 2]
        self._buffer[buffer_start + 0] = (color[0] >> 8) & 0xFF
        self._buffer[buffer_start + 1] = color[0] & 0xFF

    def set_pixel_float_color(self, pixel_index, color):
        """
        Set color for pixel.

        This is a Fast UNPROTECTED function:
        no error / range checking is done.
        its a little bit slower as `set_pixel_16bit_value`

        :param int pixel_index: 0..(pixel_count)
        :param tuple/float color: 3-tuple of R, G, B;  0..1
        """
        # self.set_pixel_16bit_value(
        #     pixel_index,
        #     int(color[0] * 65535),
        #     int(color[1] * 65535),
        #     int(color[2] * 65535)
        # )
        # speed optimization: 140ms to 30ms (@144pixel on ItsyBitsy M4)
        color = list(color)
        color[0] = int(color[0] * 65535)
        color[1] = int(color[1] * 65535)
        color[2] = int(color[2] * 65535)
        pixel_start = pixel_index * self.COLORS_PER_PIXEL
        buffer_start = self._buffer_index_lookuptable[pixel_start + 0]
        self._buffer[buffer_start + 0] = (color[2] >> 8) & 0xFF
        self._buffer[buffer_start + 1] = color[2] & 0xFF
        buffer_start = self._buffer_index_lookuptable[pixel_start + 1]
        self._buffer[buffer_start + 0] = (color[1] >> 8) & 0xFF
        self._buffer[buffer_start + 1] = color[1] & 0xFF
        buffer_start = self._buffer_index_lookuptable[pixel_start + 2]
        self._buffer[buffer_start + 0] = (color[0] >> 8) & 0xFF
        self._buffer[buffer_start + 1] = color[0] & 0xFF

    def set_pixel(self, pixel_index, value):
        """
        Set the R, G, B values for the pixel.

        this funciton hase some advanced error checking.
        it is much slower than the other provided 'bare' variants..
        but therefor gives clues to what is going wrong.. ;-)

        :param int pixel_index: 0..(pixel_count)
        :param tuple value: 3-tuple of R, G, B;
            each int 0..65535 or float 0..1
        """
        if 0 <= pixel_index < self.pixel_count:
            # convert to list
            value = list(value)
            # repr(value)
            if len(value) != self.COLORS_PER_PIXEL:
                raise IndexError(
                    "length of value {} does not match COLORS_PER_PIXEL (= {})"
                    "".format(len(value), self.COLORS_PER_PIXEL)
                )
            # tested:
            # splitting up into variables to not need the list..
            # this is about 0.25ms slower..
            # value_r = value[0]
            # value_g = value[1]
            # value_b = value[2]

            # check if we have float values
            # this modifies 'value' in place..
            self._check_and_convert(value)

            # update buffer
            # we change channel order here:
            # buffer channel order is blue, green, red
            # pixel_start = pixel_index * self.COLORS_PER_PIXEL
            # buffer_start = self._buffer_index_lookuptable[pixel_start + 0]
            # self._buffer[buffer_start + 0] = (value[2] >> 8) & 0xFF
            # self._buffer[buffer_start + 1] = value[2] & 0xFF
            # buffer_start = self._buffer_index_lookuptable[pixel_start + 1]
            # self._buffer[buffer_start + 0] = (value[1] >> 8) & 0xFF
            # self._buffer[buffer_start + 1] = value[1] & 0xFF
            # buffer_start = self._buffer_index_lookuptable[pixel_start + 2]
            # self._buffer[buffer_start + 0] = (value[0] >> 8) & 0xFF
            # self._buffer[buffer_start + 1] = value[0] & 0xFF
            # simpliefy code
            self.set_pixel_16bit_value(pixel_index, value[0], value[1], value[2])
        else:
            raise IndexError(
                "index {} out of range [0..{}]" "".format(pixel_index, self.pixel_count)
            )

    def set_pixel_all_16bit_value(self, value_r, value_g, value_b):
        """
        Set the R, G, B values for all pixels.

        fast. without error checking.

        :param int value_r: 0..65535
        :param int value_g: 0..65535
        :param int value_b: 0..65535
        """
        for i in range(self.pixel_count):
            self.set_pixel_16bit_value(i, value_r, value_g, value_b)

    def set_pixel_all(self, color):
        """
        Set the R, G, B values for all pixels.

        :param tuple 3-tuple of R, G, B;  each int 0..65535 or float 0..1
        """
        for i in range(self.pixel_count):
            self.set_pixel(i, color)

    def set_all_black(self):
        """Set all pixels to black."""
        for i in range(self.pixel_count):
            self.set_pixel_16bit_value(i, 0, 0, 0)

    # channel access
    def set_channel(self, channel_index, value):
        """
        Set the value for the provided channel.

        :param int channel_index: 0..channel_count
        :param int value: 0..65535
        """
        if 0 <= channel_index < (self.channel_count):
            # check if values are in range
            if not 0 <= value <= 65535:
                raise ValueError("value {} not in range: 0..65535")
            # temp = channel_index
            # we change channel order here:
            # buffer channel order is blue, green, red
            pixel_index_offset = channel_index % self.COLORS_PER_PIXEL
            if pixel_index_offset == 0:
                channel_index += 2
            elif pixel_index_offset == 2:
                channel_index -= 2
            # set value in buffer
            buffer_start = self._buffer_index_lookuptable[channel_index]
            struct.pack_into(">H", self._buffer, buffer_start, value)
        else:
            raise IndexError(
                "channel_index {} out of range (0..{})".format(
                    channel_index, self.channel_count
                )
            )

    # Define index and length properties to set and get each pixel as
    # atomic RGB tuples.  This provides a similar feel as using neopixels.
    def __len__(self):
        """Retrieve TLC5975 the total number of Pixels available."""
        return self.pixel_count

    def __getitem__(self, key):
        """
        Retrieve the R, G, B values for the provided channel as a 3-tuple.

        Each value is a 16-bit number from 0-65535.
        """
        if 0 <= key < self.pixel_count:
            pixel_start = key * self.COLORS_PER_PIXEL
            return (
                self._get_channel_16bit_value(pixel_start + 0),
                self._get_channel_16bit_value(pixel_start + 1),
                self._get_channel_16bit_value(pixel_start + 2),
            )
        # else:
        raise IndexError("index {} out of range [0..{}]".format(key, self.pixel_count))

    def __setitem__(self, key, value):
        """
        Set the R, G, B values for the pixel.

        this funciton hase some advanced error checking.
        it is much slower than the other provided 'bare' variants..
        but therefor gives clues to what is going wrong.. ;-)
        this shortcut is identicall to `set_pixel`

        :param int key: 0..(pixel_count)
        :param tuple 3-tuple of R, G, B;  each int 0..65535 or float 0..1
        """
        # for a more detailed version with all the debugging code and
        # comments look at set_pixel
        if 0 <= key < self.pixel_count:
            value = list(value)
            if len(value) != self.COLORS_PER_PIXEL:
                raise IndexError(
                    "length of value {} does not match COLORS_PER_PIXEL (= {})"
                    "".format(len(value), self.COLORS_PER_PIXEL)
                )
            # _check_and_convert modifies value in place..
            self._check_and_convert(value)
            self.set_pixel_16bit_value(key, value[0], value[1], value[2])
        else:
            raise IndexError(
                "index {} out of range [0..{}]" "".format(key, self.pixel_count)
            )

    class _ChannelDirekt:
        # Internal decorator to simplify mapping.

        def __init__(self, channel):
            self._channel = channel

        def __get__(self, obj, obj_type):
            # Grab the 16-bit value at the offset for this channel.
            return obj._get_channel_16bit_value(self._channel)

        def __set__(self, obj, value):
            # Set the 16-bit value at the offset for this channel.
            assert 0 <= value <= 65535
            obj.set_channel(self._channel, value)
            # if obj.auto_show:
            #     obj._write()

    # Define explicit channels for first IC.
    # → this is only for api backwards compliance.
    # not recommended for new use - use *set_channel*
    b0 = _ChannelDirekt(0)
    g0 = _ChannelDirekt(1)
    r0 = _ChannelDirekt(2)
    b1 = _ChannelDirekt(3)
    g1 = _ChannelDirekt(4)
    r1 = _ChannelDirekt(5)
    b2 = _ChannelDirekt(6)
    g2 = _ChannelDirekt(7)
    r2 = _ChannelDirekt(8)
    b3 = _ChannelDirekt(9)
    g3 = _ChannelDirekt(10)
    r3 = _ChannelDirekt(11)


##########################################


class TLC59711AutoShow(TLC59711):
    """TLC59711 16-bit 12 channel LED PWM driver with Auto-Show.

    This chip is designed to drive 4 RGB LEDs with 16-bit PWM per Color.
    The class has an interface compatible with the FancyLED library.
    and with this is similar to the NeoPixel and DotStar Interfaces.

    this TLC59711AutoShow is a subclass of TLC59711 that adds automatically
    sending changed data to the chips.
    this creates very slows responses on big changes.
    It is mainly usefull if you only have a very small number of pixels.

    :param ~busio.SPI spi: An instance of the SPI bus connected to the chip.
        The clock and MOSI/outout must be set, the MISO/input is unused.
        Maximal data clock frequence is:
        - TLC59711: 10MHz
        - TLC5971: 20MHz
    :param bool pixel_count: Number of RGB-LEDs (=Pixels) that are connected.
    """

    def __init__(self, spi, pixel_count=4):
        """Init."""
        super().__init__(spi, pixel_count=pixel_count)

    ##########################################

    def set_pixel(self, pixel_index, value):
        """
        Set the R, G, B values for the pixel.

        this funciton hase some advanced error checking.
        it is much slower than the other provided 'bare' variants..
        but therefor gives clues to what is going wrong.. ;-)

        :param int pixel_index: 0..(pixel_count)
        :param tuple value: 3-tuple of R, G, B;
            each int 0..65535 or float 0..1
        """
        super().set_pixel(pixel_index, value)
        self._write()

    def set_pixel_all(self, color):
        """
        Set the R, G, B values for all pixels.

        :param tuple 3-tuple of R, G, B;  each int 0..65535 or float 0..1
        """
        super().set_pixel_all(color)
        self._write()

    def set_all_black(self):
        """Set all pixels to black."""
        super().set_all_black()
        self._write()

    # channel access
    def set_channel(self, channel_index, value):
        """
        Set the value for the provided channel.

        :param int channel_index: 0..channel_count
        :param int value: 0..65535
        """
        super().set_channel(channel_index, value)
        self._write()

    def __setitem__(self, key, value):
        """
        Set the R, G, B values for the pixel.

        this funciton hase some advanced error checking.
        it is much slower than the other provided 'bare' variants..
        but therefor gives clues to what is going wrong.. ;-)
        this shortcut is identicall to `set_pixel`

        :param int key: 0..(pixel_count)
        :param tuple 3-tuple of R, G, B;  each int 0..65535 or float 0..1
        """
        super().__setitem__(key, value)
        self._write()

    class _ChannelDirektAutoShow:
        # Internal decorator to simplify mapping.

        def __init__(self, channel):
            self._channel = channel

        def __get__(self, obj, obj_type):
            # Grab the 16-bit value at the offset for this channel.
            return obj._get_channel_16bit_value(self._channel)

        def __set__(self, obj, value):
            # Set the 16-bit value at the offset for this channel.
            assert 0 <= value <= 65535
            obj.set_channel(self._channel, value)
            obj._write()

    # Define explicit channels for first IC.
    # → this is only for api backwards compliance.
    # not recommended for new use - use *set_channel*
    b0 = _ChannelDirektAutoShow(0)
    g0 = _ChannelDirektAutoShow(1)
    r0 = _ChannelDirektAutoShow(2)
    b1 = _ChannelDirektAutoShow(3)
    g1 = _ChannelDirektAutoShow(4)
    r1 = _ChannelDirektAutoShow(5)
    b2 = _ChannelDirektAutoShow(6)
    g2 = _ChannelDirektAutoShow(7)
    r2 = _ChannelDirektAutoShow(8)
    b3 = _ChannelDirektAutoShow(9)
    g3 = _ChannelDirektAutoShow(10)
    r3 = _ChannelDirektAutoShow(11)

Should I raise an issue on the git repo?

I am no Python expert so I am unaware of any other reasons why the code did not run as it should.

User avatar
mikeysklar
 
Posts: 13936
Joined: Mon Aug 01, 2016 8:10 pm

Re: adafruit_tlc59711.py error - NameError: name '_CHIP_BUFF

Post by mikeysklar »

@Pawelkosi,

Thanks for taking the time to modify the library and moving CHIP_BUFF... to a global. I'm not sure what solution is best for library, but opening an issue on the github repo sounds like the place to submit your work around.

https://github.com/adafruit/Adafruit_Ci ... 711/issues

I'm curious what the maintainers will suggest too.

User avatar
Pawelkosi
 
Posts: 18
Joined: Thu Jul 08, 2021 1:10 pm

Re: adafruit_tlc59711.py error - NameError: name '_CHIP_BUFF

Post by Pawelkosi »

mikeysklar wrote:@Pawelkosi,

Thanks for taking the time to modify the library and moving CHIP_BUFF... to a global. I'm not sure what solution is best for library, but opening an issue on the github repo sounds like the place to submit your work around.

https://github.com/adafruit/Adafruit_Ci ... 711/issues

I'm curious what the maintainers will suggest too.
Hi Mate,

I have posted an issue here: https://github.com/adafruit/Adafruit_Ci ... /issues/18

All the best!

User avatar
mikeysklar
 
Posts: 13936
Joined: Mon Aug 01, 2016 8:10 pm

Re: adafruit_tlc59711.py error - NameError: name '_CHIP_BUFF

Post by mikeysklar »

Perfect. Thank you.

Locked
Please be positive and constructive with your questions and comments.

Return to “Circuit Playground Classic, Circuit Playground Express, Circuit Playground Bluefruit”