Simple FAT y SD Tutorial Parte 4
En las partes anteriores de este tutorial , hemos construido tanto una biblioteca una biblioteca FAT FAT como un programa un programa de de prueba para comunicarnos comunicarnos con con la tarjeta SD . Ahora es el momento de terminar con las partes finales del código para leer un archivo de la tarjeta SD e imprimirlo. imprimirlo. Esta parte utilizará la biblioteca FAT previamente hecha con algunos ajustes: resultó que algunos bytes y valores de tamaño de palabra necesitaban conversión conversión explícita para unsigned longque los cálculos funcionaran como debían. La nueva biblioteca y todos los códigos que se muestran aquí se pueden encontrar desde el zip del proyecto actualizado .
Inicializando la tarjeta SD automáticamente 1. 2. 3. 4.
En lugar de presionar manualmente manualmente 1, 2 y 3, ahora escribiremos una sola función para inicializar la tarjeta SD al modo SPI. Para las tarjetas SD estándar (no de alta capacidad SDHC), es suficiente para: Enviar pulsos de reloj para 80 ciclos ("leer" 10 bytes) Enviar comando 0x40 (debe devolver 1) Enviar el comando 0x41 hasta que devuelva 0 (devuelve (devuelve 1 mientras la tarjeta está ocupada) Enviar el comando 0x50 para establecer el tamaño del bloque de lectura (así como el tamaño del bloque de escritura) Aquí está el código para hacer exactamente eso (sd_sector y sd_pos se usarán en breve): unsigned long sd_sector ; unsigned short sd_pos ; char SD_init () { char i ; //] r: 10 CS_DISABLE (); (); para para ( i = 0 ; i < 10 ; i ++) // inactivo para 1 bytes / 80 relojes SPI_write ( 0xFF ); // [0x40 0x00 0x00 0x00 0x00 0x95 r: 8] hasta que obtengamos "1" para "1" para ( i = 0 ; i < 10 && SD_command
( 0x40 , 0x00000000 , 0x95 , 8 ) ! = 1 ; i ++) _delay_ms _delay_ms ( 100 ); if ( i == 10 ) // la tarjeta no respondió a la inicializa inicialización ción return - 1 ; // CMD1 hasta que la tarjeta salga de inactividad, pero un máximo de 10 veces para veces para ( i = 0 ; i < 10 && SD_command ( 0x41 , 0x00000000 , 0xFF ,
8 ) ! = 0 ; i ++) _delay_ms ( 100 ); si ( i == 10 ) // la tarjeta no salió de la devolución inactiva - 2 ; // SET_BLOCKLEN a 512 SD_command ( 0x50 , 0x00000200 , 0xFF , 8 ); sd_sector = sd_pos = 0 ; return 0 ; }
Lectura de datos de la tarjeta SD Leer datos de tarjetas SD (además de escribirlos) se realiza en bloques discretos. Al comando de lectura se le asigna el desplazamiento de los datos a leer, y devuelve X bytes, donde X es el tamaño del bloque de transferencia transferencia establecido durante la inicialización. inicialización. Para las tarjetas SD normales, el tamaño del bloque puede ser menor que el 512 utilizado en el código de inicialización, pero para las tarjetas SDHC y SDXC de mayor capacidad el tamaño del bloque es siempre de 512 bytes, así que decidí usarlo para que hubiera menos cambios de código en caso de que quisiera soportar tarjetas tarjetas SDHC más adelante. Como quería admitir sistemas que solo tienen 128 o 256 bytes de SRAM (si recuerda de partes anteriores, la biblioteca biblioteca FAT usa un búfer de 32 bytes), no es posible leer todos los 512 bytes en la memoria. En cambio, capturamos capturamos una pequeña ventana de los datos. Por ejemplo, para leer los bytes 32-63 (indexación basada en cero, por lo que 32 bytes comenzando desde el 33º byte) desde un sector sector de 512 bytes, bytes, emitimos un comando comando de lectura lectura para todo el sector, sector, pero descartamos los primeros 32 bytes, luego capturamos el siguiente 32 bytes, y luego omita los 448 bytes restantes más 2 bytes CRC. Aquí está el comando de lectura: // TODO: Esta función no se cerrará correctamente correctamente si la tarjeta SD no hace lo que se debe anular la SD_read ( sin firmar larga sector , sin firmar corto desplazamiento , sin firmar Char * búfer , sin firmar corta len ) { unsigned corta i , pos = 0 ; CS_ENABLE (); SPI_write ( 0x51 ); SPI_write ( sector >> 15 ); // sector * 512 >> 24
SPI_write ( sector >> 7 ); // sector * 512 >> 16 SPI_write ( sector << 1 ); // sector * 512 >> 8 SPI_write ( 0 ); // sector * 512 SPI_write ( 0xFF ); ); para para ( i = 0 ; i < 10 && SPI_write ( 0xFF ) =! 0x00 ; i ++) {} // esperar a 0 para 0 para ( i = 0 ;
i < 10 && SPI_write ( 0xFF ) ! = 0xFE ; i ++) {} // esperar el inicio de datos para datos para ( i = 0 ; i < offset ; i ++) // "saltar" bytes SPI_write ( 0xFF ); ); para para ( i = 0 ; i < len ; i ++) // leer len bytes buffer [ i ] = SPI_write
( 0xFF ); for ( i + = offset ; i < 512 ; i ++) // "saltar" nuevamente SPI_write ( 0xFF ); // omitir suma de comprobación SPI_write ( 0xFF ); SPI_write ( 0xFF ); CS_DISABLE (); }
El comando de lectura (0x51) es muy parecido al resto de los comandos SD y toma una dirección de 32 bits: usamos el número de sector y lo multiplicamos multiplicamos por 512 ( sector<<9). Tenga en cuenta que las tarjetas SDHC utilizan direcciones de sectores y no de bytes, por lo que no no serían necesarias necesarias para SDHC. SDHC. Después de enviar enviar el comando, comando, la tarjeta SD responde con "0" para indicar que se recibió el comando, y luego envía 0xFF hasta que los datos estén listos, en ese punto envía 0xFE, luego los 512 bytes de datos y finalmente 2 bytes de suma de comprobación. ¡Muy claro!
Proporcionar funciones de disco de la biblioteca FAT Como probablemente recuerde, ahora necesitamos proporcionar a la biblioteca FAT dos funciones para navegar alrededor de la tarjeta SD: fat16_seek() y fat16_read() . Ahora que tenemos nuestra SD_read() función flexible , solo se trata de mantener dos punteros, sector SD actual ( sd_sector) y desplazamiento dentro de ese ( sd_offset), que
acabamos de establecer cuando la biblioteca quiere buscar, y pasar a SD_read cuando la biblioteca FAT FAT quiere leer bytes bytes (e incrementar incrementar los punteros luego). luego). Sin más preámbulos, preámbulos, aquí están las funciones de envoltura: void fat16_seek ( desplazamiento largo sin signo ) { sd_sector = offset >> 9 ; sd_pos = desplazamiento & 511 ; } char fat16_read ( bytes char sin signo ) { SD_read ( sd_sector , sd_pos , fat16_buffer , bytes ); sd_pos + = ( sin firmar cortos ) bytes ; if ( sd_pos
== 512 ) {
sd_pos = 0 ;
sd_sector ++; } bytes } bytes de retorno ; }
Tenga en cuenta que la función de lectura puede ser muy simple porque sabemos que las lecturas nunca cruzan los límites del sector: la biblioteca FAT FAT lee 32 bytes a la vez, y 512 es un múltiplo de eso. Si eso no fuera cierto, la función de lectura necesitaría alguna lógica adicional para manejarlo. manejarlo.
Envolviéndolo todo Ahora tenemos todas las funciones de ayuda que necesitamos. Tenemos el UART. Hablamos a través del SPI. Nosotros ordenamos la tarjeta SD. Entendemos la FAT. Aquí hay una función principal simple simple para disfrutar disfrutar de nuestros logros leyendo leyendo un archivo llamado llamado README.TXT y mostrándolo en UART: int main ( int argc , char * argv []) { char i , ret ; desplazamiento corto = 0x1B0 ; USARTInit ( 64 ); // 20 MHz / (16 * 19200 baudios) - 1 = 64.104x SPI_init (); uwrite_str ( "Iniciar \ r \ n" ); if ( ret = SD_init ()) { uwrite_str ( "SD err:" ); uwrite_hex ( ret );
return - 1 ; } if ( ret = fat16_init ()) { uwrite_str ( "FAT err:" ); uwrite_hex ( ret ); return - 1 ; } if ( ret = fat16_open_file ( "README" , "TXT" )) { uwrite_str ( "Abrir:"
);
uwrite_hex ( ret ); return - 1 ; }
while ( fat16_state . file_left ) { ret = fat16_read_file ( FAT16_BUFFER_SIZE ); ); para para ( i = 0 ; i < ret ; i ++) USARTWriteChar ( fat16_buffer [ i ]); } return 0 ; }
Aquí está la salida de nuestro glorioso programa de prueba:
¡Eso es todo! Ahora puede leer tarjetas SD con su ATmega. Y lo mejor de todo es que también debes entender cada rincón y cada grieta del código utilizado para hacer eso. ¡Muy genial! Si tiene un ATmega con 1 kB de SRAM, puede implementar fácilmente la escritura en SD, o escribir algún buen código para navegar por los directorios de la tarjeta SD. O tal vez le gustaría actualizar la biblioteca para admitir tarjetas FAT32 o SDHC. ¡La decisión es tuya!
Gracias por leer el tutorial, espero que lo haya encontrado útil. Recuerde suscribirse al feed para obtener más más tutoriales y proyectos interesantes interesantes en el futuro. futuro. Y, por supuesto, supuesto, ¡todos los comentarios son bienvenidos también!
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 en en27 de abril de 2012Auto 2012Autor r Joonas Joonas PihlajamaaC Pihlajamaa Categorías ategoríasElectrónica Etiquetas tiquetasatmega88 , fat ,sd , sdhc , tutorial , uart
33 pensamientos sobre "Simple FAT y SD Tutorial Parte 4" 1.Pingback: 1. Pingback: Análisis Análisis lógico con Bus Pirate »Código y vida
2.
Jack dice: dice:
17 de mayo de 2012 a las 20:07
Hola: parece que falta un enlace a la Parte 2. (Lo encontré de todos modos, pero presumiblemente presumible mente una caza furtiva no es lo que pretendías ... Cosas útiles: gracias por aguantarlo. Jack RESPUESTA
1.
jokkebk dice: dice:
17 de mayo de 2012 a las 22:57
Hmm, hay un enlace a la parte 2 desde el principio en este artículo, aunque un poco escondido. Sería bueno tener algún tipo de "tabla de contenidos" automática automática para el conjunto
completo de tutoriales, pero hasta ahora acabo de vincular cada publicación con la parte anterior. De todos modos, ahora que me lo recordó, también agregaré enlaces a la siguiente parte hasta el final de cada parte. RESPUESTA
3.Pingback: 3. Pingback: el el analizador lógico más simple del mundo por $ 5 »Código y vida
4.
David Cook dice: dice:
28 de mayo de 2012 a las 07:38
Muchas gracias, Sr. Pihlajamaa, por este maravilloso tutorial. Les ahorras a los principian principiantes tes mucho tiempo al ayudarlos a superar los puntos de fricción. Dave Cook RESPUESTA
5.
Stephendice:
27 de septiembre de 2012 a las 09:11
buena leccion RESPUESTA
6.
nyoman yudi dice:
12 de diciembre de 2012 a las 03:23
Gracias ... finalmente lo hice funcionar después de 2 días modifico tu ejemplo e hice attiny2313 play wav el video está aquí aquí http://www.youtube.com/watch?v=N-ZO4oiwpXY Una vez más, muchas gracias RESPUESTA
1.
jokkebk dice: dice:
27 de diciembre de 2012 a las 15:27
De nada. ¡Qué bueno ver este proyecto! Todavía no he intentado reproducir ningún audio con proyectos de MCU, MCU, su ejemplo me me da ganas de probarlo. probarlo. :) RESPUESTA
7.
Gokaydice:
31 de diciembre de 2012 a las 22:45
Hola, es un tutorial muy fructífero. En mi opinión, tengo un problema con la escritura de un solo bloque (512 bytes) de datos en la tarjeta SD. Inicialicé la tarjeta SD (CMD0,0x95) (CMD1,0xFF) y configuré la longitud del bloque ( CMD16,0xff) con éxito. A continuación puede ver el resultado. CMD 40 FF 01 FF FF FF FF FF FF FF CMD 41 FC 07 FF FF FF FF FF FF FF CMD 41 FC 07 FF FF FF FF FF FF FF CMD 41 FF FF FF FF FF FF FF FF CMD 41 FF 00 FF FF FF FF FF FF FF CMD 50 FF 00 FF FF FF FF FF FF FF El problema es que cuando envío el comando 'Single Block Write' a la tarjeta SD recibo una respuesta 0x00. ¡Sé que está bien! Después de enviar datos token que son 0xFE y enviar 512 bytes de datos y enviar dos bytes bytes CRC consecutivamente. consecutivam ente. No puedo obtener ninguna respuesta si los datos son aceptados o no. A continuación, puede ver el resultado. CMD 58 FF 00 FF FF FF FF FF FF FF. Y a continuación puedes encontrar mi código abreviado. respuesta = send_SDCommand (WRITE_SINGLE_BLOCK, startBlock << 9,0xFF); if (respuesta! = 0) { respuesta de retorno; } SD_CS_ASSERT (); // CS Low // ???????????? lo que está pasando aquí SPI_transmit SPI_trans mit (0xFE); token de datos tenemos que enviarlo antes para (i = 0; i <512; <512; i ++) SPI_transmit SPI_trans mit ( 'A'); SPI_transmit (0xFF); // \ SPI_transmit (0xFF); // \ 16 bit dummy CRC // Cada bloque de datos escrito en la tarjeta es reconocido por un token de respuesta de datos. Es un byte largo y tiene el siguiente formato // XXX0SSS1 // S = STATUS bits (3) // si S es 010 = Datos aceptados. // si S's 101'-Data rechazado debido a un error CRC. respuesta = SPI_transmit (0xFF); // 110'-Datos rechazados debido a un error de escritura. if ((respuesta & 0x1F)! = 0x05) { SD_CS_DEASSERT (); rs232_Stream ("0x05 errorrrrrr ..."); return 1; } Gracias ! RESPUESTA
1.
jokkebk dice: dice:
20 de abril de 2013 a las 17:05
Parece que has hecho un buen trabajo en la parte escrita, ¡felicidad ¡felicidades es por eso! Lamentablemente, Lamentablemente, no he intentado escribir tarjetas SD, así que no puedo ayudarte mucho. ¿Tal vez algún foro de microcontroladores tendría personas que podrían ayudar? Lo único que me viene a la mente es que si quieres enviar pulsos de reloj a la tarjeta para que pueda procesarlos, procesarlos, tal vez si envías envías datos falsos después después de escribirlos, escribirlos, obtienes una respuesta respuesta después de un tiempo. ? Ha pasado un año desde que jugué con SD y SPI, así que estoy muy mal en esta, sin embargo ... RESPUESTA
8.
Ganeshdice:
9 de enero de 2013 a las 17:40
gracias un trabajo muy útil RESPUESTA
9.
Ahmed Eldamarawy dice:
30 de abril de 2013 a las 02:32
gracias por la presentación agradable y organizada que hizo para todos nosotro nosotros. s. Espero que también puedan explicar cómo escribir en FAT16 SD. RESPUESTA
1.
jokkebk dice: dice:
9 de junio de 2013 a las 15:49
¡Gracias! Probablemente escribiré una publicación sobre escritura FAT en los meses de verano, estad atentos. :) RESPUESTA
10.
andraz dice:
25 de agosto de 2014 a las 19:15
Hola. Gracias por este tutorial, ha ayudado mucho. Tengo un problema sin embargo. En fuction sd_init, la oración para (i = 0; i <10 && SD_command SD_command (0x40, 0x00000000, 0x95, 0x95, 8)! = 1; i ++) no se compila ya que SD_comman SD_command d es nulo. Tenemos R1 escrito en buffer. Siempre obtengo la respuesta: FF01FFFFFFFFFFFF ¿Es correcto y qué función debería devolver SD_command? Me disculpo si sueno estúpido, pero soy muy nuevo en esto ... RESPUESTA
1.
Joonas Pihlajamaadice:
25 de agosto de 2014 a las 20:53
No hay problema. problema. El único problema problema es que en 30 30 meses, también he he olvidado todo sobre sobre esto. El comando SD_ está cubierto en la parte previa del tutorial, es posible que desee probarlo primero, la parte parte 3 también cubre cubre al menos parcialmente parcialmente el supuesto supuesto resultado. RESPUESTA
1.
andraz dice:
26 de agosto de 2014 a las 12:24
Entiendo. He leído la parte anterior y SD_command está declarado como vacío por lo que no devuelve nada. RESPUESTA
1.
Joonas Pihlajamaadice:
26 de agosto de 2014 a las 12:56
Ah, lo siento, mi mal. El archivo fat88.c en el zip del proyecto actualizado actualizado (enlace desde el principio en esta esta parte 4) contiene contiene una versión versión que devuelve devuelve char sin signo. RESPUESTA
1.
andraz dice:
27 de agosto de 2014 a las 19:28
Vale genial. Gracias.
11.
tzdice:
29 de enero de 2015 a las 01:57
También escribí un controlador de tarjeta SD, principalmente para openlog, pero tengo algunas variaciones. La lib está en https://git https://github.com/tz1 hub.com/tz1/sparkfun /sparkfun/tree/master/f /tree/master/fat32lib at32lib , pero también hay un "hiperlog" que agrega una RAM SPI externa a la mezcla, ya que parte de la especificación especificació n de la tarjeta SD es que puede llevar hasta 250mS para hacer la limpieza, por lo que podría perder datos a velocidades más altas. RESPUESTA
12.
Omkar dice:
1 de septiembre de 2015 a las 15:31
Por favor, dame el enlace para la parte 1 y la parte 2 Además, si este código se modifica para pic16 wud, ¿funciona igual? RESPUESTA
1.
Joonas Pihlajamaadice:
1 de septiembre de 2015 a las 19:41
Puede encontrar el enlace a la primera parte al comienzo de este artículo: http://codeandlife.com/2012/04/02/simple-fat-and-sd-tutorial-part-1/ Al final de la parte 1, hay un enlace a la parte 2, y así sucesivamente. :) Pic16 debería funcionar igual, a menos que sus voltajes sean diferentes a los de un chip AVR. RESPUESTA
1.
Omkar dice:
4 de septiembre de 2015 a las 13:51
Por favor podría decirme las modificaciones para una tarjeta sd fat32 RESPUESTA
1.
Joonas Pihlajamaadice:
6 de septiembre de 2015 a las 20:11
Es posible que originalmente haya incluido algunos enlaces a FAT32, pero básicamente tendrá que leer las especificaciones especificaciones FAT32 usted mismo y ver cuál es la diferencia, yo mismo no lo he hecho. Google rápido en "especificació "especificación n fat32" dio, por ejemplo, esta página: https://www.pjrc.com/tech/8051/ide/fat32.html RESPUESTA
13.
Omkar dice:
5 de septiembre de 2015 a las 14:39
El comando sd_command es una función vacía así que ¿cómo puede devolver cualquier valor? Usted ha utilizado el valor de retorno de la función sd_command en el ciclo for en la función sd_init. Por favor dígame cualquier solución. Muchas gracias RESPUESTA
1.
Joonas Pihlajamaadice:
6 de septiembre de 2015 a las 20:10
¡Hola! Buena pregunta. Eché un vistazo a mis archivos fuente y hay una versión de SD_command SD_comma nd en fat88.c que de hecho devuelve un valor. Entonces deberías verificarlo. :) RESPUESTA
1.
Omkar dice:
9 de septiembre de 2015 a las 14:48
Has usado la función fat16_read fat16_read en la cual hay una variable fat_buffer pero no está definida en ningún lado. Entonces, ¿dónde está definido definido?? Gracias. RESPUESTA
14. Pingback: Pingback: Simple Simple FAT y SD Tutorial Parte 3 | Código y vida 15. Pingback: Pingback: Picoscope Picoscope 2208B MSO Review - Código y vida
16.
Jdice:
22 de junio de 2017 a las 15:19
Una simple pregunta. Me irrita, tal vez tengo algo mal, pero ¿por qué se define un "desplazamiento "desplazamien to corto = 0x1B0;" en el método principal, que no se utiliza en absoluto? RESPUESTA
1.
Joonas Pihlajamaadice:
23 de junio de 2017 a las 12:58
Probablemente sobrante de alguna iteración anterior de mi código. Puedes (intentar) ignorarlo. :) RESPUESTA
17.
Vinoddice:
11 de abril de 2018 a las 16:15
Tutorial muy útil para principiantes. Solo estaba buscando este rey de cosas elaboradas. Planeo hacerlo en un chip MSP430. Empezaré con las lecturas de lectura RAW y luego cambiaré a FAT32 para que sea legible en una PC. Muchas gracias. Vinod RESPUESTA
18.
Sheza Adice:
28 de junio de 2018 a las 21:16
Hola, Estoy intentando implementar esto usando un ATmega 2560. Cambié los parámetros relevantes en fat88.c para hacerlo compatible compatible con mi dispositivo. Sin embargo, para ejecutar este proyecto como está, ¿qué archivos de su archivo comprimido usaría? Si los uso todos, mi compilador arroja errores ya que las mismas funciones están definidas en múltiples archivos / repetidos. RESPUESTA
1.
Joonas Pihlajamaadice:
28 de junio de 2018 a las 23:37
Puede mirar el archivo Makefile dentro del zip, contiene instrucciones sobre qué archivos componen qué archivo .hex (destinado a compilar el proyecto desde la línea de comandos): fat88.elf: fat88.o fat16.o. Esto significa que fat88.elf (archivo intermedio para obtener el .hex) depende de fat88.o y fat16.o, los cuales la utilidad make puede compilar a partir de los archivos .c correspondientes. correspond ientes. Entonces fat88.hex usa fat88.c y fat16.c. Del mismo modo, test_spi.hex se puede compilar a partir de test_spi.c y fat16.c Ha pasado mucho tiempo desde que hice esto, así que no hay garantías si eso funcionar funcionaráá en un estudio Atmel reciente sin errores, lo siento ...