Anda di halaman 1dari 12

Simple FAT y SD Tutorial Parte 2

En la parte 1 de la última semana de mi tutorial FAT y SD, llegamos tan lejos como para leer
las entradas de archivos en el directorio raíz y echar un vistazo a un archivo con el editor
hexadecimal. Ahora cubriremos la tabla de asignación de archivos para permitir la lectura de
archivos más largos y adaptar el código a una pequeña biblioteca FAT16.
Tabla de asignación de archivos en FAT16
En la parte anterior, aprendimos que los datos en un disco con formato FAT se almacenan
en clústeres . En nuestra imagen de prueba, el tamaño del clúster fue de 32 sectores, es decir,
16 kiB (16 384 bytes). Imaginemos un disco recién formateado con 100 clústeres libres, con los
clústeres numerados de 2 (el primer clúster al principio del área de datos) a 101 (el último
clúster). Ahora copiemos algunos archivos allí:
Operación de archivo Asignación de archivos

Copie HAMLET.TXT Grupos 2-13 asignados para el archivo (196


(193 082 bytes) en el disco 608 bytes)

Copie README.TXT
(353 bytes) en el disco Grupo 14 asignado para el archivo

Grupo 15 asignado para las entradas del


Crear SUBDIR en el disco archivo de directorio

Grupo 16 asignado para el archivo, una


nueva entrada de archivo escrita en
Crear 1.TXT en SUBDIR SUBDIR (principio del grupo 15)
Por ahora, cada archivo ocupa clústeres consecutivos, pero ¿qué sucede si ahora eliminamos
README.TXT? Seguramente es demasiado lento para mover todos los archivos que estaban
después de README.TXT un clúster restante (considere el caso en el que tendríamos cientos
de megabytes de datos después de ese archivo) - es más fácil eliminar README.TXT de la
entrada del directorio raíz, y marque el grupo 14 como libre de nuevo.
Pero ¿y si ahora copiamos un segundo HAMLET2.TXT en el disco? Sería una pérdida de
espacio simplemente continuar desde el grupo 17, dejando el grupo libre 14 sin usar. En lugar
de solo asignar clústeres 17-28 para HAMLET2.TXT, el sistema operativo puede reutilizar el
clúster 14, asignando clústeres 14 + 17-27 para HAMLET2.TXT.
Obviamente, después de las operaciones de N archivos, los archivos pueden distribuirse por
todos los clústeres, y si permitimos agregar a los archivos existentes, las partes posteriores de
un archivo pueden incluso almacenarse en clústeres que están físicamente antes de las partes
anteriores. Obviamente, necesitamos un mapa de algún tipo para realizar un seguimiento de los
clusters ocupados en un solo archivo. Y eso es exactamente lo que es una tabla de asignación
de archivos: un mapa con una ranura para cada uno de los clústeres de datos, cada ranura indica
dónde ir luego de leer un archivo. En FAT16, cada ranura ocupa 2 bytes (es decir, una palabra),
y las dos primeras (# 0 y # 1) están reservadas para tipo de disco y datos de tipo FAT; en el
caso de FAT16 en una tarjeta SD o disco duro, estos cuatro los bytes siempre serán F8 FF FF
FF. Entonces, para un disco de 100 clústeres, la FAT tendría 102 palabras (204 bytes) de
largo. Si un archivo comienza en el clúster 2,fat[2]le diría al siguiente clúster, por ejemplo,
3, fat[3]que a su vez contendría el siguiente clúster, y así sucesivamente, formando una lista
vinculada.
Si eso suena bastante simple en teoría, también es eso en la práctica. Aquí está la FAT de
nuestro test.imgoffset 0x10600:

En la captura de pantalla anterior, fat[2]está marcado en rojo (tenga en cuenta que, como
estamos hablando de palabras, fat [0] son los dos primeros bytes, fat [1] los siguientes dos, y así
sucesivamente). El byte menos significativo es el primero, por lo que el valor es 0x0003, es
decir, el sector 3. ¿Qué hay detrás del sector 3? Simplemente mira hacia arriba fat[3]: es
0x0004 (los dos bytes justo después fat[2]). Podemos continuar este derecho
hasta fat[0xD](grupo 13 en decimal), que dice 0xFFFF, lo que significa que es el último
clúster de este archivo. Para archivos que ocupen solo un clúster, 0xFFFF es la única entrada
FAT, como se puede ver para todos los archivos después de nuestro HAMLET.TXT.
Felicidades, si entendiste lo anterior, ahora entiendes todo lo que hay que saber sobre
FAT16. ¡Es hora de transformarlo en código!
Lectura de archivos completos con FAT
Digamos que queremos leer HAMLET.TXT y escribirlo en un archivo. Desde la
ejecución read_root.exey nuestra discusión anterior, sabemos que comienza desde el
clúster 2, el primer clúster en el área de datos (después de ver cómo se usan los números de
clúster como índices en FAT, es fácil ver por qué la numeración comienza en 2; de esta manera,
el clúster el número funciona directamente como un índice en FAT). Vamos a leerlo todo de
una vez:
unsigned char tampón [ 16384 ]; cluster corto sin firmar = 2 ; // ir a primera clúster fseek ( en ,
data_start + ( clúster - 2 ) * CLUSTER_SIZE , SEEK_SET ); fread ( buffer , 1 , sizeof ( buffer
), in ); // hacer algo con datos // ir a fat [cluster] fseek ( in , fat_start

