Disclaimer Visitante # Estadísticas
Esta página se actualizará proximamente...


Javier Rosell Reza
Contacto



ABSTRACT

 

            El presente trabajo trata sobre la tecnología SD (Secure Digital) Card en memorias flash y el diseño de un dispositivo basado en un microcontrolador que pueda realizar operaciones de lectura y escritura sobre ellas. Se presenta primero un panorama sobre el funcionamiento y características de la tarjeta, de la que destaca el hecho que comparte características con un disco duro tradicional, pero que adicionalmente incorpora tecnologías para la encriptación en el almacenamiento de datos, lo que ha dado gran auge a esta tecnología. Por otro lado, su interfaz es muy sencilla ya que la comunicación se lleva por medio de solo 9 pines, pero que en este proyecto solo se usaran 2, ya que se implementara el protocolo SPI estándar de transmisión serial, que basa la comunicación en el envío de comandos y la obtención de la respuesta a la petición realizada con el comando. Por ultimo, mediante el uso de un microcontrolador 8051 se implementa un sistema para realizar operaciones de lectura y escritura de archivos de texto a tarjetas SD de diferentes capacidades (8MB, 16MB y 64MB).Además se realizan la lectura de otros registros de la tarjeta para poner al alcance del usuario del sistema características particulares de cada una de las tarjetas de tal forma que se puede acceder tanto el área de almacenamiento de la misma como los registros proporcionados por el fabricante para la descripción de cada producto.


INTRODUCCIÓN

 

            Hace algunos años el mercado dio a conocer las tarjetas Flash SD Card como una opción de almacenamiento para cámaras digitales, PDAs, pocket PCs, etc. El avance que han tenido ha sido asombroso y actualmente se pueden conseguir capacidades de almacenamiento de hasta 1 GB, lo cual las hace candidatas a convertirse dentro de unos años en una fuerte tecnología para almacenamiento de cualquier tipo de datos, no solo en dispositivos pequeños, sino inclusive incorporarlas en otras aplicaciones mas complejas como una computadora portátil para reducir aún más el tamaño de éstas.

 

            El estándar SD es reservado y propietario, es decir la información acerca del mismo posee un costo, de tal manera que para fines sólo académicos resulta bastante difícil conseguirla. A lo largo de este informe, describiremos características acerca de esta tecnología. Se dividirá esta información en 4 etapas: características físicas y eléctricas de la tarjeta, diseño del sistema con el host (“slot” de la tarjeta), protocolo de comunicación y la estructura interna de la misma.

 

            Finalmente, ya que el propósito perseguido es mostrar como acceder a la información de una tarjeta con tecnología SD utilizando un microcontrolador programado en lenguaje ensamblador; en cada sección de esta última parte se irá citando el código y la lógica programada relacionada. Misma que incluye una aplicación de ejemplo que permite dispositivo capaz de realizar operaciones de acceso a la tarjeta, más específicamente de la lectura y escritura de archivos a la tarjeta, a través de cualquier aplicación de comunicación serial de de la computadora.

 

 


CARACTERÍSTICAS DE LA TARJETA

 

Características generales

 

            La comunicación de las tarjeta SD esta basada en una interfase de 9 pines: una línea de reloj (CLK), una de comandos (COMMAND), 4 líneas de datos y 3 líneas mas de alimentación. Esta diseñada para operar en rangos bajos de voltaje. El  protocolo de comunicación fue definido como parte de su especificación, pero también soporta operaciones MultiMediaCard. Además, también ofrece la posibilidad de usar el protocolo estándar SPI, lo cual la hace compatible con controladores ya existentes.

 

            La tarjeta contiene chips de memoria flash diseñados especialmente para su uso como medio de almacenamiento. Además de esto, las tarjetas incluyen un controlador inteligente que administra diferentes protocolos de comunicación, algoritmos de seguridad para la protección contra copia no autorizada de la información almacenada, algoritmos de corrección de errores de código, manejo de defectos, diagnósticos, y administración de potencia.

 

 

Características físicas

 

Las tarjetas son muy ligeras y pequeñas pesan cerca de 20g y miden cerca de 32mm de largo,  24mm de ancho y 2.1mm de grosor. Lo que las hace excelentes medios de almacenamiento para dispositivos móviles.

 

Figura 1. Características físicas de la tarjeta

 

Características eléctricas

 

            Las tarjetas trabajan en un rango de voltaje de 2.7 a 3.6 V y el consumo de corriente (Ta=25C@ 3V) varía dependiendo del estado en que se encuentren y tiene como máximos los siguientes:

 

Estado                 Valor                  Unidades

 

 

            La interfase de comunicación es a través  de 9 pines, de los cuales 6 necesitan resistencias de pull-up: línea de habilitación de la tarjeta (CS), línea de envío de comandos (CMD) y las 4 línea de datos(DAT3-0).

            Figura 2.  Características eléctricas de la tarjeta

 

 

Modos de operación

 

            Las tarjetas con tecnología SD Card tienen dos modos de operación, el modo SPI y el modo SD. Las diferencias entre los modos involucran principalmente la función de cada uno de los pines, el protocolo usado y los comandos que son específicos de cada modo. A continuación se detalla las características principales de ambos modos.

 

 

Modo SD

 

            Este modo permite que se utilicen las terminales de datos (D0 - D3) en forma bidireccional, lo cual da mayor ancho de banda durante las transmisiones. Por otro lado, los comandos se transmiten por la línea CMD en forma serial, y por ultimo la respuesta de la tarjeta al comando se transmite por la línea CMD.  La principal ventaja de este modo Al inicio de la transferencia solo se envían datos por D0, ya después se pueden ampliar el ancho de banda de los datos hasta D3. La aplicación de cada uno de estos pines es la siguiente:

 

 

