0.2.0 TLC5917

This commit is contained in:
Rob Tillaart 2024-07-14 10:18:46 +02:00
parent 76bdcc7125
commit 13df84efc4
14 changed files with 313 additions and 131 deletions

View File

@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.2.0] - 2024-07-12
- fix deviceCount in **write()**.
- fix allocation buffer for channels in constructor.
- update performance sketch.
- add demo_setChannel sketch (array param).
- rename blank to **outputEnable**.
- update readme.md.
- minor edits.
----
## [0.1.2] - 2024-06-02
- add **TLC5917_NORMAL_MODE** and **TLC5917_SPECIAL_MODE**
- add **uint8_t getMode()**

View File

@ -21,12 +21,13 @@ TLC5917 is an Arduino library for TLC5917 8-Channel Constant-Current LED Sink Dr
The **TLC5917** library allows control over the 8 channels (outputs) of a TLC5917 device.
This library also support more than one device in a daisy chain (see below).
The library allows to set the channels (outputs) individually or a group in one call.
The library allows to set the channels (outputs) on/off individually or as a group in one call.
Furthermore it allows to set a current gain for all devices connected.
The **TLC5916** is a derived class that is functional identical to the TLC5917 (for now).
The **TLC5916** is a derived class that is functional identical to the TLC5917.
When implementation proceeds this might change, the difference is in support for fetching
the status and error modi. This is not supported by the library
the status and error modi. This functionality is not supported by the library yet,
so there is no difference between the **TLC5916** and **TLC5917** for now.
The library needs more testing with hardware.
Please share your experiences.
@ -34,22 +35,30 @@ Please share your experiences.
(Changes of the interface are definitely possible).
#### Daisy chaining
### Breaking changes
This library supports daisy chaining of TLC5917 modules.
The 0.2.0 version fixed an internal storage bug which allocated way to much memory
in version 0.1.x. So these versions can be considered obsolete.
The performance of the library **write()** call improved a lot.
### Daisy chaining
This library supports daisy chaining of multiple **TLC5917** modules.
A constructor takes the number of devices as parameter and
an internal buffer is allocated (8 elements per device).
This internal buffer is clocked into the devices with **write()**.
an internal buffer is allocated (8 channels per device).
This internal buffer is clocked into the devices with the **write()** call.
So **setChannel()** calls can be changed until last moment.
#### Related
### Related
- https://www.adafruit.com/product/1429
- https://github.com/RobTillaart/TLC5917
- https://github.com/RobTillaart/TLC5947
- https://github.com/RobTillaart/PCA9634 (I2C)
- https://github.com/RobTillaart/PCA9635 (I2C)
- https://github.com/RobTillaart/PCA9685 (I2C)
- https://github.com/RobTillaart/PCA9634 (I2C, PWM)
- https://github.com/RobTillaart/PCA9635 (I2C, PWM)
- https://github.com/RobTillaart/PCA9685 (I2C, PWM)
## Interface
@ -58,36 +67,38 @@ This internal buffer is clocked into the devices with **write()**.
#include TLC5917.h
```
#### Constructor
### Constructor
- **TLC5917(uint8_t clock, uint8_t data, uint8_t latch, uint8_t blank)** constructor.
- **TLC5917(uint8_t clock, uint8_t data, uint8_t latch, uint8_t outputEnable)** constructor.
Single device constructor.
Defines the pins used for uploading / writing the PWM data to the module.
The blank pin is explained in more detail below.
- **TLC5917(int deviceCount, uint8_t clock, uint8_t data, uint8_t latch, uint8_t blank)** constructor.
Defines the pins used for uploading / writing the data to the device.
The outputEnable pin is explained in more detail below.
- **TLC5917(int deviceCount, uint8_t clock, uint8_t data, uint8_t latch, uint8_t outputEnable)** constructor.
To be used for multiple devices, typical 2 or more.
Defines the pins used for uploading / writing the PWM data to the module.
The blank pin is explained in more detail below.
Defines the pins used for uploading / writing the data to the device.
The outputEnable pin is explained in more detail below.
- **~TLC5917()** destructor. Frees the allocated memory.
#### Base
### Base
- **bool begin()** set the pinModes of the pins and their initial values.
The TLC is disabled by default, as the device has random values in its grey-scale register.
One must call **enable()** explicitly.
- **bool begin()** set the pinMode of the pins used and their initial values.
The TLC5917 is disabled by default, as the device has random values in its register.
Therefore one must call **enable()** explicitly.
- **int channelCount()** return the amount of channels == 8 x number of devices.
#### Set/Get channels
### Set/Get channels
- **bool setChannel(uint8_t channel, bool on)** set a channel on or off in the
internal buffer. The value is not written yet to the device(s).
internal buffer. The value is not written immediately to the device(s).
One has to call **write()** for that.
- **bool setChannel(uint8_t \* array)** copy a preset of channel settings in one call.
The user has to take care the the size of array holds the right amount of bytes.
The user has to take care that the size of array holds the right amount of bytes.
Typical amount is deviceCount (or more).
- **bool setAll(bool on)** set all channels on or off.
- **bool getChannel(uint8_t channel)** get current state of a channel in the cached buffer.
- **bool getChannel(uint8_t channel)** get current state of a channel from the cached buffer.
- **void write()** writes the whole buffer (deviceCount x 8 values) to the device(s).
- **void write(int n)** writes a part of the internal buffer (only **n** values) to the device.
- **void write(int channels)** writes a part of the internal buffer (only **channels** values) to the device.
Typical used to speed up if less than max number e.g. only 17 channels are used
and needs to be updated.
**experimental, might have side effects**
@ -99,34 +110,41 @@ channels as fast as possible.
See also **TLC5917_performance.ino** for an indication of time needed.
#### Blank line TODO CHECK
### OutputEnable line
The blank pin (OE line) is used to set all channels on or off.
The **outputEnable** pin (OE or blank) is used to set all channels on or off.
This allows to "preload" the registers with values and enable them all at once
with very precise timing.
Default a TLC device is disabled (by begin), so one should enable it "manually".
Default a TLC device is disabled, by **begin()**, so one should enable it "manually".
(P13 datasheet)
- **void enable()** all channels reflect last PWM values written.
- **void enable()** all channels reflect last values written.
- **void disable()** all channels are off / 0.
- **bool isEnabled()** returns status of blank line.
- **bool isEnabled()** returns status of outputEnable line.
The library only supports one **enable() / blank line**. If you want
a separate **enable()** per device you might need to connect the devices
The library only supports one **enable() line**.
If you want a separate **enable()** per device you might need to connect the devices
"in parallel" instead of "in series" (daisy chained).
The blank parameter in the constructor should be set to -1 (out of range value).
The outputEnable parameter in the constructor should be set to -1 (out of range value).
It might be possible to use a PWM pin on the OE line to dim the LEDS.
This is neither tested or supported by the library.
#### PWM
It might be possible to use a PWM pin on the **outputEnable** line to dim the LEDS.
This is neither tested nor supported by the library.
Note that writing to the TLC5917 needs a HIGH **outputEnable** so the PWM value needs
to be set again.
#### Configure gain
### Configure gain
See datasheet page 23 for details.
- **void setNormalMode()** to send the data for the LEDS.
- **void setSpecialMode()** to configure the gain.
- **void setNormalMode()** switch to normal mode (default) to send the data for the IO pins.
- **void setSpecialMode()** switch to special mode to configure the gain.
Note that calling **setSpecialMode()** and **setNormalMode()** disables the output.
So one should enable it again if one wants to.
The special mode needs to be set for the following functions:
@ -176,7 +194,41 @@ Actual current depends on Rext == external resistor (see datasheet).
## Performance
See **TLC5917_performance.ino** for an indicative test.
Based upon **TLC5917_performance.ino** for an indicative test.
and issue #9 for the LGT8F328 data
Timing in microseconds, 800 channels = 100 devices.
| Version | Function | UNO 1.8.19 | LGT8F328 | other |
|:-------:|:-----------------|:----------:|:--------:|:--------:|
| 0.2.0 | SETCHANNEL TRUE | 2572 | 1152 |
| 0.2.0 | SETCHANNEL FALSE | 2840 | 1296 |
| 0.2.0 | SETALL TRUE | 236 | 108 |
| 0.2.0 | SETALL FALSE | 232 | - |
| 0.2.0 | WRITE optimized | 1772 | - |
| 0.2.0 | WRITE normal | 9420 | - |
_The "WRITE optimized" is AVR only._
So setting and writing 8 channels (e.g. a single 7 segment display) takes
28.40 + 17.72 = 46.12 < 50 microseconds.
So in theory an UNO could update it roughly 20K times per second.
## Hardware SPI
First investigations show that **write()** could be a hardware SPI transaction.
However the **setNormalMode()**, **setSpecialMode()** and especially the
**writeConfiguration()** function are no standard 8 bit SPI transactions.
This of course includes the **gain** functions that use these.
To solve this one still has to provide the CLOCK, LATCH and OUTPUT ENABLE pins.
Especially the CLOCK pin is part of the SPI pins, and it would depend on the
board and optional number of HW SPI ports of the board.
To prevent this complexity the library does not have a hardware SPI constructor.
It looks like it is possible to create a simplified class (stripped version)
without the gain control that might work well with HW SPI for many application.
The added value is however limited as the (optimized) SW is pretty fast already.
## Future
@ -184,29 +236,31 @@ See **TLC5917_performance.ino** for an indicative test.
#### Must
- update documentation
- buy hardware
- test test test
- do hardware test test test ...
#### Should
- investigate daisy chaining. (hardware needed).
- investigate daisy chaining. (extra hardware needed).
- max CLOCK speed when chained (50% DutyCycle)
- what is clock in practice (e.g. an ESP32 240 MHz)
- now the CurrentGain is set to the same value for all devices.
- needs array, one value (uint8_t or float) per device, investigate.
- needs array, one value (uint8_t) per device, investigate.
#### Could
- **index operator []** to get set channels, might be better?
- reading error codes from SDO
- do brightness test with analogWrite(OE, value);
- it would be mandatory to have OE be a PWM pin.
#### Wont (unless needed)
- **void getChannel(uint8_t array)** fill an array with current data.
- redo constructor,
- move deviceCount to last place with default = 1 (one constructor less).
- **void getChannel(uint8_t \* array)** fill an array with current data.
- error handling in special mode
- over-temperature, open-load, short to GND, short to VLED (5917 only).
- investigate if hardware SPI is possible
- which mode?
- over-temperature, open-load, short to GND, short to VLED (TLC5917 only).
- implement hardware SPI, see above.
## Support

View File

@ -1,7 +1,7 @@
//
// FILE: TLC5917.cpp
// AUTHOR: Rob Tillaart
// VERSION: 0.1.2
// VERSION: 0.2.0
// DATE: 2024-03-17
// PURPOSE: Arduino library for TLC5917 8-Channel Constant-Current LED Sink Drivers.
// URL: https://github.com/RobTillaart/TLC5917
@ -25,7 +25,7 @@ TLC5917::TLC5917(int deviceCount, uint8_t clock, uint8_t data, uint8_t LE, uint8
_le = LE;
_oe = OE;
_mode = TLC5917_NORMAL_MODE;
_buffer = (uint8_t *) calloc(_channelCount, sizeof(uint8_t));
_buffer = (uint8_t *) calloc(deviceCount, sizeof(uint8_t));
_configuration = 0xFF; // page 23 datasheet
}
@ -108,10 +108,10 @@ void TLC5917::write()
}
void TLC5917::write(int chan)
void TLC5917::write(int channels)
{
if (chan > _channelCount) chan = _channelCount;
if (chan < 0) return;
if (channels < 0) return;
int devices = (channels > _channelCount) ? _channelCount / 8 : channels / 8;
#if defined(ARDUINO_ARCH_AVR) || defined(ARDUINO_ARCH_MEGAAVR)
@ -127,11 +127,11 @@ void TLC5917::write(int chan)
uint8_t cbmask1 = digitalPinToBitMask(_clock);
uint8_t cbmask2 = ~cbmask1;
for (int channel = chan - 1; channel >= 0; channel--)
for (int dev = devices - 1; dev >= 0; dev--)
{
for (uint8_t mask = 0x80; mask; mask >>= 1)
{
if (_buffer[channel] & mask)
if (_buffer[dev] & mask)
{
*_dataOutRegister |= outmask1; // digitalWrite(_dat, HIGH);
}
@ -146,17 +146,17 @@ void TLC5917::write(int chan)
#else
// also write when blank == LOW
// also write when outputEnable == LOW
// to "preload the registers"
// local variables to maximize speed.
uint8_t _clk = _clock;
uint8_t _dat = _data;
for (int channel = chan - 1; channel >= 0; channel--)
for (int dev = devices - 1; dev >= 0; dev--)
{
for (uint8_t mask = 0x80; mask; mask >>= 1)
{
digitalWrite(_dat, _buffer[channel] & mask ? HIGH : LOW);
digitalWrite(_dat, _buffer[dev] & mask ? HIGH : LOW);
digitalWrite(_clk, HIGH);
digitalWrite(_clk, LOW);
}

View File

@ -2,13 +2,13 @@
//
// FILE: TLC5917.h
// AUTHOR: Rob Tillaart
// VERSION: 0.1.2
// VERSION: 0.2.0
// DATE: 2024-03-17
// PURPOSE: Arduino library for TLC5917 8-Channel Constant-Current LED Sink Drivers.
// URL: https://github.com/RobTillaart/TLC5917
#define TLC5917_LIB_VERSION (F("0.1.2"))
#define TLC5917_LIB_VERSION (F("0.2.0"))
#include "Arduino.h"
@ -39,8 +39,11 @@ class TLC5917
{
public:
// single device constructor
// LE = Latch Enable
// OE = Output Enable
TLC5917(uint8_t clock, uint8_t data, uint8_t LE, uint8_t OE);
// multi device constructor - for daisy chaining)
// multi device constructor
// - for daisy chaining
TLC5917(int deviceCount, uint8_t clock, uint8_t data, uint8_t LE, uint8_t OE);
virtual ~TLC5917();
@ -49,15 +52,16 @@ public:
bool setChannel(uint8_t channel, bool on);
// size array must be equal or larger than deviceCount.
// one bit per channel.
bool setChannel(uint8_t * array);
bool setAll(bool on);
bool getChannel(uint8_t channel);
// write the buffer to the TLC5917 device(s).
void write(int n);
// write the internal buffer to the TLC5917 device(s).
void write(int channels);
void write();
// control the blank (OE) line.
// control the outputEnable (OE) line.
void enable();
void disable();
bool isEnabled(); // returns status

View File

@ -1,20 +1,20 @@
//
// FILE: TLC5917_NightRider.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo running leds.
// URL: https://github.com/RobTillaart/TLC5917
// FILE: TLC5917_NightRider.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo running leds.
// URL: https://github.com/RobTillaart/TLC5917
#include "TLC5917.h"
int DEVICES = 1;
const int CLOCK = 13;
const int DATA = 12;
const int LATCH = 11;
const int BLANK = 10;
const int DEVICES = 1;
const int CLOCK = 13;
const int DATA = 12;
const int LATCH = 11;
const int ENABLE = 10;
TLC5917 tlc(DEVICES, CLOCK, DATA, LATCH, BLANK);
TLC5917 tlc(DEVICES, CLOCK, DATA, LATCH, ENABLE);
void setup()
@ -53,7 +53,7 @@ void loop()
tlc.write();
pos += delta;
if ((pos == 0) || (pos == (tlc.channelCount() - 1)))
if ((pos == 0) || (pos == (tlc.channelCount() - 1)))
{
delta = -delta;
}

View File

@ -8,13 +8,13 @@
#include "TLC5917.h"
const int DEVICES = 2;
const int CLOCK = 13;
const int DATA = 12;
const int LE = 11;
const int OE = 10;
const int DEVICES = 1;
const int CLOCK = 13;
const int DATA = 12;
const int LATCH = 11;
const int ENABLE = 10;
TLC5917 tlc(DEVICES, CLOCK, DATA, LE, OE);
TLC5917 tlc(DEVICES, CLOCK, DATA, LATCH, ENABLE);
void setup()
@ -37,20 +37,29 @@ void setup()
Serial.println(tlc.channelCount());
tlc.enable();
for (int ch = 0; ch < tlc.channelCount(); ch++)
{
tlc.setChannel(ch, true);
tlc.write();
delay(100);
tlc.setChannel(ch, false);
}
tlc.write();
}
void loop()
{
Serial.println("ON");
for (int ch = 0; ch < tlc.channelCount(); ch++)
{
Serial.println(ch);
tlc.setChannel(ch, true);
tlc.write();
delay(100);
}
Serial.println("OFF");
for (int ch = 0; ch < tlc.channelCount(); ch++)
{
Serial.println(ch);
tlc.setChannel(ch, false);
tlc.write();
delay(100);
}
}

View File

@ -3,18 +3,19 @@
// AUTHOR: Rob Tillaart
// PURPOSE: demo writeConfiguration() to set gain.
// URL: https://github.com/RobTillaart/TLC5917
//
// needs investigation.
#include "TLC5917.h"
const int DEVICES = 1;
const int CLOCK = 13;
const int DATA = 12;
const int LE = 11;
const int OE = 10;
const int CLOCK = 13;
const int DATA = 12;
const int LATCH = 11;
const int ENABLE = 10;
TLC5917 tlc(DEVICES, CLOCK, DATA, LE, OE);
TLC5917 tlc(DEVICES, CLOCK, DATA, LATCH, ENABLE);
void setup()
@ -34,53 +35,57 @@ void setup()
Serial.print("Channels: ");
Serial.println(tlc.channelCount());
// set gain level
tlc.setSpecialMode();
tlc.writeConfiguration(255);
// set all leds ON
tlc.setNormalMode();
tlc.enable();
for (int ch = 0; ch < tlc.channelCount(); ch++)
{
tlc.setChannel(ch, true);
}
tlc.write();
delay(1000);
Serial.print("MODE:\t");
Serial.println(tlc.getMode());
tlc.setSpecialMode();
Serial.print("MODE:\t");
Serial.println(tlc.getMode());
uint32_t start = millis();
for (int conf = 0; conf < 256; conf++)
{
tlc.writeConfiguration(conf);
delay(100);
}
uint32_t stop = millis();
tlc.setNormalMode();
Serial.print("TIME:\t");
Serial.println(stop - start);
Serial.print("MODE:\t");
Serial.println(tlc.getMode());
Serial.println("\ndone...");
Serial.println("25%");
tlc.writeConfiguration(0); // very low.
tlc.setNormalMode();
tlc.enable();
delay(3000);
tlc.setSpecialMode();
tlc.writeConfiguration(255); // MAX!
tlc.setNormalMode();
tlc.enable();
delay(3000);
Serial.println("\ndone...");
}
void loop()
{
// increase gain
for (int conf = 0; conf < 256; conf++)
{
tlc.writeConfiguration(conf);
delay(10);
}
// decrease gain
for (int conf = 255; conf > 0; conf--)
{
tlc.writeConfiguration(conf);
delay(10);
}
}

View File

@ -0,0 +1,56 @@
//
// FILE: TLC5917_demo_setChannel.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo basic usage
// URL: https://github.com/RobTillaart/TLC5917
#include "TLC5917.h"
const int DEVICES = 1;
const int CLOCK = 13;
const int DATA = 12;
const int LATCH = 11;
const int ENABLE = 10;
TLC5917 tlc(DEVICES, CLOCK, DATA, LATCH, ENABLE);
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.print("TLC5917_LIB_VERSION: \t");
Serial.println(TLC5917_LIB_VERSION);
Serial.println();
Serial.println(sizeof(tlc));
if (tlc.begin() == false)
{
Serial.println("error");
while (1);
}
Serial.print("Channels: ");
Serial.println(tlc.channelCount());
tlc.enable();
}
void loop()
{
uint8_t arr[DEVICES];
for (int idx = 0; idx < DEVICES; idx++)
{
arr[idx] = random(256);
}
tlc.setChannel(arr);
tlc.write();
delay(100);
}
// -- END OF FILE --

View File

@ -10,12 +10,12 @@
const int DEVICES = 1;
const int CLOCK = 13;
const int DATA = 12;
const int LE = 11;
const int OE = 10;
const int CLOCK = 13;
const int DATA = 12;
const int LATCH = 11;
const int ENABLE = 10;
TLC5917 tlc(DEVICES, CLOCK, DATA, LE, OE);
TLC5917 tlc(DEVICES, CLOCK, DATA, LATCH, ENABLE);
void setup()

View File

@ -7,13 +7,14 @@
#include "TLC5917.h"
const int DEVICES = 100;
const int CLOCK = 13;
const int DATA = 12;
const int LATCH = 11;
const int BLANK = 10;
TLC5916 tlc(DEVICES, CLOCK, DATA, LATCH, BLANK);
const int DEVICES = 100;
const int CLOCK = 13;
const int DATA = 12;
const int LATCH = 11;
const int ENABLE = 10;
TLC5916 tlc(DEVICES, CLOCK, DATA, LATCH, ENABLE);
uint32_t start, stop;
@ -24,6 +25,7 @@ void setup()
Serial.println(__FILE__);
Serial.print("TLC5917_LIB_VERSION: \t");
Serial.println(TLC5917_LIB_VERSION);
Serial.println();
if (tlc.begin() == false)
{
@ -60,6 +62,7 @@ void testSetChannel()
stop = micros();
Serial.print("SETCHANNEL TRUE:\t");
Serial.println(stop - start);
tlc.write();
delay(100);
start = micros();
@ -70,6 +73,7 @@ void testSetChannel()
stop = micros();
Serial.print("SETCHANNEL FALSE:\t");
Serial.println(stop - start);
tlc.write();
delay(100);
start = micros();
@ -77,6 +81,15 @@ void testSetChannel()
stop = micros();
Serial.print("SETALL TRUE:\t\t");
Serial.println(stop - start);
tlc.write();
delay(100);
start = micros();
tlc.setAll(false);
stop = micros();
Serial.print("SETALL FALSE:\t\t");
Serial.println(stop - start);
tlc.write();
delay(100);
}
@ -89,6 +102,7 @@ void testWrite()
stop = micros();
Serial.print("WRITE:\t\t\t");
Serial.println(stop - start);
delay(100);
}

View File

@ -0,0 +1,29 @@
Arduino UNO
IDE: 1.8.19
(AVR optimized write() on UNO)
TLC5917_performance\TLC5917_performance.ino
TLC5917_LIB_VERSION: 0.2.0
Channels: 800
SETCHANNEL TRUE: 2572
SETCHANNEL FALSE: 2840
SETALL TRUE: 236
SETALL FALSE: 232
WRITE: 1772
Done...
(default write() on UNO for reference)
TLC5917_LIB_VERSION: 0.1.3
Channels: 800
SETCHANNEL TRUE: 2572
SETCHANNEL FALSE: 2848
SETALL TRUE: 236
SETALL FALSE: 232
WRITE: 9420
Done...

View File

@ -9,12 +9,12 @@
const int DEVICES = 1;
const int CLOCK = 13;
const int DATA = 12;
const int LE = 11;
const int OE = 10;
const int CLOCK = 13;
const int DATA = 12;
const int LATCH = 11;
const int ENABLE = 10;
TLC5917 tlc(DEVICES, CLOCK, DATA, LE, OE);
TLC5917 tlc(DEVICES, CLOCK, DATA, LATCH, ENABLE);
void setup()

View File

@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/TLC5917.git"
},
"version": "0.1.2",
"version": "0.2.0",
"license": "MIT",
"frameworks": "*",
"platforms": "*",

View File

@ -1,5 +1,5 @@
name=TLC5917
version=0.1.2
version=0.2.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for TLC5917 8-Channel Constant-Current LED Sink Drivers.