+ cluster * 2 , SEEK_SET ); fread (& cluster , 2 , 1 , in ); if ( cluster == 0xFFFF ) return ; fseek


( en , data_start + ( clúster - 2 ) * CLUSTER_SIZE , SEEK_SET ); fread ( buffer , 1 , sizeof (
buffer ), in );
...
El código anterior obviamente no tiene algunos detalles importantes, pero confío en que
obtenga la idea básica:
 Ir al inicio del clúster de datos
 Leer contenido del clúster
 Ir a la entrada FAT para este clúster
 Lea el número de siguiente clúster
 Si number! = 0xFFFF, vaya al inicio del siguiente clúster de datos ...
Una vez que agregamos un bucle real para hacer lo anterior, y ajustamos el tamaño del búfer a
un valor ligeramente más pequeño para manejar sectores de 4 kB y 8 kB con gracia, obtenemos
la siguiente rutina. Tenga en cuenta que estoy pasando los valores preparados en el método
principal a esta función para minimizar las distracciones.
void fat_read_file ( ARCHIVO * en , ARCHIVO * a cabo , sin signo largo fat_start , unsigned
largo data_start , unsigned largo CLUSTER_SIZE , unsigned corto clúster , unsigned largo
file_size ) { unsigned char tampón [ 4096 ]; size_t bytes_read , bytes_to_read
, file_left = file_size ,

cluster_left = cluster_size ; // Ir al primer grupo de datos fseek ( en , data_start +


CLUSTER_SIZE * ( clúster - 2 ), SEEK_SET ); // Leer hasta que nos quedemos sin archivos o
clústeres mientras ( file_left > 0 && cluster ! = 0xFFFF ) { bytes_to_read = sizeof ( buffer
); // no leer más allá del archivo o el extremo del clúster if ( bytes_to_read >

file_left )
bytes_to_read = file_left ; si ( bytes_to_read > cluster_left ) bytes_to_read =
cluster_left ; // leer datos del clúster, escribir en el archivo bytes_read = fread ( buffer , 1 ,
bytes_to_read , in ); fwrite ( buffer , 1 , bytes_read , fuera ); printf ( " Copiado %

d bytes \ ", bytes_read ); // disminuir los contadores de bytes para el clúster actual y el archivo
completo cluster_left - = bytes_read ; file_left - = bytes_read ; // si hemos leído todo
el clúster, lea el siguiente cluster # de FAT if ( cluster_left == 0 ) { fseek ( en ,
fat_start + cluster * 2 , SEEK_SET ); fread (& cluster , 2 ,

1 , en );

printf ( " Fin del clúster alcanzado , el siguiente clúster % d \ ", clúster );

fseek ( en , data_start + CLUSTER_SIZE * ( clúster - 2 ), SEEK_SET


); cluster_left = cluster_size ; // restablecer el contador de bytes del clúster } } }

