////////////////////////////////////////////////////////////////////////////////
///////////////      CONTROLADOR DE LEDS RGB CON TLC5940    ////////////////////
///////////////      v.0.1                                 /////////////////////
////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Incluye:                                                                                              //
// - Control total de intensidad por canal, entre 0 y 4095                                               //
// - 15 Colores predefinidos.                                                                            //
// - 13 patrones de 18 estados.                                                                          //
// - Selector de patrones.                                                                               //
// - Selector de velocidad de patrones o BPM, entre 0,002ms y 15 segundos de intervalo por transición.   //
// - Selección entre transición "brusca" y "progresiva"                                                  //
// - En la transición progresiva, ajuste de la proporción de tiempo entre transición y color estable.    //
///////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <16F876.h>
#use delay(clock=12000000)
#fuses HS,NOWDT,NOPUT,NOBROWNOUT,NOLVP,NOPROTECT,NOCPD,NOWRT
#use rs232(BAUD=19200 ,XMIT=PIN_C6 ,RCV=PIN_C7 ,BITS=8)

#use fast_io(A)
#use fast_io(B)

/***************************
*  ETIQUETAS Y VARIABLES   *
****************************/

#byte   PORTA = 0x05      // Puerto A
#bit  RA0 = 0x05.0
#bit  RA1 = 0x05.1
#bit  RA2 = 0x05.2
#bit  RA3 = 0x05.3
#bit  RA4 = 0x05.4
#bit  RA5 = 0x05.5

#byte   PORTB = 0x06      // Puerto B
#bit  RB0 = 0x06.0
#bit  RB1 = 0x06.1
#bit  RB2 = 0x06.2
#bit  RB3 = 0x06.3
#bit  RB4 = 0x06.4
#bit  RB5 = 0x06.5
#bit  RB6 = 0x06.6
#bit  RB7 = 0x06.7

#byte   PORTC = 0x07      // Puerto C
#bit  RC0 = 0x07.0
#bit  RC1 = 0x07.1
#bit  RC2 = 0x07.2
#bit  RC3 = 0x07.3
#bit  RC4 = 0x07.4
#bit  RC5 = 0x07.5
#bit  RC6 = 0x07.6
#bit  RC7 = 0x07.7

#define SIN    PIN_B1   // Patilla 26 del TLC5940
#define SCLK   PIN_B2   // Patilla 25 del TLC5940
#define XLAT   PIN_B3   // Patilla 24 del TLC5940
#define VPRG   PIN_B4   // Patilla 27 del TLC5940
#define DCPRG  PIN_B5   // Patilla 19 del TLC5940
#define BLANK  PIN_B6   // Patilla 23 del TLC5940
#define GSCLK  PIN_C0   // Patilla 18 del TLC5940

#define CHANNELS  16

long gss[CHANNELS];  // Array que almacena los valores de color (0 a 4095) de cada uno de los 16 canales.
int dcs[CHANNELS];   // Array que almacena los valores de ajuste fino (0 a 63) de cada uno de los 16 canales.
long colresul[3];    // Array auxiliar para almacenar las 3 componentes de un color de transición. Usado en "cambia complejo"

int sec = 0;         // Indicadora del modo de ejecución. Fijo, automático o automático progresivo.

int recibido = 0;    // Indicador de dato recibido en puerto serie
char dato;           // Variable que almacena el dato recibido por el puerto serie

int needXLAT = 0;    // Indicador de envío pendiente de XLAT para actualización de registros en el TLC5940

long bpm = 52;       // Variable que el usuario controla para cambiar indirectamente "bpm2"
long bpm2 = 0;       // Variable que almacena el intervalo de tiempo entre colores o transiciones y colores en los modos automaticos.
long tempo = 0;      // Variable contadora para comparar con bmp2.
long factor = 10;    // Factor de multiplicación para determinar el tiempo de color fijo en el modo "automático progresivo". 

