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
DatasheetVamos 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
aquiY eso esto, amigos.