Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for TLC5947 24-Channel PWM driver. #551

Merged
merged 3 commits into from
Jan 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,14 @@ P9813Spi2MhzMethod KEYWORD1
P9813Spi1MhzMethod KEYWORD1
P9813Spi500KhzMethod KEYWORD1
P9813SpiHzMethod KEYWORD1
Tlc5947Method KEYWORD1
Tlc5947Method16Bit KEYWORD1
Tlc5947Spi30MhzMethod KEYWORD1
Tlc5947Spi30MhzMethod16Bit KEYWORD1
Tlc5947Spi15MhzMethod KEYWORD1
Tlc5947Spi15MhzMethod16Bit KEYWORD1
Tlc5947SpiMethod KEYWORD1
Tlc5947SpiMethod16Bit KEYWORD1
NeoPixelAnimator KEYWORD1
AnimUpdateCallback KEYWORD1
AnimationParam KEYWORD1
Expand Down
6 changes: 6 additions & 0 deletions src/NeoPixelBrightnessBus.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,12 @@ template<typename T_COLOR_FEATURE, typename T_METHOD> class NeoPixelBrightnessBu
{
}

NeoPixelBrightnessBus(uint16_t countPixels, uint8_t pinClock, uint8_t pinData, uint8_t pinLatch, uint8_t pinOutputEnable = NOT_A_PIN) :
NeoPixelBus<T_COLOR_FEATURE, T_METHOD>(countPixels, pinClock, pinData, pinLatch, pinOutputEnable),
_brightness(255)
{
}

NeoPixelBrightnessBus(uint16_t countPixels) :
NeoPixelBus<T_COLOR_FEATURE, T_METHOD>(countPixels),
_brightness(255)
Expand Down
8 changes: 8 additions & 0 deletions src/NeoPixelBus.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ License along with NeoPixel. If not, see
#include "internal/Lpd6803GenericMethod.h"
#include "internal/Ws2801GenericMethod.h"
#include "internal/P9813GenericMethod.h"
#include "internal/Tlc5947GenericMethod.h"

#if defined(ARDUINO_ARCH_ESP8266)

Expand Down Expand Up @@ -153,6 +154,13 @@ template<typename T_COLOR_FEATURE, typename T_METHOD> class NeoPixelBus
{
}

NeoPixelBus(uint16_t countPixels, uint8_t pinClock, uint8_t pinData, uint8_t pinLatch, uint8_t pinOutputEnable = NOT_A_PIN) :
_countPixels(countPixels),
_state(0),
_method(pinClock, pinData, pinLatch, pinOutputEnable, countPixels, T_COLOR_FEATURE::PixelSize, T_COLOR_FEATURE::SettingsSize)
{
}

NeoPixelBus(uint16_t countPixels) :
_countPixels(countPixels),
_state(0),
Expand Down
208 changes: 208 additions & 0 deletions src/internal/Tlc5947GenericMethod.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
/*-------------------------------------------------------------------------
NeoPixel library helper functions for Tlc5947 24 channel PWM controller using general Pins.

Written by Michael C. Miller.
Written by Dennis Kasprzyk.

I invest time and resources providing this open source code,
please support me by dontating (see https://github.com/Makuna/NeoPixelBus)

-------------------------------------------------------------------------
This file is part of the Makuna/NeoPixelBus library.

NeoPixelBus is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.

NeoPixelBus is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with NeoPixel. If not, see
<http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------*/

#pragma once

// must also check for arm due to Teensy incorrectly having ARDUINO_ARCH_AVR set
#if defined(ARDUINO_ARCH_AVR) && !defined(__arm__)
#include "TwoWireBitBangImpleAvr.h"
#else
#include "TwoWireBitBangImple.h"
#endif

#define TLC5947_MODULE_PWM_CHANNEL_COUNT 24