Tómese su tiempo para comprender el código anterior. Básicamente, es un bucle que lee datos
hasta que se consume todo el archivo y escribe todo en el archivo de salida. Siempre que se
hayan consumido todos los bytes de un clúster (el contador cluster_left pasa a cero), se realiza
una búsqueda FAT para ir al siguiente clúster. Tenga en cuenta que para las entradas de archivo
válidas, la condición cluster==0xFFFFnunca se debe alcanzar, ya que el tamaño del
archivo siempre debe agotarse antes de eso (en el mejor de los casos, se agotan al mismo
tiempo para que se realice una búsqueda FAT pero la condición nunca se verifica antes de
file_left = = 0).
Armado con esta función, modifiqué read_root.cun poco para tomar la imagen del sistema
de archivos y el archivo para leerlos como parámetros de línea de comandos (tenga en cuenta
que el nombre del archivo distingue entre mayúsculas y minúsculas, por lo que solo funcionará
HAMLET.TXT, no hamlet.txt o Hamlet. TXT). Guardé el resultado
como read_file.c. Simplemente compila eso y pruébalo:

También deberías intentar leer HAMLET.TXT. Puede descargar el UTF-8 Hamlet del Proyecto
Gutenberg y usar fc HAMLET.TXT pg1524.txtver si el archivo extraído coincide - el
mío sí :)
Resumiéndolo en una biblioteca FAT16
Ahora que estamos leyendo con éxito la imagen del sistema de archivos en una PC, es hora de
comenzar a pensar cómo este conocimiento puede trasladarse al lado del AVR de 8 bits. El
primer problema con la implementación actual es la huella de memoria: los chips AVR pueden
tener tan solo 128 bytes de SRAM, por lo que un buffer de lectura de 4096 bytes o incluso la
lectura de todo el sector de arranque en la memoria está fuera de discusión. Lo segundo es que
la E / S será completamente diferente con una tarjeta SD real.
Para lidiar con el problema de la memoria, decidí establecer límites un poco menos estrictos
que Petit FatFS (44 bytes + alguna pila) para evitar el exceso de optimización de código para el
tamaño, pero aún así mantenerme por debajo de 64 bytes. Para leer datos, decidí usar un búfer
de 32 bytes, que es suficiente para almacenar una entrada completa de archivo Fat16 de una
vez. Para las variables de estado, elegí la siguiente estructura de 19 bytes que es suficiente para
navegar por el sistema de archivos y leer los archivos después de la inicialización, lo que
resulta en una huella de memoria de 51 bytes y algo de pila:
// Datos de estado requeridos por la librería FAT16 typedef struct { unsigned long fat_start ; //
posición de inicio FAT sin signo largo data_start ; // posición de inicio de datos unsigned char
sectores_per_cluster ; // tamaño del clúster en sectores sin firmar corto racimo ; // cluster actual
se lee unsigned long cluster_left ; // bytes restantes en el clúster actual sin firmar long file_left ;
// bytes restantes en el archivo que se está leyendo } __attribute

(( empaquetado )) Fat16State ; #define FAT16_BUFFER_SIZE 32 // Variables globales para


datos de lectura y estado de biblioteca extern char sin signo fat16_buffer [
FAT16_BUFFER_SIZE ]; extern Fat16State fat16_state ;

Tenga en cuenta que estas son básicamente las variables esenciales utilizadas en
la fat_read_file()rutina anterior. Para abstraer el acceso de E / S, la biblioteca FAT16 se
basa en dos métodos que el usuario debe
proporcionar: fat16_seek(offset)y fat16_read(bytes). El primero se utiliza para
ir a un desplazamiento dado, y el siguiente para leer una cantidad dada de bytes
a fat16_buffer. Se supone que estas rutinas incrementan y recuerdan la posición del
archivo como lo hacen las secuencias normales de archivos C, por lo que para una
implementación SD, se necesitaría una variable "offset actual" adicional. Pero mientras que en
PC, las siguientes implementaciones son adecuadas:
ARCHIVO * en ; void fat16_seek ( desplazamiento largo sin signo ) { fseek ( in , offset ,
SEEK_SET ); } Char fat16_read ( unsigned char bytes ) { retorno ( Char ) fread ( fat16_buffer ,
1 , bytes , en ); }
Y, por supuesto, el inidentificador de archivo debe inicializarse fopen()antes de llamar a
cualquier función en la biblioteca FAT. También comenté las partes no utilizadas
de Fat16BootSectormodo que las partes relevantes se pueden leer de una vez en el búfer
(la estructura resultante recibe su nombre Fat16BootSectorFragment). El código
en read_file.cse ha refactorizado de la siguiente manera:
 Las definiciones de estructura, las declaraciones de variables y funciones se movieron a un