Modo SPI

 

            En este modo la tarjeta solo permite la entrada de datos por DATA IN (2) y la salida de datos por la terminal DATA OUT(7). Los comandos se mandan también por la terminal DATA IN. La habilitación de la tarjeta se hace por la terminal CS(1). La señal de CLK que se envía desde el host o controlador es la que establece la velocidad de la comunicación, es por esto que el diseño del host se facilita ya que no se tienen problemas de desincronización cuando el host es más lento que la tarjeta. Sin embargo, como sólo se envían los datos por una terminal se tiene menor utilización del potencial de la memoria.  Ya que el protocolo SPI es un estándar muy conocido, se decidió implementar este protocolo de comunicación con la tarjeta.

 

 

 

DISEÑO DEL DISPOSITIVO

           

Sistema de desarrollo

 

            Para la implementación del dispositivo fue usado como host, un slot de inserción para la tarjeta, el cual permite acceder físicamente más fácil a las 9 líneas de comunicación de la tarjeta, además agrega otras 2 líneas al sistema que no pertenecen propiamente a la tarjeta sino al host usado (slot de inserción de la tarjeta) las cuales nos permiten saber si la tarjeta ha sido insertada en el host y/o si está protegida contra escritura. Ambas señales son activas en estado bajo y funcionan mediante un sencillo mecanismo físico, como se indica a continuación:

                                     

                                                  

                            No habilitada                                       Habilitada

 

            Este “host” está conectado a un sistema de desarrollo basado en un microcontrolador 8051 el cual había sido previamente construido para la clase de Microprocesadores I. Dicho sistema tiene las siguientes características:

 

  • El sistema cuenta con dos modos, los cuales se seleccionan cambiando el interruptor de dos polos que está conectado al pin 1 de la GAL20V8, la que La genera toda la lógica de selección para al elegir la ejecución desde memoria ROM o memoria RAM.

 

  • La EPROM contiene un el programa principal del sistema que permite la recepción, procesamiento y almacenamiento de un programa en la memoria RAM para su posterior ejecución y verificación.

 

  • Los 8KB de memoria RAM externa están localizados a partir de la dirección 0000H en el mapa de memoria, en esta parte se almacena el programa que se recibe a través del serial y que se va a verificar.

 

  • La conversión de las señales generadas y recibidas en las terminales del microcontrolador para que sean compatibles con el estándar RS-232C y viceversa, se usa un MAX232.

 

El diagrama esquemático del sistema de desarrollo, así como los componentes necesarios para dicho sistema se muestran a continuación:


Figura 3.  Sistema de desarrollo

 

Resistencias                 Capacitores                 Circuitos integrados                 Otros componentes

(1/4 watt, 5%)                C1,C2 — 20 pF              Microcontrolador 8051                SW1 Interruptor NA

R1 — 8.2 KW                      C3 — 10 µF                   GAL 20V8                                 SW2 — Push button

R2 — 100 W                  C4-7 — 22 µF                Latch 74LS373                          Conector DB9 macho

R3 — 10 KW                                                                       RAM 6264

                                                Cristal                          ROM 2716

                                    XTAL1 12 MHz               MAX232

            Para la interfase de este sistema con la tarjeta se utilizó el puerto P1 del sistema de desarrollo. Las conexiones que se realizaron a este sistema se muestran a continuación donde se observan las resistencias de pull-up (DIN, CS) y las líneas extras que son parte del host (CARD INSERT y WRITE ENABLE). Para obtener el voltaje en un rango de voltaje entre 2.7 y 3 V, se uso una resistencia y un diodo Zener.

 

Figura 4.  Interfase de la tarjeta con el sistema de desarrollo

 

 

COMUNICACIÓN CON LA TARJETA


Protocolo de comunicación

 

            El protocolo de comunicación esta basado en comandos, los cuales son enviados serialmente a través de DOUT y la respuesta al comando es recibida a través de DIN.

 

 

 

 

 

 

 

 


Los comandos de la tarjeta tienen un tamaño fijo (6 bytes). Donde la convención es que primero se envía el bit más significativo del byte más significativo del comando. El formato de los comandos es el siguiente:

 

 

 

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 ultimo byte representa el CRC7 1 que es calculado por la tarjeta de la siguiente forma:

 

 

 

 

Existen muchos comandos para la tarjeta en modo SPI, los cuales se dividen en distintas clases dependiendo de las funcionalidades del comando.

 

 

Figura 5.  Tabla de clasificación del los comandos en modo SPI

 

 

 

 

[1] Ver mayor descripción del CRC7 en SanDisk Manual v1.9 pág 3-13

 

A continuación se presenta una tabla con los comandos, indicando el numero de comando, si es aplicable en el modo SPI, el argumento del comando y el tipo de respuesta que tiene, además de la abreviación del nombre del comando y una pequeña  descripción del mismo.

 

 

 

Sin embargo, para este proyecto solo se usaron los siguientes comandos:

CMD0       400000000095H                  Coloca la tarjeta en inactividad

CMD1       410000000001H                  Activa la tarjeta

 

CMD9       490000000001H                  Responde con el contenido del CSD

CMD10    4A0000000001H                  Responde con el contenido del CID

 

CMD17   510000000001H                  Permite leer un sector de la tarjeta

CMD24   580000000001H                  Permite escribir un sector en la tarjeta

La respuesta a un comando tiene tamaño estándar de 3 bytes que indica si el comando enviado tuvo éxito o no. Si el comando enviado a la tarjeta SD indica lectura a algún sector o registro, esta regresa el contenido del registro (23 bytes) o el contenido del sector (512 bytes). En este último caso existe antes del bloque de 512 bytes un token que indica la correcta recepción del comando.

           

            COMANDO   RESPUESTA                       INDICA

            CMD0             FF01FFH                               En espera

            CMD1             FF00FFH                               Activo (Lista)

 

            CMD9             (23 bytes)                               Contenido CSD

            CMD10          (23 bytes)                               Contenido CID

 

            CMD17          Token + (512 bytes) Leer un sector de la tarjeta

            CMD24          Token (Espera bloque)        Escribir un sector a la tarjeta

                                    Token (Recepción correcta)           

 

                                    Figura 6.  Respuestas para los comandos usados

 

 

Lectura y escritura de bloques

 

