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

* Add support for TLC5947 24-Channel PWM driver.

* Refactor code to match coding guidelines and better documentation.
This commit is contained in:
xonestonex
2022-01-15 20:09:54 +01:00
committed by GitHub
parent 3060e4cd28
commit e31677ad32
5 changed files with 252 additions and 0 deletions

View File

@@ -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

View File

@@ -83,6 +83,12 @@ public:
{
}
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)

View File

@@ -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)
@@ -153,6 +154,13 @@ public:
{
}
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),

View File

@@ -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

View File

@@ -39,6 +39,17 @@ public:
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:
@@ -50,6 +61,17 @@ public:
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: