291 lines
7.4 KiB
Plaintext
291 lines
7.4 KiB
Plaintext
|
/* Tinct firmware, version ??? (no version number scheme yet)
|
||
|
Written by Jonathan M. Guberman
|
||
|
jonathan@upwardnotnorthward.com
|
||
|
www.upwardnotnorthward.com
|
||
|
|
||
|
Released under a Creative Commons Attribution-Noncommercial-Sharealike license
|
||
|
Can be used and adapted for noncommercial purposes, as long as proper credit is
|
||
|
given to the original author, and any derivatives works are released under a
|
||
|
similar license.
|
||
|
|
||
|
*/
|
||
|
|
||
|
//Debugging definitions: uncomment the relevant line to turn it on
|
||
|
//#define REDALERT 100 //Draw colour is forced to red if the serial receive buffer has more than the specified number of characters in it
|
||
|
//#define REPORTBUFFER -1 //Sends out the current size of the Serial buffer using command ID 15 if the buffer size is greater than the defined value. Set to -1 to always report.
|
||
|
|
||
|
// Serial data transfer rate
|
||
|
#define BAUD 115200
|
||
|
|
||
|
/* Size of the serial buffer before the Tinct is forced to parse it continually.
|
||
|
The buffer size is 128 bytes, and if it gets there the Tinct can (and will) crash.
|
||
|
The largest command size is 9 bytes, so 119 is an absolute maximum value.
|
||
|
Set it lower than this to be safe.
|
||
|
|
||
|
If the Tinct hits this limit, it will start to flicker, and might miss commands,
|
||
|
but it won't crash. Probably.
|
||
|
*/
|
||
|
#define TOOFULL 100
|
||
|
|
||
|
//TLC5940NT pin definitions
|
||
|
#define VPRG 2
|
||
|
#define SIN 11 // MOSI - Hardware SPI, can't be changed
|
||
|
#define SCLK 13 // SCK - Hardware SPI, can't be changed
|
||
|
#define XLAT 4
|
||
|
#define BLANK 5
|
||
|
#define DCPRG 6
|
||
|
#define GSCLK 7
|
||
|
|
||
|
/* These pins are from the Hardware SPI, but aren't connected to the TLC5940
|
||
|
These definitions are only here for clarity, and aren't used. MISO can not
|
||
|
be used for anything else, while SS can be used for any OUTPUT. It can NOT
|
||
|
be used for an input. Currently, it is used for INCLOCKPIN, the 165's clock
|
||
|
pin setting.
|
||
|
*/
|
||
|
#define MISO 12 //MISO - Hardware SPI, can't be changed (not connected, can't be used for anything other than MISO)
|
||
|
#define SS 10 //SS - Hardware SPI, not used and therefore can be used for something else, but ONLY AS AN OUTPUT!!
|
||
|
|
||
|
// Default draw colour. Each channel can be between 0 and 4095.
|
||
|
int red = 0;
|
||
|
|
||
|
/*This is the mapping of the physical position of the boards to the correct
|
||
|
output of the TLC5940s. The left halves are rotated to correspond to the
|
||
|
orientation of the boards*/
|
||
|
const byte remap[7][5] = {
|
||
|
{6,17,24,35,42},
|
||
|
{5,16,23,34,41},
|
||
|
{4,11,22,33,40},
|
||
|
{3,10,21,32,39},
|
||
|
{2,9,20,27,38},
|
||
|
{1,8,19,26,37},
|
||
|
{0,7,18,25,36},
|
||
|
};
|
||
|
|
||
|
boolean A[7][5] = {
|
||
|
{0,1,1,1,0},
|
||
|
{1,0,0,0,1},
|
||
|
{1,1,1,1,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
};
|
||
|
|
||
|
boolean B[7][5] = {
|
||
|
{1,1,1,1,0},
|
||
|
{1,0,0,0,1},
|
||
|
{1,1,1,1,0},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,1,1,1,0},
|
||
|
};
|
||
|
|
||
|
boolean C[7][5] = {
|
||
|
{0,1,1,1,0},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,0},
|
||
|
{1,0,0,0,0},
|
||
|
{1,0,0,0,0},
|
||
|
{1,0,0,0,1},
|
||
|
{0,1,1,1,0},
|
||
|
};
|
||
|
|
||
|
boolean D[7][5] = {
|
||
|
{1,1,1,1,0},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,1,1,1,0},
|
||
|
};
|
||
|
|
||
|
boolean E[7][5] = {
|
||
|
{0,1,1,1,1},
|
||
|
{1,0,0,0,0},
|
||
|
{1,1,1,1,1},
|
||
|
{1,0,0,0,0},
|
||
|
{1,0,0,0,0},
|
||
|
{1,0,0,0,0},
|
||
|
{0,1,1,1,1},
|
||
|
};
|
||
|
|
||
|
boolean F[7][5] = {
|
||
|
{0,1,1,1,0},
|
||
|
{1,0,0,0,1},
|
||
|
{1,1,1,1,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
};
|
||
|
|
||
|
boolean G[7][5] = {
|
||
|
{0,1,1,1,0},
|
||
|
{1,0,0,0,1},
|
||
|
{1,1,1,1,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
};
|
||
|
|
||
|
boolean H[7][5] = {
|
||
|
{0,1,1,1,0},
|
||
|
{1,0,0,0,1},
|
||
|
{1,1,1,1,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
{1,0,0,0,1},
|
||
|
};
|
||
|
|
||
|
|
||
|
// Holds the current colour level for each of the buttons
|
||
|
int levelR[64] = {
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||
|
|
||
|
// Variables for interpreting the serial commands
|
||
|
byte tempR;
|
||
|
byte tempC;
|
||
|
byte lastread;
|
||
|
byte command = 0;
|
||
|
byte ready = true;
|
||
|
|
||
|
// For interrupt timing; needed only to do intermediate clock speeds
|
||
|
/* Divide interrupt frequency by a factor of FREQ. It is preferable to keep
|
||
|
FREQ as small as possible, and control the frequency of the interrupts
|
||
|
using the hardware clock. Setting it to 1 disables this entirely, which,
|
||
|
if it works, is ideal; this should be the same as commenting out the
|
||
|
"#define FREQ" statement entirely.
|
||
|
*/
|
||
|
#define FREQ 1 // How many interrupts occur before the serial commands are read
|
||
|
#if FREQ > 1
|
||
|
byte int_counter = 0;
|
||
|
#endif
|
||
|
|
||
|
// Transfer a character out over hardware SPI
|
||
|
char spi_transfer(volatile byte data)
|
||
|
{
|
||
|
SPDR = data; // Start the transmission
|
||
|
while (!(SPSR & (1<<SPIF))) // Wait the end of the transmission
|
||
|
{
|
||
|
};
|
||
|
return SPDR; // return the received byte
|
||
|
}
|
||
|
|
||
|
// Run this animation once at startup. Currently unfinished.
|
||
|
void startup(){
|
||
|
for(byte x = 0; x < 7; ++x){
|
||
|
for(byte y = 0; y <5; ++y){
|
||
|
//levelR[remap[x][y]] = 4095;
|
||
|
levelR[remap[x][y]] = 500;
|
||
|
for(int i = 0; i <50; ++i) makemagic();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void setup() {
|
||
|
//Setup data directions, and set everything to the correct initial levels,
|
||
|
// For TLC5940
|
||
|
pinMode(VPRG, OUTPUT);
|
||
|
pinMode(SIN, OUTPUT);
|
||
|
pinMode(SCLK, OUTPUT);
|
||
|
pinMode(XLAT, OUTPUT);
|
||
|
pinMode(BLANK, OUTPUT);
|
||
|
pinMode(DCPRG, OUTPUT);
|
||
|
pinMode(GSCLK, OUTPUT);
|
||
|
pinMode(MISO, INPUT);
|
||
|
pinMode(SS,OUTPUT);
|
||
|
digitalWrite(SS,HIGH); //disable device
|
||
|
digitalWrite(SIN, LOW);
|
||
|
digitalWrite(SCLK, LOW);
|
||
|
digitalWrite(XLAT, LOW);
|
||
|
digitalWrite(VPRG, LOW);
|
||
|
digitalWrite(BLANK, HIGH);
|
||
|
digitalWrite(GSCLK, HIGH);
|
||
|
digitalWrite(DCPRG, LOW); // USE EEPROM DC register if LOW
|
||
|
|
||
|
//Setup the Hardware SPI registers
|
||
|
// SPCR = 01010000
|
||
|
//interrupt disabled,spi enabled,msb 1st,master,clk low when idle,
|
||
|
//sample on leading edge of clk,system clock/4 (fastest)
|
||
|
byte clr;
|
||
|
SPCR = (1<<SPE)|(1<<MSTR);
|
||
|
clr=SPSR;
|
||
|
clr=SPDR;
|
||
|
delay(10);
|
||
|
|
||
|
|
||
|
// Start the serial port
|
||
|
Serial.begin(BAUD);
|
||
|
delay(10);
|
||
|
// Run the startup animation
|
||
|
startup();
|
||
|
}
|
||
|
|
||
|
void loop () {
|
||
|
letra(A);
|
||
|
for(int i = 0; i <500; ++i) makemagic();
|
||
|
letra(B);
|
||
|
for(int i = 0; i <500; ++i) makemagic();
|
||
|
letra(C);
|
||
|
for(int i = 0; i <500; ++i) makemagic();
|
||
|
letra(D);
|
||
|
for(int i = 0; i <500; ++i) makemagic();
|
||
|
letra(E);
|
||
|
for(int i = 0; i <500; ++i) makemagic();
|
||
|
}
|
||
|
|
||
|
void makemagic(){
|
||
|
/* Clear the transistors, set the TLC5940 to the correct channel value with setGreys,
|
||
|
turn the transistor on to give voltage to the LEDs, and then pulse the TLC5940's clock.
|
||
|
*/
|
||
|
setGreysR();
|
||
|
feedPorts();
|
||
|
}
|
||
|
|
||
|
void setGreysR() {
|
||
|
digitalWrite(BLANK, HIGH);
|
||
|
digitalWrite(XLAT,LOW);
|
||
|
for(int i = 21; i>=0; i--){
|
||
|
spi_transfer( (levelR[2*i+1] & 0x0FF0) >> 4 );
|
||
|
spi_transfer( ((levelR[2*i+1] & 0xF) << 4) | ((levelR[2*i] & 0x0F00) >> 8) );
|
||
|
spi_transfer( levelR[2*i] & 0xFF);
|
||
|
}
|
||
|
digitalWrite(XLAT,HIGH);
|
||
|
digitalWrite(XLAT,LOW);
|
||
|
digitalWrite(BLANK, LOW);
|
||
|
}
|
||
|
|
||
|
void feedPorts() {
|
||
|
// Clock for TLC5940's PWM
|
||
|
digitalWrite(BLANK, HIGH);
|
||
|
digitalWrite(BLANK, LOW); //=all outputs ON, start PWM cycle
|
||
|
for (int i=0; i<4096; i++) {
|
||
|
pulseGSCLK();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void pulseGSCLK() {
|
||
|
//ultra fast pulse trick, using digitalWrite caused flickering
|
||
|
PORTD |= 0x80 ; // bring pin 7 high, but don't touch any of the other pins in PORTB
|
||
|
//16 nanosecs is the min pulse width for the 5940, but no pause seems needed here
|
||
|
PORTD &= 0x7F; // bring pin 7 low without touching the other pins in PORTB
|
||
|
}
|
||
|
|
||
|
void letra(boolean let[7][5] ) {
|
||
|
for(byte x = 0; x < 7; ++x){
|
||
|
for(byte y = 0; y <5; ++y){
|
||
|
//levelR[remap[x][y]] = 4095;
|
||
|
if (let[x][y]) levelR[remap[x][y]] = 500;
|
||
|
else levelR[remap[x][y]] = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|