Para la transferencia de bloques este protocolo de comando-respuesta varía un poco.

 

 

 

 

 

 

 

 

 

 


                                    Figura 7.  Lectura de un bloque de la tarjeta

 

En el caso de la lectura por ejemplo, al enviar el comando de lectura de un bloque (CMD 17) este debe llevar como argumento la dirección del bloque de 512 bytes que se desea. Es decir, que por ejemplo para leer el sector 2 de la tarjeta, el comando 17 debe ser modificado de la siguiente forma:

 

CMD17        5100 0002 0001H

 
           

 

Una vez que el comando sea recibido por la tarjeta, esta regresa una respuesta de 3 bytes y un token de inicio, seguidos del bloque de 512 bytes y el CRC del mismo. Este token es de la forma

                                                FF FF 00 FF FE

 

Aunque en la práctica se encontraron algunas diferencias en las distintas tarjetas.

Por otro lado, en el caso de la escritura, primero se envía el comando que de igual forma que en el caso de la lectura lleva como argumento la dirección del sector que se escribirá. Sin embargo, a diferencia de la lectura después de recibir la respuesta estándar de 3 bytes que indica que el comando se acepta, el host enviara un token de inicio seguido del bloque de 512 bytes de datos y dos token de fin de datos. Por último, una vez que la transmisión del bloque termina la tarjeta envía una respuesta que incluye si hubo error y/o si la tarjeta esta ocupada.

                                    Figura 8.  Escritura de un bloque a la tarjeta

 

 

Implementación de la comunicación

 

            La comunicación bidireccional con la tarjeta para el protocolo anteriormente descrito se implementó de forma modular, primero a bajo nivel (envío y recepción de bytes) y luego a nivel comando (envío de 3 bytes), nivel respuesta (recepción de 3, 23 o 56 bytes) y nivel bloque (envío / recepción de bloques de 512 bytes).

 

Tanto en la transmisión como en la recepción de bytes, el dato debe ser valido durante la transición positiva de la señal de reloj, esto se implementó mediante el uso de constantes para reducir el código, así una constante contenía valor del dato y el bit de CLK en bajo y posteriormente el bit de CLK en alto, con lo que se lograba el dato valido al mismo tiempo que la transición positiva, sin necesidad de instrucciones adicionales que reducirían la frecuencia de transmisión.

 

 

;           CLK |  CS  |  DI   |  DO

; 3F         0       0      1        1     >> RECIBIR  (CLK EN BAJO)

; BF         1       0      1        1     >> RECIBIR  (CLK EN ALTO)

; 3F         0       0      1        1     >> ENVIAR 1

; BF         1       0      1        1 

; 0F         0       0      0        0     >> ENVIAR 0

; 8F          1       0      0        0  

           

                                    Figura 9.  Definición de constantes del programa

 

 

A continuación se muestran las rutinas de bajo nivel que sirven para el envió y recepción de un byte y mas adelante se presentaran y explicaran las de alto nivel.

 

Como se puede ver en ambas rutinas, la velocidad de la transferencia al final se dejo variable pues esta dada por directamente por la duración de la rutina WAIT, la cual en la implementación final se dejo con duración de 2 microsegundos, lo cual da una señal de reloj con frecuencia máxima de 0.25 MHz.

 

; ---------------------------------------------------------------------------------------------------------------------            

; RUTINA QUE ENVIA 1 BYTE A LA TARJETA( El byte este contenido en el Acumulador)

; ---------------------------------------------------------------------------------------------------------------------            

SNDBYTE

            MOV     R6, #8D                        ; SE ENVIARAN 8 bits

ACAS:  RLC      A                                  ; MAS SIGNIFICATIVO >> MENOS SIGNIFICATIVO

            JNC      ZERO              

ONE:    MOV     P1, #ENVIA1_B             ; 0011  

            LCALL WAIT

            MOV     P1, #ENVIA1_A             ; 1011   Cambia el CLK y el DOUT = 1

            LCALL WAIT

            SJMP   SEW

ZERO:  MOV     P1, #ENVIA0_B             ; 0001  

            LCALL WAIT

            MOV     P1, #ENVIA0_A             ; 1001   Cambia el CLK y el DOUT = 1

            LCALL WAIT

SEW:   DJNZ    R6, ACAS                    

            RET

; ---------------------------------------------------------------------------

; RECIBE UN BYTE ( Regresa el byte en el Acumulador)

; ---------------------------------------------------------------------------

LEE_BYTE:      

            MOV     R7, #8D                        ; 8 BITS POR BYTE

BBIT     MOV     P1, #RECIBE_B            ; 3F     0 011 

            LCALL  WAIT

            MOV     C, P1.5                         ; BIT RECIBIDO

            RLC      A                                  ; MAS SIGNIFICATIVOàMENOS SIGNIFICATIVO

            MOV     P1, #RECIBE_A

            LCALL  WAIT

            DJNZ    R7, BBIT

            RET

           

                        Figura 10.  Rutinas de comunicación bidireccional a bajo nivel

 

Por otro lado, las rutinas de transmisión de comandos y obtención de la respuesta se optimizaron basadas en lo anterior y quedan como se presentara a continuación.

            ; ---------------------------------------------------------------------------------------      

            ; ENVIA EL COMANDO APUNTADO POR DPTR

            ; Se envian 6 bytes bit por bit   /   Se usan R7 #bytes, R6 # bits

            ; ---------------------------------------------------------------------------------------

            SNDCMD:        

MOV     R7, #6D            ; SE ENVIARAN 6 BYTES

CLR      A                      ; A = 0

MOVC A,@A+DPTR     ; Tomo primer byte        

LCALL  SNDBYTE        

                        INC       DPTR                ; Apunto a siguiente byte

                        DJNZ    R7, BYTE          ; Si es cero ya termine

                        RET

           

                        Figura 11.  Rutina de transmisión de comando

