PicManía by RedRaven
 

Búsqueda personalizada

 

PROYECTOS : AUX Quad-EEPROM I2C

 
 

512 Kbytes de EEPROM con cuatro 24AA1025

 

 

 

Circuito auxiliar de Memoria EEPROM 1024 Kbits x 4 : 8 = 512 Kbytes

 
Descripción del Proyecto:
 
  • La finalidad de este proyecto es la de dotar de una cantidad suficiente de memoria no volátil a nuestra RRBOARD2. De los varios tipos de memorias no volátiles que podríamos utilizar vamos en este caso a usar una Array de EEPROMS serie externas, todas ellas accesibles mediante un único bus I2C.

  • La idea central es la de desarrollar una Driver en CCS C mediante el que podamos acceder a la totalidad de la memoria disponible "en línea". Desde el primer byte del primer chip hasta el último byte del último chip como si fuese un chip único que englobase la suma de todas las posiciones de memoria direccionables de forma continua.

  • Para este proyecto en concreto vamos a utilizar la EEPROM externa 24AA1025, desarrollada por la gente de Microchip y que han tenido la gentileza de enviarme tres unidades para realizar estos experimentos.

  • Por limitaciones de Hardware inherentes al diseño de las 24AA1025 solo podemos "apilar" un máximo de cuatro unidades de este tipo, por lo que nuestro proyecto va a ceñirse a esta limitación y vamos a diseñar todo en función de este número de elementos.

  • Así la conclusión del proyecto está en leer y/o escribir un byte, mediante las dos funciones correspondientes, en las posiciones de memoria desde 0x00000 hasta 0x7FFFF, ambas inclusive. Un total de 0x80000 posiciones diferentes, o lo que es lo mismo 524.288 posiciones direccionables directamente, lo que significan 524.288 / 1024 = 512 Kbytes.
 
Implementación y consideraciones Hardware:
 
  • Como hemos dicho mas arriba vamos a utilizar para nuestro pequeño proyecto cuatro chips del modelo 24AA1025. Cada uno de ellos dispone de un total de 1024 Kbits lo que significan 1024/8 = 128 Kbytes de memoria, distribuidos en dos bancos de 64 Kbytes cada uno de ellos.

  • Estos chips son accesibles mediante un bus I2C, dos líneas son necesarias para ello: SDASCL con sus correspondientes resistencias Pull-Up a Vcc, que corresponden a los Pines 5 y 6 del chip. Las resistencias Pull-Ups que vamos a utilizar son de 10 KOhmios cada una de ellas.
  • Para direccionar cada uno de los cuatro chips debemos usar las distintas configuraciones hardware que podemos implementar usando los terminales A0 y A1 de cada uno de los chips, Pines 1 y 2 respectivamente, nótese que A2, correspondiente al Pin 3 del chip, no es posible usarlo para este fin ya que ha de ser siempre conectado a Vcc. Estas direcciones hardware posibles combinando A0 y A1 conectándolos a VCC (1) y GND (0) según cada caso son: 00, 01, 10 y 11 y de ahí la limitación a cuatro chips de que hablábamos antes.
  • Podríamos usar el terminal WP, Pin 7 del Chip, para proteger contra escritura nuestra memoria EEPROM ya que éste deshabilita la posibilidad de modificar el contenido de la EEPROM si es conectado a Vcc, pero en este caso no vamos a hacerlo y vamos a conectarlo permanentemente a GND para habilitar siempre la posibilidad de modificación de su contenido.
  • Los Pines 4 y 8 del chip corresponden con la alimentación del mismo y han de ir conectados correspondientemente a GND y VCC.
Las distintas funciones de cada uno de los pines puede verse en la siguiente tabla extraída del Datasheet del 24AA1025:
 


 