archivo de encabezado separado fat16.h
 fseek()llamadas reemplazadas con fat16_seek()llamadas
 fread() Reemplazado con fat16_read()
 Las estructuras de datos separadas para las particiones, el sector de arranque y las entradas de
archivos ( pt bs, entry) se reemplazan por macros que realmente apuntan a datos
actualmente enfat16_buffer
 Todas las impresiones están rodeadas de #ifdef DEBUGenunciados para que puedan
omitirse fácilmente
 Comparaciones con memcmp()reemplazado por for loops
 El código de inicialización se convierte en fat16_init(), finaliza con el puntero de archivo
al comienzo del directorio raíz
 La lectura de las entradas de archivos se convierte fat16_open_file(filename,ext),
el código se modifica para que también pueda escanear subdirectorios (son similares al
directorio raíz, pero se almacenan como archivos normales que pueden abarcar varios clústeres,
en realidad usa el fat16_read_file()interno)
 La lectura de un solo archivo se convierte en fat16_read_file(bytes)una invocación
que puede leer 32 bytes como máximo, y el resultado siempre se almacena enfat16_buffer
 fat16_init()y fat16_open_file()devuelve cero en caso de éxito y códigos de error
distintos de cero, fat16_read_file()devuelve la cantidad de bytes leídos - cero indica el
final del archivo
 Con un tamaño de búfer de 32 bytes, ningún método debería
invocar fat16_read(bytes)de una manera que cruzara los límites del sector (útil para la
implementación de SD más adelante)
Con los cambios anteriores, usar las rutinas FAT16 es fácil: solo incluya fat16.h,
proporcione las dos rutinas de E / S y enlace contra compilado fat16.c. Esta es la función
principal del módulo de prueba de la biblioteca test_lib.c:
int main ( int argc , char * argv []) { FILE * out = fopen ( "HAMLET.TXT" , "wb" ); char
bytes_read ; // Abrir imagen de disco para que fat16_seek y fat16_read funcionen en = fopen
( "test.img" , "rb" ); // Usa la biblioteca FAT16 fat16_init (); fat16_open_file (
"HAMLET" , "TXT" ); mientras ( fat16_state
. file_left ) { bytes_read = fat16_read_file ( FAT16_BUFFER_SIZE ); fwrite (
fat16_buffer , 1 , bytes_read , fuera ); // Escribir bytes de lectura } // Cerrar el archivo
maneja fclose ( out ); fclose ( en ); return 0 ; }

Además de la fat16_seek(offset)y fat16_read(bytes)se muestra más arriba, esto