class Tlc5947Converter8Bit
{
public:
static const size_t sizeChannel = 1;
static void ConvertFrame(uint8_t* sendBufferPtr, uint8_t* channelPtr)
{
// Write 2 channels into 3 bytes scaling 8-bit to 12-bit per channel
for (int indexChannel = 0; indexChannel < TLC5947_MODULE_PWM_CHANNEL_COUNT; indexChannel += 2)
{
*sendBufferPtr++ = *channelPtr;
*sendBufferPtr++ = (*channelPtr-- & 0xf0) | (*channelPtr >> 4);
*sendBufferPtr++ = ((*channelPtr << 4) & 0xf0) | (*channelPtr-- >> 4);
}
}
};

class Tlc5947Converter16Bit
{
public:
static const size_t sizeChannel = 2;
static void ConvertFrame(uint8_t* sendBufferPtr, uint8_t* sourceBufferPtr)
{
uint16_t* channelPtr = (uint16_t*)sourceBufferPtr;

// Write 2 channels into 3 bytes using upper 12-bit of each channel
for (int indexChannel = 0; indexChannel < TLC5947_MODULE_PWM_CHANNEL_COUNT; indexChannel += 2)
{
*sendBufferPtr++ = *channelPtr >> 8;
*sendBufferPtr++ = (*channelPtr-- & 0xf0) | (*channelPtr >> 12);
*sendBufferPtr++ = *channelPtr-- >> 4;
}
}
};