La distribución física de los pines en el Chip es la siguiente:


 

 
Esquema:
 

 

  • Y este es el esquema definitivo que vamos a construir, en el que podemos destacar algunos detalles:
  • .- La comunicación con la RRBOARD2 la realizamos mediante nuestro viejo amigo el conector CON-ML10 para cable plano de 10 hilos (alimentación y un puerto completo de 8 bits)

  • .- JP1-PB y JP2-PC permiten seleccionar la conexión de los SDA y SCL del I2C a los pines 0..1 ó 3..4 del puerto al que estén conectados (recordad de la RRBOARD2 está diseñada para las familias 18F4550 y 16F877 y el I2C lo implementa la primera en los pines RB0 y RB1 y la segunda en los RC3 y RC4) 

  • .- Dependiendo de dónde conectemos nuestro circuito en la RRBOARD2 podemos necesitar o no las resistencias Pull-Up imprescindibles para el bus I2C. Si lo conectamos al PORTB tenemos disponibles la internas del PIC, en cualquier otro caso podemos hacer uso del jumper JP3-PU para conectar dichas resistencias Pull-Up a VCC.

  • .- Por oscuras razones de diseño que ni yo mismo soy capaz de explicarme del todo, a pesar de haber realizado personalmente el mismo, he optado por asignar las direcciones Hardware de cada uno de los chips en orden inverso al natural: Al chip número uno la dirección 11, al dos la 10, al tres la 01 y al cuatro la 00. La verdad es que es irrelevante a la hora de utilizarlos ya que muy fácilmente podremos cambiar la configuración del driver para usar cualquier combinación de direcciones que deseemos utilizar. (Este extremo se verá en detalle en la sección dedicada al driver)

 


 


Circuito impreso:
 
  • El esquema anterior debidamente ruteado a un tamaño de QUARTER_EUROBOARD genera el siguiente PCB:


 


Fotografías:
 
No disponibles aún.
   

Recursos:
 
  • Datasheet del chip  
Driver y Software:
 


 

  • Créditos: Todo lo aquí desarrollado se basa en dos trabajos que no he realizado yo mismo:
  • Pero dejémonos de mas dilaciones innecesarias y entremos en faena con nuestro Driver I2C para memorias EEPROM de gran tamaño:

 
  • Según la tabla anterior en que se nos muestra el método I2C de asignar una dirección de memoria a las 24AA1025 para leer o escribir sobre ella, y siguiendo el modo y manera en que CCS C hace uso del bus I2C debemos implementar dos simples funciones que esquemáticamente van a ser como sigue:

.- Escribir un byte (data) en una dirección de memoria (memAddress):

Es este caso inicializamos el bus I2C con start(), enviamos con write() el Control Byte, que nosotros llamamos baseAddress, y a continuación los dos bytes de dirección de memoria a escribir, memAddress, empezando con el más significativo y después del menos significativo, seguido del byte, data, que deseamos guardar.

void writeByte24AA1025(int32 memAddress, int8 data){
  i2c_start();
  i2c_write(baseAddress);
  i2c_write(memAddress>>8);
  i2c_write(memAddress);
  i2c_write(data);
  i2c_stop();
}

 

.- Leer un byte de una dirección de memoria (memAddress):

Para leer en una posición de memoria debemos realizar el mismo posicionamiento en una dirección de memoria tal como hicimos en la función de escritura seguido de una nueva función start() y una función write() con el bit Read del Control Byte activado. A continuación realizamos un read() del bus I2C y recibiremos el contenido de dicha dirección de memoria.

int8 readByte24AA1025(int32 memAddress){

  int8 returnByte=0;

  i2c_start();
  i2c_write(baseAddress);
  i2c_write(memAddress>>8);
  i2c_write(memAddress);
  i2c_start();
  i2c_write(baseAddress|1);
  returnByte = i2c_read(0);
  i2c_stop();
  return returnByte;
}

Todo esto está muy bien en principio pero hay una serie de importantes detalles que no hemos tomado en cuenta aún y que son imprescindibles si deseamos llevar a buen puerto nuestro proyecto:


 

En primer lugar está el asunto que cómo configurar apropiadamente el Control Byte. Es un único Byte y en él debemos incluir:

El Control Code. Que siempre son los mismo Bits, 7..4, y que para estos chips es 1010.

