-
Notifications
You must be signed in to change notification settings - Fork 2
/
TLC5947.cpp
348 lines (303 loc) · 11.1 KB
/
TLC5947.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
/*
TLC5947 Control Library
Used to control the TI TLC5947 LED driver chip
Zack Phillips - [email protected]
Product Page: http://www.ti.com/product/tlc5947
Copyright (c) 2018, Zachary F. Phillips
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of the <organization> nor the
names of its contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL Z. PHILLIPS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "TLC5947.h"
#include <SPI.h>
#define TLC5947_SPI_MODE SPI_MODE0
#define SPI_DELAY_US 0
/* Bit Quantities (Change to match other TLC driver chips) */
#define LATCH_DELAY 1
void TLC5947::init(int8_t num_latches, int8_t num_tlc_one_row, uint8_t use_2D,
uint8_t spi_mosi, uint8_t spi_clk, uint8_t blank)
{
_num_tlc_one_row = num_tlc_one_row;
_num_latches = num_latches;
_spi_clk = spi_clk;
_spi_mosi = spi_mosi;
_blank = blank;
mSettings = SPISettings(spi_baud_rate, MSBFIRST, SPI_MODE0);
_use_2D = use_2D;
// Initialize SPI library
SPI.setMOSI(_spi_mosi);
SPI.begin();
// Set up latch
for(uint8_t i=0; i<num_latches; i++){
auto lat = _latches[i];
pinMode(lat, OUTPUT);
digitalWrite(lat, LOW);
}
// set up blank
pinMode(_blank, OUTPUT);
digitalWrite(_blank, HIGH);
// Set default color channel indicies
setRgbPinOrder(rgb_order_default[0], rgb_order_default[1], rgb_order_default[2]);
}
void TLC5947::setSpiBaudRate(uint32_t new_baud_rate)
{
// Store old baud rate
spi_baud_rate = new_baud_rate;
// update mSettings
mSettings = SPISettings(spi_baud_rate, MSBFIRST, SPI_MODE0);
}
uint32_t TLC5947::getSpiBaudRate()
{
// Return current baud rate
return spi_baud_rate;
}
void TLC5947::setGsclkFreq(uint32_t new_gsclk_frequency)
{
//not used. only kept for compatibility with TLC5955
}
uint32_t TLC5947::getGsclkFreq()
{
return 0;
}
void TLC5947::setRgbPinOrder(uint8_t rPos, uint8_t grPos, uint8_t bPos)
{
if (COLOR_CHANNEL_COUNT == 3)
{
for (int8_t chip = _tlc_count - 1; chip >= 0; chip--)
{
for (int8_t channel = 0; channel < LEDS_PER_CHIP; channel++)
{
_rgb_order[chip][channel][0] = rPos;
_rgb_order[chip][channel][1] = grPos;
_rgb_order[chip][channel][2] = bPos;
}
}
} else
Serial.println(F("ERROR (TLC5947::setRgbPinOrder): Color channel count is not 3"));
}
void TLC5947::setPinOrderSingle(uint16_t led_number, uint8_t color_channel_index, uint8_t position)
{
uint8_t chip = (uint16_t)floor(led_number / LEDS_PER_CHIP);
uint8_t channel = (uint8_t)(led_number - LEDS_PER_CHIP * chip); // Turn that LED on
_rgb_order[chip][channel][color_channel_index] = position;
}
void TLC5947::setRgbPinOrderSingle(uint16_t led_number, uint8_t rPos, uint8_t grPos, uint8_t bPos)
{
uint8_t chip = (uint16_t)floor(led_number / LEDS_PER_CHIP);
uint8_t channel = (uint8_t)round(led_number - LEDS_PER_CHIP * chip); // Turn that LED on
_rgb_order[chip][channel][0] = rPos;
_rgb_order[chip][channel][1] = grPos;
_rgb_order[chip][channel][2] = bPos;
}
void TLC5947::setAllLed(uint16_t gsvalue)
{
for (int8_t chip = _tlc_count - 1; chip >= 0; chip--)
{
for (int8_t a = 0; a < LEDS_PER_CHIP; a++)
{
for (int8_t b = 0; b < COLOR_CHANNEL_COUNT; b++)
_grayscale_data[chip][a][b] = gsvalue;
}
}
}
void TLC5947::setAllLedRgb(uint16_t red, uint16_t green, uint16_t blue)
{
if (COLOR_CHANNEL_COUNT == 3)
{
for (int8_t chip = _tlc_count - 1; chip >= 0; chip--)
{
for (int8_t channel = 0; channel < LEDS_PER_CHIP; channel++)
{
_grayscale_data[chip][channel][2] = blue;
_grayscale_data[chip][channel][1] = green;
_grayscale_data[chip][channel][0] = red;
}
}
} else
Serial.println(F("ERROR (TLC5947::setAllLedRgb): Color channel count is not 3"));
}
double TLC5947::getTotalCurrent()
{
// Get number of counts for current pattern
uint32_t power_output_counts = 0;
for (int chip = _tlc_count - 1; chip >= 0; chip--)
for (int8_t led_channel_index = (int8_t)LEDS_PER_CHIP - 1; led_channel_index >= 0; led_channel_index--)
for (int8_t color_channel_index = (int8_t)COLOR_CHANNEL_COUNT - 1; color_channel_index >= 0; color_channel_index--)
power_output_counts += _grayscale_data[chip][led_channel_index][color_channel_index];
double power_output_amps = ((double)power_output_counts / (double)UINT16_MAX) * maxCurrentValue;
return power_output_amps;
}
int TLC5947::updateLeds(double* output_current) {
double power_output_amps = getTotalCurrent();
if (output_current != nullptr)
{
*output_current = power_output_amps;
}
if (enforce_max_current && power_output_amps > max_current_amps)
return 1;
// 0. comparity check is OK since we know all currents are positive
if (power_output_amps == 0.) {
digitalWrite(_blank, HIGH);
}
if (_use_2D){
updateLeds_2D();
} else {
updateLeds_1D();
}
return 0;
}
void TLC5947::updateLeds_1D(uint16_t *const_value){
// ASSUMING that _grayscale_data is declared as [][LEDS_PER_CHIP][COLOR_CHANNEL_COUNT]
SPI.beginTransaction(mSettings);
for (int latch_index = _num_latches-1; latch_index>=0; latch_index--)
{
for (int chip = _num_latches * _num_tlc_one_row - (_num_latches - latch_index);
chip >= 0;
chip-=_num_latches)
{
updateChip_Leds(chip, const_value);
}
} //end of latch loop
for (int latch_index=0; latch_index < _num_latches; latch_index++){
latch(latch_index);
}
SPI.endTransaction();
digitalWrite(_blank, LOW);
}
void TLC5947::updateLeds_2D(uint16_t *const_value){
// ASSUMING that _grayscale_data is declared as [][LEDS_PER_CHIP][COLOR_CHANNEL_COUNT]
for (int latch_index = _num_latches-1; latch_index>=0; latch_index--)
{
SPI.beginTransaction(mSettings);
for (int chip = _num_latches * _num_tlc_one_row - (_num_latches - latch_index);
chip >= 0;
chip-=_num_latches)
{
updateChip_Leds(chip, const_value);
}
latch(latch_index);
SPI.endTransaction();
} //end of latch loop
digitalWrite(_blank, LOW);
}
void TLC5947::updateChip_Leds(uint8_t chip, uint16_t * const_value){
uint16_t pwm[2];
uint8_t buffer[3];
for (int8_t led_channel_index = (int8_t)(LEDS_PER_CHIP * COLOR_CHANNEL_COUNT - 2);
led_channel_index >= 0;
led_channel_index-=2)
{
// color_channel_ordered = _rgb_order[chip][led_channel_index][(uint8_t) color_channel_index];
// color_channel_ordered = _rgb_order[chip][led_channel_index][(uint8_t) color_channel_index];
if (const_value == nullptr) {
pwm[0] = getLEDValuePerChip(chip, led_channel_index);
pwm[1] = getLEDValuePerChip(chip, led_channel_index + 1);
}
else {
pwm[0] = *const_value;
pwm[1] = *const_value;
}
// pwm[0] is a number between
// 0x0000 and 0xFFFF
// in the illuminate library
// Consider the number 0xABCD
// We don't have enough precision to change the LED brightness based on the
// 4 LSB bits.
// Therefore, we only consider
// 0xABCX
// the 2 MSB nibbles, are 0xAB
// The 1 LSB nibble is 0xC
// 12 bits per channel, send MSB first
buffer[0] = (pwm[1] & 0xFF00) >> 8;
buffer[1] = ((pwm[0] & 0xF000) >> 12) + ((pwm[1] & 0x00F0) << 0);
buffer[2] = (pwm[0] & 0x0FF0) >> 4;
SPI.transfer(buffer[0]);
#if SPI_DELAY_US != 0
delayMicroseconds(SPI_DELAY_US);
#endif
SPI.transfer(buffer[1]);
#if SPI_DELAY_US != 0
delayMicroseconds(SPI_DELAY_US);
#endif
SPI.transfer(buffer[2]);
#if SPI_DELAY_US != 0
delayMicroseconds(SPI_DELAY_US);
#endif
}
}
void TLC5947::clearLeds()
{
uint16_t value = 0x0000;
uint16_t * value_pointer = &value;
if (_use_2D){
updateLeds_2D(value_pointer);
} else {
updateLeds_1D(value_pointer);
}
}
void TLC5947::setChannel(uint16_t channel_number, uint16_t value)
{
// Change to multi-channel indexing
int16_t chip_number = floor(channel_number / (COLOR_CHANNEL_COUNT * LEDS_PER_CHIP));
int16_t channel_number_new = (int16_t) floor((channel_number - chip_number * LEDS_PER_CHIP * COLOR_CHANNEL_COUNT) / COLOR_CHANNEL_COUNT);
int16_t color_channel_number = (int16_t) (channel_number - chip_number * LEDS_PER_CHIP * COLOR_CHANNEL_COUNT) % COLOR_CHANNEL_COUNT;
// Set grayscale data
_grayscale_data[chip_number][channel_number_new][color_channel_number] = value;
}
void TLC5947::setLed(int led_number, uint16_t red, uint16_t green, uint16_t blue)
{
int chip = led_number / LEDS_PER_CHIP;
int channel = led_number % LEDS_PER_CHIP;
_grayscale_data[chip][channel][0] = red;
_grayscale_data[chip][channel][1] = green;
_grayscale_data[chip][channel][2] = blue;
}
void TLC5947::setLed(int led_number, uint16_t rgb)
{
int chip = led_number / LEDS_PER_CHIP;
int channel = led_number % LEDS_PER_CHIP;
_grayscale_data[chip][channel][0] = rgb;
_grayscale_data[chip][channel][1] = rgb;
_grayscale_data[chip][channel][2] = rgb;
}
void TLC5947::latch(int latch_index)
{
auto lat = _latches[latch_index];
delayMicroseconds(LATCH_DELAY);
digitalWrite(lat, HIGH);
delayMicroseconds(LATCH_DELAY);
digitalWrite(lat, LOW);
delayMicroseconds(LATCH_DELAY);
}
// Get a single channel's current values
uint16_t TLC5947::getChannelValue(uint16_t channel_number, int color_channel_index)
{
if (color_channel_index >= COLOR_CHANNEL_COUNT)
return 0;
uint8_t chip = (uint16_t)floor(channel_number / LEDS_PER_CHIP);
uint8_t channel = (uint8_t)(channel_number - LEDS_PER_CHIP * chip);
return _grayscale_data[chip][channel][color_channel_index];
}
uint16_t TLC5947::getLEDValuePerChip(uint16_t chip, int led_number){
int color_channel_index = led_number % COLOR_CHANNEL_COUNT;
int channel = led_number / COLOR_CHANNEL_COUNT;
return _grayscale_data[chip][channel][color_channel_index];
}