#include "Panel Led.h"

/* CharData is a two dimensional constant array that holds the 8-bit column values of
   individual rows for ASCII characters that are to be displayed on a 8x8 matrix format.
   **OJO** Para matriz con columnas en Anodo Comun.
*/
const unsigned short CharData[][8] ={
{0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000},
{0b00000100, 0b00000100, 0b00000100, 0b00000100, 0b00000100, 0b00000100, 0b00000000, 0b00000100}, // ! Falta
{0b00001010, 0b00001010, 0b00001010, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000}, // " Falta
{0b00000000, 0b00001010, 0b00011111, 0b00001010, 0b00011111, 0b00001010, 0b00011111, 0b00001010}, // # Falta
{0b00000111, 0b00001100, 0b00010100, 0b00001100, 0b00000110, 0b00000101, 0b00000110, 0b00011100}, // $ Falta
{0b11000010, 0b11000110, 0b00001100, 0b00011000, 0b00110000, 0b01100110, 0b11000110, 0b00010011}, // %
{0b00000110, 0b00001010, 0b00010010, 0b00010100, 0b00001001, 0b00010110, 0b00010110, 0b00001001}, // & Falta
{0b00011000, 0b00011000, 0b00110000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000}, // '
{0b00011100, 0b00100000, 0b01100000, 0b01100000, 0b01100000, 0b00100000, 0b00011100, 0b00000010}, // (
{0b01110000, 0b00001000, 0b00001100, 0b00001100, 0b00001100, 0b00001000, 0b01110000, 0b00001000}, // )
{0b00010101, 0b00001110, 0b00011111, 0b00001110, 0b00010101, 0b00000000, 0b00000000, 0b00000000}, // *  Falta
{0b00000000, 0b00000000, 0b00000100, 0b00000100, 0b00011111, 0b00000100, 0b00000100, 0b00000000}, // +  F
{0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00011000, 0b00011000, 0b00110000, 0b00001000}, // ,
{0b00000000, 0b00000000, 0b00000000, 0b01111100, 0b00000000, 0b00000000, 0b00000000, 0b00000000}, // -
{0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b01100000, 0b01100000, 0b00000100}, // .
{0b00000001, 0b00000010, 0b00000010, 0b00000100, 0b00000100, 0b00001000, 0b00001000, 0b00010000}, // /  F
{0b01111100, 0b11000110, 0b11001110, 0b11010110, 0b11100110, 0b11000110, 0b01111100, 0b00001110}, // 0
{0b00011000, 0b00111000, 0b01011000, 0b00011000, 0b00011000, 0b00011000, 0b01111100, 0b00011111}, // 1
{0b01111100, 0b10000110, 0b00000110, 0b01111100, 0b11000000, 0b11000000, 0b11111110, 0b00011111}, // 2
{0b11111100, 0b00000110, 0b00000110, 0b01111100, 0b00000110, 0b00000110, 0b11111100, 0b00001110}, // 3
{0b00111100, 0b01101100, 0b11001100, 0b11111110, 0b00001100, 0b00001100, 0b00001100, 0b00000100}, // 4
{0b11111110, 0b11000000, 0b11111100, 0b00000110, 0b00000110, 0b10000110, 0b01111100, 0b00011110}, // 5
{0b00111100, 0b01000000, 0b10000000, 0b11111100, 0b11000110, 0b11000110, 0b01111100, 0b00001110}, // 6
{0b11111110, 0b00000110, 0b00001100, 0b00011000, 0b00110000, 0b00110000, 0b00110000, 0b00001110}, // 7
{0b01111100, 0b11000110, 0b11000110, 0b01111100, 0b11000110, 0b11000110, 0b01111100, 0b00001110}, // 8
{0b01111100, 0b11000110, 0b11000110, 0b01111110, 0b00000110, 0b00001100, 0b01111000, 0b00000001}, // 9
{0b00000000, 0b00110000, 0b00110000, 0b00000000, 0b00000000, 0b00110000, 0b00110000, 0b00000000}, // :
{0b00000000, 0b00011000, 0b00011000, 0b00000000, 0b00011000, 0b00011000, 0b00110000, 0b00001000}, // ;  F
{0b00000001, 0b00000010, 0b00000100, 0b00001000, 0b00001000, 0b00000100, 0b00000010, 0b00000001}, // < F
{0b00000000, 0b00000000, 0b01111100, 0b00000000, 0b01111100, 0b00000000, 0b00000000, 0b00000000}, // =
{0b00010000, 0b00001000, 0b00000100, 0b00000010, 0b00000010, 0b00000100, 0b00001000, 0b00010000}, // > F
{0b00111100, 0b01100110, 0b01100110, 0b00000110, 0b00001100, 0b00001100, 0b00001100, 0b00000100}, // ?
{0b00001110, 0b00010001, 0b00010001, 0b00010101, 0b00010101, 0b00010001, 0b00010001, 0b00011110}, // @ Falta
{0b01111100, 0b11000110, 0b11000110, 0b11111110, 0b11000110, 0b11000110, 0b11000110, 0b00010001}, // A
{0b11111100, 0b11000010, 0b11000010, 0b11111100, 0b11000010, 0b11000010, 0b11111100, 0b00011110}, // B
{0b01111100, 0b11000110, 0b11000000, 0b11000000, 0b11000000, 0b11000110, 0b01111100, 0b00000111}, // C
{0b11111100, 0b11000110, 0b11000110, 0b11000110, 0b11000110, 0b11000110, 0b11111100, 0b00011100}, // D
{0b11111110, 0b11000000, 0b11000000, 0b11111100, 0b11000000, 0b11000000, 0b11111110, 0b00011111}, // E
{0b11111110, 0b11000000, 0b11000000, 0b11111100, 0b11000000, 0b11000000, 0b11000000, 0b00010000}, // F
{0b01111100, 0b11000010, 0b11000000, 0b11011100, 0b11000010, 0b11000010, 0b01111100, 0b00001110}, // G
{0b11000110, 0b11000110, 0b11000110, 0b11111110, 0b11000110, 0b11000110, 0b11000110, 0b00000000}, // H
{0b01111000, 0b00110000, 0b00110000, 0b00110000, 0b00110000, 0b00110000, 0b01111000, 0b00011111}, // I
{0b11111110, 0b00011000, 0b00011000, 0b00011000, 0b10011000, 0b11111000, 0b01110000, 0b00001000}, // J
{0b11000110, 0b11001000, 0b11110000, 0b11100000, 0b11110000, 0b11001000, 0b11000110, 0b00010001}, // K
{0b11000000, 0b11000000, 0b11000000, 0b11000000, 0b11000000, 0b11000000, 0b11111110, 0b00011111}, // L
{0b11000110, 0b11000110, 0b11101110, 0b11010110, 0b11010110, 0b11000110, 0b11000110, 0b00010001}, // M
{0b11000110, 0b11100110, 0b11010110, 0b11001110, 0b11000110, 0b11000110, 0b11000110, 0b00010001}, // N
{0b01111100, 0b11000110, 0b11000110, 0b11000110, 0b11000110, 0b11000110, 0b01111100, 0b00001110}, // O
{0b11111100, 0b11000010, 0b11000010, 0b11111100, 0b11000000, 0b11000000, 0b11000000, 0b00010000}, // P
{0b01111100, 0b11000110, 0b11000110, 0b11010110, 0b11001110, 0b11000100, 0b01111010, 0b00001111}, // Q
{0b01111100, 0b11000110, 0b11000110, 0b11111100, 0b11001000, 0b11000100, 0b11000010, 0b00010001}, // R
{0b01111100, 0b11000010, 0b11000000, 0b01111110, 0b00000110, 0b10000110, 0b01111100, 0b00001110}, // S
{0b11111100, 0b00110000, 0b00110000, 0b00110000, 0b00110000, 0b00110000, 0b00110000, 0b00000100}, // T
{0b11000110, 0b11000110, 0b11000110, 0b11000110, 0b11000110, 0b11000110, 0b01111100, 0b00001110}, // U
{0b11000110, 0b11000110, 0b11000110, 0b11000110, 0b11000110, 0b01101100, 0b00010000, 0b00000100}, // V
{0b11000110, 0b11000110, 0b11000110, 0b11010110, 0b11010110, 0b11010110, 0b01101100, 0b00001010}, // W
{0b11000110, 0b11000110, 0b01101100, 0b00010000, 0b01101100, 0b11000110, 0b11000110, 0b00010001}, // X
{0b11000110, 0b11000110, 0b01000110, 0b00111110, 0b00000110, 0b00000110, 0b01111100, 0b00000100}, // Y
{0b11111110, 0b00000110, 0b00001100, 0b00011000, 0b00110000, 0b11100000, 0b11111111, 0b00011111}, // Z
{0b01111100, 0b01100000, 0b01100000, 0b01100000, 0b01100000, 0b01100000, 0b01111100, 0b00001110}, // [
{0b00010000, 0b00001000, 0b00001000, 0b00000100, 0b00000100, 0b00000010, 0b00000010, 0b00000001}, // \  F
{0b00111110, 0b00000110, 0b00000110, 0b00000110, 0b00000110, 0b00000110, 0b00111110, 0b00001110}, // ]
{0b00000100, 0b00001010, 0b00010001, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000}, // ^  F
{0b00000000, 0b00000000, 0b00000000, 0b01111110, 0b00000000, 0b00000000, 0b00000000, 0b00011111}, // -
{0b00001000, 0b00000100, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000}, // `   F
{0b00000000, 0b00000000, 0b00111100, 0b00000010, 0b00111110, 0b01100110, 0b00111110, 0b00001111}, // a
{0b00000000, 0b01100000, 0b01100000, 0b01111100, 0b01100010, 0b01100010, 0b01111100, 0b00011100}, // b
{0b00000000, 0b00000000, 0b00111110, 0b01100000, 0b01100000, 0b01100000, 0b00111110, 0b00001110}, // c
{0b00000000, 0b00000110, 0b00000110, 0b00111110, 0b01100110, 0b01100110, 0b00111110, 0b00000111}, // d
{0b00000000, 0b00000000, 0b00111100, 0b01100010, 0b01111110, 0b01100000, 0b00111110, 0b00001110}, // e
{0b00000000, 0b00001100, 0b00010010, 0b00011000, 0b00111100, 0b00011000, 0b00011000, 0b00000100}, // f
{0b00000000, 0b00000000, 0b00111110, 0b01000110, 0b00111110, 0b00000110, 0b01111100, 0b00001100}, // g
{0b00000000, 0b01100000, 0b01100000, 0b01111100, 0b01100110, 0b01100110, 0b01100110, 0b00010010}, // h
{0b00011000, 0b00000000, 0b00111000, 0b00011000, 0b00011000, 0b00011000, 0b00111100, 0b00000100}, // i
{0b00000000, 0b00000110, 0b00000110, 0b00000110, 0b01100110, 0b01100110, 0b00111100, 0b00001100}, // j
{0b00000000, 0b01100100, 0b01101000, 0b01110000, 0b01101000, 0b01100100, 0b01100010, 0b00010000}, // k
{0b00000000, 0b00111000, 0b00011000, 0b00011000, 0b00011000, 0b00011000, 0b00111100, 0b00001100}, // l
{0b00000000, 0b00000000, 0b01111100, 0b11010110, 0b11010110, 0b11010110, 0b11000110, 0b00010001}, // m
{0b00000000, 0b00000000, 0b01011100, 0b01100110, 0b01100110, 0b01100110, 0b01100110, 0b00010010}, // n
{0b00000000, 0b00000000, 0b00111100, 0b01100110, 0b01100110, 0b01100110, 0b00111100, 0b00001100}, // o
{0b00000000, 0b00000000, 0b01111100, 0b01100110, 0b01111100, 0b01100000, 0b01100000, 0b00010000}, // p
{0b00000000, 0b00000000, 0b00111110, 0b01100110, 0b00111110, 0b00000110, 0b00000110, 0b00000001}, // q
{0b00000000, 0b00000000, 0b01101100, 0b01110010, 0b01100000, 0b01100000, 0b01100000, 0b00001000}, // r
{0b00000000, 0b00000000, 0b00111110, 0b01100000, 0b00111100, 0b00000110, 0b01111100, 0b00011110}, // s
{0b00000000, 0b00011000, 0b01111110, 0b00011000, 0b00011000, 0b00011000, 0b00011100, 0b00001100}, // t
{0b00000000, 0b00000000, 0b01100110, 0b01100110, 0b01100110, 0b01100110, 0b00111100, 0b00001100}, // u
{0b00000000, 0b00000000, 0b01100110, 0b01100110, 0b01100110, 0b00100100, 0b00011000, 0b00000100}, // v
{0b00000000, 0b00000000, 0b11000110, 0b11010110, 0b11010110, 0b11010110, 0b01101100, 0b00001010}, // w
{0b00000000, 0b00000000, 0b01100110, 0b01111110, 0b00011000, 0b01100110, 0b01100110, 0b00010001}, // x
{0b00000000, 0b00000000, 0b01000010, 0b00100100, 0b00011000, 0b00010000, 0b00100000, 0b00010000}, // y
{0b00000000, 0b00000000, 0b01111110, 0b00001100, 0b00010000, 0b01100000, 0b01111110, 0b00011111}, // z
{0b00000010, 0b00000100, 0b00000100, 0b00000100, 0b00001000, 0b00000100, 0b00000100, 0b00000010}, // {  F
{0b00000100, 0b00000100, 0b00000100, 0b00000100, 0b00000100, 0b00000100, 0b00000100, 0b00000100}, // |  F
{0b00001000, 0b00000100, 0b00000100, 0b00000100, 0b00000010, 0b00000100, 0b00000100, 0b00001000}, // }  F
{0b00000000, 0b00000000, 0b00000000, 0b00001010, 0b00011110, 0b00010100, 0b00000000, 0b00000000}  // ~  F
};