Como puede observarse se hace uso del DPTR, esto último debido a todo el código reside en la memoria RAM, la cual en este sistema es tratada como memoria externa por el microcontrolador.

            ; ---------------------------------------------------------------------------

            ; OBTENER RESPUESTA (R0)

            ; NBYTES para numero de bytes de transferencia

            ; DPTR apunta a donde se va a guardar la respuesta

            ; B = 8 BITS

            ; ---------------------------------------------------------------------------

            GTRESP:         

MOV     R7, #8D                        ; 8 BITS POR BYTE

RESPU:MOV    P1, #RECIBE_B

LCALL  WAIT

MOV     C, P1.5                         ; BIT RECIBIDO

RLC      A                                  ; MSB à LSB

MOV     P1, #RECIBE_A

LCALL  WAIT

JNZ       R7, RESPU

MOVX   @DPTR, A

INC       DPTR

DJNZ    NBYTES, GTRESP

RET

;  ---------------------------------------------------------------------------

;   RUTINA READ BLOCK

;  ---------------------------------------------------------------------------

READBLK

            CLR      F0                                ; Primeros dos bytes del comando

            MOV     A, #51H

            LCALL  SNDBYTE                    

            MOV     A, #00H

            LCALL  SNDBYTE                    

            MOV     R1, #ADDR                   ; Enviar la dirección que accesa el comando

            MOV     A, @R1

            LCALL  SNDBYTE        

            INC       R1

            MOV     A, @R1

            LCALL  SNDBYTE

            MOV     A, #00H                        ; Dos ultimos bytes del comando

            LCALL  SNDBYTE                    

            MOV     A, #01H

            LCALL  SNDBYTE

            LCALL  WAIT40             ; Espero 40 ms

            LCALL  SUPER_TOKEN

NADA   MOV     DPTR, #RESP               ; Ahora si 512 bytes

            MOV     NBYTES, #255D

            LCALL  GTRESP

            MOV     NBYTES, #255D

            LCALL  GTRESP

            MOV     NBYTES, #2

            LCALL  GTRESP

            LCALL  WAIT40

            JB        F0,READBLK                ; Si fue buena respuesta seguir

            RET

 

 

 

 

; -----------------------------------------------------------------------------------------------------------

; RECIBE Y CHECA EL TOKEN DE COMANDOS DE LECTURA DE BLOQUE

; -----------------------------------------------------------------------------------------------------------

SUPER_TOKEN

            MOV     DPTR, #RESP               ; Voy a recibir el token. No se guarda

            MOV     NBYTES, #5D               ; así que después se sobrescribirá

            LCALL  GTRESP

            MOV     DPTR, #RESP               ; Se compara si el token es correcto

            MOVX   A, @DPTR

            CJNE    A, #0FFH, NOES          

            INC       DPTR

            MOVX   A, @DPTR

            CJNE    A, #0FFH, NOES

            INC       DPTR

            MOVX   A, @DPTR

            CJNE    A, #00H, NOES

            INC       DPTR

            MOVX   A, @DPTR

            CJNE    A, #0FFH, NOES

            INC       DPTR

            MOVX   A, @DPTR

            CJNE    A, #0FEH, NOES

            CLR      F0       

            SJMP   FINI

NOES   SETB    F0

FINI      RET

 

 

; -------------------------------------------------------------------------------------

;  LA VARIABLE RESP TIENE LOS DATOS A ESCRIBIR

; -------------------------------------------------------------------------------------

WRITEBLK

            MOV     A, #58H

            LCALL  SNDBYTE

            MOV     A, #00H

            LCALL  SNDBYTE

            MOV     R1, #ADDR                   ; Enviar la direccion del bloque

            MOV     A, @R1                         ; que accesa el comando

            LCALL  SNDBYTE        

            INC       R1

            MOV     A, @R1

            LCALL  SNDBYTE

            MOV     A, #00H                        ; Dos ultimos bytes del comando 24

            LCALL  SNDBYTE                    

            MOV     A, #01H

            LCALL  SNDBYTE

            LCALL  WAIT40             ; Espero 40 MS                                                

 

                                                            ; RECIBIR LA RESPUESTA

            MOV     DPTR, #R_CORTA         ; Voy a recibir la respuesta al comando

            MOV     NBYTES, #3D               ; no me importa asi que lo voy a desechar

            LCALL  GTRESP

            MOV     DPTR, #R_CORTA         ; Checa los primeros 3 bytes para confirmar

            MOVX   A,@DPTR                     ; que el envio del comando fue exitoso

 

            CJNE    A,#0FFH,OTRAVEZ       ; FF00FF

            INC       DPTR

            MOVX   A,@DPTR

            CJNE    A,#00H,OTRAVEZ

            INC       DPTR

            MOVX   A,@DPTR

            CJNE    A,#0FFH,OTRAVEZ

            CLR      F0                                ;  Indico que hubo exito

            SJMP   YA_                              ;  Salto al fin de la rutina

OTRAVEZ         SETB    F0                                ;  Indico que fallo, para intentar otra vez

YA_      JB        F0,WRITEBLK               ; Si fracaso intentar de nuevo

 

            MOV     A, #0FEH                      ; Tokens de inicio

            LCALL  SNDBYTE

 

            MOV     DPTR, #RESP               ; Apunto al bloque a enviar

            MOV     B, #255D                       ; Primeros 255 bytes

ESCR1 MOVX   A, @DPTR

            LCALL  SNDBYTE        

            DJNZ    B, ESCR1                     ; Hasta que termine el ciclo1

            MOV     B, #255D          

ESCR2 MOVX   A, @DPTR

            LCALL  SNDBYTE

            DJNZ    B, ESCR2

            MOV     B, #2D             

ESCR3 MOVX   A, @DPTR

            LCALL  SNDBYTE

            DJNZ    B, ESCR3

 

            MOV     A, #0FEH                      ; Tokens de fin de envio escritura

            LCALL  SNDBYTE

            MOV     A, #0FEH

            LCALL  SNDBYTE

 

            MOV     DPTR, #R_CORTA         ; Voy a recibir la respuesta del write block

            MOV     NBYTES, #3D  

            LCALL  GTRESP

            MOV     DPTR, #R_CORTA         ; Voy a recibir el token

            MOV     NBYTES, #3D               ; no me importa asi que lo voy a desechar

            LCALL  PRINT_RESP

            RET

 

                                    Figura 12.  Rutinas de comunicación a alto nivel

 

