// _EEPROM_RS232_I2C.c

#include <16f876a.h>                          // Definiciones del PIC 16F876A
#fuses XT,NOWDT,NOPROTECT,NOLVP,PUT,BROWNOUT  // Los Fuses de siempre
#use delay(clock=4000000)                     // Oscilador a 4 Mhz
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)// Definicion RS232 estandar
#use i2c(master,sda=PIN_C4, scl=PIN_C3)       // Definicion I2C estandar

#include <ctype.h>
#include <string.h>

// CONSTANTES /////////////////////////////////////////////////////////////////

int const lenbuff=32;                  // Longitud de buffer, Ajustar
                                       // a lo que desees (o te sea posible)

int const address_EEPROMR=0b10100001;  // Direccion I2C de EEPROM (para lectura)
int const address_EEPROMW=0b10100000;  // Direccion I2C de EEPROM (para escritura)
                                       // 1010 000 0
                                       // ---- --- -
                                       //   |   |  R/W
                                       //   |  Hard
                                       // Fijo
int const ddeeeprom=32;                // Delay Default EEPROM

// VARIABLES EN RAM ///////////////////////////////////////////////////////////

int  xbuff=0x00;                       // Índice: siguiente char en cbuff
char cbuff[lenbuff];                   // Buffer
char rcvchar=0x00;                     // último caracter recibido
int1 flagcommand=0;                    // Flag para indicar comando disponible

// Declaración de Funciones ///////////////////////////////////////////////////

void presmenu(void);                   // Presenta el menú
void inicbuff(void);                   // Borra buffer
int  addcbuff(char c);                 // añade caracter recibido al buffer
void echos(char c);                    // Eco selectivo sobre RS232
void comando(void);                    // Procesa comando
int  ascii2hex(char d);                // Convierte un caracter ascii a hex
void i2cw1(int i2cdev, int i2cdir, int i2cdat); // Rutina de escritura I2C completa
void i2cw2(int i2cdev, int i2cdat);             // Rutina de escritura I2C parcial
int  i2cr(int i2cdev);                          // Rutina de lectura I2C

// INTERRUPCIONES /////////////////////////////////////////////////////////////

#int_rda
void serial_isr() {                    // Interrupción recepción serie USART

   rcvchar=0x00;                       // Inicializo caracter recibido
   if(kbhit()){                        // Si hay algo pendiente de recibir ...
      rcvchar=getc();                  // lo descargo y ...
      addcbuff(rcvchar);               // lo añado al buffer y ...
      echos(rcvchar);                  // hago eco (si procede).
   }
}

// Desarrollo de Funciones ////////////////////////////////////////////////////

void presmenu(void){                   // Presenta el menú --------------------
   delay_ms(25);
   printf("\r\n");
   printf("** EEPROM I2C OS **\r\n\n");  // Presenta menú
   printf("** Control del buffer:\r\n");
   printf("[Enter]  Procesa comando\r\n");
   printf("[Escape] Borra todo el buffer\r\n");
   printf("[Delete] Borra último carácter del buffer\r\n");
   printf("\n");
   printf("** Comandos EEPROM:\r\n");
   printf("/? Presenta Menú\r\n");
   printf("/B Formatea (borra) eeprom iniciando <indice> a 0.\r\n");
   printf("/r Lee contenido completo de eeprom y vuelca a RS232.\r\n");
   printf("/w <dat> Escribe <dat> en eeprom a partir de <indice>.\r\n");
   printf("/i <dir> Coloca índice de eeprom a <0xdir> sin borrar contenido.\r\n");
   printf("\n");
   delay_ms(25);
}

int addcbuff(char c){                  // Añade a cbuff -----------------------

      switch(c){
         case 0x0D:                    // Enter -> Habilita Flag para procesar
            flagcommand=1;             // Comando en Main
            break;
         case 0x08:                    // Del   -> Borra último caracter del Buffer
            if(xbuff>0) cbuff[--xbuff]=0x00;
            break;
         case 0x01B:                   // Esc   -> Borra el Buffer completamente
            inicbuff();
            break;
         default:
            cbuff[xbuff++]=c;          // Añade caracter recibido al Buffer
            if(xbuff>lenbuff) xbuff=lenbuff;
      }
}