sint Screen[ROW][ASCIILEN];          // ASCIILEN*8 = 768 bits
MENSAJE Mensajes[MSGINRAMMAX];       // Arreglo de mensajes a mostrar
sint MsgPriori[MSGINRAMMAX];         // Contiene el orden en que se van a mostrar los mensajes

bit SHOW;              // Muestra u Oculta
bit HLR_SCROLL;        // Scroll horizontal Left-Right
bit HRL_SCROLL;        // Scroll hotizontal Right-Left
bit VUD_SCROLL;        // Scroll vertical Up-Down
bit VDU_SCROLL;        // Scroll verticar Down-Up
bit CHLR;              // Cortina Horizontal Left-Right (Muestra)
bit CHRL;              // Cortina Horizontal Right-Left (Oculta)
bit BLINK;             // Habilita o Deshabilita
bit SCROLL;            // Habilita o Deshabilita
bit TROGGLE;           // Habilita o Deshabilita
bit TROGGLE_FLAG;      // Cambia de direccion el scroll
bit EFECTO_IN;         // Animacion de entrada en proceso
bit EFECTO_OUT;        // Animacion de salida en proceso
bit TIMER_INOUT;       // Indica el estado del temporizador de transición entre efecto entrada y efecto de salida
bit Blink_Status;      // ON/OFF
bit FINISH_EFECT;      // Indica que el texto entro y salio, ejecuta el siguiente paso