template<typename T_BITCONVERT, typename T_TWOWIRE> class Tlc5947MethodBase
{
public:
typedef typename T_TWOWIRE::SettingsObject SettingsObject;

// 24 channel * 12 bit
static const size_t sizeSendBuffer = 36;

Tlc5947MethodBase(uint8_t pinClock, uint8_t pinData, uint8_t pinLatch, uint8_t pinOutputEnable, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
_countModule((pixelCount * elementSize + TLC5947_MODULE_PWM_CHANNEL_COUNT - 1) / TLC5947_MODULE_PWM_CHANNEL_COUNT),
_sizeData(_countModule * TLC5947_MODULE_PWM_CHANNEL_COUNT + settingsSize),
_wire(pinClock, pinData),
_pinLatch(pinLatch),
_pinOutputEnable(pinOutputEnable)
{
_data = static_cast<uint8_t*>(malloc(_sizeData));
pinMode(pinLatch, OUTPUT);
pinMode(pinOutputEnable, OUTPUT);
digitalWrite(pinOutputEnable, HIGH);
}

Tlc5947MethodBase(uint8_t pinClock, uint8_t pinData, uint8_t pinLatch, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
Tlc5947MethodBase(pinClock, pinData, pinLatch, -1, pixelCount, elementSize, settingsSize)
{
}

#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
Tlc5947MethodBase(uint8_t pinLatch, uint8_t pinOutputEnable, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
Tlc5947MethodBase(SCK, MOSI, pinLatch, pinOutputEnable, pixelCount, elementSize, settingsSize)
{
}

Tlc5947MethodBase(uint8_t pinLatch, uint16_t pixelCount, size_t elementSize, size_t settingsSize) :
Tlc5947MethodBase(SCK, MOSI, pinLatch, -1, pixelCount, elementSize, settingsSize)
{
}
#endif

~Tlc5947MethodBase()
{
free(_data);
pinMode(_pinLatch, INPUT);
pinMode(_pinOutputEnable, INPUT);
}

bool IsReadyToUpdate() const
{
return true; // dot stars don't have a required delay
}

#if defined(ARDUINO_ARCH_ESP32)
void Initialize(int8_t sck, int8_t miso, int8_t mosi, int8_t ss)
{
_wire.begin(sck, miso, mosi, ss);
}
#endif

void Initialize()
{
_wire.begin();
memset(_data, 0, _sizeData);
}

void Update(bool)
{

digitalWrite(_pinOutputEnable, HIGH);

digitalWrite(_pinLatch, LOW);
_wire.beginTransaction();

// We need to write the channels in reverse order. Get a Pointer to the last channel.
uint8_t* lastChannelPtr = _data + ((_countModule * TLC5947_MODULE_PWM_CHANNEL_COUNT - 1) * T_BITCONVERT::sizeChannel);
for (uint16_t countSend = 0; countSend < _countModule; countSend++)
{
// We pass a pointer to the last channel and ConvertFrame reads the channels backwards
T_BITCONVERT::ConvertFrame(_sendBuffer, lastChannelPtr);
_wire.transmitBytes(_sendBuffer, sizeSendBuffer);
lastChannelPtr -= TLC5947_MODULE_PWM_CHANNEL_COUNT * T_BITCONVERT::sizeChannel;
}

_wire.endTransaction();
digitalWrite(_pinLatch, HIGH);
digitalWrite(_pinLatch, LOW);
digitalWrite(_pinOutputEnable, LOW);
}

uint8_t* getData() const
{
return _data;
};

size_t getDataSize() const
{
return _sizeData;
};

void applySettings(const SettingsObject& settings)
{
_wire.applySettings(settings);
}

private:
const uint16_t _countModule; // Number of tlc5947 modules
const size_t _sizeData; // Size of '_data' buffer below

T_TWOWIRE _wire;
uint8_t* _data; // Holds LED color values
uint8_t _sendBuffer[sizeSendBuffer]; // Holds channel values for one module
uint8_t _pinLatch;
uint8_t _pinOutputEnable;
};

typedef Tlc5947MethodBase<Tlc5947Converter8Bit, TwoWireBitBangImple> Tlc5947Method;
typedef Tlc5947MethodBase<Tlc5947Converter16Bit, TwoWireBitBangImple> Tlc5947Method16Bit;

#if !defined(__AVR_ATtiny85__) && !defined(ARDUINO_attiny)
#include "TwoWireSpiImple.h"

// for standalone
typedef Tlc5947MethodBase<Tlc5947Converter8Bit, TwoWireSpiImple<SpiSpeed30Mhz>> Tlc5947Spi30MhzMethod;
typedef Tlc5947MethodBase<Tlc5947Converter16Bit, TwoWireSpiImple<SpiSpeed30Mhz>> Tlc5947Spi30MhzMethod16Bit;

// for cascaded devices
typedef Tlc5947MethodBase<Tlc5947Converter8Bit, TwoWireSpiImple<SpiSpeed15Mhz>> Tlc5947Spi15MhzMethod;
typedef Tlc5947MethodBase<Tlc5947Converter16Bit, TwoWireSpiImple<SpiSpeed15Mhz>> Tlc5947Spi15MhzMethod16Bit;

typedef Tlc5947MethodBase<Tlc5947Converter8Bit, TwoWireSpiImple<SpiSpeed15Mhz>> Tlc5947SpiMethod;
typedef Tlc5947MethodBase<Tlc5947Converter16Bit, TwoWireSpiImple<SpiSpeed15Mhz>> Tlc5947SpiMethod16Bit;


#endif



22 changes: 22 additions & 0 deletions src/internal/TwoWireSpiImple.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,17 @@ class SpiSpeed40Mhz
static const uint32_t Clock = 40000000L;
};

class SpiSpeed30Mhz
{
public:
typedef NeoNoSettings SettingsObject;
SpiSpeed30Mhz() {};

static void applySettings(const SettingsObject& settings) {}

static const uint32_t Clock = 30000000L;
};

class SpiSpeed20Mhz
{
public:
Expand All @@ -50,6 +61,17 @@ class SpiSpeed20Mhz
static const uint32_t Clock = 20000000L;
};

class SpiSpeed15Mhz
{
public:
typedef NeoNoSettings SettingsObject;
SpiSpeed15Mhz() {};

static void applySettings(const SettingsObject& settings) {}

static const uint32_t Clock = 15000000L;
};

class SpiSpeed10Mhz
{
public:
Expand Down