void echos(char c){                    // Echo selectivo ----------------------

   int i;

   switch(c){
      case 0x0D: printf("\r\n");       // Si he pulsado la tecla [Intro]
                 break;
      case 0x08: printf("\r%s \b",cbuff); // Si he pulsado la tecla [Retroceso]
                 break;
      case 0x1B: printf("\r");         // Si he pulsado la tecla [Escape]
                 for(i=0;i<lenbuff;i++){
                   printf(" ");        // Borra display (en la longitud del buffer)
                 }
                 printf("\r");
                 break;
      default:   putc(rcvchar);        // Echo de cualquier otro caracter
   }
}

void inicbuff(void){                   // Inicia a \0 cbuff -------------------

   int i;

   for(i=0;i<lenbuff;i++){             // Bucle que pone a 0 todos los
      cbuff[i]=0x00;                   // caracteres en el buffer
   }
   xbuff=0x00;                         // Inicializo el indice de siguiente
                                       // caracter
}

int  ascii2hex(char d){                // Convierte un caracter ascii a hex ---

   int r=0x00;

   if(isxdigit(d)){
      if(isdigit(d)){
         r=d-'0';
      }
      if(isalpha(d)){
         d=toupper(d);
         r=10+(d-'A');
      }

   }
   return(r);
}

void i2cw1(int i2cdev, int i2cdir, int i2cdat){ // Rutina de escritura I2C completa

   i2c_start();                  // Inicializo comunicación I2C
   i2c_write(i2cdev);            // Envio Dirección de dispositivo I2C + R/W
   i2c_write(i2cdir);            // Envio address eeprom donde escribir
   i2c_write(i2cdat);            // Envio byte a escribir
   i2c_stop();                   // Cierro comunicacion
   delay_ms(ddeeeprom);          // Espero a que escriba correctamente
}

void i2cw2(int i2cdev, int i2cdat){             // Rutina de escritura I2C parcial

   i2c_start();                  // Inicializo comunicación I2C
   i2c_write(i2cdev);            // Envio Dirección de dispositivo I2C + R/W
   i2c_write(i2cdat);            // Envio byte a escribir
   i2c_stop();                   // Cierro comunicacion
   delay_ms(ddeeeprom);          // Espero a que escriba correctamente

}

int  i2cr(int i2cdev){           // Rutina de lectura I2C

   int r=0x00;

   i2c_start();                     
   i2c_write(i2cdev);
   r=i2c_read();
   i2c_stop();
   delay_ms(ddeeeprom);
   return(r);
}


// Programa Principal /////////////////////////////////////////////////////////

void main() {

   inicbuff();                         // Borra buffer al inicio
   presmenu();                         // Presenta el menú

   enable_interrupts(int_rda);         // Habilita Interrupción RDA
   enable_interrupts(global);          // Habilita interrupciones

   delay_ms(25);

   do {

      if(flagcommand) comando();       // Si hay comando pendiente
                                       // de procesar ... lo procesa.

   } while (TRUE);

}

// Procesador de Comandos /////////////////////////////////////////////////////