ORGANIZACIÓN DE LA TARJETA

 

            La organización interna de la tarjeta esta constituida por un área de almacenamiento, una zona de registros de configuración/información acerca de la misma como se puede apreciar en la Figura 13. A su vez, el área de almacenamiento se subdivide en 2 partes independientes: el área protegida, la cual no se puede acceder a menos que el dispositivo se identifique, y en el área de almacenamiento común.  Además, posee 5 registros de información, el CID, CSD, OCR, SCR y RCA1. Para los objetivos de este proyecto se desplegaron sólo los registros CID y CSD.

 

[1] Ver información sobre los registros en SanDisk Manual v1.9 pág 3-13

 

       

                                    Figura 13.  Organización física interna de la tarjeta

 

El registro CID (Card Identification Information) posee una longitud de 16 bytes y almacena un número único de identificación de la tarjeta. La figura 14 muestra el CID desplegado en desde la consola a través del microcontrolador. Obsérvese que se reciben en total 20 bytes de los cuales, los primeros 4 corresponden a la respuesta del comando de la cual se hablará más tarde.

                        Figura 14: Impresión de CID en formato bruto e interpretado

 

Para acceder a la información de este registro es necesario enviar a la tarjeta en comando 10. Las siguientes líneas de código son empleadas para llevar a cabo esta lectura:

                        ………………

                        CMD10 DB        4AH,00H,00H,00H,00H,01H       

                        ………………

            MOV     DPTR, #CMD10            

                        LCALL SNDCMD                      ; Enviar el comando

                        MOV     DPTR, #RESP  

                        MOV     NBYTES, #20D              ; Se esperan 20 bytes

                        LCALL GTRESP                       ; Leer la respuesta

                        MOV     DPTR, #RESP               ;apuntar a la variable que contiene la respuesta

            MOV     NBYTES, #20D

                        LCALL  PRINT_RESP                ; imprimir respuesta en formato bruto

                        LCALL WAIT40             ; Esperar 40 mseg para asegurar finalizacion exitosa

La figura 14 muestra adicionalmente la misma información interpretada, ver anexo 1 para obtener detalles acerca de la decodificación de la repuesta.[1]

 

De igual forma, la tarjeta regresa su CSD con formato de 16 bytes más 4 adicionales de respuesta al comando. El CSD contiene información de configuración necesaria para poder acceder a la tarjeta y es posible obtenerlo enviando el comando 9 a la tarjeta. El código en ensamblador requerido para accesarlo es el siguiente:

 

            CMD9   DB        49H,00H,00H,00H,00H,01H        

            ...

            MOV     DPTR, #CMD9              

                        LCALL SNDCMD                      ; Enviar el comando

           

                        MOV     DPTR, #RESP  

                        MOV     NBYTES, #20D              ; Se esperan 20 bytes

                        ACALL GTRESP                       ; Leer la respuesta

                        MOV     DPTR, #RESP               ; imprimir respuesta.

                        MOV     NBYTES, #20D

                        LCALL  PRINT_RESP

                        LCALL  WAIT40

 

                                   

                       

                                    Figura 15: Impresión de CSD en formato bruto

 

El envío de este comando proveyó de varias características importantes acerca de una tarjeta en particular: la capacidad del dispositivo, y el formato mediante el cual son almacenados los datos en el mismo. La figura 3 muestra un trozo de la tabla de decodificación del CSD. Consideramos que esta información es muy relevante ya que nos muestra que tipo de sistema de archivos posee la tarjeta a leer. En nuestra prueba, el bit correspondiente al FILE_FORMAT_GRP es el 15, el cual se encuentra en cero y  los bits asociados al FILE_FORMAT son el 10 y 11 los cuales tienen un valor de cero. Esto nos permite aseverar que la tarjeta posee una organización de almacenamiento para área de datos similar a la de un disco duro, de la cual se hablará más adelante.

 

                                   

                                                Figura 16: Tipos de formato

 

Las tarjetas SD poseen un mecanismo de protección de Copyright que les permite identificarse cuando son accesadas a través de una PDA, o cualquier otro dispositivo. Dicha información es almacenada en una partición de la tarjeta; ésta zona es conocida como Write Protecction Group (WP Group). Como se mencionó se encuentra en una partición aislada del área de almacenamiento y posee su propia tabla de particiones, así como comandos especiales para su acceso. Esta partición es de tamaño variable y viene especificada por el CSD de la tarjeta.

 

El área de almacenamiento común ofrece la posibilidad de guardar cualquier tipo de información. Como se mencionó líneas más arriba, el esquema de organización de archivos es semejante al de un disco duro, el cual se mostrará a continuación.

 

La unidad básica de lectura de una tarjeta SD es un bloque de 512 bytes llamado sector. La estructura interna de la tarjeta está decidida en el sector cero de la misma el cual contiene el master boot record and partition table, la cual provee el bloque en el cual se encuentra la única partición de datos simples[2]. La figura 18 describe la ubicación de cada uno de los diferentes elementos que conforman un área de almacenamiento y la figura 19 nos muestra una tabla de particiones, es decir nos sitúa en la dirección 0000h. El código necesario para leer[3] el sector 0 viene dado de la siguiente manera:

 

            MOV     R0,       #ADDR             ; Apuntar a la direccion 0000H

            MOV     @R0, #00H       ; Master Boot Record

            INC       R0

            MOV     @R0, #00H

            LCALL  READBLK

           

 

Figura 17.  Organización lógica interna de la tarjeta

 

Figura 18.  Organización del mapa de memoria de la tarjeta

 

La primera entrada de la tabla de particiones corresponde a la única partición del sistema y en la cual tenemos almacenados todos los datos que hemos ido guardando en nuestra SD.

Dirección

Contenido

Tipo

+ 000h

Código de la partición

 

+ 1BEh

1ª entrada en la tabla de partición

16 bytes

+ 1CEh

2ª entrada en la tabla de partición

16 bytes

+ 1DEh

3ª entrada en la tabla de partición

16 bytes

+ 1EEh

4ª entrada en la tabla de partición

16 bytes

+ 1FEh

Identificación AA55H

2 bytes

 

Figura 19: Contenido del Master Boot Record

 

Dentro de la primera entrada de la tabla de particiones, encontramos un formato como el siguiente (figura 20). El desplazamiento 8 apunta al sector de arranque de la partición. Cabe mencionar que un disco duro convencional destina 4 bytes para calcular esta dirección de lboque; sin embargo, una SD Card solo utiliza 2 bytes ya que la capacidad que posee es muy pequeña para compararse con un disco duro tradicional. De cualquier forma, las tarjetas SD respetan este formato y destinan 4 bytes para poder almacenar su sector de arranque. El fragmento de código mostrado a continuación realiza el cálculo del relative sector de una tarjeta:

 

            MOV     R0, #RELATIVE_SECTOR          ;  = RESP[454] + 256 * RESP[455]

            MOV     DPTR, #RESP                           ; Apunto a RESP

            MOV     A,#255                                      ; 454 no cabe en una variable de 8 bits

CICLOPS         

INC       DPTR

            DJNZ    A, CICLOPS

            MOV     A,#199

PARTE2           

INC       DPTR

            DJNZ    A,PARTE2

            MOVX   A, @DPTR                                ; Posiciono byte bajo de la dirección

            MOV     @R0, A                                     ; Guardo byte bajo

            INC       R0

            INC       DPTR

            MOVX   A, @DPTR                                ; Posiciono byte alto

            MOV     @R0, A                                     ; Guardo parte alta de la dirección

 

Figura 20: Contenido de una entrada de la tabla de particiones

           

 

MOV     R0, #RELATIVE_SECTOR          ;  = RESP[454] + 256 * RESP[455]

            MOV     DPTR, #RESP               ; Apunto a RESP

            MOV     A,#255

CICLOPS          INC       DPTR

            DJNZ    A, CICLOPS

            MOV     A,#199

PARTE2            INC       DPTR

            DJNZ    A,PARTE2

            MOVX   A, @DPTR                    ; Posiciono byte 1

            MOV     @R0, A                         ; Guardo

            INC       R0

            INC       DPTR

            MOVX   A, @DPTR                    ; Posiciono byte alto

            MOV     @R0, A                         ; Guardo

 

Es necesario multiplicar este relative sector por 2 para obtener el partition boot sector. Esta multiplicación es una exigencia del estándar de la tarjeta y se mantiene inalterable de una capacidad a otra; es decir, cada vez que se calcule una dirección, es necesario multiplicarla por 2 para obtener la dirección correcta.

 

El partition boot sector es un bloque de 512 bytes que contiene la información referente a la estructura de la tabla 1. La jerarquía ya mencionada es que el master boot record sector nos da la información necesaria para localizar el partition boot sector y éste último nos aporta las direcciones y datos para localizar y describir a las estructuras restantes FAT, root directory y data area.La tabla 2 muestra la información y los desplazamientos requeridos para calcular esta información. El código referente a esta operación se localiza en la rutina CALCULA localizada en el anexo 1.

 

Figura 21: Partition Boot Sector

 

El programa realizado calcula y despliega algunas variables importantes del partition bootsector (tabla 2).

 

Figura 22: Datos obtenidos del Partition boot sector

 

Uva vez calculadas y almacenadas estas variables es posible acceder al FAT y al directorio raíz y al área de datos. El FAT1 y FAT2 son los siguientes escalones en la tabla número 1. El FAT1 contiene toda la información de apuntadores a archivos  contenidos en una tarjeta dada, el FAT2 es una copia de seguridad del FAT1. En un inicio, la tecnología SD implementó FAT 12 como sistema de archivos; en la actualidad, las tarjetas SD del mercado manejan capacidades desde los 32 MB en adelante utilizando el sistema de archivos FAT16.

 

El FAT 16 almacena en 2 bytes la dirección a un cluster del área de datos en la cual un archivo se encuentra localizado. Los primeros 4 bytes del FAT están reservados para indicar el tipo de File System (Cluster 0)  FFF8h para FAT 16 y el cluster 1 es un cluster reservado, indicado por FFFFh. Esto significa que desde el cluster 2 podemos almacenar direcciones de archivos (figura 23).

 

Figura 23: FAT 16

Supongamos que un archivo tiene como cluster inicial el 0003h; el archivo posee 3 clusters. Al ir al cluster 0003h en el FAT, encontramos que aparece la dirección 0005h; esta dirección significa que el siguiente cluster esta en la 0005h. ahora bien, leyendo ese cluster del FAT encontramos que su contenido es FFFFh lo cual significa que este es el fin de archivo y ya no hay otra liga. En pocas palabras, el FAT es una lista encadenada de apuntadores al área de datos ya que no solo contiene el cluster en el área de datos, sino también la liga al siguiente cluster.

 

El formato de lectura para un cluster en el FAT es little endian; primero se lee la parte baja de la dirección y posteriormente la alta. Observe que el código mostrado en el anexo 1 mantiene el mismo formato para una lógica más sencilla:

 

                        MOV     DPTR, #RESP                           ; Apunto a RESP

                        MOV     R0,#RELATIVE_SECTOR           ; Apunto a var en la que voy a  salvar

           

                        MOVX   A, @DPTR                                ; Posiciono byte 1

                        MOV     @R0, A                                     ; Guardo parte baja de la dirección

                        INC       R0                   

                        INC       DPTR

                        MOVX   A, @DPTR                                ; Posiciono byte alto

                        MOV     @R0, A                                     ; Guardo parte alta de la dirección

           

 