// variables de control
sint output_port;
sint prioindex;                   // indexa la tabla de prioridades
sint msgindex;                   // indica el mensaje a mostrar

ulong StringWidth;              // longitud en pixel del mensaje (puede ser tan grande como (ASCIILE*8) = 768)
int CenterScreen;               // Indica el centro de la pantalla si aplica al MENSAJE (StringWidth < SCREENWIDTH)
sint shift_step;
char uart_rd;
ulong OffsetR;          // -
                        //  |- Indican margen izquierdo y derecho de la pantalla
ulong OffsetL;          // -
sint totalmsg;                   // Indica el total de mensajes contenidos en la memoria externa
sint totalpriori;                // Indica el total de los mensajes a mostrar en la tabla de prioridades
sint ScrollVeticalindex;
bit  Link;                       // Indica si esta conectado a la PC

// Temporizadores..
int timer;
sint timer_shift;
sint timer_blink;
sint timer_ledblink;
sint timer_shift_setpoint;
sint timer_blink_setpoint;
sint time_delay_setpoint;

// Usadas en screen_frame
sint Mask, l, byte_row = 0, b = 0;
sint byte_blink = 0, Mask_Blink;
volatile sint Flag;
sint temp ;
int t; // 16 bits
unsigned int byte_row_cut;
sint byte_blink_cut;
sint mask_cut;
sint mask_blink_cut;
unsigned int temp_cut;
sint offset_cut;
sint offset_rcut;
sint b_temp;

/*Propiedades Generales del mensaje son:
    shift_step
    timer_shift_setpoint
    timer_blink_setpoint
    time_delay_setpoint
*/


void interrupt() {

     if(SHOW == TRUE) {
          timer_ledblink++;
     }
     else {
          PORTA.RA1 = 1;
     }

     // timer para el blink del led de actividad
     if( timer_ledblink == TIMER_LED) {
         PORTA.RA1 = ~PORTA.RA1;
         timer_ledblink = 0;
     }
     
     INTCON.TMR0IF = 0;    // Desborda cada 1mS
     TMR0H = 160;           //
     TMR0L = 0;
}