void comando(void){

   int  i,j,u,h;
   int1 flagvalido=0;                  // Flag para detectar comandos invalidos
   char arg[lenbuff];                  // Argumento de comando (si lo tiene)

   disable_interrupts(int_rda);        // Dehabilito Interrupción RDA durante procesado
   flagcommand=0;                      // Desactivo flag de comando pendiente.

   for(i=0;i<lenbuff;i++){             // Limpia el argumento (por si lo hay)
      arg[i]=0x00;
   }

   // Comando /?

   if(cbuff[0]=='/'&&cbuff[1]=='?'){   // Comparo inicio del buffer con comando "/?"
      flagvalido=1;                    // Marco comando válido
      presmenu();                      // Presenta el menú
   }

   // Comando /B

   if(cbuff[0]=='/'&&cbuff[1]=='B'){   // Comparo inicio del buffer con comando "/B"
      flagvalido=1;                    // Marco comando válido

      printf("\r\nFormateando ");
      j=0;
      for(i=0;i<255;i++){

         i2cw1(address_EEPROMW,i,0x00);// Envio Dirección de dispositivo I2C + Escribir
                                       // Envio address eeprom donde escribir
                                       // Envio byte a escribir (0x00)
         if(++j>14){                   // Monitorizo en bloques de 15 bytes
            j=0;
            putc('.');
         }
      }
      printf("\r\nFormateado Ok.\r\n\r\n");
   }

   // Comando /r

   if(cbuff[0]=='/'&&cbuff[1]=='r'){   // Comparo inicio del buffer con comando "/R"
      flagvalido=1;                    // Marco comando válido

      i2cw2(address_EEPROMW,0x00);     // Inicializo dirección a partir de la que leer
                                       // que fijo desde el primer byte
      for(i=0;i<16;i++) printf("%X ",i); // Pongo cabecera de direcciones
      printf("\r\n");
      for(i=0;i<16;i++) printf("== ");
      printf("\r\n");
      j=0;
      i2c_start();                     // Realizo la lectura completa de la EEPROM
      i2c_write(address_EEPROMR);
      for(i=0;i<255;i++){
         u=i2c_read();
         printf("%X ",u);              // y vuelco en bloques de 15 bytes
         if(++j>15){
            j=0;
            printf("\r\n");
         }
      }
      i2c_stop();
      printf("\r\n\r\n");
   }

   // Comando /w dat

   if(cbuff[0]=='/'&&cbuff[1]=='w'){   // Comparo inicio del buffer con comando "/w"
      flagvalido=1;                    // Marco comando válido
      i=3;
      do{                              // Extraemos argumento del buffer
         arg[i-3]=cbuff[i];            // a partir del 4º byte y hasta \0.
      }while(cbuff[++i]!=0x00);

      // recupero posición inicial <indice> para escribir

      i2cw2(address_EEPROMW,0x00);     // Inicializo dirección a partir de la que leer
                                       // que fijo desde el primer byte
      h=i2cr(address_EEPROMR);         // leo el valor de índice

      ++h;
      printf("\r\nEscribir %s a partir de %X\r\n\r\n",arg,h);
      i2cw2(address_EEPROMW,h);        // Inicializo dirección a partir de la que leer
                                       // que fijo desde el <indice>
      // escribo argumento a partir de índice
      i=0;
      do{

         i2cw1(address_EEPROMW,h,arg[i]);// Envio Dirección de dispositivo I2C + Escribir
                                         // Envio address eeprom donde escribir -> h
                                         // Envio byte a escribir -> arg[i]
         ++h;
         ++i;
      }while(arg[i]!=0x00);
      // Actualizo indice
      --h;
      i2cw1(address_EEPROMW,0x00,h);// Envio Dirección de dispositivo I2C + Escribir
                                    // Envio address eeprom donde escribir -> 0x00
                                    // Envio byte a escribir -> h

      printf("Buffer Escrito.\r\n\r\n");
   }

   // Comando /i dir

   if(cbuff[0]=='/'&&cbuff[1]=='i'){   // Comparo inicio del buffer con comando "/i"
      flagvalido=1;                    // Marco comando válido
      i=3;
      do{                              // Extraemos argumento del buffer
         arg[i-3]=cbuff[i];            // a partir del 4º byte y hasta \0.
      }while(cbuff[++i]!=0x00);

      h=(16*ascii2hex(arg[0]))+(ascii2hex(arg[1])); // Convierto de Hex-Ascci-2-digitos
                                                    // al entero correspondiente
      i2cw1(address_EEPROMW,0x00,h);                // Escribo nuevo indice en 0x00
      printf("> i=%X hex (%u dec)\r\n",h,h);        // Monitorizo lo realizado.
   }


   // Retorno error o comando invalido
   if(!flagvalido) printf("¿%s?\r\n",cbuff);

   inicbuff();                         // Borro buffer.
   enable_interrupts(int_rda);         // Habilita de nuevo Interrupción RDA

}

