TODOPIC
Microcontroladores PIC => Todo en microcontroladores PIC => Mensaje iniciado por: Chenao en 26 de Noviembre de 2011, 13:19:14
-
En el presente hilo no pretendo inventar si no organizar y describir desde una perspectiva pedagógica el funcionamiento del sistema FAT 16 para aplicarlo a las memorias microsd. Algunos pensaran que las librerías ya están hechas y que no hay necesidad de indagar tanto para conseguir un resultado, pero soy de los que piensa que para aprender es necesario hacer las cosas uno mismo, y eso exactamente lo que quiero con el presente hilo aprender y que los compañeros interesados en el presente hilo se sumen para generar una discusión que permita entre todos desentrañar el sistema FAT16.
Para empezar con el tema he elaborado preámbulo del sistema FAT16 basado en el artículo de compañero Ariel “FAT AL DESNUDO” y del documento “FAT: GENERAL OVERVIEW OF ON – DISK FORMAT”, para mayor información puede consultar los documentos para tener una idea mayor de lo que aquí se expondrá.
DEFINICIONES:
CLUSTER: Un clúster es la unidad de almacenamiento en un disco (ZIP, rígido o flexible) con una determinada cantidad fija de bytes. Un disco está dividido en miles de clústeres de igual tamaño y los archivos son repartidos y almacenados en distintos clústeres. El tamaño se determina en el formateo del disco y suele ser de 512 bytes, pero la cifra puede ascender a 4.096bytes.
Cuando se almacena en disco un archivo más grande que un clúster, este se subdivide en distintos clústeres. Por ejemplo: Imaginemos un disco con clústeres de 512 bytes. Si se almacena un fichero de 1500 bytes, 512 bytes de ése fichero serán almacenados en un solo clúster en una posición física del disco, otros 512 bytes serán almacenados en otro clúster en otra posición determinada del disco, que puede o no ser congruente físicamente al anterior. Los 476 bytes restantes de ése fichero serán almacenados en otro clúster, sobrando 36 bytes que quedan sin utilizar del clúster (ese espacio está virtualmente perdido). Por lo tanto ese archivo quedará segmentado en tres clústeres que pueden o no ocupar partes físicas congruentes en un disco. Cuando se intenta leer ese fichero, el cabezal lector deberá moverse de un lado a otro del disco para poder leerlo definitivamente.
SECTOR: Un sector de un disco duro es la sección de la superficie del mismo que corresponde al área encerrada entre dos líneas radiales de una pista.
Pueden almacenar una cantidad fija de bytes, generalmente suele ser de 0,5 Kb hasta 64 Kb, pasando por todas las potencias de 2 (20=1; 21=2; 22=4; 23=8; 24=16; etc.). Esto se puede configurar al formatear una unidad de almacenamiento, en la opción de tamaño de unidad de asignación.
Cada sector almacena una cantidad fija de información. El formateado típico de este medio provee espacio para 512 bytes (para discos magnéticos) ó 2048 bytes (para discos ópticos) de información accesible para el usuario por sector.
PISTA: Las pistas son círculos concéntricos de datos que se almacenan en los discos que revolucionan dentro de los discos magnéticos.
(http://img411.imageshack.us/img411/1543/clusterg.jpg)
En los discos duros hay varios discos y por lo tanto, varios círculos del mismo diámetro. Todas las pistas que tienen el mismo diámetro, constituyen un cilindro.
INTRODUCCIÓN:
FAT es el acrónimo de File Allocation Table (Tabla de alocación de archivos) y es un sistema de ordenamiento de los archivos en el disco rígido que usaba MS-DOS y versiones de Windows anteriores a XP. Se usa también para acomodar los archivos en los disquetes. En la actualidad coexisten tres sistemas de archivos tipo FAT: FAT12, FAT16 y FAT32. Las diferencias básicas entre esos subtipos, y la razón de sus nombres, es el tamaño, en bits, de las entradas en la estructura FAT en el disco. Todos los sistemas de archivos FAT fueron pensados originalmente para la arquitectura del IBM PC. Este es un dato importante, debido a que este hardware hace uso de las estructuras de datos en formato “little endian”.
DIVISIÓN DE LOS DISCOS CON FAT
Un disco con sistema de archivo FAT está dividido en 4 zonas:
• Zona reservado
• Zona de la FAT
• Zona del directorio raíz
• Zona de datos y directorios y archivos
BIOS PARAMETER BLOCK (BPB)
La primer estructura de datos importante en un disco con FAT es la llamada BIOS Parameter Block (en adelante BPB), que se encuentra en el primer sector del disco dentro de la zona reservada. Esta estructura contiene una serie de campos que permiten conocer parámetros importantes del disco como el número de bytes por sector, número de sectores reservados, números de copias FAT etc (ver más adelante).
El BPB del sistema de archivos FAT32 coincide exactamente con el BPB de las FAT12 y FAT16, hasta el campo BPB_TotSec32 (ver más adelante). Las diferencias comienzan en el offset 36, dependiendo si se trata de un sistema FAT32 o FAT12/FAT16. El punto importante de destacar es que los primeros 36 bytes coinciden, independientemente del tipo de FAT que estemos utilizando. Esto permite la máxima compatibilidad entre diferentes sistemas, y permite a los drivers entender y soportar cualquiera de los tipos, ya que siempre contienen los mismos campos.
DESCRIPCIÓN DE ALGUNOS CAMPOS DEL BPB
BPB_BytsPerSec (Dirección 11 – Tamaño 2 bytes) : Número de bits por sector y pueden tomar algunos de los siguientes valores 512, 1024, 2048 o 4096.
BPB_SecPerClus (Dirección 13 – Tamaño 1 byte): Número de bytes por cluster, este valor debe ser una potencia de 2 mayor que 0 (1,2,4,8……128).
BPB_RsvdSecCnt (Dirección 14 – Tamaño 2 bytes): Número de sectores reservados Este campo no puede ser 0. Para sistemas de FAT12 y FAT16 siempre debe ser 1.
BPB_NumFats (Dirección 16 – Tamaño 1 byte): Número de copias FAT en el volumen. Este campo debería tener un valor de 2, independientemente del tipo de FAT implementada. Sin embargo, cualquier valor igual o mayor a 1 es válido.
BPB_RootEntCnt (Dirección 17 – Tamaño 2 bytes): Número de entradas al directorio raíz de 32 bits. Este valor debe ser tal, que al multiplicarlo por 32 resulte en un múltiplo de BPB_BytsPerSec. En el caso de volúmenes con FAT32, este campo debe ser "00 00". Por motivos de compatibilidad, en sistemas FAT16, este valor debería ser 512.
BPB_TotSec16 (Dirección 19 – Tamaño 2 bytes): Contiene el número de sectores en todas las 4 zonas del sistema FAT.
BPB_FATz16 (Dirección 22 – Tamaño 2 bytes): Número de sectores ocupados por una copia FAT.
BPB_SecPerTrk (Dirección 24 – 2 bytes): En aquellos soportes que tienen una "geometría" compuesta por pistas, cabezas y cilindros, tales como los discos duros, este valor especifica el numero de sectores por pista.
En las próximas entregas se explicara porque inicialmente se explicaron estos campos del BPB. En la siguiente entrega se hablara de la memoria microsd y la implementación del algunas funciones (lectura, escritura, etc.) para aplicarlas al sistema FAT16.
-
Felicitaciones por la iniciativa Chenao! :-/
-
Felicitaciones por la iniciativa Chenao! :-/
+1!
Estaría bueno ahora trabajar en la función que inicia el sistema FAT ;-) O sea, la función que lee el sector de arranque y determina que tipo de FAT es, donde estan las copias FAT, donde comienza el directorio raíz, cuantos sectores compone un cluster, etc.. :)
Saludos!
-
Estaría bueno ahora trabajar en la función que inicia el sistema FAT O sea, la función que lee el sector de arranque y determina que tipo de FAT es, donde estan las copias FAT, donde comienza el directorio raíz, cuantos sectores compone un cluster, etc..
Correcto Suky, pero aguanta, en la próxima entrega tratare de introducir el manejo de una memoria MicroSd por interfaz SPI, algo de teória y algunas funciones para el activar leer y escribir un bloque de la memoria, esto es debido a que se debe conocer el contenido del directorio raíz ;-).
-
Inicialización de la memoria MicroSD
Para inicializar la MicroSd es necesario enviar los comandos CMD0, CMD1 y CMD16 en una misma rutina. Los comandos tienen un tamaño fijo de 6 bytes. La convección establece que primero se envía el byte más significativo. El formato de los comandos se muestra en la siguiente figura:
(http://img411.imageshack.us/img411/1374/comandospi.jpg)
Donde el primer byte contiene el bit de inicio, el host y el identificador del comando a enviar, los siguientes 4 bytes contienen el argumento para dicho comando y el último byte representa el CRC7. La memoria MicroSD posee muchos comandos que pueden ser usados en modo SPI, como se muestra en la siguiente figura, pero en la presente entrega sólo se hablara de los siguientes comandos:
(http://img502.imageshack.us/img502/9673/comandosc.jpg)
CMD0 400000000095H Coloca la tarjeta en inactividad
CMD1 4100000000FFH Activa la tarjeta
CMD16 5000000000FFH Configura la posición y el largo del bloque a leer/escribir
CMD17 5100000000FFH Permite leer un sector de la tarjeta
CMD24 5800000000FFH Permite escribir un sector en la tarjeta
Para que la memoria pueda escribir o leer un sector determinado primero se debe inicializar en modo SPI, es un paso fundamental en el manejo de la memoria, además para que la ella se inicialice de forma correcta se debe enviar el comando CMD0, CMD1 y CMD16 sin activar el pin CS (0 V).
Para inicializar la memoria el PIC debe enviar los seis Bytes del comando CMD0, lo seis Bytes de comando CMD1 y los seis Bytes del comando CMD16 esperando las debida respuestas.
Al iniciar la memoria se establece en modo SD. Para entrar en modo SPI se debe enviar el comando CMD0 con el pin CS a 0V, si la memoria reconoce la petición de cambio de protocolo responde con la respuesta R1 (0x01).
A continuación el código en CCS, la rutina retorna con 1 si hubo error si retorna 0 todo bien
int init_mmc(void)
{
int i;
SETUP_SPI(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_16 | SPI_XMIT_L_TO_H);
OUTPUT_HIGH(PIN_C2);
for(i=0; i<20; i++)
{
SPI_WRITE(0xFF); // espero a que se inicialice la memoria que van desde 8 a 10 pulso del reloj
}
OUTPUT_LOW(PIN_C2); // Activo la targeta
for(i=0; i<20; i++)
{
SPI_WRITE(0xFF); // espero a que se inicialice la memoria que van desde 8 a 10 pulso del reloj
}
SPI_WRITE(0x40);
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x95); // Comando CMDO datos a enviar
if(mmc_respuesta(0x01)==1)
{
SPI_WRITE(0XFF);
OUTPUT_HIGH(PIN_C2);
return 1; // Error en memoria por que se supero el tiempo
}
i = 0;
do
{
SPI_WRITE(0x41); // Envia comando hasta que de la respuesta y hasta que se cumpla el tiempo
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0xFF); // Secuencia del comando CMD1
i= i + 1;
}
while((i<250) && (mmc_respuesta(0x00)==0x01));
if(i>=250)
{
SPI_WRITE(0XFF);
OUTPUT_HIGH(PIN_C2);
return 1; // si se supera el tiempo de respuesta no se ha inicializado bien la targeta
}
SPI_WRITE(0x50);
SPI_WRITE(0x00);
SPI_WRITE(0x00);
SPI_WRITE(0x02);
SPI_WRITE(0x00);
SPI_WRITE(0xFF); // Comando CMD16 para configurarlo de 512 bytes
if(mmc_respuesta(0x00)==0x01)
{
SPI_WRITE(0XFF);
OUTPUT_HIGH(PIN_C2);
return 1; // se ha inicializado incorrectamente
}
OUTPUT_HIGH(PIN_C2);
return 0; // se ha inicializado correctamente
}
La rutina mmc_respuesta(unsigned char respuesta) lo único que hace es verficar la respuesta R1 que envia la MicroSD por el bus SPI en un determinado tiempo.
Código en CCS, la rutina retorna con 1 si hubo error si retorna 0 todo bien
int mmc_respuesta(unsigned char respuesta)
{
unsigned long espera = 0xFFFF;
while(spi_read(0XFF)!= respuesta && espera> 0)
{
espera= espera - 1;
}
if(espera==0)
{
return 1; // se ha superado el tiempo
}
else
{
return 0;
}
}
-
Hace unos meses me interesé en el desarrollo de algunas funciones en CCS para el manejo de una MicroSd por bus SPI, inicialmente logre buenos resultados pero he estado mejorándolas para adaptarlas al sistema FAT16 y precisamente hoy vengo hablar de la memoria MicroSd y de las funciones para inicilizar, configurar y leer/escribir algún secto de la misma.
Ahora vamos a explicar de forma resumida la escritura/lectura de un sector de la memoria
Escritura y lectura de un bloque
Las operaciones de lectura y escritura se realizan enviando el comando correspondiente junto a la dirección del primer byte del bloque con el largo indicado anteriormente (Comando CMD16). El largo del bloque puede ser desde 1 hasta 512 Bytes, y no está permitido realizar operaciones en dos sectores a la vez, o sea que si el largo de bloque fijado en CMD16 es 512 Bytes, la dirección para realizar lectura o escritura debe ser la del byte inicial del sector.
Para realizar escritura de un único bloque debemos enviar el comando CMD24 (0x58, 0x00, 0x00, 0x02, 0x00, 0xFF) indicando la dirección del bloque en el argumento de la función. La memoria al reconocer el comando envía la respuesta R1, donde puede indicar si hay algún error. Si todo es correcto el PIC debe enviar un token (0xFE) y luego los 512 (tener en cuenta la configuración del comando CMD16) datos del bloque más 2 bytes de CRC. Luego de enviados estos datos debemos quedar a la espera de una respuesta de la memoria indicando si los datos se han recibido correctamente o ha ocurrido un error, además de condición de desocupado. Si no hubo un error la memoria responde con 0x05.
Código en CCS, retorna 1 si hubo error si retorna 0 todo bien.
/////////////////////////////////////// Rutina que escribe 512 bytes en la memoria //////////////////////////////////////
int mmc_escribir_bloque(int32 dir, int *buffer)
{
int trama[4];
long i,a;
a = 0;
i = 0;
trama[3] = make8(dir,3);
trama[2] = make8(dir,2);
trama[1] = make8(dir,1);
trama[0] = make8(dir,0);
OUTPUT_HIGH(PIN_C2);
for(i=0; i<16; i++)
{
SPI_WRITE(0xFF); // Unos cuantos ciclos del reloj
}
OUTPUT_LOW(PIN_C2);
for(i=0; i<16; i++)
{
SPI_WRITE(0xFF); // Espero a que se estabilice la memoria
}
SPI_WRITE(0x58);
SPI_WRITE(trama[3]);
SPI_WRITE(trama[2]);
SPI_WRITE(trama[1]);
SPI_WRITE(trama[0]);
SPI_WRITE(0xFF); // Comando CMD24 pero escribé en el segundo bloque 0x200
if(mmc_respuesta(0x00)==0x01) // Espero la respuesta RB1
{
return 1; // no respondio correctamente la memoria
}
SPI_WRITE(0xFE); // Envio el token de inicio
i = 0;
while(i<512)
{
spi_write(*buffer++);
i = i +1;
}
SPI_WRITE(0xFF);
SPI_WRITE(0xFF); // espero ha que se estabilice o envio 2 CRC
if((SPI_READ(0xFF)&0x0f)!=0x05)
{
return 1; // No se escribio correctamente el bloque de 512 bytes diferente de 00101
}
while(spi_read(0XFF)== 0)
OUTPUT_HIGH(PIN_C2);
return 0;
}
En el parámetro dir (32 bits) se especifica el sector donde se desea realizar la escritura, el parámetro *buffer es una variable que contiene la dirección del buffer a escribir.
Para realizar una lectura debemos enviar el comando CMD17 (0x51, 0x00, 0x00, 0x02, 0x00, 0xFF) indicando en el argumento la dirección del bloque. Luego se espera la respuesta R1 desde la memoria, si todo es correcto se pasa a recibir el token (0xFE) y luego los datos, la cantidad es establecida por el largo del bloque(CMD16).
Código en CCS, retorna 1 si hubo error si retorna 0 todo bien.
///////////////////////////////// Rutina que lee un bloque de 512 bytes de la memoria ///////////////////////////////////
int mmc_leer_bloque(int32 dire, int *buffer)
{
int tram[4];
int16 i;
i = 0;
tram[3] = make8(dire,3);
tram[2] = make8(dire,2);
tram[1] = make8(dire,1);
tram[0] = make8(dire,0);
OUTPUT_LOW(PIN_C2);
SPI_WRITE(0x51);
SPI_WRITE(tram[3]);
//delay_us(10);
SPI_WRITE(tram[2]);
//delay_us(10);
SPI_WRITE(tram[1]);
//delay_us(10);
SPI_WRITE(tram[0]);
//delay_us(10);
SPI_WRITE(0xFF); // Comando CMD17 para leer el bloque x de la memoria
if(mmc_respuesta(0x00)==0x01) // Espero el comando RB1 la respuesta
{
SPI_WRITE(0XFF);
OUTPUT_HIGH(PIN_C2);
return 1; // Hubo error en la lectura del bloque direccion de la memoria
}
if(mmc_respuesta(0xFE)==0x01)
{
SPI_WRITE(0XFF);
OUTPUT_HIGH(PIN_C2);
return 1; // espera por el token de respuesta
}
if((SPI_READ(0XFF)&0XE0) == 0X00) // Rectifica si es el correcto
{
SPI_WRITE(0XFF);
OUTPUT_HIGH(PIN_C2);
return 1;
}
for (i= 0; i<512; i++)
{
*buffer++ = spi_read(0XFF);
}
OUTPUT_HIGH(PIN_C2);
spi_write(0XFF);
SPI_WRITE(0XFF);
return 0;
}
Para finalizar con la presente entrega quiero agradecer al compañero Suky por la ayuda prestada en el desarrollo de las presentes rutinas ya hace algunos meses. Si alguién tiene alguna sugerencia o nota algún error en las presentes funciones hacerlo saber, debido a que, el exito de desarrollar algunas funciones para el manejo del sistema FAT 16 depende en gran medida de una buena programación de la rutinas antes mencionadas.
Nos vemos en una próxima entrega ;-).