El Block Select Bit. Que indica en que Banco de los dos de 65535 bytes que tiene cada chip es al que nos estamos refiriendo.

El Chip Select Bits. Que es la dirección por Hardware que le asignamos a cada uno de los cuatro chips con los que estamos trabajando.

El Read/Write bit. Donde indicamos si estamos leyendo o escribiendo una posición de memoria.

En segundo lugar debemos hacer una conversión de la dirección de memoria donde queremos escribir desde la absoluta que enviamos a nuestras funciones, entre 0x00000 y 0x7FFFF, a la relativa de Chip/Banco/0x0000 a 0x1FFFF que es la máxima dirección accesible dentro de un Banco dentro de un Chip en concreto.

O sea que en función de la dirección absoluta que enviemos a nuestras funciones debemos componer el Control Byte seleccionando el Chip al que nos referimos, el Banco dentro de ese chip y la dirección concreta por la que estamos preguntando.

Partimos de una Control Byte base de: int8 const baseAddress24AA1025 = 0b10100000;

Y le configuramos los distintos bits. Esto lo solventamos con las siguientes funciones auxiliares:

int8 memAbsoluteAddress2deviceOrder(int32 memAddress);

Que nos devuelve 1, 2, 3 ó 4 dependiendo de si memAddress es mayor 0x00000 y menor que 0x1FFFF, ó es mayor que 0x20000 y menor que 0x3FFFF ... etc. etc.

int8 memAbsoluteAddress2deviceAddress(int32 memAddress);

Que nos devuelve la dirección Hardware del Chip 1, 2, 3 ó 4 al que corresponde la dirección absoluta memAddress. Esta dirección hardware es la parte Chip Select Bits del Control Byte.

Estas deciveAddress las extraemos en función del anterior deviceOrder haciendo uso de la siguiente tabla de asignación de direcciones Hardware:

// device hardware address
int8 const deviceAddress1 = 0b00000011; // #3
int8 const deviceAddress2 = 0b00000010; // #2
int8 const deviceAddress3 = 0b00000001; // #1
int8 const deviceAddress4 = 0b00000000; // #0

int32 memAbsoluteAddress2menRelativeAddress(int8 deviceOrder,int32 memAddress);
Que nos va a ajustar memAddress a la dirección relativa dentro de cada uno de los Chip, haciendo que cada una de ellas comienze realmente en 0x00000.
int1 menRelative2BankSelect(int32 memRelativeAddress);

Y ésta que por último nos selecciona el Banco, alto o bajo, de cada uno de los Chips. Corresponde al Block Select Bit del Control Byte.

Con todo esto podemos ya completar nuestras funciones principales, añadiéndoles las cabeceras correspondientes para ajustar todos y cada uno de los parámetros:

  int8 baseAddress;
  int8 deviceAddress;
  int8 deviceOrder=0;
  int32 memRelativeAddress;
  int8 bank=0;

  deviceAddress = memAbsoluteAddress2deviceAddress(memAddress);
  deviceOrder = memAbsoluteAddress2deviceOrder(memAddress);
  memRelativeAddress = memAbsoluteAddress2menRelativeAddress(deviceOrder, memAddress);
  bank = menRelative2BankSelect(memRelativeAddress);
  baseAddress = baseAddress24AA1025 + (bank<<3) + (deviceAddress<<1);
 

 
  Driver: 24aa1025x4.c  
  ///////////////////////////////////////////////////////////////////////////////
//
// 4 x EEPROM 24AA1025 Driver with absolute memory addressing
//
///////////////////////////////////////////////////////////////////////////////

#define EEDEBUG

// device hardware address
int8 const deviceAddress1 = 0b00000011; // #3
int8 const deviceAddress2 = 0b00000010; // #2
int8 const deviceAddress3 = 0b00000001; // #1
int8 const deviceAddress4 = 0b00000000; // #0
// baseAddress = 8 bits which mean ...
//
// Fixed (4 bits) -> 1010
// EE internal Block (1 bit) -> 0 for 0000/FFFF, 1 for 10000/1FFFF
// Dev Hard Address (2 bits) -> 00 or 01 or 10 or 11
// R/W (1 bit) -> 0 -> Write / 1 -> Read

