Ejemplo comunicación serie I2C entre un PIC y la memoria EEPROM 24LC256A
Uso de una memoria EEPROM externa 24LC256A
MICROCONTROLADORES
Biblioman
12/18/20097 min leer
En este ejemplo voy a mostrar lo fácil que es leer y grabar datos en una memoria EEPROM Externa desde un PIC utilizando el lenguaje C y el compilador CCS, concretamente la memoria a utilizar será la 24LC256A fabricada por Microchip.
Para empezar vamos a describir algunas características del nuevo componente que vamos a utilizar y cuyo patillaje tenéis en la figura de abajo.
Esta es una memoria que tiene un tamaño de 32 Kbytes, su estructura está organizada en palabras de 1 byte (8 bits) de longitud, por lo tanto dispondremos en total de 32x8=256 kbits para almacenar información.
La característica principal de esta memoria es que implementa el interfaz I2C para su comunicación serie con otros dispositivos electrónicos.
El protocolo I2C fue desarrollado por la empresa Philips, ahora llamada NXP, hace ya bastante tiempo impulsado por la necesidad en el mercado de establecer una interfaz que comunicara entre si varios dispositivos electrónicos y que ahorrara el máximo numero de patillas en los componentes, hay mucha información en la red sobre este protocolo por lo que solo voy ha citar a modo de resumen las características que considero mas relevantes acerca de este protocolo. Al final del artículo pondré los enlaces a varias fuentes para el que quiera profundizar en este protocolo de comunicación.
Características básicas del protocolo I2C
El bus de comunicación utiliza dos cables. Uno para transmitir los datos (SDA) y otro para la señal de reloj (SCL). Ambas líneas se tienen que conectar a la alimentación (Vcc) a través de resistencias Pull-UP (en el esquema del ejemplo se muestra el conexionado). Además la masa de los componentes que se interconexionan entre si debe de ser común.
El protocolo de comunicación es del tipo Maestro-Esclavo, aunque el protocolo permite que haya varios maestros, lo normal es que solo haya uno, el dispositivo que hace de maestro es el que gobierna la comunicación y es el que controla la señal de reloj (SCL). Los dispositivos que hacen de esclavos responden a las peticiones del maestro.
Cada dispositivo conectado al bus se identifica por una única dirección, el protocolo admite utilizar 7 ó 10 bits para el direccionamiento, aunque lo normal es que sean siete.
Los cuatro primeros bits están asignados al fabricante del dispositivo y los tres últimos son configurables por hardware. En el caso de la memoria que estamos viendo el código del fabricante es: 1010, como tenemos tres bits configurables podremos conectar entre si hasta ocho memorias 24LC256 juntas.
La trama de un mensaje simple donde el dispositivo que hace de maestro envía datos para ser escritos en la EEPROM sería el siguiente:
Se envía el bit de inicio (S)
Se envía la dirección del esclavo, en este caso la memoria EEPROM
Se envía un bit más junto con la dirección del esclavo (R/W) para indicar que lo que se quiere es leer o escribir. Si este bit es cero el proceso será de escritura.
Después el maestro manda un pulso de la señal de reloj, durante el cual el esclavo debe contestar con un ACK (señal de reconocimiento), si el maestro no recibe esta señal interrumpe la comunicación.
Después se envían los bytes de dirección necesarios para identificar el registro de la EEPROM donde queremos realizar el proceso de escritura.
Mandamos el dato que se almacenará en la EEPROM
Y por último se manda el bit de parada para finalizar la comunicación.
¿Como se gestiona todo esto en C y con el compilador CCS?
CCS dispone de librerías específicas para gestionar la comunicación I2C de determinados componentes, entre ellos algunas memorias EEPROM. Basta con incluir la librería correspondiente por medio de la directiva #include y utilizar sin más las funciones de la librería. Pero si el componente que queremos utilizar no dispone del driver pertinente en CCS, tenemos otra opción, que es desarrollar nuestro propio driver, para ello disponemos como ayuda de una directiva homologa a #use rs232 (options), se trata de #use i2c (options).
Al incluir esta directiva el compilador nos permite utilizar las siguientes funciones para controlar la comunicación serie I2C entre varios dispositivos:
i2c_isr_state()
i2c_poll()
i2c_read()
i2c_slaveaddr()
i2c_start()
i2c_stop()
i2c_write()
Además los pics que soportan este tipo de comunicación disponen de modulos y registros específicos para facilitar la comunicación I2C por hardware, lo mismo que teníamos con la comunicación serie RS232.
Como veis esto da pie a muchos ejemplos, ya que hay bastantes dispositivos (Relojes en tiempo real, convertidores A/D, expansión de puertos de E/S, LCDs, etc) que implementan esta interfaz, también tenemos la posibilidad de comunicar varios PICs entre si, uno haciendo de maestro y los otros de esclavos.
Pero vamos poco a poco y para empezar vamos hacer el ejemplo del contador "su turno" que ya teníamos hecho, pero en vez de guardar los datos en la memoria EEPROM interna del PIC lo vamos hacer en la memoria externa 24LC256.
El esquema modificado en Proteus será el siguiente:
Como veis en la imagen de arriba el circuito es el mismo que habíamos hecho para el ejemplo del uso de la memoria EEPROM interna del PIC incluyendo la memoria 24LC256. Las patillas A0, A1 y A2 de la memoria están a masa, por lo que la dirección física del componente en la red será: 1010000. Los pines SDA y SCL son los que se conectan al bus IC2, a través de las resistencias pullup.
Puede que alguien se pregunte que valor hay que poner a estas resistencias (pullup) en un circuito real, pues la verdad es que no existe un valor ideal para todos los casos, dependerá de factores como la velocidad a la que hagamos trabajar el bus (100-400 kbps) y de la carga capacitiva que tenga el circuito (max. 400 pF), un valor que se aconseja como estándar es de 4k7. Pero para que el circuito simule bien en Proteus se deben poner las genéricas pullup.
El pin 7 de la memoria (WP) es para proteger ó no los datos guardados en la memoria, si está a masa los datos no están protegidos y se puede escribir o sobreescribir los datos ya guardados. Si está a uno (Vcc), no es posible escribir en la EEPROM.
Otro componente de Proteus incluido en el circuito es el I2C Debugger con el cual podemos ver la trama de datos en tiempo de ejecución. En la figura de abajo se detallan los bits de la trama en un proceso de escritura en la EEPROM.
El código fuente del Microcontrolador es el siguiente:
/*-----------------------------------------------------------------------*
|Ejemplo comunicación serie I2C entre un PIC y la memoria EEPROM 24LC256A |
|autor: biblioman |
*------------------------------------------------------------------------*/
#include <16F877.h>
//Configuración de los fusibles.
#FUSES NOWDT, XT, NOPUT, NOPROTECT, NOBROWNOUT, NOLVP, NOCPD, NOWRT, NODEBUG
#use delay(clock=4000000) //Frecuencia de reloj 4MHz
#byte puerto_D = 0x08 // Identificador para el puerto C.
#include "24256.c" //Incluimos librería 24256
#int_EXT
void EXT_isr( void )
{
if ((read_ext_eeprom(0)==0x99)||(read_ext_eeprom(0)==0xFF))
{
write_ext_eeprom(0,0);
puerto_D=read_ext_eeprom(0);
}
else if ((read_ext_eeprom(0) & 0x0F)<0x09)
{
write_ext_eeprom(0,(read_ext_eeprom(0))+1);
puerto_D=read_ext_eeprom(0);
}
else if ((read_ext_eeprom(0) & 0x0F)>=0x09)
{
write_ext_eeprom(0,(read_ext_eeprom(0))+7);
puerto_D=read_ext_eeprom(0);
}
}
void main()
{
set_tris_b(0xFF); //Puerto B como entrada
set_tris_d(0x00);//Puerto D como salida
enable_interrupts(GLOBAL); // Se habilita la interrupción global
enable_interrupts(INT_EXT); // Se habilita la interrupción externa
puerto_D =0xFF; //inicializo puerto D
init_ext_eeprom();//inicializamos memoria EEPROM externa
//write_ext_eeprom(0,0xFF);//Resetear registro 00 EEPROM
while(true)
{
//Resto del programa
}
}
Comentario del programa
Como he dicho anteriormente en este primer ejemplo del uso del protocolo serie I2C vamos a utilizar el driver que nos facilita CCS. Para ello simplemente lo incluimos en nuestro programa por medio de la directiva #include:
#include "24256.c" //Incluimos librería 24256
Nota: por defecto la librería define los pines RB1 y RB0 como SDA y SCL, como en nuestro ejemplo RB0 ya está ocupado. Hay que modificar esos valores en la librería y sustituirlos por RC4 y RC3, de la siguiente manera.
Donde pone:
#define EEPROM_SDA PIN_B1
#define EEPROM_SCL PIN_B0
poner:
#define EEPROM_SDA PIN_C4
#define EEPROM_SCL PIN_C3
Las funciones que incluye esta librería son:
init_ext_eeprom(); //llamada a la función de inicialización. Debe de ir antes que las dos de abajo
write_ext_eeprom(a, d); //Escribe el byte d en la dirección a
d = read_ext_eeprom(a); // lee el valor que hay en la dirección a y se lo asigna a la variable d
Pues bien la única modificación que hay que hacerle al código, con respecto al que habíamos hecho para grabar los datos en la EEPROM interna del PIC es sustituir estas funciones, según se muestra en el código fuente, fácil no!!.
Proteus nos permite también ver el estado de los registros de está memoria en el proceso de simulación.