#define DEBUG
//*****************************************************************************
//*****************************************************************************
//*****************************************************************************
void main()
{
     sint i, j;
     sint buffin[130];
     
     init_pic();
     T0CON.TMR0ON = 0;

     I2C1_Init(400000);
     UART1_Init(9600);               // Initialize UART module at 9600 bps

     Delay_ms(100);                  // Wait for UART module to stabilize

     load_msg_from_i2c();
     shift_step = 1;                 // Se puede eliminar toda referencia a esta variable
     prioindex = 0;                  // Indexa los Mensajes de la tabla
     msgindex = MsgPriori[prioindex]; // Mostramos los Mensajes de acuerdo al orden de la tabla
     fill_buffer();   // Colocamos el texto en el buffer de pantalla e inicializamos los parametros de animación

     UART1_Write_Text("Start");

     SHOW = 1;
     T0CON.TMR0ON = 1;
     
     while(1) {
              if(SHOW == TRUE) {
                   if(TIMER_INOUT == TRUE) { // Cuando termina la animación de entrada este timer hace una pausa antes de empezar la de salida
                        timer++;
                   }
                   
                   timer_shift++;  // variables de temporización para el SCROLL y el blinking
                   timer_blink++;
                   
                   // timers del scroll
                   if(timer_shift == timer_shift_setpoint) {
                        timer_shift = 0;
                        
                        if(SCROLL == TRUE) {
                             if(HRL_SCROLL == TRUE) {  // horizontal right to left
                                  OffsetL = OffsetL+shift_step;

                                  if((StringWidth < SCREENWIDTH) && (EFECTO_IN == TRUE)) {
                                       if(OffsetL == CenterScreen) {
                                            if(Mensajes[msgindex].flags & ANIMOUT_MASK) { // si esta habilitado el efecto de animacion de salida, hace una parada si solo si
                                                 HRL_SCROLL = FALSE;    // el texto cabe en pantalla (StringWidth < SCREENWIDTH), si no, sigue animando hasta
                                                 TIMER_INOUT = TRUE;    // que salga el texto de pantalla y finaliza
                                                 timer = 0;
                                            }
                                       }
                                  }
                                  if(OffsetL == (StringWidth+SCREENWIDTH)) {  // Salio el texto de la pantalla visible??
                                       if((EFECTO_OUT == TRUE) /*|| ((StringWidth > SCREENWIDTH) && (Mensajes[msgindex].flags & ANIMOUT_MASK))*/) {  // si viene de una animacion de salida, finaliza este texto
                                            EFECTO_OUT = FALSE;
                                            FINISH_EFECT = TRUE;
                                       }
                                  }
                                  if((StringWidth > SCREENWIDTH) && (OffsetL == SCREENWIDTH)) { // Hace una pausa de tiempo time_delay
                                       Mensajes[msgindex].efecout = HRLSCROLL_MASK; // forzado para que el texto termine de salir
                                       HRL_SCROLL = FALSE;
                                       TIMER_INOUT = TRUE;
                                       timer = 0;
                                  }
                             } // HRL_SCROLL
                             else if(HLR_SCROLL == TRUE) { // horizontal left to right
                                   OffsetL = OffsetL-shift_step;

                                   if((OffsetL == 0) || ((StringWidth > SCREENWIDTH) && (Mensajes[msgindex].flags & ANIMOUT_MASK))) {
                                        if(EFECTO_OUT == TRUE) {
                                             EFECTO_OUT = FALSE;
                                             FINISH_EFECT = TRUE;
                                        }
                                        else
                                            OffsetL = StringWidth+SCREENWIDTH;
                                   }
                                   if((StringWidth < SCREENWIDTH) && (EFECTO_IN == TRUE)) {
                                       if(OffsetL == CenterScreen) {
                                            if(Mensajes[msgindex].flags & ANIMOUT_MASK) {
                                                 HLR_SCROLL = FALSE;
                                                 TIMER_INOUT = TRUE;
                                                 timer = 0;
                                            }
                                       }
                                   }
                             } // HLR_SCROLL
                             else if(VUD_SCROLL) {  // Vertical up-down
                                  screen_frame();
                                  fill_buffer_for_vertical_scroll_anim(ScrollVeticalindex++, TRUE, VUD);
                                  screen_frame();
                             
                                  if(ScrollVeticalindex == (CHARHEIGTH+1)) {
                                       fill_buffer_for_vertical_scroll_anim(ScrollVeticalindex-1, FALSE, VUD);
                                       VUD_SCROLL = FALSE;
                                       TIMER_INOUT = TRUE;
                                       timer = 0;
                                  }
                             
                             
                             } // VUD_SCROLL
                             else if(VDU_SCROLL) { // vertical down-up
                                  screen_frame();
                                  fill_buffer_for_vertical_scroll_anim(ScrollVeticalindex, TRUE, VDU);
                                  screen_frame();

                                  if(ScrollVeticalindex == 0) {
                                       fill_buffer_for_vertical_scroll_anim(0, FALSE, VDU);
                                       VDU_SCROLL = FALSE;
                                       TIMER_INOUT = TRUE;
                                       timer = 0;
                                  }
                                  ScrollVeticalindex--;

                             } // VDU_SCROLL
                             else if(CHLR == TRUE) { // cortina horizontal left to right
                                  OffsetR--;

                                  if((OffsetR == 0) || (OffsetR == (OffsetL-StringWidth-1))) {
                                       OffsetR = 0;

                                       if(Mensajes[msgindex].flags & ANIMOUT_MASK) {
                                            CHLR = FALSE;
                                            TIMER_INOUT = TRUE;
                                            timer = 0;
                                       }
                                  }
                             }// CHLR
                             else if(CHRL == TRUE) {  // cortina horizontal right to left
                                  OffsetR++;

                                  if((OffsetR > SCREENWIDTH) || (OffsetR > OffsetL)) {
                                       OffsetR = SCREENWIDTH;

                                       // Debido a que es una animación solamente de salida, al terminar finaliza
                                       CHRL = FALSE;
                                       EFECTO_OUT = FALSE;
                                       FINISH_EFECT = TRUE;
                                  }

                             }// CHRL
                        } // SCROLL
                   }
                   // timer del blinking de caracter (define frecuencia deparpadeo)
                   if(timer_blink == timer_blink_setpoint) {
                        if(BLINK == TRUE) {
                             Blink_Status = ~Blink_Status;
                        }

                        timer_blink = 0;
                   }
                   // timer para efecto de entrada y salida de texto en pantalla
                   // después que termine el efecto de entrada se habilita este timer para empezar el efecto de salida para despues finalizar el texto
                   if(timer == time_delay_setpoint) {
                        TIMER_INOUT = FALSE;
                        EFECTO_IN = FALSE;

                        if(Mensajes[msgindex].flags & ANIMOUT_MASK) { // si esta habilitado el efecto de animacion de salida
                             EFECTO_OUT = TRUE;

                             if(Mensajes[msgindex].efecout & HRLSCROLL_MASK) {
                                  HRL_SCROLL = TRUE;
                             }
                             if(Mensajes[msgindex].efecout & HLRSCROLL_MASK) {
                                  HLR_SCROLL = TRUE;
                             }
                             if(Mensajes[msgindex].efecout & CHRL_MASK) {
                                  CHRL = TRUE;
                             }
                             if(Mensajes[msgindex].efecout & NONE_MASK) { // termina el texto sin animación alguna
                                  EFECTO_OUT = FALSE;
                                  FINISH_EFECT = TRUE;
                             }
                        }

                        timer = 0;
                   }
                   
                   // Muestra un frame en pantalla
                   screen_frame(); // OJO: En este punto esta pendiente de optimización. Me gustaría mover esta función de aqui y hacerla independiente
                                   // del proceso que se ejecuta
              }

              
              
              if (UART1_Data_Ready() == 1 /*PIR1.RCIF*/) {      // If data is received
                 uart_rd = UART1_Read();      //   read the received data

                 switch(uart_rd) {
                     case 'h':
                          SCROLL = ~SCROLL;
                          break;
                     case 'm':
                          SHOW = ~SHOW;
                          break;
                     case 'o':
                          SHOW = FALSE;
                          break;
                     case 'b':
                          BLINK = ~BLINK;
                          Blink_Status = SHOW;
                          break;
                     case '-':
//                          if(timer_shift_setpoint < 10) {
                              timer_shift_setpoint++;
//                          }
                          break;
                     case '+':
                          if(timer_shift_setpoint > 1) {
                              timer_shift_setpoint--;
                              timer_shift = timer_shift_setpoint-1;
                          }
                          break;
                     case 'r':  // resetea index de Mensajes
                          SHOW = FALSE;
                          prioindex = 0;
                          msgindex = MsgPriori[prioindex];
                          fill_buffer();    // convierte de ascii a mapa de bit el mensaje a mostrar
                          SHOW = TRUE;
                          break;
                     case 'n':  // forza siguiente mensaje
                          SHOW = FALSE;
                          prioindex++;
                          if(prioindex == totalpriori) {
                               prioindex = 0;
                          }
                          msgindex = MsgPriori[prioindex];
                          fill_buffer();    // convierte de ascii a mapa de bit el mensaje a mostrar
                          SHOW = TRUE;
                          break;
                     case 'p':  // forza previo mensaje
                          SHOW = FALSE;
                          prioindex--;
                          if(prioindex < 0) {
                               prioindex = 0;
                          }
                           msgindex = MsgPriori[prioindex];
                           fill_buffer();    // convierte de ascii a mapa de bit el mensaje a mostrar
                           SHOW = TRUE;
                          break;
                     case 's':
                          shift_step++;
                          break;
                     case 'd':
                          if(!shift_step)
                               break;
                               
                          shift_step--;
                          break;
                     case PCLINK:
                          /*cls();
                          strcpy(Mensajes[totalpriori].ascii, "PCLink OK");
                          memset(Mensajes[totalpriori].blink, 0, BLINKFLAGSLEN);
                          Mensajes[totalpriori].flags = 0x07;
                          Mensajes[totalpriori].efecin = NONE_MASK;
                          Mensajes[totalpriori].efecout = NONE_MASK;
                          Mensajes[totalpriori].stringlen = 9;
                          msgindex = totalpriori;
                          fill_buffer();
                          SHOW = TRUE;
                          Delay_ms(1000);       // Muestra por 1 seg
                          SHOW = FALSE;
                          cls();*/
                          SHOW = FALSE;
                          UART1_Write(CONNECTOK);
                          while(ack_wait());
                          UART1_Write(SN_H);
                          while(ack_wait());
                          UART1_Write(SN_L);
                          Link = TRUE;
                      
                          while( Link ) {
                             if (UART1_Data_Ready()) {      // If data is received,
                                uart_rd = UART1_Read();
                                switch (uart_rd) {
                                       case TOTAL:
                                            UART1_Write(totalmsg+48);
                                            while(ack_wait());
                                            UART1_Write(totalpriori+48);  // Envia en ASCII
                                            break;
                                       case SENDALLMSG:
                                            UART1_Write(totalmsg); // total de Mensajes a transferir
                                            while(ack_wait());
                                            UART1_Write(totalpriori);
                                            while(ack_wait());
                                            
                                            for(j = 0; j <  MSGINRAMMAX; j++) {
                                                 UART1_Write(MsgPriori[j]);
                                                 while(ack_wait());
                                            }
                                            
                                            for( i = 0; i < totalmsg; i++) {
//                                                 UART1_Write(STARTDATA);                   // Start Condition
                                                 UART1_Write(Mensajes[i].addrh);
                                                 while(ack_wait());
                                                 UART1_Write(Mensajes[i].addrl);
                                                 while(ack_wait());
                                                 UART1_Write(Mensajes[i].flags);
                                                 while(ack_wait());
                                                 UART1_Write(Mensajes[i].efecin);
                                                 while(ack_wait());
                                                 UART1_Write(Mensajes[i].efecout);
                                                 while(ack_wait());
                                                 UART1_Write(Mensajes[i].scroll_setpoint);
                                                 while(ack_wait());
                                                 UART1_Write(Mensajes[i].blink_setpoint);
                                                 while(ack_wait());
                                                 UART1_Write(Mensajes[i].delay_setpoint);
                                                 while(ack_wait());
                                                 UART1_Write(Mensajes[i].stringlen);
                                                 while(ack_wait());
//                                                 UART1_Write_Text(Mensajes[i].ascii);
                                                 for(j = 0; j < ASCIILEN; j++) {
                                                      UART1_Write(Mensajes[i].ascii[j]);
                                                      while(ack_wait());
                                                 }
//                                                 UART1_Write_Text(Mensajes[i].blink);
                                                 for(j = 0; j < BLINKFLAGSLEN; j++) {
                                                      UART1_Write(Mensajes[i].blink[j]);
                                                      while(ack_wait());
                                                 }
//                                                 UART1_Write(STOPDATA);                  // Stop Condition
                                                 
//                                                 Delay_ms(500);
                                            }
                                            break;
                                       case GETALLMSG:
                                            memset(Mensajes, 0, sizeof(Mensajes));
                                            memset(MsgPriori, 0, sizeof(MsgPriori));

                                            totalmsg = read_wait();
//                                            totalmsg = uart_rd;
                                            totalpriori = read_wait();
//                                            totalpriori = uart_rd;
                                            // de aqui en adelante se reciben por paquetes
//                                            UART1_Read_Text(buffin, "PC", 255);
//                                            strncpy(MsgPriori, buffin, MSGINRAMMAX); // guarda informacion
                                            for(i = 0; i < totalpriori; i++) {
                                                  MsgPriori[i] = read_wait();
                                            }
                                            
                                            for(i = 0; i < totalmsg; i++) {
                                                  UART1_Read_Text(buffin, "PC", 255);
                                                  Mensajes[i].addrh = buffin[0];
                                                  Mensajes[i].addrl = buffin[1];
                                                  Mensajes[i].flags = buffin[2];
                                                  Mensajes[i].efecin = buffin[3];
                                                  Mensajes[i].efecout = buffin[4];
                                                  Mensajes[i].scroll_setpoint = buffin[5];
                                                  Mensajes[i].blink_setpoint = buffin[6];
                                                  Mensajes[i].delay_setpoint = buffin[7];
                                                  Mensajes[i].stringlen = buffin[8];
                                                  for(j = 0; j < ASCIILEN; j++) {
                                                        Mensajes[i].ascii[j] = buffin[9+j];
                                                  }
                                                  for(j = 0; j < BLINKFLAGSLEN; j++) {
                                                        Mensajes[i].blink[j] = buffin[9+ASCIILEN+j];
                                                  }
                                            }
                                            break;
                                       case FINISH:
                                            prioindex = 0;
                                            msgindex = MsgPriori[prioindex];
                                            fill_buffer();    // convierte de ascii a mapa de bit el mensaje a mostrar
                                            SHOW = TRUE;
                                            Link = FALSE;
                                            break;
                                
                                }
                             }
                          }
                          break;
                 }
                 
//                 T0CON.TMR0ON = 1;
              }
              
              // verifica si termino acción de entrar y salir
              if(FINISH_EFECT == TRUE) {
                   if(totalpriori == 1) {// si es el unico mensaje que se esta mostrando, sigue mostrando
                        FINISH_EFECT = FALSE;
                        init_attribute();    // Comenzamos de nuevo
                        continue;
                   }
                   // si hay mas de uno, pasa a mostrar el siguiente
                   SHOW = FALSE;
                   prioindex++;
                   if(prioindex == totalpriori) {
                        prioindex = 0;
                   }
                   msgindex = MsgPriori[prioindex];
                   fill_buffer();    // convierte de ascii a mapa de bit el mensaje a mostrar
                   FINISH_EFECT = FALSE;
                   SHOW = TRUE;
              }
     }
}