int8 const baseAddress24AA1025 = 0b10100000;

// Aux Functions //////////////////////////////////////////////////////////////

int1 menRelative2BankSelect(int32 memRelativeAddress){
  int1 returnBit=0;
  if(memRelativeAddress>(int32) 0xffff) returnBit = 1;
  return returnBit;
}

int32 memAbsoluteAddress2menRelativeAddress(int8 deviceOrder, int32 memAddress){
  int32 returnInt32 = 0;
  switch(deviceOrder){
    case 1: returnInt32 = memAddress;
            break;
    case 2: returnInt32 = memAddress - 0x20000;
            break;
    case 3: returnInt32 = memAddress - 0x40000;
            break;
    case 4: returnInt32 = memAddress - 0x60000;
            break;
  }
  return returnInt32;
}

int8 memAbsoluteAddress2deviceOrder(int32 memAddress){
  int8 returnByte=0;
  if((memAddress>0x05ffff)&&(memAddress<0x080000)) returnByte=4;
  if((memAddress>0x03ffff)&&(memAddress<0x060000)) returnByte=3;
  if((memAddress>0x01ffff)&&(memAddress<0x040000)) returnByte=2;
  if( (memAddress<0x020000)) returnByte=1;
  return returnByte;
}

int8 memAbsoluteAddress2deviceAddress(int32 memAddress){
  int8 deviceOrder=0, returnByte=0;
  deviceOrder = memAbsoluteAddress2deviceOrder(memAddress);
  switch(deviceOrder){
    case 0: returnByte = 0xff;
            break;
    case 1: returnByte = deviceAddress1;
            break;
    case 2: returnByte = deviceAddress2;
            break;
    case 3: returnByte = deviceAddress3;
            break;
    case 4: returnByte = deviceAddress4;
            break;
  }
  return returnByte;
}
// Main Functions /////////////////////////////////////////////////////////////
void writeByte24AA1025(int32 memAddress, int8 data){

  int8 baseAddress;
  int8 deviceAddress;
  int8 deviceOrder=0;
  int32 memRelativeAddress;
  int8 bank=0;

  deviceAddress = memAbsoluteAddress2deviceAddress(memAddress);
  deviceOrder = memAbsoluteAddress2deviceOrder(memAddress);
  memRelativeAddress = memAbsoluteAddress2menRelativeAddress(deviceOrder, memAddress);
  bank = menRelative2BankSelect(memRelativeAddress);
  baseAddress = baseAddress24AA1025 + (bank<<3) + (deviceAddress<<1);

#ifdef EEDEBUG
  printf("\r\n\nAbsolute Address: %LX Relative Address: %Lx Device: %u Bank: %u\r\n",memAddress,memRelativeAddress,deviceOrder,bank);
#endif

  i2c_start();
  i2c_write(baseAddress);
  i2c_write(memRelativeAddress>>8);
  i2c_write(memRelativeAddress);
  i2c_write(data);
  i2c_stop();
  delay_ms(5);
}

int8 readByte24AA1025(int32 memAddress){

  int8 returnByte=0;
  int8 baseAddress;
  int8 deviceAddress;
  int8 deviceOrder=0;
  int32 memRelativeAddress;
  int8 bank=0;

  deviceAddress = memAbsoluteAddress2deviceAddress(memAddress);
  deviceOrder = memAbsoluteAddress2deviceOrder(memAddress);
  memRelativeAddress = memAbsoluteAddress2menRelativeAddress(deviceOrder, memAddress);
  bank = menRelative2BankSelect(memRelativeAddress);
  baseAddress = baseAddress24AA1025 + (bank<<3) + (deviceAddress<<1);

#ifdef EEDEBUG
  printf("\r\n\nAbsolute Address: %LX Relative Address: %Lx Device: %u Bank: %u\r\n",memAddress,memRelativeAddress,deviceOrder,bank);
#endif

  i2c_start();
  i2c_write(baseAddress);
  i2c_write(memRelativeAddress>>8);
  i2c_write(memRelativeAddress);
  i2c_start();
  i2c_write(baseAddress|1);
  returnByte = i2c_read(0);
  i2c_stop();
  return returnByte;
}