Utilizando el formato de FAT16 se tiene que cada bloque leído de FAT contiene hasta 256 direcciones de cluster. La implementación llevada a cabo, solo mantiene en memoria 1 sector de FAT, por lo que es necesario, una vez leída la dirección del siguiente cluster, calcular en qué sector del FAT se encuentra. Para ello se realiza una operación de división y residuo, de tal forma que se obtienen dos datos: el sector en el cual está ese cluster y el desplazamiento relativo que hay que hacer dentro de éste cluster.

Por ejemplo, supongamos que tengo el cluster 0A33h. Deseo saber el sector del FAT en el que se encuentra. Al llevar a cabo la división entre 256, encontramos que directamente, la parte alta del cluster se convierte en el sector y la baja en el residuo. Es decir, se leerá el sector 000Ah y una vez leído se avanzarán 0033h palabras para quedar localizado en el nuevo cluster.

 

Para calcular la dirección del FAT se proporciona la siguiente fórmula:

 

FAT1_ADDR =  Partition Boot Sector Address +

                           Partition Boot Sector [14] + Partition Boot Sector [15]*256

                         

 

La dirección de la copia del FAT viene dada por la siguiente fórmula

 

FAT2_ADDR = FAT1_ADDR +

                          Partition Boot Sector [22] + Partition Boot Sector [22]*256

 

Se mencionó que el FAT contiene las direcciones de cluster en las cuales está almacenado un archivo. Ahora bien, si embargo, no se mencionó el origen del primer cluster del archivo. Dicha dirección se obtiene del directorio raíz o cualquier otro directorio. Hablemos del directorio raíz.

 

El área del directorio raíz está conformada por 512 entradas, divididas en  32 sectores. En cada sector del directorio raíz hay contenidas 16 entradas (una entrada ocupa 32 bytes). La primera entrada del directorio raíz contiene la etiqueta de Volumen. Las entradas subsecuentes pueden ser desde archivos, carpetas, archivos ocultos, de sistema, etc (figura 25)

 

Figura 24: Contenido de una entrada del directorio raíz

 

La figura 24 muestra el orden de campos contenidos en una entrada del DR. Observe que el nombre del archivo ocupa 8 caracteres mientras que la extensión ocupa solo 3, los nombres y extensiones se escriben en mayúsculas y en caso de que los nombres sean mayores a 8 caracteres, se truncan hasta el sexto carácter y se les agrega el símbolo ~ seguido de un número; este tipo de formato se conoce como 8.3 y se utilizó en las versiones de MS DOS. Windows empieza a implementar la política de long file names lo cual permite escribir nombres largos desperdiciando campos del directorio para almacenar el nombre. Las tarjetas SD permiten ambas opciones y para fines prácticos solo se validó que los archivos cumplan con el formato 8.3.

 

Figura 24: Atributos de la entrada

 

Para validar si una entrada del directorio raíz es válida, se deben verificar 2 casos: que esté vacía o que esta borrada. La validación mencionada se muestra a continuación:

 

NEXT    MOVX   A, @DPTR                    ; LEO EL PRIMER CARACTER

                        CJNE    A, #0FFH, POS1           ; verifico si es una entrada válida

                        SJMP   NO                               ; esta entrada esta vacía....        

POS1   CJNE    A, #00H, POS2              ; esta entrada esta vacía....

                        SJMP   NO

POS2   CJNE    A, #0E5H, YEAH           ; esta entrada esta borrada....

                        LJMP    NO

YEAH   MOV     R7, A                            ; ENTRADA VALIDA

 

Notamos que según el dispositivo que le de formato a la tarjeta, una entrada vacía puede ser representada por FFh o 00h. Una entrada borrada se representa con el caracter E0h.

 

Para calcular la dirección del Directorio raíz se proporciona la siguiente fórmula:

 

RD_ADDR = FAT2_ADDR +

                        Partition Boot Sector [22] + Partition Boot Sector [22]*256

 

 

SDCARD       .                       0000   00000000

SARA             .           TXT    0002   00000003

PABLO          .           TXT    0003   00000004

PRUEBA       .           DOC   0004   0000001A

Figura 25: Directorio raíz

 

 

Ya se mencionó que para tener información acerca de un archivo es necesario localizarlo en el directorio raíz para obtener su cluster de inicio y posteriormente ir al FAT y leer cada cluster hasta encontrar uno que diga fin de archivo (FFFFh). Ahora hablemos que hacer con estas direcciones de cluster que obtenemos en el FAT.

 

Cada vez que se obtiene una dirección de cluster, es necesario ir al área de datos y acceder a ese cluster en particular. Definamos la fórmula de dirección del área de datos:

 

DTA_ADDR = DR_ADDR + 20h

 

El valor de 20h se obtiene de dividir las entradas del directorio raíz entre 512 (porque cada sector ocupa 512 bytes) y multiplicarlas por 32 (cada entrada ocupa 32 bytes). Como siempre hay 512 entradas en el directorio raíz, entonces éste siempre ocupa 32 sectores de espacio.

 

Recordemos que las tarjetas SD solo pueden hacer accesos de tamaño bloque y que la dirección que nos proporcionan tanto el FAT como el Directorio raíz son cluster. Es necesario aplicar una conversión de cluster a sector para poder hacer el acceso. El sector lógico del cluster X esta dado como sigue:

 

SLCx = DTA_ADD + (cluster - 2) * partition boot sector [13];

 

Se decrementa el cluster en 2 ya que como se mencionó en páginas anteriores, los 2 primeros clusters del área de datos están reservados.

Además, hay que tener en mente que un cluster esta formado de varios sectores (definidos en la variable sectores por cluster, del Partition Boot Sector) y que cuando se hace una lectura de bloque a un cluster, es necesario terminar de leer el cluster en su totalidad; para esto basta con incrementar la dirección de sector en 2 y mantener un contador que alcance la cuenta de sectores por cluster.  Esto se aprecia más fácilmente en el siguiente segmento de código:

 

MOV     R3, SECTOR_CLUSTER            ;variable sectores por cluster

