627 lines
19 KiB
Plaintext
627 lines
19 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;
|
||
|
int green = 4095;
|
||
|
int blue = 0;
|
||
|
|
||
|
// Auxiliary analog output definitions
|
||
|
#define ANALOG0 3 //Output pin definitions
|
||
|
#define ANALOG1 4
|
||
|
boolean adc[2] = { //On or off state
|
||
|
0, 0};
|
||
|
byte analogval[2]; //The last reported value
|
||
|
byte tempADC; //Temporary storage for comparison purposes
|
||
|
|
||
|
/* The transistor bases are done via port manipulation:
|
||
|
RED goes to analog pin 5
|
||
|
GREEN goes to analog pin 0
|
||
|
BLUE goes to analog pin 1
|
||
|
*/
|
||
|
// Pin definitions for the 74HC164 SIPO shift register (drives button rows high)
|
||
|
#define DATAPIN 16 // aka analog pin 3 (what, you didn't know that analog pins 0-5 are also digital pins 14-19? Well, now you do!)
|
||
|
#define CLOCKPIN 3
|
||
|
|
||
|
// Pin definitions for the 74HC165 PISO shift register (reads button column state)
|
||
|
#define INDATAPIN 9
|
||
|
#define INCLOCKPIN 10
|
||
|
#define INLOADPIN 8 // toggling this tell the 165 to read the value into its memory for reading
|
||
|
|
||
|
// Holds the last state of the button press
|
||
|
boolean pressed[8][8] = {
|
||
|
{1,1,1,1,1,1,1,1},
|
||
|
{1,1,1,1,1,1,1,1},
|
||
|
{1,1,1,1,1,1,1,1},
|
||
|
{1,1,1,1,1,1,1,1},
|
||
|
{1,1,1,1,1,1,1,1},
|
||
|
{1,1,1,1,1,1,1,1},
|
||
|
{1,1,1,1,1,1,1,1},
|
||
|
{1,1,1,1,1,1,1,1}
|
||
|
};
|
||
|
|
||
|
/*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[8][8] = {
|
||
|
{16,17,19,20, 12,13,14,15},
|
||
|
{18,21,22,23, 11,10, 9, 8},
|
||
|
{24,25,26,27, 7, 6, 5, 2},
|
||
|
{31,30,29,28, 4, 3, 1, 0},
|
||
|
{32,33,35,36, 48,49,51,52},
|
||
|
{34,37,38,39, 50,53,54,55},
|
||
|
{40,41,42,43, 56,57,58,59},
|
||
|
{47,46,45,44, 63,62,61,60},
|
||
|
};
|
||
|
|
||
|
/*const byte remap[8][8] = {
|
||
|
{48,49,51,52, 12,13,14,15},
|
||
|
{50,53,54,55, 11,10, 9, 8},
|
||
|
{56,57,58,59, 7, 6, 5, 2},
|
||
|
{63,62,61,60, 4, 3, 1, 0},
|
||
|
{32,33,35,36, 28,29,30,31},
|
||
|
{34,37,38,39, 27,26,25,24},
|
||
|
{40,41,42,43, 23,22,21,18},
|
||
|
{47,46,45,44, 20,19,17,16},
|
||
|
};*/
|
||
|
|
||
|
// 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};
|
||
|
int levelG[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};
|
||
|
int levelB[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(levelR[remap[3][3]] = 0; levelR[remap[3][3]] < 4000; levelR[remap[3][4]] = levelR[remap[3][3]] += 64){
|
||
|
makemagic();
|
||
|
}
|
||
|
for(levelB[remap[2][2]] = 0; levelB[remap[2][2]] < 4000; levelB[remap[2][2]] += 64){
|
||
|
levelR[remap[3][3]] -= 64;
|
||
|
makemagic();
|
||
|
}
|
||
|
for(levelG[remap[1][1]] = 0; levelG[remap[1][1]] < 4000; levelG[remap[1][1]] += 64){
|
||
|
levelB[remap[2][2]] -= 64;
|
||
|
makemagic();
|
||
|
}*/
|
||
|
|
||
|
|
||
|
for(byte x = 0; x < 8; ++x){
|
||
|
for(byte y = 0; y <8; ++y){
|
||
|
levelR[remap[x][y]] = 4095;
|
||
|
makemagic();
|
||
|
makemagic();
|
||
|
makemagic();
|
||
|
levelB[remap[x][y]] = 4095;
|
||
|
makemagic();
|
||
|
makemagic();
|
||
|
makemagic();
|
||
|
levelG[remap[x][y]] = 4095;
|
||
|
makemagic();
|
||
|
makemagic();
|
||
|
makemagic();
|
||
|
/*levelR[remap[x][y]] = 0;
|
||
|
makemagic();
|
||
|
makemagic();
|
||
|
makemagic();
|
||
|
levelB[remap[x][y]] = 0;
|
||
|
makemagic();
|
||
|
makemagic();
|
||
|
makemagic();
|
||
|
levelG[remap[x][y]] = 0;
|
||
|
makemagic();
|
||
|
makemagic();
|
||
|
makemagic();*/
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* levelR[remap[0][0]] = 4095;
|
||
|
levelG[remap[1][1]] = 4095;
|
||
|
levelB[remap[2][2]] = 4095;
|
||
|
levelR[remap[3][3]] = 4095;
|
||
|
levelG[remap[3][3]] = 4095;
|
||
|
levelB[remap[3][3]] = 4095;
|
||
|
levelR[remap[4][4]] = 4095;
|
||
|
levelG[remap[4][4]] = 4095;
|
||
|
levelB[remap[5][5]] = 4095;
|
||
|
levelR[remap[5][5]] = 4095;
|
||
|
levelG[remap[6][6]] = 4095;
|
||
|
levelB[remap[6][6]] = 4095;
|
||
|
levelR[remap[7][7]] = 4095;
|
||
|
levelG[remap[7][7]] = 4095;
|
||
|
levelB[remap[7][7]] = 4095;
|
||
|
*/
|
||
|
}
|
||
|
|
||
|
//The timer interrupt routine, which periodically interprets the serial commands
|
||
|
ISR(TIMER2_OVF_vect) {
|
||
|
sei(); //Reenable global interrupts, otherwise serial commands will get dropped
|
||
|
#if FREQ > 1
|
||
|
if(++int_counter == FREQ){ // Only do this once every FREQ-th interrupt
|
||
|
int_counter = 0;
|
||
|
#endif //FREQ
|
||
|
do{ // This do ensures that the data is always parsed at least once per cycle
|
||
|
if(Serial.available()){
|
||
|
#ifdef REDALERT // if REDALERT is defined, draw colour turns red when the buffer is getting dangerously full
|
||
|
if(Serial.available() > REDALERT){
|
||
|
blue = 0;
|
||
|
green = 0;
|
||
|
red = 4095;
|
||
|
}
|
||
|
#endif //REDALERT
|
||
|
#ifdef REPORTBUFFER // if REPORTBUFFER is defined, send out the size of the serial receive buffer using command ID 15
|
||
|
if(Serial.available() > REPORTBUFFER){
|
||
|
Serial.print(15 << 4, BYTE);
|
||
|
Serial.print(Serial.available(), BYTE);
|
||
|
}
|
||
|
#endif //REPORTBUFFER
|
||
|
if(ready){ // If the last command has finished executing, read in the next command and reset the command flag
|
||
|
command = Serial.read();
|
||
|
ready = false;
|
||
|
}
|
||
|
switch (command >> 4) { //Execute the appropriate command, but only if we have received enough bytes to complete it. We might one day add "partial completion" for long command strings.
|
||
|
case 1: // set colour
|
||
|
if( Serial.available() > 2 ) {
|
||
|
red = Serial.read() * 16;
|
||
|
green = Serial.read() * 16;
|
||
|
blue = Serial.read() * 16;
|
||
|
ready=true;
|
||
|
}
|
||
|
break;
|
||
|
case 2: // led_on
|
||
|
if( Serial.available() ) {
|
||
|
lastread = Serial.read();
|
||
|
tempR = lastread >> 4;
|
||
|
tempC = lastread & B1111;
|
||
|
levelR[remap[tempC][tempR]] = red;
|
||
|
levelG[remap[tempC][tempR]] = green;
|
||
|
levelB[remap[tempC][tempR]] = blue;
|
||
|
ready = true;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
case 3: // led_off
|
||
|
if( Serial.available() ) {
|
||
|
lastread = Serial.read();
|
||
|
tempR = lastread >> 4;
|
||
|
tempC = lastread & B1111;
|
||
|
levelR[remap[tempC][tempR]] = 0;
|
||
|
levelG[remap[tempC][tempR]] = 0;
|
||
|
levelB[remap[tempC][tempR]] = 0;
|
||
|
ready = true;
|
||
|
}
|
||
|
break;
|
||
|
case 4: // led_row1
|
||
|
if( Serial.available() ) {
|
||
|
tempR = command & B1111;
|
||
|
lastread = Serial.read();
|
||
|
for(tempC = 0; tempC < 8; ++tempC){
|
||
|
if(lastread & (1 << tempC) ){
|
||
|
levelR[remap[tempR][tempC]] = red;
|
||
|
levelG[remap[tempR][tempC]] = green;
|
||
|
levelB[remap[tempR][tempC]] = blue;
|
||
|
}
|
||
|
else {
|
||
|
levelR[remap[tempR][tempC]] = 0;
|
||
|
levelG[remap[tempR][tempC]] = 0;
|
||
|
levelB[remap[tempR][tempC]] = 0;
|
||
|
}
|
||
|
}
|
||
|
ready = true;
|
||
|
}
|
||
|
break;
|
||
|
case 5: // led_col1
|
||
|
if( Serial.available() ) {
|
||
|
tempC = command & B1111;
|
||
|
lastread = Serial.read();
|
||
|
for(tempR = 0; tempR < 8; ++tempR){
|
||
|
if(lastread & (1 << tempR) ){
|
||
|
levelR[remap[tempR][tempC]] = red;
|
||
|
levelG[remap[tempR][tempC]] = green;
|
||
|
levelB[remap[tempR][tempC]] = blue;
|
||
|
}
|
||
|
else {
|
||
|
levelR[remap[tempR][tempC]] = 0;
|
||
|
levelG[remap[tempR][tempC]] = 0;
|
||
|
levelB[remap[tempR][tempC]] = 0;
|
||
|
}
|
||
|
}
|
||
|
ready = true;
|
||
|
}
|
||
|
break;
|
||
|
case 8: //frame
|
||
|
if( Serial.available() > 7 ) {
|
||
|
for(tempR=0; tempR<8; ++tempR){
|
||
|
lastread = Serial.read();
|
||
|
for(tempC = 0; tempC < 8; ++tempC){
|
||
|
if(lastread & (1 << tempC) ){
|
||
|
levelR[remap[tempR][tempC]] = red;
|
||
|
levelG[remap[tempR][tempC]] = green;
|
||
|
levelB[remap[tempR][tempC]] = blue;
|
||
|
}
|
||
|
else {
|
||
|
levelR[remap[tempR][tempC]] = 0;
|
||
|
levelG[remap[tempR][tempC]] = 0;
|
||
|
levelB[remap[tempR][tempC]] = 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
ready = true;
|
||
|
}
|
||
|
break;
|
||
|
case 9: //clear
|
||
|
if(command & 1){
|
||
|
for(int x = 0; x< 64;++x){
|
||
|
levelR[x] = red;
|
||
|
levelG[x] = green;
|
||
|
levelB[x] = blue;
|
||
|
}
|
||
|
}
|
||
|
else{
|
||
|
for(int x = 0; x<64;++x){
|
||
|
levelR[x] = 0;
|
||
|
levelG[x] = 0;
|
||
|
levelB[x] = 0;
|
||
|
}
|
||
|
}
|
||
|
ready = true;
|
||
|
break;
|
||
|
case 12:
|
||
|
switch(command & 15){
|
||
|
case 0:
|
||
|
adc[0] = true;
|
||
|
analogval[0] = (analogRead(ANALOG0) >> 2);
|
||
|
Serial.print(14 << 4, BYTE);
|
||
|
Serial.print(analogval[0], BYTE);
|
||
|
break;
|
||
|
case 1:
|
||
|
adc[1] = true;
|
||
|
analogval[1] = (analogRead(ANALOG1) >> 2);
|
||
|
Serial.print(14 << 4 | 1, BYTE);
|
||
|
Serial.print(analogval[1], BYTE);
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
ready = true;
|
||
|
break;
|
||
|
case 13:
|
||
|
adc[command & 15] = false;
|
||
|
ready = true;
|
||
|
break;
|
||
|
default:
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
// If the serial buffer is getting too close to full, keep executing the parsing until it falls below a given level
|
||
|
// This might cause flicker, or even dropped messages, but it should prevent a crash.
|
||
|
while (Serial.available() > TOOFULL);
|
||
|
#if FREQ > 1
|
||
|
}
|
||
|
#endif //FREQ
|
||
|
}
|
||
|
|
||
|
void setup() {
|
||
|
//Setup data directions, and set everything to the correct initial levels,
|
||
|
// For TLC5940
|
||
|
PORTC |= B00100011;
|
||
|
DDRC |= B00100011;
|
||
|
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);
|
||
|
|
||
|
// 165 Setup
|
||
|
pinMode(INDATAPIN, INPUT);
|
||
|
pinMode(INCLOCKPIN, OUTPUT);
|
||
|
pinMode(INLOADPIN, OUTPUT);
|
||
|
|
||
|
// 164 Setup
|
||
|
pinMode(DATAPIN, OUTPUT);
|
||
|
pinMode(CLOCKPIN, OUTPUT);
|
||
|
|
||
|
// Start the serial port
|
||
|
Serial.begin(BAUD);
|
||
|
|
||
|
/* Setup the timer interrupt
|
||
|
|
||
|
Settings for TCCR2B: (remember, interrupts occur every 255 cycles)
|
||
|
000 = Clock stopped
|
||
|
001 = No prescaling (16MHz)
|
||
|
010 = 1/8 (2MHz)
|
||
|
011 = 1/32 (500kHz)
|
||
|
100 = 1/64 (250kHz)
|
||
|
101 = 1/128 (125kHz)
|
||
|
110 = 1/256 (62.5khZ)
|
||
|
111 = 1/1024 (15.625kHz)
|
||
|
|
||
|
010 (2MHz) with FREQ set to 10 or less seem to work reasonably well
|
||
|
Trying to use 011 (500kHz), to eliminate the if statement needed for FREQ
|
||
|
Might even work as slow as 100 (250kHz).
|
||
|
*/
|
||
|
TCCR2A = 0;
|
||
|
TCCR2B = 0<<CS22 | 1<<CS21 | 1<<CS20;
|
||
|
|
||
|
//Timer2 Overflow Interrupt Enable
|
||
|
TIMSK2 = 1<<TOIE2;
|
||
|
|
||
|
delay(10);
|
||
|
// Run the startup animation
|
||
|
startup();
|
||
|
}
|
||
|
|
||
|
void loop () {
|
||
|
// Turn on the lights
|
||
|
makemagic();
|
||
|
|
||
|
// Check the button states
|
||
|
checkButtons();
|
||
|
|
||
|
// Check and report the ADC states, if necessary
|
||
|
//checkADC();
|
||
|
}
|
||
|
|
||
|
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.
|
||
|
|
||
|
*/
|
||
|
PORTC |= B00100011;
|
||
|
setGreysR();
|
||
|
PORTC &= B11011111;
|
||
|
feedPorts();
|
||
|
|
||
|
PORTC |= B00100011;
|
||
|
setGreysG();
|
||
|
PORTC &= B11111101;
|
||
|
feedPorts();
|
||
|
|
||
|
PORTC |= B00100011;
|
||
|
setGreysB();
|
||
|
PORTC &= B11111110;
|
||
|
feedPorts();
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
void setGreysR() {
|
||
|
digitalWrite(BLANK, HIGH);
|
||
|
digitalWrite(XLAT,LOW);
|
||
|
for(int i = 31; 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 setGreysG() {
|
||
|
digitalWrite(BLANK, HIGH);
|
||
|
digitalWrite(XLAT,LOW);
|
||
|
for(int i = 31; i>=0; i--){
|
||
|
spi_transfer( (levelG[2*i+1] & 0x0FF0) >> 4 );
|
||
|
spi_transfer( ((levelG[2*i+1] & 0xF) << 4) | ((levelG[2*i] & 0x0F00) >> 8) );
|
||
|
spi_transfer( levelG[2*i] & 0xFF);
|
||
|
}
|
||
|
digitalWrite(XLAT,HIGH);
|
||
|
digitalWrite(XLAT,LOW);
|
||
|
digitalWrite(BLANK, LOW);
|
||
|
}
|
||
|
|
||
|
void setGreysB() {
|
||
|
digitalWrite(BLANK, HIGH);
|
||
|
digitalWrite(XLAT,LOW);
|
||
|
for(int i = 31; i>=0; i--){
|
||
|
spi_transfer( (levelB[2*i+1] & 0x0FF0) >> 4 );
|
||
|
spi_transfer( ((levelB[2*i+1] & 0xF) << 4) | ((levelB[2*i] & 0x0F00) >> 8) );
|
||
|
spi_transfer( levelB[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 checkButtons(){
|
||
|
|
||
|
digitalWrite(CLOCKPIN,LOW);
|
||
|
digitalWrite(DATAPIN, HIGH);
|
||
|
for(byte c = 0; c < 8; c++){
|
||
|
digitalWrite(CLOCKPIN, HIGH);
|
||
|
|
||
|
digitalWrite(INLOADPIN, LOW); // read into register
|
||
|
digitalWrite(INLOADPIN, HIGH); // done reading into register, ready for us to read
|
||
|
|
||
|
for(int r=7; r >= 0; r--){ // read each of the 165's 8 inputs (or its snapshot of it rather)
|
||
|
|
||
|
// tell the 165 to send the first inputs pin state
|
||
|
digitalWrite(INCLOCKPIN, LOW);
|
||
|
// read the current output
|
||
|
//int tempvalue = digitalRead(INDATAPIN);
|
||
|
//Serial.print(tempvalue,DEC);
|
||
|
|
||
|
if(pressed[r][c] != digitalRead(INDATAPIN)){ // read the state
|
||
|
pressed[r][c] = digitalRead(INDATAPIN);
|
||
|
if(!pressed[r][c]){
|
||
|
on_release(r, c);
|
||
|
}
|
||
|
else {
|
||
|
on_press(r, c);
|
||
|
}
|
||
|
}
|
||
|
// tell the 165 we are done reading the state, the next inclockpin=0 will output the next input value
|
||
|
digitalWrite(INCLOCKPIN, 1);
|
||
|
|
||
|
}
|
||
|
|
||
|
//Serial.println();
|
||
|
|
||
|
digitalWrite(CLOCKPIN, LOW);
|
||
|
digitalWrite(DATAPIN, LOW);
|
||
|
|
||
|
|
||
|
}
|
||
|
|
||
|
//Serial.println("new row");
|
||
|
//delay(2000);
|
||
|
|
||
|
}
|
||
|
|
||
|
void on_press(byte r, byte c){
|
||
|
Serial.print( 1, BYTE);
|
||
|
//Serial.print( 1, BYTE);
|
||
|
Serial.print( (r << 4) | c, BYTE);
|
||
|
}
|
||
|
|
||
|
void on_release(byte r, byte c){
|
||
|
Serial.print( 0, BYTE);
|
||
|
//Serial.print( 0, BYTE);
|
||
|
Serial.print( (r << 4) | c, BYTE);
|
||
|
}
|
||
|
|
||
|
void checkADC(){
|
||
|
// For all of the ADC's which are activated, check if the analog value has changed,
|
||
|
// and send a message if it has.
|
||
|
if(adc[0]){
|
||
|
tempADC = (analogRead(ANALOG0) >> 2);
|
||
|
if(abs((int)analogval[0] - (int)tempADC) > 3 ){
|
||
|
analogval[0] = tempADC;
|
||
|
Serial.print(14 << 4, BYTE);
|
||
|
Serial.print(analogval[0], BYTE);
|
||
|
}
|
||
|
}
|
||
|
if(adc[1]){
|
||
|
if(analogval[1] != (analogRead(ANALOG1) >> 2)){
|
||
|
analogval[1] = (analogRead(ANALOG1) >> 2);
|
||
|
Serial.print(14 << 4 | 1, BYTE);
|
||
|
Serial.print(analogval[1], BYTE);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|