///////////////////////////////////////////////////////////////////////////////
   
 
 

 

 
 
Como os dije mas arriba el siguiente programa es una adaptación del programa de ejemplo ex_extee.c de la gente de CCS C que podéis encontrar en el directorio examples de la instalación de dicho compilador.
 
 
  test_eeprom_24aa1025x4.c  
  ///////////////////////////////////////////////////////////////////////////////
//
// eeprom_24aa1025x1.c
//
// based upon ex_extee.c example by CCS
// and "Microchip 24AA512 512k I2C EEPROM driver" by UFAnders
// in http://www.ccsinfo.com/forum/
//
// by RedPic from http://picmania.garcia-cuervo.net
//
// October 2006
//
///////////////////////////////////////////////////////////////////////////////

#include <18F4550.h>
#fuses HS,NOWDT,NOPROTECT,NOLVP
#use delay(clock=20000000)
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
#use i2c(master, sda=PIN_B0, scl=PIN_B1)

#include <input.c>
#include <24aa1025x4.c>

const char Version[]="1.0.F\0";

///////////////////////////////////////////////////////////////////////////////
//
// Main
//
///////////////////////////////////////////////////////////////////////////////

void main() {

  BYTE value, cmd;
  int32 address;
  int8 device;

  delay_ms(300);
  printf("\r\n\n");
  printf("[RRBOARD2] EEPROM Total Commander %s\r\n",version);
  printf("based on 24AA1025 Microchip Hardware\r\n");
  printf("\xa9 10.2006 by RedPic\r\n\n");

  do{

    do{
      printf("\r\nDo you Read or Write? : ");
      cmd=getc();
      cmd=toupper(cmd);
      putc(cmd);
    }while ((cmd!='R') && (cmd!='W'));

    printf("\n\rGive me EEPROM internal absolute Address (24 bits) in hex : ");

    address = gethex();
    address = (address<<8)+gethex();
    address = (address<<8)+gethex();

    device = memAbsoluteAddress2deviceAddress(address);

    if(cmd=='R')
      printf("\r\nReturn Value (8 bits) in hex is : %X\r\n",readByte24AA1025(address));

    if(cmd=='W') {
      printf("\r\nEnter new value (8 bits) in hex : ");
      value = gethex();
      printf("\n\r");
      writeByte24AA1025(address, value );
    }
  }while(TRUE);
}
   
 
 

 

 
 
 

 
Funcionando:
 
  • Para realizar las primeras pruebas he realizado físicamente el siguiente esquema y PCB que implementa uno de estos chips 24AA1025:
 

 

 
 

 
 
 
PCB's en PDF Listos para imprimir.

 

 

 

 

 

Esta página se modificó el 27/12/2008


Esta página usa la letra Ñ

Nota pública importante sobre las consultas al Webmaster de PicManía.


Sugerencias a Picmanía... (que serán leídas pero seguramente no podrán ser contestadas)

Esta página pertenece al grupo de páginas de Diego RedRaven

 

 



Nota: Esta página Web esta repleta de imágenes, textos, logotipos y demás material extraídos de los mas variados medios de los que no soy ni autor ni depositario de los correspondientes derechos de autor, uso y/o reproducción. Si Ud. es depositario de dichos derechos y desea que el material correspondiente sea eliminado de esta Web no dude en ponerse en contacto conmigo mediante e-mail y será inmediatamente retirado. Gracias.
 
Visitas
Totales : 13554 Hoy: 2 Activas: 1 Vistas: 13554

Esta página fue modificada el 07-08-2010 22:41:25

           
 DmSoft WAMP Escribir Unreal