Hi all,
I am currently working on an installation wherein I'll be programming 96 solenoids to play different percussive objects. The solenoids are split up into 12 8-solenoid groups. Each solenoid will also have a corresponding RGB LED, so 96 in total- which amounts to 288 individual LEDs.
The primary issue I seem to be having is with programming the LEDs.
Each group's 8 RGB LEDs will be controlled using a TLC5947 grayscale 24-channel 12-bit LED driver. I am currently trying to control brightness values for each LED via serial data sent from Max MSP. Because the data is 12-bit and SPI writes in 8-bits at a time, when new 12-bit data is received for each LED, it is first stored in an array which is then parsed (two LED values at a time) into 1.5 bytes. Each pair of LED values will have either their 8 most significant bits or 8 least significant bits stored into bytes, and will then share a third byte for their remaining 4 bits each. This parsed data is recorded into three different arrays which is then combined into a single 36-byte array in the order in which it will be shifted out to the TLC5947 via SPI.
This method works somewhat, but it tends to freeze very easily when I send too much serial data too quickly from Max. When it freezes, the Arduino does take any other commands and only works upon resetting it. Upon adding delays to many of the LED messages in Max, the freezing seems to be negated to a certain degree- but then there is some lag in the LED's response time.
Are there ways in which I could better optimize my code to stop the freezing? I've read the tagged introduction to serial communications post as well as a number of videos and tutorials on serial communication but I still don't quite grasp how to make it less apt to freezing... I've also tried using the TLC5947 library instead of writing the code myself, but I run into the same freezing issue.
The full code is below- any input or suggestions is much appreciated! Thanks y'all.
Amina
#include <SPI.h>
//Node & Checksum Parameter
int NODE = 1; //Header byte for 1 chandelier pillar.
byte TOTAL_BYTES = 3; //Serial data checksum # of bytes.
//LED Parameters
int LED_NUM[]= {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}; //LED header bytes
int L_LATCH = 10;
int LED_VAL[24];
byte SPI_BYTEMSB[12]; //Used for parsing 12-bit LED[VAL] arrays into 8-bit bytes (1.5 bytes each)
byte SPI_BYTEMID[12]; //Bytes are arranged MSBFIRST into SPI_WRITE[] to be transmitted
byte SPI_BYTELSB[12];
byte SPI_WRITE[36];
byte i = 0; //For waking up TLC5947 shift register (takes 4 clock pulses to wake)
//Solenoid Parameters
int S_LATCH = 6;
int SOL_NUM[] = {0, 1, 2, 3, 4, 5, 6, 7}; //Solenoid header bytes
byte SHIFT_VAL = 0; //Used to record last on/off data for each of the 8 solenoids to compare with incoming data.
byte MULT[] = {1, 2, 4, 8, 16, 32, 64, 128}; //Used to write on/off data to corresponding bit of the byte shifted out to the solenoid shift register.
int RESET = 8;
int OUTPUT_PINS[] = {6, 8, 10};
void LED_CHECK(){
for(int y = 0; y < 24; y ++){
if(Serial.peek() == LED_NUM[y]){
Serial.read();
if(LED_VAL[y] != Serial.peek()){ //If the save LED_VAL[x] is different than the new data coming in
LED_VAL[y] = Serial.read(); //Writes the new data to LED_VAL[y]
LED_DATAPARSE(); //Parses the 12-bit values into 1.5 bytes of SPI_WRITE[]
LED_DATAWRITE(); //And transfers new data out to the TLC5947
break;
}
else{Serial.read(); break;}; //Otherwise it removes the byte and ends the for().
}
};
}
void LED_DATAPARSE(){
for(int x = 0; x < 24; x += 2){ //Processes two LED_VAL[] 12-bit bytes at a time.
SPI_BYTELSB[(x - 2) / 2] = ((LED_VAL[x + 1] & 4080) >> 4); //LED_VAL[x + 1]'s first 8 MSBits are parsed into SPI_BYTELSB
SPI_BYTEMID[(x - 2) / 2] = ((LED_VAL[x] >> 8) + ((LED_VAL[x + 1] & 15) << 4)); //LED_VAL[x + 1]'s last 4 LSBits are added to LED_VAL[x]'s first 4 MSBits and parsed to SPI_BYTEMID
SPI_BYTEMSB[(x - 2) / 2] = LED_VAL[x] & 255; //LED_VAL[x]'s last 8 LSBits are parsed into SPI_BYTE[MSB].
};
for(int h = 0; h < 36; h += 3){ //SPI_BYTEMSB, SPI_BYTEMID & SPI_BYTELSB are stored into SPI_WRITE[36]
SPI_WRITE[h] = SPI_BYTEMSB[(h / 3) - 1]; //in the order of how they are clocked to the TLC5947.
SPI_WRITE[h + 1] = SPI_BYTEMID[(h / 3) - 1];
SPI_WRITE[h + 2] = SPI_BYTELSB[(h / 3) - 1];
};
};
void LED_DATAWRITE(){
SPI.transfer(i); //Initializes the TLC5947.
for(int g = 35; g > -1; g --){ //Clocks SPI_WRITE[36] bytes to TLC5947.
SPI.transfer(SPI_WRITE[g]);};
digitalWrite(L_LATCH, HIGH);
digitalWrite(L_LATCH, LOW);
};
void SOL_CHECK(){
for(int v = 0; v < 8; v ++){
if(Serial.peek() == SOL_NUM[v]){
Serial.read(); //Removes header byte.
if(SHIFT_VAL ^ ((Serial.peek() * MULT[v]))){ //If the new serial data is different than the old data,
if(Serial.peek() == 1){ //and if the new data is a 1,
Serial.read(); //removes data byte
SHIFT_VAL = (SHIFT_VAL + MULT[v]); //turns the corresponding bit in SHIFT_VAL from 0 to 1
SOL_DATAWRITE();
break;}
else if(Serial.peek() == 0){ //or if the new data is a 0,
Serial.read(); //removes data byte
SHIFT_VAL = (SHIFT_VAL - MULT[v]); //turns the corresponding bit in SHIFT_VAL from 1 to 0
SOL_DATAWRITE();
break;};
}
else{Serial.read(); break;}; //Or ends the for statement if data is redundant.
};
};
}
void SOL_DATAWRITE(){
SPI.transfer(i); //Initializes the TLC5947.
SPI.transfer(SHIFT_VAL); //Writes byte to solenoid shift register.
digitalWrite(S_LATCH, HIGH);
digitalWrite(S_LATCH, LOW);
};
void RESET_CHECK(){
if(Serial.available() == 1){
if(Serial.peek() == 'R'){
Serial.read(); digitalWrite(RESET, HIGH);
};
};
}
void setup() {
Serial.begin(115200);
SPI.begin();
SPISettings (15000000, MSBFIRST, SPI_MODE0);
for(int x = 0; x < 3; x ++){
pinMode(OUTPUT_PINS[x], OUTPUT);};
digitalWrite(RESET, LOW);
digitalWrite(S_LATCH, LOW);
digitalWrite(L_LATCH, LOW);
LED_DATAWRITE();
SOL_DATAWRITE();
}
void loop() {
RESET_CHECK();
if(Serial.available() == TOTAL_BYTES){
if(Serial.peek() == NODE){
Serial.read(); //Removes node header byte.
SOL_CHECK();
LED_CHECK();
}else{Serial.read(); Serial.read(); Serial.read();};
};
}