//*****************************************************************************
// Espera que llegue el dato por RS232 y lo regresa a la función que llama
unsigned short int read_wait(void) {
     uart_rd = 0;
     
     while(!uart_rd) {
          if (UART1_Data_Ready()) {      // If data is received,
             uart_rd = UART1_Read();      //   read the received data,
          }
     }
     return uart_rd;
}

//*****************************************************************************
// Espera que la PC responda con un byte
unsigned short int ack_wait(void) {
     if (UART1_Data_Ready()) {      // If data is received,
        if(UART1_Read() == ACK)
             return FALSE;
     }
     
     return TRUE;
}

//*****************************************************************************
// Algoritmo para enviar los bits desde el buffer de pantalla hacia los 595
// el mismo proceso se repite para cada una de las filas
void screen_frame(void)
{
    output_port = 0x7E;  // 01111110
    PORTD = 0xFF;
    
    // Aqui tenemos dos opciones, la idea es esta
/*     Opcion 1: La variable OffsetL no ha pasado la longitud SCREENWIDTH

              | - - - - - - - - - - - - - - - - - - - - - - - Pantalla Visible - - - - - - -- - - - - - - - - - - - - - - - - -|

              |----------------------------------------------------------------------------------------------------------------|
              |            1010010110010101011001010110101011110100000010000000010000000001000000101011000010000101010000001000|0100000000000010010000001001000010101
              |            0000100000000100101001010101010000000100000100100000101010100100000001000000101001001000000100100000|0001001010101010101010101010010000011
              |            1000000101111010111010101010100010111101010101010101010101010101010101010101010101010101010101010101|0101010101010101010101010101010101011
              |            0101010101010101010101010101000000000000000000000000000000000000000000000000000000000000000000000000|0000000000000000000000000000000000000
              |            0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|0000000000000000000000000000000000000
              |            1011101010101010101111111111111111111111111101010101010000000000000101010101010101010101010101010101|0101010101010101010101010101010101010
              |            1010001001111000100100010010111010000100001001000100101010000000000000000000000000000000000000000000|0000000000000000000000000000000000000
              |----------------------------------------------------------------------------------------------------------------

              | - - - - - - - - - - - - - - - - - - - - -   SCREENWIDTH = 40 columnas - - - - - - - - - - - - - - - - - - - - -|
              
        Opcion 2: La variable OffsetL pasó la longitud SCREENWIDTH
        
              |----------------------------------------------------------------------------------------------------------------|
  101001011001|0101011001010110101011110100000010000000010000000001000000101011000010000101010000001000010000000000001001000000|1001000010101
  000010000000|0100101001010101010000000100000100100000101010100100000001000000101001001000000100100000000100101010101010101010|1010010000011
  100000010111|1010111010101010100010111101010101010101010101010101010101010101010101010101010101010101010101010101010101010101|0101010101011
  010101010101|0101010101010101000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|0000000000000
  000000000000|0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000|0000000000000
  101110101010|1010101111111111111111111111111101010101010000000000000101010101010101010101010101010101010101010101010101010101|0101010101010
  101000100111|1000100100010010111010000100001001000100101010000000000000000000000000000000000000000000000000000000000000000000|0000000000000
              |----------------------------------------------------------------------------------------------------------------

              | - - - - - - - - - - - - - - - - - - - - -   SCREENWIDTH = 40 columnas - - - - - - - - - - - - - - - - - - - - -|
              
        En este caso hago los calculos para determinar el byte y el bit exacto para mandar a los 595 solo sol 40 bits visibles
        Noten que el blinking de cada letra esta definido por un bit, asi por cada 8 letras ascii es un byte en la variable blink, la
        cual hay que calcular tambien para que no haya defase
              
*/
       
    if(OffsetL > SCREENWIDTH) {
         offset_cut = SCREENWIDTH;
         temp_cut = OffsetL - SCREENWIDTH;
         byte_row_cut = temp_cut/8;
         mask_cut = temp_cut%8;
         b_temp = mask_cut;
         b = b_temp;
         mask_cut =  0x80 >> mask_cut;
         
         //Calculo del blink
         byte_blink_cut = byte_row_cut/8;
         mask_blink_cut = byte_row_cut%8;
         mask_blink_cut = 0x80 >> mask_blink_cut;
         
         byte_row = byte_row_cut;
         Mask = mask_cut;
         byte_blink = byte_blink_cut;
         Mask_Blink = mask_blink_cut;
    }
    else {
         offset_cut = OffsetL;
         byte_row = 0;
         Mask = 0x80;
         byte_blink = 0;
         Mask_Blink = 0x80;
         mask_cut = 0x80;
         mask_blink_cut = 0x80;
         byte_row_cut = 0;
         byte_blink_cut = 0;
         b = 0;
         b_temp = 0;
    }
    
    offset_rcut = offset_cut - OffsetR;
    
    if(offset_rcut < 0) {
         offset_rcut = OffsetR;
    }
    
    PORTE.PINCLEAR = 0;
    PORTE.PINCLEAR = 1;

    for(l = 0; l < ROW; l++) {
          temp = Screen[l][byte_row];


          for(t = 0; (t < offset_cut); t++) {
                Flag = (temp & Mask);

                PORTE.PINCLOCK = 0;
                PORTE.PINDATA = 0;



                if(Flag && (t < offset_rcut)) {
                        if(Mensajes[msgindex].blink[byte_blink] & Mask_Blink) {
                             if(Blink_Status == TRUE)
                                  PORTE.PINDATA = 1;
                        }
                        else {
                             PORTE.PINDATA = 1;
                        }
                }

                PORTE.PINCLOCK = 1;
                PORTE.PINCLOCK = 0;
                Mask = Mask >> 1;
                b++;

                if(b == 8) {  // 8 bits??
                     byte_row++;
                     temp = Screen[l][byte_row];
                     Mask = 0x80;
                     b = 0;

                     if(Mask_Blink & 0x01) {
                          Mask_Blink = 0x80;
                          byte_blink++;     // cada 64 bits aumenta un byte
                     }
                     else
                         Mask_Blink = Mask_Blink >> 1;
                }
          }
          PORTD = 0xFF;
          Delay_us(10);
          PORTA.PINSTCLOCK = 1;
          PORTA.PINSTCLOCK = 0;
          PORTD = output_port;
          Delay_us(450);
          PORTE.PINCLEAR = 0;
          PORTE.PINCLEAR = 1;
          asm {
              bsf       STATUS, C
              rlcf     _output_port, f
          }
          b = b_temp;
          byte_row = byte_row_cut;
          Mask = mask_cut;
          Mask_Blink = mask_blink_cut;
          byte_blink = byte_blink_cut;
    }
    delay_us(450);
//    clear_latch();
    cls();
    
    PORTD = 0xFF;
}
//*****************************************************************************

