Autor Tema: Ejemplito 16F876A: Desde el PC a una EEPROM I2C y viseversa a través de mi PIC  (Leído 5665 veces)

0 Usuarios y 1 Visitante están viendo este tema.

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5416
    • Picmania by Redraven
Lo prometido es deuda, y aquí está. En nuestro anterior ejemplito Recibiendo del RS232 sobre un Buffer y procesandolo posteriormente. hablabamos de una aplicación para estos comandos. La idea que me asaltó fué la de hacer un menú estilo Telnet que me posibilitase comunicarme con el PIC desde el PC vía RS232 y al que pudiese dar ordenes complejas.

Este ejemplito va a mostar como utilizar este método de comunicación para manejar una EEPROM externa al PIC y conectada a él vía I2C. Con nuestros comandos vamos a poder enviar datos (textos) al PIC para que los guarde en la EEPROM o reclamarle el contenido de la misma y que nos la presente en el PC.

La EEPROM que vamos a usar es la PCF8582C de Phillips que tiene 256 bytes y es conectable vía I2C. Aquí podeis ver su Datasheet

Vamos a tener dos tipos de comandos: los de teclado con los que editamos el buffer que va manejando el PIC para configurarle los distintos comandos y los de alto nivel que son los encargados de manejar la EEPROM:

El programa va guardando lo recibido vía RS232 en un buffer. Con la tecla [Intro] mandamos procesar su contenido. Con [Retroceso] y [Escape] podemos borrar el último caracter del buffer o borrarlos todos respectivamente.

Con /r leemos el contenido completo de la EEPROM, con /i dir escribimos un indice en la posición de memoria 00h de la EEPROM a partir de cual podemos escribir los datos enviados con /w data, que a su vez escribe el contenido data del buffer a partir de la posición que este escrita en 00h.

Con /B podemos formatear, borrar, todo el contenido de la EEPROM incluso la posición indice 00h poniendo todas sus posiciones a 0.

El programa queda tal como sigue:

Codigo:

// _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("
" );
   printf("** EEPROM I2C OS **

" );  // Presenta menú
   printf("** Control del buffer:
" );
   printf("[Enter]  Procesa comando
" );
   printf("[Escape] Borra todo el buffer
" );
   printf("[Delete] Borra último carácter del buffer
" );
   printf("
" );
   printf("** Comandos EEPROM:
" );
   printf("/? Presenta Menú
" );
   printf("/B Formatea (borra) eeprom iniciando <indice> a 0.
" );
   printf("/r Lee contenido completo de eeprom y vuelca a RS232.
" );
   printf("/w <dat> Escribe <dat> en eeprom a partir de <indice>.
" );
   printf("/i <dir> Coloca índice de eeprom a <0xdir> sin borrar contenido.
" );
   printf("
" );
   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("
" );       // Si he pulsado la tecla [Intro]
                 break;
      case 0x08: printf("%s ",cbuff); // Si he pulsado la tecla [Retroceso]
                 break;
      case 0x1B: printf("" );         // Si he pulsado la tecla [Escape]
                 for(i=0;i<lenbuff;i++){
                   printf(" " );        // Borra display (en la longitud del buffer)
                 }
                 printf("" );
                 break;
      default:   putc(rcvchar);        // Echo de cualquier otro caracter
   }
}

void inicbuff(void){                   // Inicia a 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("
Formateando " );
      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("
Formateado Ok.

" );
   }

   // 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("
" );
      for(i=0;i<16;i++) printf("== " );
      printf("
" );
      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("
" );
         }
      }
      i2c_stop();
      printf("

" );
   }

   // 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 .
      }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("
Escribir %s a partir de %X

",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.

" );
   }

   // 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 .
      }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)
",h,h);        // Monitorizo lo realizado.
   }


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

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

}




Y ahora como siempre os pego un par de ejemplos de comunicación entre mi PC y el PIC:

Es esta resetamos el PIC, formateamos (borramos) la EEPROM, escribimos sobre ella y volcamos el contenido.



Y en esta establecemos el indice a 30H y volvemos a escribir y volcar el contenido.




Si deseais el fuente y el hex correspondiente lo podeis descargar de aqui

Y eso esto, amigos.

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado Nocturno

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 17506
    • MicroPIC
Me ha parecido impecable tu ejemplito, paisano.

Y además, veo que usas un sistema para atender la interrupción que bien podría servir a tantos usuarios que preguntan cómo hacer para que unas interrupciones no se molesten con otras.

Eso de marcar un flagcommand para luego atenderlo en el main es una solución muy elegante.
Un saludo desde Sevilla, España.
Visita MicroPIC                                                                                        ɔ!doɹɔ!ɯ ɐʇ!s!ʌ

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5416
    • Picmania by Redraven
Gracias amigo Nocturno por tu indulgencia para con mis trabajillos.

Aplico el principio aquel de : En una interrupción haz exactamente lo imprescindible para atenderla y pasa de ella. Que despues se lia todo. Esto lo aprendí con la primera RTCC que escribi y que tardaba más en ejecutarse de lo que tardaba la siguiente en llegar. El Stack reventó y salían los bytes por la Vss ... Muchas risas

Desde entonces ... lo justo y nada más. En este ejemplo fíjate tanbien en otra cosa: Para evitar que se solapen comandos ejecutandose deshabilito la interrupción de recepción de la RS232 mientras estoy procesando el ultimo comando recibido en comando() Cuando termino vuelvo a habilitar RDA y podemos volver a empezar. (Truco de viejo perro programador que no tiene mucha idea de PIC pero de programas que hacen muchas cosas si que entiendo Divertido )


¡Ah! Nota importante para lo que usen esta EEPROM en concreto: El retardo en ms tras cada escritura ddeeprom=32 es CRITICO. Si lo poneis mas corto es posible que se le envie otra orden de escritura sin haber terminado aún de escribir la anterior. He ido probando retardos y he colocado éste que es un 20% superior al que me funcionaba bien.



Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado mario_pastor

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 101
Saludos RedPic, el programa que usas SERIAL INPUT/OUTPUT MONITOR lo diseñaste o lo bajaste de algún lado? Si la respuesta es la segunda, serías tan amable de dejarme el link... gracias por tu tiempo Giño
Saludos,
Mario.

Lima - Perú

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5416
    • Picmania by Redraven
Amigo Mario:

Lo hice yo mismo empezando desde cero. ¿No le he puesto un enlace para descargarlo? ... pues me pongo manos a la obra y en unos minutos te paso la referencia ...

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5416
    • Picmania by Redraven
Que sí, hombre, que sí lo había puesto

La última línea del post dice exactamente:

Si deseais el fuente y el hex correspondiente lo podeis descargar de aqui

¡Que no me leéis bien los mensajes! Ardiendo

Sonrisa GiganteSonrisa GiganteSonrisa GiganteSonrisa GiganteSonrisa Gigante


Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado mario_pastor

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 101
jajaja... pues creo que no me exprese bien Enfurruñado  me refiero que usas un software para monitorear el puerto serie, como se muestra en la imagen... Payaso
Saludos,
Mario.

Lima - Perú

Desconectado RedPic

  • Administrador
  • DsPIC33
  • *******
  • Mensajes: 5416
    • Picmania by Redraven
¡Efectivamente!

El monitor RS232 que trae el CCS PICC (busca en Tools/Serial Port Monitor) o directamente en el directorio de instalación el siow.exe

Ea, siempre toca, si no es un pito, es una pelota.

Contra la estupidez los propios dioses luchan en vano. Schiller
Mi Güeb : Picmania

Desconectado mario_pastor

  • Colaborador
  • PIC16
  • *****
  • Mensajes: 101
muy agradecido....    Rebotado
Saludos,
Mario.

Lima - Perú