|
|
Circuito auxiliar de Memoria EEPROM 1024 Kbits
x 4 : 8 = 512 Kbytes
|
| |
Descripción
del Proyecto:
|
|
|
|
|
|
|
|
|
- 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:
|
|
|
- Estos chips son accesibles mediante un
bus I2C, dos líneas son necesarias para ello: SDA
y SCL 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:
|
|
|
-
3º.- 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.
|
-
4º.- 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
24AA1025 (PDF 357 Kb)
|
| |
Driver y Software: |
| |
|

|
- Créditos: Todo lo aquí desarrollado se
basa en dos trabajos que no he realizado yo mismo:
|
|
|
|
|
 |
| |
|
|
1ª.- 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();
}
|
| |
2ª.- 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);
|
| |