void clear_latch() {
     sint i;
     
     PORTE.PINDATA = 0;
     
     for(i = 0; i < SCREENWIDTH; i++) {
           PORTE.PINCLOCK = 0;
           PORTE.PINCLOCK = 1;
     }
     
     PORTA.PINSTCLOCK = 1;    // Carga Latch con ceros
     PORTA.PINSTCLOCK = 0;
}

//*****************************************************************************
void cls(void) {
     PORTE.PINCLEAR = 0;   // Limpia registro
     PORTE.PINCLEAR = 1;
     PORTA.PINSTCLOCK = 1;    // Carga Latch con ceros
     PORTA.PINSTCLOCK = 0;
}


//*****************************************************************************
void fill_buffer(void)
{
       sint c, f;
       volatile char index;
       
       param_reset();

       StringWidth = (Mensajes[msgindex].stringlen*8);         // en bits
       CenterScreen = SCREENWIDTH;
       
       if(StringWidth < SCREENWIDTH) {
            CenterScreen = ((SCREENWIDTH-StringWidth)/2) + StringWidth;
       }
       
       init_attribute();             // inicializa los atributos del mensaje

       memset(Screen, 0, sizeof(Screen));

       /*if(VUD_SCROLL == FALSE) {
            for(c = 0; c < Mensajes[msgindex].stringlen; c++) {  // contador de caracteres
                 index = Mensajes[msgindex].ascii[c];
                 for(b = 0; b < (CHARWIDTH/shift_step); b++) { // contador de Ancho de caracter
                      for(f = 0; f < ROW; f++) {    // contador de filas
//                         index = Mensajes[msgindex].ascii[c];
                           temp = CharData[index-32][f];

                           Screen[f][c] = (Screen[f][c] << shift_step)|(temp >> ((8-shift_step)-(b*shift_step)));
                      }
                 }
            }
       }*/
       
       if((VUD_SCROLL == FALSE) && (VDU_SCROLL == FALSE)) {
           for(c = 0; c < Mensajes[msgindex].stringlen; c++) {  // contador de caracteres
                index = Mensajes[msgindex].ascii[c]-32; // letra ascii como indice para la tabla
                for(f = 0; f < ROW; f++) {    // contador de filas
                     Screen[f][c] = CharData[index][f];
                }
            }
       }
}

