Como utilizar una memoria EEPROM desde Arduino.
El Arduino es una pequeña maravilla, en esto estarán de acuerdo con migo los que ya la conocen. Yo tuve que hacer un proyecto para un amigo, con el Arduino. Me comentó sobre la necesidad de usar algún tipo de almacenaje externo, para guardar los últimos datos, en caso de perdida de la alimentación.
En la actualidad, hay muchas opciones de almacenaje disponibles y pensé en el uso de una 24LC256 (a todas luces, con una capacidad demasiado grande para lo que iba a utilizar), parece ser una opción muy popular, pero no podía sacrificar 4 pines I/O sólo para almacenar unos pocos bytes. Entonces, me puse a indagar, el modo de guardar dichos datos en un lugar accesible utilizando el mínimo de pines de la tarjeta de Arduino y partí de la información del Atmega168.
Según la documentación del Atmega168, el Arduino equipado con éste microcontrolador, tiene una memoria interna EEPROM (No Volátil) de tan sólo 512 bytes (ver referencia), que puede ser insuficiente en algunos casos. Finalmente, en el proyecto utilicé unos pocos bytes de la propia memoria NV del Atmega168 con el que hice el proyecto. Sin embargo en mi cabeza, rondaba la idea inicial del uso de una EEPROM adicional y así empezó este tutorial, voy a usar una EEPROM serie 24LC64 ya que se trata de un elemento bastante utilizado en TV y otros elementos del mercado.
Así que, vamos usar una EEPROM externa, en mi caso he usado una EEPROM serie 24LC64 que tiene una capacidad de 64 Kbit, es I²C y un precio alrededor de 2.88 euros. Este es el enlace de hojas de especificaciones para los que quieran utilizar la 24LC256 y para el caso de la 24LC64, en este sitio.
DESCRIPCIÓN FUNCIONAL DE LA EEPROM.
El 24xx64 soporta un bus bidireccional de 2 cables y el protocolo de transmisión de datos. Un dispositivo que envía datos en el bus es definido como un transmisor, y un dispositivo que recibe datos como un receptor. El bus debe ser controlado por un dispositivo maestro que genera el reloj serie (SCL), controla el acceso de bus, y genera las condiciones de START y STOP mientras el 24xx64 trabaja como un esclavo. Tanto el maestro como el esclavo pueden funcionar como un transmisor o receptor pero el dispositivo maestro determina cual modo es activado.
Arduino y la EEPROM se comunican entre entre sí, mediante el bus I²C, el cual se trata de un bus de comunicaciones serie, formado por dos líneas: una para los datos y otra para el reloj. Como ya debe saber, este sistema de comunicación permite la intercomunicación de hasta 114 dispositivos en una misma red, aunque en ciertos casos se puede llegar a los 127 dispositivos.
La librería Wire que podemos encontrar en el mismo IDE Arduino, permite manejar un bus I²C. En este artículo del web y este otro artículo (del portugués Daniel Gonçalves) para el uso de I²C EEPROM con el Arduino, son perfectos, ya que sólo ocupará 2 pines (pin 4 y 5). Los pines A0 a A2 se usan para indicar la dirección del bus I²C EEPROM, si están todos conectados a GND la dirección de Arduino es 0x50, ver hoja de datos. Por lo tanto, podría poner hasta 8 eeproms en el mismo bus y lograr hasta 2 Mbyte de memoria.
El pin 7 (WP) puede ser conectado a Vss, Vcc o dejarlo flotando. Una resistencia interna lo polariza a masa, este pin mantendrá el dispositivo en el estado de no protección, al dejarlo flotando. Así pues, de ser conectado a VCC, las operaciones de escritura son inhibidas. Las operaciones de lectura no se ven afectadas. Por lo tanto, al ser conectado a Vss, permitirá la operación de memoria normal (lectura/escritura, la memoria entera 0000-1FFF).
Con el código disponible sobre el enlace Playground de Arduino, usted no tiene que preocuparse del bit de control. La dirección del dispositivo EEPROM en binario será 1010XXX donde XXX es Address 2, Address 1 y Address 0 en este orden. Por ejemplo, si el pin 3, 2,1 es Bajo-Bajo-Alto, en ese orden, la dirección binaria será 1010 001, o hex 0x50.
En definitiva los pines 3, 2 y 1 determinan la dirección del chip; I²C utiliza las direcciones de 7 bits + 1 bit de control. Para simplificar, si conecta los tres pines a masa, le dará la dirección 1010000, o en hex 0x50, que corresponden a los 7 bits (MSB) más significativos, el bit 0 no debe preocuparnos, lo genera la misma orden de lectura/escritura.
En cuanto a las conexiones de los pines: el pin 5 de la EEPROM (SDA) lo conectamos a la entrada analógica 4 de nuestro Arduino, el pin 6 (SCL) a la entrada analógica 5 (observar en la foto de más abajo que, los cables de color no se cruzan, por la disposición de la placa de pruebas), el pin 8 a +5V y todos los demás a tierra o masa. Los pines analógicos 4 y 5 de Arduino son los que usa la librería Wire.
No debemos olvidarnos de inicializar la conexión Wire.begin(); antes de usar estas funciones:
Como muestra de como, escribir y leer en una EEPROM, a continuación, pongo el ejemplo de kans, que encontré en la red y con el que yo probé: se trata de escribir una cadena de caracteres en una EEPROM, cuando se alimente nuestro Arduino, pasando después leerla y enviarla al ordenador vía serie. Para ver los resultados, podemos utilizar el monitor de la IDE Arduino o descargarnos el programa GTerminal que es una aplicación libre y que puede encontrar en la red.
Al tratarse de una memoria no volátil, debemos comentar la (línea 59) de escritura, para comprobar que permanecen los datos guardados con anterioridad.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
/* * Uso del I2C bus con la EEPROM 24LC64 * Ejemplo: eeprom.pde * Este código pertenece a: * Autor: kans * Fecha: 05/03/2008 * * http://pastebin.com/f3a36644b */ #include <Wire.h> //libreria "Wire.h" I²C // Las siguientes funciones para lectura y escritura en una EEPROM // se encuentran en el wiki de Arduino: // http://www.arduino.cc/playground/Code/I²CEEPROM void I²C_eeprom_write_byte( int deviceaddress, unsigned int eeaddress, byte data ) { int rdata = data; Wire.beginTransmission(deviceaddress); Wire.send((int)(eeaddress >> 8)); // MSB Wire.send((int)(eeaddress & 0xFF)); // LSB Wire.send(rdata); Wire.endTransmission(); } //ADVERTENCIA: address es una dirección de página, de 6 bits termina envolverá // alrededor además, los datos pueden ser como máximo alrededor 30 byte, porque // la libreria Wire tiene un búfer de 32 byte</ void I²C_eeprom_write_page( int deviceaddress, unsigned int eeaddresspage, byte* data, byte length ) { Wire.beginTransmission(deviceaddress); Wire.send((int)(eeaddresspage >> 8)); // MSB Wire.send((int)(eeaddresspage & 0xFF)); // LSB byte c; for ( c = 0; c < length; c++) Wire.send(data[c]); Wire.endTransmission(); } byte I²C_eeprom_read_byte( int deviceaddress, unsigned int eeaddress ) { byte rdata = 0xFF; Wire.beginTransmission(deviceaddress); Wire.send((int)(eeaddress >> 8)); // MSB Wire.send((int)(eeaddress & 0xFF)); // LSB> Wire.endTransmission(); Wire.requestFrom(deviceaddress,1); if (Wire.available()) rdata = Wire.receive(); return rdata; } // tal vez no hay que leer más de 30 o 32 bytes de una vez! void I²C_eeprom_read_buffer( int deviceaddress, unsigned int eeaddress, byte *buffer, int length ) { Wire.beginTransmission(deviceaddress); Wire.send((int)(eeaddress >> 8)); // MSB Wire.send((int)(eeaddress & 0xFF)); // LSB Wire.endTransmission(); Wire.requestFrom(deviceaddress,length); int c = 0; for ( c = 0; c < length; c++ ) if (Wire.available()) buffer[c] = Wire.receive(); } void setup() { char cadena[] = "hola mundo desde una eeprom"; // cadena a escribir Wire.begin(); // es obligatorio inicializar la conexion Serial.begin(9600); I²C_eeprom_write_page(0x50, 0, (byte *)cadena, sizeof(cadena)); // escribir la cadena al // principio de la EEPROM; comentar esta línea para probar que la memoria es no volátil delay(10); //pequeña pausa despues de escribir en la memoria } void loop() { int addr=0; //dirección a leer byte b = I²C_eeprom_read_byte(0x50, 0); // acceso a la primera posición de memoria while (b!=0) { Serial.print((char)b); //enviar al ordenador addr++; //siguiente direccion b = I²C_eeprom_read_byte(0x50, addr); //acceso a posición de memoria } Serial.println(); delay(2000); } |
El código anterior, con un copiar/pegar puede descargarlo en su editor de texto habitual y guárdelo como «eeprom.pde», por ejemplo y corra su propia experiencia, son unos pocos elementos los que intervienen, cuatro trozos de hilo de colores y la EEPROM, observe la foto.
La última parte de este tutorial, es en realidad utilizar el código en su software. Dado que el creador de las funciones relacionadas anteriormente (Kans) no ha creado un archivo .h por sus motivos, simplemente tendremos que copiar y pegar todas esta funciones, al principio de su código.
El código incluye cuatro funciones:
I²C_eeprom_write_byte,
I²C_eeprom_write_page,
byte I²C_eeprom_read_byte, y
I²C_eeprom_read_buffer.
Estas se explican por sí mismo si se lee el código, por ejemplo, para escribir los datos byte «0xAB» para bloquear la 1 ª (recuerde una EEPROM 64kbit tiene disponibles 8192×8 bloques) de la EEPROM, cuya dirección es 0x50, se debería utilizar:
I²C_eeprom_write_byte (0x50, 1, 0xAB);
Y para leer los datos debe escribir:
temp = I²C_eeprom_read_byte (0x50, 1);
El parámetro deviceaddress (dirección del dispositivo) al que hacen referencia estas funciones en el caso de esta memoria se trata del 0×50.