OTRO_SECTOR_CLUSTER                                           

                        LCALL  READBLK                                 ; Lee el sector indicado por ADDR

                        MOV     DPTR, #RESP                           ; Apunto al bloque leído en memoria

                       

                        MOV     R0, #ADDR                               ; Calculo la dir del siguiente sector lógico

                        INC       R0                                            ; dentro de este mismo cluster

                        MOV     A, @R0                        

                        CLR      C

                        ADD     A, #02                           ; sólo incremento 2

                        MOV     @R0, A                         ; actualizo la variable que apunta al sig. sector

                        DJNZ    R3, OTRO_SECTOR_CLUSTER

 

Para salir de idle state, es responsabilidad del bus master (microcontrolador) aplicar una secuencia de ciclos de reloj para asegurar que el voltaje es llevado hasta el máximo punto de operación (ya que pueden estar conectadas varias SD). Para esto se requieren 64 ciclos de reloj. Se agregan 10 ciclos más para eliminar problemas de sincronización de voltaje. 

 

 

 

El segmento de código que describe esta operación se muestra en seguida:

 

OPEN:

                                    MOV     B, #74D                        ; 74 ciclos de reloj

CLK74: MOV     P1, #7FH          ; Pulso de reloj (senal bajo)

                                    LCALL  WAIT

                                    MOV     P1, #FFH          ; Pulso de reloj (senal alto)

                                    LCALL  WAIT

                                    DJNZ    B, CLK74          ; Hasta que termine                               

 

Cuando la tarjeta SD despierta, se encuentra en modo SD. Entrará en modo SPI siempre y cuando la señal de CS se ponga en bajo durante la recepción del comando cero (CMD0). Una vez en este modo, la tarjeta responde para indicar que entró al modo de forma exitosa. El comando cero, (40 00 00 00 00 95) se envía a la tarjeta posteriormente de la secuencia de pulsos de reloj y su codificación en ensamblador es la siguiente:

 

SEND0:

MOV     DPTR, #CMD0               ; Apuntar al comando 0

                        LCALL SNDCMD                       ; Enviar el comando

                        MOV     DPTR, #RESP               ; Lugar donde se va a guardar la resp

                        MOV     NBYTES, #3D               ; Se esperan 3 bytes de respuesta

                        ACALL GTRESP                        ; LEER LA RESPUESTA

                        MOV     DPTR, #RESP               ; IMPRIME RESPUESTA

                        MOV     NBYTES, #3D               ; se recibe una respuesta de 3 bytes

                        LCALL  PRINT_RESP                ; imprime respuesta en la pantalla

                        MOV     DPTR, #RESP               ; CHECA RESPUESTA

                        MOVX   A, @DPTR                    ; verificar que la respuesta sea correcta

                        CJNE    A, #FFH, SEND0           ; FF

                        INC       DPTR   

                        MOVX   A, @DPTR       

                        CJNE    A, #01H, SEND0            ; 01

                        INC       DPTR

                        MOVX   A, @DPTR

                        CJNE    A, #FFH, SEND0           ; FF

 

La respuesta al comando cero es FF 01 FF; si esta respuesta no se recibe correctamente es necesario reenviar el comando cero tantas veces sea necesario hasta obtener una respuesta exitosa. Una vez en modo SPI, es necesario inicial el proceso de inicialización de la tarjeta mediante en comando 1; en este caso, se envía 2 veces, para asegurar un correcto reset de la misma. El segmento de código que define el envío del comando 1 es el que sigue:

           

            SEND11:

                        MOV     DPTR, #CMD1               ; Apuntar el Comando 1

                        LCALL SNDCMD                       ; Enviar el comando

                        MOV     DPTR, #RESP

                        MOV     NBYTES, #3D               ; Se esperan 3 bytes

                        LCALL GTRESP                        ; Leer la respuesta

                        MOV     DPTR, #RESP               ; IMPRIME RESPUESTA

                        MOV     NBYTES, #3D

                        LCALL  PRINT_RESP

                        MOV     DPTR, #RESP               ; Comparo respuesta con FF01FF

                        MOVX   A, @DPTR

                        CJNE    A, #FFH, SEND11

                        INC       DPTR

                        MOVX   A, @DPTR

                        CJNE    A, #01H, SEND11

                        INC       DPTR

                        MOVX   A, @DPTR

                        CJNE    A, #FFH, SEND11

                        LCALL WAIT40

            SEND12:

                        MOV     DPTR, #CMD1               ; Apuntar el comando 1

                        ACALL SNDCMD                                  ; Enviar el comando

                        MOV     DPTR, #RESP  

                        MOV     NBYTES, #3D               ; Se esperan 3 bytes

                        ACALL GTRESP                                   ; Leer la respuesta

                        MOV     DPTR, #RESP               ; IMPRIME RESPUESTA

                        MOV     NBYTES, #3D

                        LCALL  PRINT_RESP

                        MOV     DPTR, #RESP  

                        MOVX   A, @DPTR

                        CJNE    A, #FFH, SEND12

                        INC       DPTR

                        MOVX   A, @DPTR

                        CJNE    A, #00H, SEND12

                        INC       DPTR

                        MOVX   A, @DPTR

                        CJNE    A, #FFH, SEND12        

                        LCALL  WAIT40

 

 

En el primer envío, la tarjeta responde indicando que esta en el modo SPI (recepción de comando correcta), en el segundo envío, la respuesta generada por la SD es reset exitoso (FF 00 FF). (figura 26)

 

 

 

Figura 26. Respuestas a los comandos de inicialización

 

 

A partir de este momento, la tarjeta se encuentra lista para recibir comandos. El diagrama de flujo mostrado en la figura 27 muestra la secuencia de pasos llevados a cabo:

 

Figura 27. Algoritmo de inicialización

           

 

 

 

Pag 3-8 dice lo de las resistencias

Bus timing 32-113

 

 

 

 

 

 

 



[1] Ver tabla de CID y CSD. SanDisk Manual v1.9 pág 3-13

[2] No se olvide que el partition write protection es un área independiente del área de datos.

[3] Cada vez que se haga alguna lectura el área de datos debe usarse el comando 17