//*****************************************************************************
void fill_buffer_for_vertical_scroll_anim(sint rows, sint solo_visible, sint direccion)
{
     sint c, f, ch;
     volatile char index;

     ch = Mensajes[msgindex].stringlen;
     
     if(solo_visible == TRUE) {
         if(Mensajes[msgindex].stringlen > 5) {
              ch = SCREENWIDTH/CHARWIDTH; // OJO (c < 5) si SCREENWIDTH es igual a 40 (40/8)=5 caracteres por pantalla
         }
     }

     memset(Screen, 0, sizeof(Screen));

     if(direccion == VUD) {
          for(c = 0; c < ch; c++) {  // contador de caracteres
               index = Mensajes[msgindex].ascii[c]-32;
               for(f = 0; f < rows; f++) {    // contador de filas
                    Screen[f][c] = CharData[index][(CHARHEIGTH-rows)+f];  // informacion de la ultima fila pasa a la primera
               }
          }
     }
     else {
          for(c = 0; c < ch; c++) {  // contador de caracteres
               index = Mensajes[msgindex].ascii[c]-32;
               for(f = 0; f < 7-rows; f++) {    // contador de filas
                    Screen[rows+f][c] = CharData[index][f];
               }
          }
     }
}

//*****************************************************************************
void init_attribute(void)
{
     if(Mensajes[msgindex].flags & ANIMIN_MASK ) {
          sint intro;

          intro = Mensajes[msgindex].efecin;
          
          EFECTO_IN = TRUE;
          EFECTO_OUT = FALSE;

          if(intro == HRLSCROLL_MASK) {
               OffsetR = 0;
               OffsetL = 0;
               HRL_SCROLL = TRUE;
          }
          else if(intro == HLRSCROLL_MASK) {
               OffsetR = 0;
               OffsetL = StringWidth+SCREENWIDTH;
               HLR_SCROLL = TRUE;
          }
          else if(intro == VUDSCROLL_MASK) {
               if(StringWidth > SCREENWIDTH) {
                    OffsetL = SCREENWIDTH;
               }
               else {
                    OffsetL = ((SCREENWIDTH-StringWidth)/2) + StringWidth; // centro de la pantalla
               }
               OffsetR = 0;
               VUD_SCROLL = TRUE;
               ScrollVeticalindex = 0;
          }
          else if(intro == VDUSCROLL_MASK) {
               if(StringWidth > SCREENWIDTH) {
                    OffsetL = SCREENWIDTH;
               }
               else {
                    OffsetL = ((SCREENWIDTH-StringWidth)/2) + StringWidth; // centro de la pantalla
               }
               OffsetR = 0;
               VDU_SCROLL = TRUE;
               ScrollVeticalindex = 6;
          }
          else if(intro == CHLR_MASK) {
               OffsetR = SCREENWIDTH;
               OffsetL = SCREENWIDTH;
               CHLR = TRUE;
               timer = 0;
          }
          else if(intro == CENTER_MASK) {
               OffsetR = 0;
               OffsetL = ((SCREENWIDTH-StringWidth)/2) + StringWidth;
               TIMER_INOUT = TRUE;                     // como no hay animacion de entrada, muestra, temporiza y sale
               timer = 0;
          }
          else if(intro == NONE_MASK) {
               OffsetR = 0;
               OffsetL = SCREENWIDTH;
               TIMER_INOUT = TRUE;                     // como no hay animacion de entrada, muestra, temporiza y sale
               timer = 0;
          }
     }
     if(Mensajes[msgindex].flags & SCROLLON_MASK) {
          SCROLL = TRUE;
     }
     if(Mensajes[msgindex].flags & BLINKON_MASK) {
          BLINK = TRUE;
     }
     
     timer_shift_setpoint = Mensajes[msgindex].scroll_setpoint;
     timer_blink_setpoint = Mensajes[msgindex].blink_setpoint;
     time_delay_setpoint = Mensajes[msgindex].delay_setpoint;
}

//*****************************************************************************
void clear_regs(void) {
     volatile sint c;

     asm {
              movlw    SCREENWIDTH
              movwf    c
     sigbit:
              BCF      PORTE, 0
              BCF      PORTE, 1
              BSF      PORTE, 0
              DECFSZ   c, f
              GOTO     sigbit
              return
     }
}


 //****************************************************************************
/* la EEPROM pagina de la siguiente forma:
00:00        NTM        NTMM        M1        M2        M3        M4        M5        M6
00:08        M7         ..          ..        ..        ..        ..        M18       M19
00:10        M20        M21        M22        M23       M24      M25       HBSM      LBSM
00:18        FLAGS      EFE        EFS        TSSP     TBSP      TDSP      LONG       MSG0
00:20        MSG0        ..        ..         ..       ..        ..        ..        ..
00:28        BM0        BM1        BM2        BM3      BM4       BM5       BM6       BM7
00:30        BM8        BM9        BM10      BM11      HBSM     LBSM       FLAGS     EFE
00:38        EFS        TSSP       TBSP      TDSP      LONG     MSG1        …        …
00:40        BM0        BM1        BM2        ..        ..       ..        ..        ..
00:48
00:50
00:58


Leyenda:
NTM:        Número Total de Mensajes
NTMM:        Número Total de Mensaje a Mostrar
M1-M25        Indica el Index u orden de los Mensajes a Mostrar
HBSM:        High Byte de la Direcci;on del Siguiente Mensaje
LBSM:        Low Byte de la Dirección del Siguiente Mensaje
FLAGS:        Atributos
EFE:        Efecto de Entrada
EFS:        Efecto de Salida
TSSP:        Time Scroll SetPoint
TBSP:        Time Blink SetPoint
TDSP:        Time Delay SetPoint
LONG:        Longitud del Mensaje
MSGX:        Mensaje en ASCII (Un carácter por direccion)
BM0-BM11        Blink de Mensaje  (Un bit por carácter ASCII)

*/