int pattern = 0;     // Indice del patron en la tabla de patrones
int guia = 0;        // Indice del color en el array de colores (o patron)
int guiax = 0;       // La transición entre colores se efectua en 9 pasos intermedios, esta variable indica esos pasos
int indcolor;        // Auxiliar 
int detiene = 1;     // Auxiliar que indica que estoy en color fijo y no en una transición. Usada en modo "automatico progresivo"

int red;
int green;
int blue;

int indinicial;      // Indice del color inicial en el array de colores, en transiciones de color. Usada en modo "automatico progresivo"
int indobjetivo;     // Igual, pero índice del color objetivo
int primera = 1;     // Al cambiar de un modo a otro, indica que es la primera ejecución del array.


/*
La siguiente tabla de colores ha sido definida para la comodidad de uso de los leds RGB.
En teoría con 4095 colores por cada canal RGB se podrían generar millones de colores, pero en la
práctica, o al menos en mi caso, no requiero tal gama de colores, además de que dudo que con mis
leds (RGB de 5mm y cátodo comun) sea técnicamente posible.

Para determinar los colores que me resultaban "bonitos" a la vista, o al menos lo suficientemente
distintos de otros, dejé uno fijo y fuí cambiando los otros dos. 

El nombre asignado a cada color es puramente arbitrario, sometido a mi limitada percepción visual y
al modelo exacto de RGB que yo uso.


ÍNDICE   NOMBRE      ROJO     VERDE    AZUL
-----------------------------------------
0        Rojo puro   4095     0        0
1        Verde puro  0        4095     0
2        Azul puro   0        0        4095
3        Naranja     4095     285      0
4        Amarillo    4095     760      0
5        Verde pist  4095     3430     0
6        Turquesa    0        3050     2090
7        Otro        0        1045     4095
8        Celeste     475      190      4095
9        Violeta     2100     0        4095
10       Rosa fuert  4095     0        4095
11       Fuxia       4095     0        770
12       Anaranjado  4095     855      770
13       Morado      4095     0        2100
14       Blanco      3525     2670     2860


El programa permite el control de las 16 salidas del TLC5940, no obstante el generado de colores
fijos y secuencias de colores se produce en las siguientes patillas:

ROJO --> Out 12
VERDE--> Out 13
AZUL --> Out 14
*/


// TABLA DE COLORES
long const colores[15][3] = {{4095,0,0},
                             {0,4095,0},
                             {0,0,4095},
                             {4095,285,0},
                             {4095,760,0},
                             {4095,3430,0},
                             {0,3050,2090},
                             {0,1045,4095},
                             {475,190,4095},
                             {2100,0,4095},
                             {4095,0,4095},
                             {4095,0,770},
                             {4095,855,770},
                             {4095,0,2100},
                             {3525,2670,2860}};