es realmente todo lo que se necesita para usar la biblioteca. Además, si lee las notas anteriores
cuidadosamente, notará que se puede acceder a los subdirectorios. Para abrir SUBDIR\1.TXT,
simplemente reemplace la fat16_open_file()declaración en el código anterior con lo
siguiente - abrir_archivo básicamente coloca el puntero de lectura al principio de un archivo
dado, y los subdirectorios son esencialmente solo archivos con entradas de archivo como sus
contenidos:
fat16_open_file ("SUBDIR", "");
fat16_open_file ("1", "TXT");
Es importante recordar que los nombres de los archivos distinguen entre mayúsculas y
minúsculas y deben rellenarse con espacios de 8 + 3 caracteres, de lo contrario, la biblioteca no
encontrará sus archivos. Además, el uso de mayúsculas y minúsculas al crear archivos creará
entradas de archivos ficticios que se utilizan para almacenar "nombres de archivos largos" para
los archivos, por lo que "Hamlet.txt" se convierte en algunas entradas ficticias y el archivo real
podría llamarse HAMLET ~ 1.TXT ( Estoy seguro de que traerá recuerdos para muchos de
ustedes :).
Palabras de cierre
Ahora hemos creado una biblioteca bastante agradable, aunque de solo lectura para leer el
sistema de archivos FAT16. Recomiendo que ahora tome el archivo zip del proyecto
actualizado y pase algún tiempo revisando el fat16.h, fat16.cy test_lib.cpara
comprender cómo funciona la biblioteca y cómo se usa (incluí un archivo MAKE para que
pueda usar "make" para compilar test_lib.exe. es la única forma en que realmente se
beneficiará de esto (después de todo, si todo lo que busca son rutinas preparadas, hay más
librerías funcionalmente completas disponibles). Como beneficio adicional, podrá realizar
cambios si ¡Desea lograr algo que no está cubierto en este tutorial! Aquí hay algunos ejemplos
que podría probar como ejercicios:
 Agregue una nueva función fat16_eof()que devolverá 1 si el archivo ha sido
completamente leído
 En lugar de fat16_open_file(), haga un método fat16_next_file()que avance las
entradas del archivo una a una, lo que le permite, por ejemplo, imprimir la estructura del
archivo y navegar por los directorios
 Agregar fat16_skip_bytes()método para habilitar la "búsqueda directa" en un archivo
 Abra el archivo de imagen de lectura / escritura y haga que el fat16_write()usuario
proporcione la función y fat16_write_file()que pueda sobrescribir los bits del archivo
actualmente leído
En las siguientes partes, comenzaremos a interactuar con la tarjeta SD y transferiremos las
bibliotecas a ATtiny2313. Hasta entonces, ¡cuídate!
Pase a la siguiente parte de este tutorial

PUBLICADO POR

Joonas Pihlajamaa
Codificación desde 1990 en Basic, C / C ++, Perl, Java, PHP, Ruby y Python, por nombrar algunos. También está
interesado en matemáticas, películas, anime y ocasionalmente slashdot de vez en cuando. Ah, y también tengo una vida
real, ¡pero no hablemos de eso! Ver todos los mensajes de Joonas Pihlajamaa
Publicado en7 de abril de 2012AutorJoonas PihlajamaaCategoríasElectrónica Etiquetasfat , fat16 , fatfs ,file allocation
table , library , petit fatfs , tutorial
20 pensamientos sobre "Simple FAT y SD Tutorial Parte 2"

1. Pingback: 3.3V UART con MAX3232CPE »Código y vida

2. Eddydice:
13 de abril de 2012 a las 02:04

No puedo esperar para ver el próximo tutorial con uc ... ..


RESPUESTA

3. Pingback: Simple FAT y SD Tutorial Parte 3 »Código y vida


4. Pingback: Simple FAT y tutorial SD Parte 4 »Código y vida
5. Pingback: Simple FAT y SD Tutorial Parte 1 »Código y vida

6. Saravanandice:
27 de octubre de 2013 a las 13:56

¿cómo podemos saber dónde comienza el clúster 0 (sector)?


RESPUESTA

1. Joonas Pihlajamaadice:
27 de octubre de 2013 a las 21:22

Comienza desde el sector después de las tablas de asignación de archivos, es decir, el clúster 0
= inicio de los datos de partición. Al menos si mi memoria me sirve correctamente, ha pasado
un tiempo desde que escribí esto.
RESPUESTA

7. Neeraj Deshpandedice:
18 de julio de 2014 a las 15:28

¿Dónde puedo obtener fat32.c y fat32.h?


Estos archivos no son parte de ninguna cremallera tutorial.
Neerous
RESPUESTA

1. Joonas Pihlajamaadice:
18 de julio de 2014 a las 16:21

Wow, parece que en algún momento comencé a hablar de FAT32 cuando me refería a
FAT16. Es solo un conjunto de errores tipográficos, todo aquí debe ser sobre FAT16. FAT32 es
un tema (un poco) más complejo que no he abordado, y también se lo deja al lector como un
ejercicio. :)
RESPUESTA

8. Joshdice:
14 de agosto de 2014 a las 03:08