void load_msg_from_i2c(void) {
     volatile sint ii, jj, addrh, addrl, addrhc, addrlc;

     INTCON.GIE = 0;
     
     memset(Mensajes, 0, sizeof(Mensajes));
     memset(MsgPriori, 0, MSGINRAMMAX);
     
     Init_Rd(0, 0);
     totalmsg = I2C1_Rd(1);
     totalpriori = I2C1_Rd(0);
     Fin_Rd();
     
     if(!totalmsg) {
               UART1_Write_Text("No Found Text");
               return;
     }
     
     addrh = 0;
     addrl = 2;
     
     // Lee tabla de prioridad...
     Init_Rd(addrh, addrl);
     for(ii = 0; ii < totalpriori; ii++) {
            MsgPriori[ii] = I2C1_Rd(1);
     }
     Fin_Rd();
     
     addrh = 0;
     addrl = MSGINRAMMAX+2;

     Init_Rd(addrh, addrl);
     
     UART1_Write(totalmsg);
     
     for(ii = 0; (ii < totalmsg) || (ii <  MSGINRAMMAX); ii++) {
         addrhc = addrh;          // guarda la direccion del mensaje actual
         addrlc = addrl;

         addrh = I2C1_Rd(1);      // direccion del siguiente mensaje
         addrl = I2C1_Rd(1);
         
         Mensajes[ii].addrh = addrhc;
         Mensajes[ii].addrl = addrlc;
         Mensajes[ii].flags = I2C1_Rd(1);
         Mensajes[ii].efecin = I2C1_Rd(1);
         Mensajes[ii].efecout = I2C1_Rd(1);
         Mensajes[ii].scroll_setpoint = I2C1_Rd(1);
         Mensajes[ii].blink_setpoint = I2C1_Rd(1);
         Mensajes[ii].delay_setpoint = I2C1_Rd(1);
         Mensajes[ii].stringlen = I2C1_Rd(1);
         for(jj = 0; jj < (Mensajes[ii].stringlen+BLINKFLAGSLEN); jj++) {
                if(jj < Mensajes[ii].stringlen) {
                      Mensajes[ii].ascii[jj] = I2C1_Rd(1);
                }
                else {
                    Mensajes[ii].blink[BLINKFLAGSLEN-((Mensajes[ii].stringlen+BLINKFLAGSLEN)-jj)] = I2C1_Rd(1);
                }
         }
         Mensajes[ii].ascii[Mensajes[ii].stringlen] = 0;
     }

     Fin_Rd();
     INTCON.GIE = 1;
}

//*****************************************************************************
void Init_Rd(unsigned short adh, unsigned short adl) {
     I2C1_Start();           // issue I2C start signal
     I2C1_Wr(0xA0);          // send byte via I2C  (device address + W)
     I2C1_Wr(adh);          // send data address HIGH byte
     I2C1_Wr(adl);          // send data address LOW byte
     I2C1_Repeated_Start();  // issue I2C signal repeated start
     I2C1_Wr(0xA1);          // send byte (device address + R)
}

//*****************************************************************************
void Fin_Rd(void) {
     I2C1_Rd(0);
     I2C1_Stop();
}

//*****************************************************************************
void save_msg_to_i2c(void)
{


}

//*****************************************************************************
void Init_Wr(unsigned short adh, unsigned short adl)
{
     I2C1_Start();           // issue I2C start signal
     I2C1_Wr(0xA0);          // send byte via I2C  (device address + W)
     I2C1_Wr(adh);          // send data address HIGH byte
     I2C1_Wr(adl);          // send data address LOW byte
    /*I2C1_Repeated_Start();  // issue I2C signal repeated start
     I2C1_Wr(0xA1);          // send byte (device address + R)*/
}

//*****************************************************************************
void Fin_Wr(void)
{
     I2C1_Rd(0);
     I2C1_Stop();
}
//*****************************************************************************
void init_pic(void) {
     ADCON1 |= 0x0F;            // Configure AN pins as digital
     CMCON  |= 0x07;            // turn off comparators
     
//     Configuramos Timer0
     /*T0CON.PSA = 0;
     T0CON.T0CS = 0;
     T0CON.T08BIT = 1;
     T0CON.TMR0ON = 1;*/

    /* 001 -> 4  -> 0xC1
       010 -> 8  -> 0xC2
       011 -> 16 -> 0xC3
       100 -> 32 -> 0xC4
       101 -> 64 -> 0xC5
       110 -> 128
       111 -> 256*/
       
       // 0x89 = 16 bit timer, sin preescaler
     
     T0CON = 0xC6;              // 0xC5=11000101 (TMR0 a 8 bit y con preescalador 1:64); C6=11000110 TMR0 a 8 bit y con preescalador 1:128
     SSPSTAT.SMP = 0;
     SSPSTAT.CKE = 1;
     INTCON.TMR0IE = 1;
     INTCON.TMR0IF = 0;
     INTCON2.TMR0IP = 1;        // Asigna alta prioridad
     RCON.IPEN = 1;
     INTCON.GIE = 1;

     TMR0H = 160;
     TMR0L = 0;                //
     
     TRISA = 0;             // 11100000,
     TRISB = 0xE0;
     TRISC = 0xD8;
     TRISD = 0;
     TRISE = 0;
     
     PORTA = 0;
     PORTB = 0;
     PORTC = 0;
     PORTD = 0xFF;
     PORTE = 0;
     
     PORTE.PINCLEAR = 1;
     Link = FALSE;

     param_reset();
     
     timer_shift_setpoint = 1;
     timer_blink_setpoint = 5;
     time_delay_setpoint = 60;
     timer_ledblink = 0;
     msgindex = 0;
}

void param_reset(void)
{
     OffsetR = OffsetL = 0;
     timer_shift = 0;
     timer = 0;
     Blink_Status = TRUE;
     TIMER_INOUT = FALSE;
     FINISH_EFECT = FALSE;
     HLR_SCROLL = FALSE;
     HRL_SCROLL = FALSE;
     VUD_SCROLL = FALSE;
     CHLR = FALSE;
     CHRL = FALSE;
     BLINK = 0;
     SCROLL = 0;
     SHOW = 0;
}

// USART Functions