// TABLA DE PATRONES
int const patron[14][18] = {{0,8,13,1,3,4,1,2,11,9,0,6,0,2,7,5,10,14},
                             {1,2,3,4,5,6,7,8,9,10,11,0,13,14,13,12,11,10},
                             {8,7,6,5,4,5,6,7,0,1,9,8,10,0,10,2,10,1},
                             {0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1,0,1},
                             {0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2},
                             {1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2},
                             {0,1,2,0,1,2,0,1,2,0,1,2,0,1,2,0,1,2},
                             {10,1,0,2,10,1,0,2,10,1,0,2,10,1,0,2,10,1},
                             {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
                             {1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
                             {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},
                             {11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11},
                             {14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14}};


////////////////////////////////////////////////////////////////////////////////
///////////////   Rutina de Interrupcion del PUERTO SERIE  /////////////////////
////////////////////////////////////////////////////////////////////////////////
#INT_RDA
void Dato_Recibido_USART(void)
{

dato=getch();  // Almaceno el dato presente en el buffer de entrada del puerto serie
recibido = 1;  // Activo el testigo de recepción

}


////////////////////////////////////////////////////////////////////////////////
////////////////////////   Rutina del Temporizador 1  //////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Esta interrupción se activa cuando hemos transmitido 4095 pulsos de GSCLK (a través de PORTC0) //
// y nos avisa de que debemos generar el pulso de reseteo BLANK //

#INT_TIMER1
void inter1(){

output_low(GSCLK);   // Fijamos GSCLK (C0) a 0, 
tempo++;


if (needXLAT){       // Compruebo si necesito un pulso XLAT para actualizar los registros
output_high(XLAT);   // En caso afirmativo lo genero, y reseteo el testigo que lo solicitaba
output_low(XLAT);
needXLAT=0;
}

output_high(BLANK);  // Genero el pulso BLANK
output_low(BLANK);

set_timer1(0xF000);   // Recarga el TIMER1 para una nueva cuenta de 4095

}



////////////////////////////////////////////////////////////////////////////////
////////////////////////         Rutina DOT           //////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Setea los 6 bits de ajuste fino (DotCorrection) para todos los canales //

void setDC(int dc[]){

int i;
int j;
int tmp;

output_low(SCLK);    // Bajo el reloj de transmisión serie
output_high(VPRG);   // Activo modo ajuste fino (DotCorrection).
output_low(XLAT);    // Bajo XLAT para bloquear los valores de los registros del TLC5940 hasta que no finalice la transmisión


for (i=0; i<CHANNELS; i++){            // En este primer bucle recorro todos los canales del mayor al menor
      tmp = dc[(CHANNELS - 1) - i];    // y guardo el valor DC correspondiente en una variable auxiliar.
      for (j=0; j<6; j++){
            if (tmp & 0x20){           // En este bucle utilizo la variable auxiliar con el dato de 6 bits para enviarla
               output_high(SIN);       // bit a bit a través de SIN, sincronizada con cada flanco de subida de SCLK,
            }                          // y comenzando por el bit MSB de la palabra de 6 bits.
            else{
               output_low(SIN);
            }                          
            output_high(SCLK);
            tmp <<= 1;
            output_low(SCLK);
      }
}

// Dado que la actualización de los registros DC la necesito rara vez, y solamente al inicio, no me importa un breve parpadeo,
// por lo que lo hago aqui mismo y sin importar el momento en el que se encuentre el contador PWM.

output_high(XLAT);                     // Al finalizar el envío produzco un pulso de XLAT para su actualización en el TLC5940
output_low(XLAT);                      // Dejo XLAT a 0 para bloquear el contenido de los registros.


}

////////////////////////////////////////////////////////////////////////////////
//////////////////////         Rutina GREYSCALE       //////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Setea los valores de brillo (GreyScale) para todos los canales //
// (en la primera pasada añade un pulso SCLK adicional) //

void setGS(long gs[], int first){

int i;
int j;
long tmp;

output_low(SCLK);    // Bajo el reloj de transmisión serie 
output_low(VPRG);    // Activo modo brillo (GrayScale).
output_low(XLAT);    // Bajo XLAT para bloquear los valores de los registros del TLC5940 hasta que no finalice la transmisión

for (i=0; i<CHANNELS; i++){            // En este primer bucle recorro todos los canales del mayor al menor
      tmp = gs[(CHANNELS - 1) - i];    // y guardo el valor GS correspondiente en una variable auxiliar.
      for (j=0; j<12; j++){
            if (tmp & 0x800){          // En este bucle utilizo la variable auxiliar con el dato de 12 bits para enviarla
               output_high(SIN);       // bit a bit a través de SIN, sincronizada con cada flanco de subida de SCLK,
            }                          // y comenzando por el bit MSB de la palabra de 6 bits.
            else{
               output_low(SIN);
            }
            output_high(SCLK);
            tmp <<= 1;
            output_low(SCLK);
      }
}

// Para evitar parpadeos bruscos en el momento de la actualización de datos, la postpongo hasta el siguiente paso por cero del 
// contador PWM, justo en el instante en que vaya a generar el pulso de BLANK. Para dejar indicada esta tarea pendiente, utilizo
// la variable needXLAT.

needXLAT=1;

// Por lo visto y según se indica en varias fuentes, en la primera actualización de los registros es necesario un pulso adicional 
// del reloj SCLK para que se efectúe correctamente. Con la variable first indico si es la primera vez, en cuyo caso lo ejecuto.

if (first){
   output_high(XLAT);
   output_low(XLAT);
   output_high(SCLK);
   output_low(SCLK);
   needXLAT=0;
}

}


////////////////////////////////////////////////////////////////////////////////
////////////////////       Rutina CAMBIA COLOR     /////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void cambia_color(){

guia++;           // Incremento la posición en el array del patron de colores
if (guia > 17){
   guia = 0;
}
if (primera==1){  // Si procedo de otro modo y acabo de entrar en modo "automatico", primera vale 1 y comienzo el array de colores desde cero.
   guia=0;
   primera = 0;
}

indcolor = patron[pattern][guia];   // En indcolor almaceno el nuevo color a enviar

// A continuación se actualiza el array de valores de brillo a enviar, cada componente en su canal correspondiente.
gss[12]= colores[indcolor][0];      // ROJO.
gss[13]= colores[indcolor][1];      // VERDE
gss[14]= colores[indcolor][2];      // AZUL   



setGS(gss, false);   // Llamo a la rutina de envío del array de brillo hacia el TLC5940, indico que no es la primera vez.

}


////////////////////////////////////////////////////////////////////////////////
////////////////////      Rutina CAMBIA COMPLEJO    ////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// La rutina cambia_complejo() se utiliza en el modo "automatico progresivo".  //
// Esta rutina progresa entre estados de colores fijos y estados de transición.//
// Las transiciones de colores son conducidas por la variable guiax, la cual   //
// produce 9 valores intermedios de color para dar el efecto de transición     //



void cambia_complejo(){

long aux;
long aux1;
long aux2;
long aux3;
int iz;
int sig;


guiax++;          // Incremento guiax, si valia 0, se va con 1
if (primera==1){  // Si procedo de otro modo y acabo de entrar en modo "automatico", primera vale 1 y comienzo el array de colores desde cero.
   guiax=0;
   primera = 0;
}

if (guiax > 9){
   guiax = 0;
   guia++;
   if (guia > 17){
      guia = 0;
   }
}

indinicial = patron[pattern][guia];       // Obtengo el color actual y el siguiente
if (guia >= 17){
   indobjetivo = patron[pattern][0];
}
else{
   sig = guia+1;
   indobjetivo = patron[pattern][sig];
}


if(guiax == 0){                // Si guiax vale 0 se asigna el color fijo inicial y activo "detiene" para que permanezca fijo
   detiene = 1;
   colresul[0]= colores[indinicial][0];
   colresul[1]= colores[indinicial][1];
   colresul[2]= colores[indinicial][2];
}
else{               // Si guiax es distinto de 0, estoy en modo transición. Segun su valor se determina en que punto de la transicion estoy.
   detiene = 0;
   iz = 0;
   while (iz < 3){  // Para cada componente de color RGB se evalúa si entre el color actual y el siguiente hay que incrementar o decrementar.
         if ((colores[indinicial][iz]) < (colores[indobjetivo][iz])){   // En este caso la componente objetivo es mayor que el inicial.
            aux= (colores[indobjetivo][iz])-(colores[indinicial][iz]);  // Se obtiene la diferencia
            aux1=aux/10;  // Se divide entre 10
            aux2=aux1*guiax; // Multiplicamos por el punto de transición actual (de 1 a 9)
            aux3=(colores[indinicial][iz]) + aux2; // Se suma ese valor a la componente inicial, con lo que nos acercamos al objetivo
            colresul[iz]=aux3;   // La componente resultante se guarda en el lugar correspondiente del array de color "colresul[]"
         }
         else if ((colores[indinicial][iz]) > (colores[indobjetivo][iz])){ // En este caso la componente objetivo es menor que el inicial
            aux= (colores[indinicial][iz])-(colores[indobjetivo][iz]); 
            aux1=aux/10;
            aux2=aux1*guiax;
            aux3=(colores[indinicial][iz]) - aux2; // Se resta ese número de la componente inicial, con lo que nos acercamos al objetivo.
            colresul[iz]=aux3;
         }
         else{
            colresul[iz]=colores[indobjetivo][iz]; // En este caso el color objetivo es igual al inicial, no se hace nada.
         }
         iz = iz + 1;
   }
}


// A continuación se actualiza el array de valores de brillo a enviar, cada componente en su canal correspondiente.

gss[12]= colresul[0];   // ROJO
gss[13]= colresul[1];   // VERDE
gss[14]= colresul[2];   // AZUL

setGS(gss, false);      // Llamo a la rutina de envío del array de brillo hacia el TLC5940, indico que no es la primera vez.
}


////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////  PRINCIPAL  //////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
void main(){

int index;           // Indica el canal actual seleccionado
int i;               // Auxiliar
int buff;            // Auxiliar
int changeGS = 0;    // Indicador de cambio en un registro GS
int changeDC = 0;    // Indicador de cambio en un registro DC

set_tris_A(0x00); // Puerto A como salida
set_tris_B(0x00); // Puerto B como salida
//set_tris_C(0x00); // Puerto C como salida

PORTA=(0x00);  // Inicializa puerto A todo a 0
PORTB=(0x00);  // Inicializa puerto B todo a 0


// Inicializamos todos las señales de control del TLC5940
output_low(SIN);
output_low(SCLK);
output_low(XLAT);// Modo lectura de datos del registro fijo.
output_low(VPRG); // Modo GS
output_high(DCPRG);  // Tomo los valores DC de los registros (no de la eeprom).
output_low(GSCLK);
output_low(BLANK); // Modo OUT controladas por PWM.


// Inicializamos DC y GS
for (i=0; i<CHANNELS; i++){
      dcs[i] = 63; // Ajuste fino al máximo
      gss[i] = 4095; // Brillo al máximo
}
index = 0;   // Por defecto comienzo en el canal 0


printf("  Controlador de LEDs RGB con TLC5940, por Khronos \r\n");

setDC(dcs);          // Llamo a la subrutina de envío de valores de ajuste fino (DC)
setGS(gss,true);     // Llamo a la subrutina de envío de valores de brillo (GS), e indico que es la primera vez.

output_low(GSCLK);   // Reseteo GSCLK
output_high(BLANK);  // Flanco de bajada de BLANK
output_low(BLANK);



setup_timer_1 (T1_INTERNAL | T1_CLK_OUT | T1_DIV_BY_1); // Configuracion de TIMER1, reloj interno, preescaler de 1 y salida por PORTC0.
enable_interrupts(INT_TIMER1);   // Habilita el flag de interrupción de TIMER1
enable_interrupts(INT_RDA);   // Habilita el flag de interrupción de RDA (recepcion serie)
enable_interrupts(GLOBAL); // Activamos interrupciones globales
set_timer1(0xF000);   // Recarga el TIMER0 para contar 4095 pulsos




///////////////////////////////////
// Bucle principal del programa  //
///////////////////////////////////

// El control de las distintas funciones del PIC se realiza mediante el envío de comandos serie. Para ello utilizaremos la consola "siow".
// Las teclas de control y sus funciones son las siguientes:
/*
   - Selecciona el modo de funcionamiento                ---       ****    --->  Tecla "a" (incremental y cíclica) 
     Siendo: Modo 0 = Automatico desactivado, salida fija.
             Modo 1 = Automatico activado, ejecucion de patrones.
             Modo 2 = Automatico progresivo, ejecucion de patrones con transiciones de color.

   - Selecciona el canal a modificar                     ---      0 a 15   --->  Teclas "q" y "w" (decrementa, incrementa)
   - Modifica  el valor de ajuste fino (registro DC)     ---      0 a 64   --->  Teclas "d" y "e" (decrementa, incrementa)
   - Modifica  el valor de brillo (registro GS)          ---      0 a 4095 --->  Teclas "f" y "r" (decrementa, incrementa)
   - Escoge el patron a ejecutar                         ---      0 a 13   --->  Teclas "j" y "k" (decrementa, incrementa) 
   - Modifica el "tempo" para patrones en saltos de 1    ---      1 a 1100 --->  Teclas "o" y "p" (decrementa, incrementa)
   - Modifica el "tempo" para patrones en saltos de 10   ---     10 a 1100 --->  Teclas "9" y "0" (decrementa, incrementa)
   - Ajusta la proporcion transición/fijo                ---      0 a 30   --->  Teclas "z" y "x" (decrementa, incrementa)  
     en transiciones progresivas.
   - Muestra por pantalla los valores GS y DC de         ---       ****    --->  Tecla "i"  
     todos los canales.      
*/

for (;;){

   if (recibido){                               // Compruebo el testigo de recepción de datos serie
         recibido = 0;                          // Reseteo testigo de recepción
         switch (dato){                         // Compruebo el dato recibido y actúo en consecuencia
         case 'q':                              // COMANDO: Decremento de canal a modificar.
                  if (index > 0){
                  index=index-1;                // El canal actual se almacena en index          
                  putc(13);
                  printf("q");                
                  putc(13);
                  }
                  break;

         case 'w':                              // COMANDO: Incremento de canal a modificar
                  if (index < CHANNELS-1){
                  index=index+1;                // El canal actual se almacena en index 
                  putc(13);
                  printf("w");                 
                  putc(13);
                  }
                  break;


         case 'd':                              // COMANDO: Decremento del valor de ajuste fino (DC) del canal seleccionado, en pasos de 3.
                  if (dcs[index] >=3){
                  dcs[index]=dcs[index]-3;      // Modifico el canal "index" del array "dcs[]"   
                  changeDC = 1;                 // Indico que ha habido un cambio en un registro DC
                  putc(13);
                  printf("d");
                  putc(13);
                  }
                  break;

         case 'e':                              // COMANDO: Incremento del valor de ajuste fino (DC) del canal seleccionado, en pasos de 3.
                  if (dcs[index] <= 60){
                  dcs[index]= dcs[index]+3;     // Modifico el canal "index" del array "dcs[]"  
                  changeDC = 1;                 // Indico que ha habido un cambio en un registro DC
                  putc(13);
                  printf("e");
                  putc(13);
                  }
                  break;


         case 'f':                              // COMANDO: Decremento del valor de brillo (GS) del canal seleccionado, en pasos de 95.
                  if (gss[index] >= 95){
                  gss[index]=gss[index]-95;     // Modifico el canal "index" del array "gss[]"   
                  changeGS = 1;                 // Indico que ha habido un cambio en un registro GS
                  putc(13);
                  printf("f");
                  putc(13);
                  }
                  else if (gss[index] == 10){   // Truco para poder llegar al mínimo, 0, decrementando en pasos de 95
                  gss[index]=0;                 // Valor mínimo.  
                  changeGS = 1;                 // Indico que ha habido un cambio en un registro GS
                  putc(13);
                  printf("f");
                  putc(13);
                  }
                  break;

         case 'r':                           // COMANDO: Incremento del valor de brillo (GS) del canal seleccionado, en pasos de 95.
                  if (gss[index] <= 4000){
                  gss[index]=gss[index]+95;  // Modifico el canal "index" del array "gss[]" 
                  changeGS = 1;              // Indico que ha habido un cambio en un registro GS
                  putc(13);
                  printf("r");
                  putc(13);
                  }
                  else if (gss[index] == 4085){ // Truco para poder llegar al máximo, 4095, incrementando en pasos de 95
                  gss[index]=4095;              // Valor maximo.
                  changeGS = 1;                 // Indico que ha habido un cambio en un registro GS    
                  putc(13);
                  printf("r");
                  putc(13);
                  }
                  break;


         case 'j':                        // COMANDO: Selección de patron predefinido, decremento.
                  if (pattern > 0){
                     pattern--;           // La variable pattern es el índice del array de patrones.
                     guia = 0;            // La variable guia es el índice del sub-array de colores dentro de un patrón. Pongo a 0 para inicio.
                  }
                  printf("PATRON: %2d\r\n", pattern);
                  break;
                  
         case 'k':                        // COMANDO: Selección de patron predefinido, incremento.
                  if (pattern < 13){
                     pattern++;           // La variable pattern es el índice del array de patrones.
                     guia = 0;            // La variable guia es el índice del sub-array de colores dentro de un patrón. Pongo a 0 para inicio.
                  }
                  printf("PATRON: %2d\r\n", pattern);
                  break;


         case 'o':                        // COMANDO: Decremento del valor de "tempo" en ejecución de patrones. En pasos de 1.
                  if (bpm > 1){
                     bpm=bpm-1;           
                  }
                  printf("BPM: %4ld\r\n", bpm);
                  break;
                  
         case 'p':                        // COMANDO: Incremento del valor de "tempo" en ejecución de patrones. En pasos de 1.
                  if (bpm < 1100){
                     bpm++;               
                  }
                  printf("BPM: %4ld\r\n", bpm);
                  break;


         case '9':                        // COMANDO: Decremento del valor de "tempo" en ejecución de patrones. En pasos de 10.
                  if (bpm > 10){
                     bpm=bpm-10;
                  }
                  printf("BPM: %4ld\r\n", bpm);
                  break;

         case '0':                        // COMANDO: Incremento del valor de "tempo" en ejecución de patrones. En pasos de 10.
                  if (bpm < 1090){
                     bpm = bpm + 10;
                  }
                  printf("BPM: %4ld\r\n", bpm);
                  break;


         case 'z':                        // COMANDO: Variación de la proporción transición/fijo para transiciones progresivas. Decremento.
                  if (factor > 1){
                     factor = factor-1;
                  }
                  printf("FACTOR: %4ld\r\n", factor);
                  break;
                  
         case 'x':                        // COMANDO: Variación de la proporción transición/fijo para transiciones progresivas. Incremento.
                  if (factor < 30){
                     factor = factor + 1;
                  }
                  printf("FACTOR: %4ld\r\n", factor);
                  break;


         case 'i':                        // COMANDO: Muestra por pantalla los valores de GS y DC para todos los canales
                  for (i=0; i<CHANNELS; i++){ 
                     printf("ch%d: dc: %2d gs %4ld\r\n", i, dcs[i], gss[i]);
                  }
                  printf("current channel: %d\r\n", index);
                  break;


         case 'a':                        // COMANDO: Modifica el modo de funcionamiento, fijo, automático y automático progresivo.
                  if (sec == 0){          // La variable "sec" indica el modo de funcionamiento
                     sec = 1;
                     printf("Automatico activado \r\n");
                     primera = 1;         // La variable auxiliar "primera" indica que comienze la ejecución del patron desde el primer color.
                  }
                  else if (sec == 1){
                     sec = 2;
                     primera = 1;       
                     detiene = 0;
                     printf("Automatico PROGRESIVO \r\n");
                  }
                  else if (sec == 2){
                     sec = 0;
                     printf("Automatico desactivado \r\n");
                  }
                  break;
      }



      if (dato!= 'i'){           // Para la pulsación de cualquier tecla distinta de "i", muestro los valores DC y GS del canal actual
      
         printf("current channel: %d, dc: %d, gs: %ld\r\n", index, dcs[index], gss[index]);
      }


      if (changeDC == 1){        // Si se ha notificado un cambio en un registro DC, llamo a la rutina de envío DC
         setDC(dcs);
         changeDC=0;
      }
      if (changeGS == 1){        // Si se ha notificado un cambio en un registro GS, llamo a la rutina de envío GS
         setGS(gss, false);
         changeGS=0;
      }

   }

/*
      El siguiente código solo se ejecuta si estoy en modo automático o automático progresivo.
      Su función es la de establecer el valor final de tempo.
      - Si estamos en modo automático, ese valor será el resultado de multiplicar bpm x 10.
      - Si estamos en modo automático progresivo el valór será el resultado de multiplicar bpm por la proporción
        transición/fijo almacenada en la variable "factor". 

      Este truco se realiza porque al cambiar entre los modos "automatico" y "automático progresivo", el tiempo percibido entre uno color
      y el siguiente se duplica.
      
      Explicación de este hecho:
      
      Tal y como esta planteado el código, en modo "automatico" la variable "tempo" determina el tiempo de permanencia en cada color antes de
      pasar al siguiente. Puesto que en el modo "automatico" los cambios de color son bruscos, podemos saber con exactitud el número de cambios
      de color que se van a realizar en un minuto. A ojos del programa en este modo, el primer estado es el 1er color del array de colores,
      el segundo estado es el 2º color, el tercero es el 3er color... y así sucesivamente.
      
      Sin embargo en el modo "automatico progresivo" dicha variable "tempo" determina tanto el tiempo de permanencia como el tiempo de transición.
      A ojos del programa, cuando se recorre el patron de colores en este modo, el primer estado es el 1er color del array, el segundo es la
      transición entre el color actual (1º) y el siguiente (2º), el tercero es el 2º color, el cuarto estado es la transición.... y así
      sucesivamente. De este modo el número de colores por minuto se duplica.
      

      Nuestro deseo es el de poder tener un determinado número de colores por minuto, independientemente de si estamos en "automatico" o 
      "automático progresivo". Esto es útil para en una futura ampliación poder indicar el número de colores por minuto que queremos mediante
      un teclado o interfaz externa (por ejemplo DMX). Y por supuesto la aplicación mas directa es la de funcionamiento al ritmo de la música.
      Detectando los bpm (beats per minute) de una canción mediante un circuito externo, podemos cambiar los colores al mismo ritmo.
      
      La solución a este problema de velocidad es tan simple como dividir el valor de "tempo" entre 2 cuando pasemos al modo
      "automatico progresivo". Esto solucionaría el problema, pero la duración del led iluminado fijo, y la duración de la transición serán
      siempre la misma. Puede ocurrir que nos interesen otros efectos, por ejemplo, que la iluminación fija dure 1 seg y la transición hacia
      el siguiente color tan solo 0,2 seg. Este efecto resulta muy llamativo. Por este motivo se crea este pequeño "apaño".
      
      Se puede controlar la proporción de tiempo transición/fijo, pero queda pendiente diseñar otra solución para que el ritmo de cambio entre
      colores se mantenga igual al cambiar del modo "automatico" al "automático progresivo".

*/

   if (sec==1){                  // Modo automático
      bpm2 = bpm * 10;
      if (tempo > bpm2){         // Si se cumple es que ha llegado el momento de cambiar de color
         tempo = 0;
         cambia_color();
      }
   }
   else if (sec==2){             // Modo automático progresivo
      if (detiene==1){           // Estoy en la fase de color fijo
         bpm2 = bpm * factor;
      }
      else{                      // Estoy en la fase de transición
         bpm2 = bpm;
      }
      if (tempo > bpm2){         // Si se cumple es que ha llegado el momento de cambiar de estado (fijo-->transición-->fijo-->transición....)
         tempo = 0;
         cambia_complejo();
      }
   }


}

}



