Hola, ¿hay alguna manera de leer el arranque desde la SD sin el archivo .img?
RESPUESTA

1. Joonas Pihlajamaadice:
14 de agosto de 2014 a las 12:45
No estoy seguro de entenderlo, pero comenzar HxD con derechos de administrador le permite
leer la SD directamente, llegando así al sector de arranque sin crear primero un archivo .img.
RESPUESTA

1. Joshdice:
14 de agosto de 2014 a las 16:12

Hola amigo, gracias por tu respuesta solo quiere decir que tu tutorial es increíble. Ah, ya
veo. Ya puedo leer los archivos de la SD directamente, sin el archivo .img. Estoy atascado con
ese punto ahora, ¿no quiero usar el archivo .img en cualquier dirección que alguien me pueda
indicar?
RESPUESTA

1. Joonas Pihlajamaadice:
14 de agosto de 2014 a las 20:59

No estoy seguro de lo que estás tratando de lograr? ¿Lees el sector de arranque con la
PC? ¿Mirando el sector de arranque con el editor hexadecimal? ¿Lectura de la tarjeta SD con
un dispositivo integrado como AVR?
El tutorial está estructurado de modo que las dos primeras partes introducen la estructura del
sistema de archivos FAT16 y los ejemplos de código leen un archivo .img. Las partes
posteriores del tutorial luego usan el mismo código en AVR para leer archivos de la tarjeta SD
directamente.
RESPUESTA

1. Joshdice:
15 de agosto de 2014 a 01:07

Hola joonas, revisé todo tu tutorial. Estoy impresionado con eso. Sí, estoy implementando el
código en un dispositivo incrustado, y estoy leyendo perfectamente desde archivos de la tarjeta
SD, como si (ret = fat16_open_file ("TEST", "TXT")) que funciona, puedo leer el archivo y ver
qué hay dentro de él y eso no está en el archivo .img, así que básicamente todo lo que quiero
hacer ahora es saber qué archivos hay dentro de la tarjeta sd y luego leerlos como test1 test2
test3 y usarlos para mi proyecto pero no lo hago quiero usar el archivo .img más o menos lo
que estás haciendo con read_root.c pero sin .img o incluso más para saber si hay más de un
archivo .img (ejemplo test1.img y test2.img) Tengo una pantalla en el proyecto para que pueda
mostrar el texto en mi LCD (puedo mostrar todos los archivos en la pantalla LCD para que el
usuario elija algo) ¿sabes lo que quiero decir, señor?
Muchas gracias por todo tu tiempo

9. jamesdice:
30 de marzo de 2015 a las 02:12
¿Cómo escribirías a un Fat16?
RESPUESTA

1. Joonas Pihlajamaadice:
30 de marzo de 2015 a las 10:54

Esa es una cuestión más compleja. Tengo la funcionalidad en proceso y si logro hacerlo,
escribiré sobre ello, pero su opción actual es verificar algunas bibliotecas FAT / SD más
extensas para ver cómo lo hacen.
RESPUESTA

10. blakedice:
16 de agosto de 2016 a las 09:22

Muy buen tutorial muchas gracias,


hay algo que realmente no entiendo;
por qué en read_file.c, en la parte donde se accede al siguiente número de clúster en la FAT es:
fseek (in, fat_start + cluster * 2, SEEK_SET);
en lugar de:
fseek (en, fat_start + cluster + 2, SEEK_SET); ?
¿Por qué se duplica el desplazamiento?
RESPUESTA

1. Joonas Pihlajamaadice:
16 de agosto de 2016 a las 23:22

Casi me he olvidado de este código, pero probablemente es porque cada elemento tiene un
número de 16 bits, es decir, dos bytes de largo ...
RESPUESTA

1. blakedice:
17 de agosto de 2016 a las 06:57

Sí eso es todo, gracias


RESPUESTA

11. Bryan Rodríguezsays:


27 de noviembre de 2017 a las 23:05
Estoy contento de haber encontrado esto, de hecho estoy implementando FAT32 para un
proyecto universitario y esta información ha sido excelente para mí.
¡Gracias!

Anda mungkin juga menyukai