Como un
ejemplo un poco más complicado (pero muy útil) del uso del protocolo SPI
veremos en más detalle el uso de tarjetas de memoria flash en su formato SD
(SecureDigital). Entre las posibilidades de comunicación que ofrecen dichas
tarjetas está un modo SPI. Este modo, aunque más lento que el modo SD nativo,
es más sencillo de implementar.
En este tutorial nos centraremos en las conexiones hardware, la inicialización de la tarjeta (tanto para tarjetas SD standard como para las SDHC de alta capacidad), y la obtención de las características de la tarjeta (capacidad, número de sectores, número de serie, etc.) a través de sus registros CSD y CID.
El objetivo es conseguir el volcado de información que aparece a la derecha
En este tutorial nos centraremos en las conexiones hardware, la inicialización de la tarjeta (tanto para tarjetas SD standard como para las SDHC de alta capacidad), y la obtención de las características de la tarjeta (capacidad, número de sectores, número de serie, etc.) a través de sus registros CSD y CID.
El objetivo es conseguir el volcado de información que aparece a la derecha
En una entrada posterior veremos como leer/escribir datos en la tarjeta, considerando como optimizar dicha transferencia de datos.
Código asociado a esta entrada: SPI_sdhc.c
--------------------------------------------------------------------
Hardware:
Las imágenes muestran el pin-out de una tarjeta SD. No todos los pines mostrados se usan en la interfaz SPI. La siguiente tabla especifica los pines usados y su función:
Las imágenes muestran el pin-out de una tarjeta SD. No todos los pines mostrados se usan en la interfaz SPI. La siguiente tabla especifica los pines usados y su función:
PIN
|
Name
|
Tipe
|
Description
|
1
|
CS
|
In
|
Chip
Select (asserted low)
|
2
|
SDI
|
In
|
Serial
Data input
|
3
|
Vss
|
Gnd
|
Ground
|
4
|
Vdd
|
V+
|
Power
(3.3V usually)
|
5
|
SCL
|
In
|
Clock
|
6
|
Vss
|
Gnd
|
Ground
|
7
|
SDO
|
Out
|
Serial
Data out
|
Otros formatos (como por ejemplo microSD, a la derecha) tendrán la misma funcionalidad, aunque su pinout puede variar.
Necesitaremos
también el socket para la tarjeta elegida (SD o microSD). En Ebay, sparkfun (https://www.sparkfun.com/products/204)
o Mikroelektronika (http://www.mikroe.com/add-on-boards/storage/)
podéis adquirir diversas tarjetas con un socket SD o microSD y alguna
electrónica adicional. Yo estoy usando un simple socket ($5-10 sin electrónica)
como el mostrado a la izquierda.
Obviamente tendremos el mismo problema de antes: la diferencia de voltaje
entre los 5V del PIC y los 3.3V de la tarjeta. Lo he resuelto de la misma forma
cutre de antes (ver entrada de memoria flash): un regulador para obtener 3.3V a
partir de los 5V del PIC, con divisores de tensión en las lineas micro à tarjeta y conexión directa de la
tarjeta (3.3V) al micro (5V).

Algunas de
las tarjetas que se pueden adquirir (como la de Mikroe mostrada a la derecha
$20), incorporan un regulador de 5 a
3.3V y conversores de niveles que serían más fiables. En este caso es sólo una
cuestión de
conectar
los pines correspondientes.
Recalcar
de nuevo la necesidad de resistencias pull-up en las líneas MISO y MOSI que
mantengan alta la línea en ausencia de actividad por parte del micro o tarjeta.
De nuevo, algunas de las opciones comentadas incluyen estas resistencias
pull-up.
Configuración del módulo SPI
Antes de
hacer nada debemos elegir el modo SPI (CPOL,CPHA), velocidad, etc. para
inicializar el módulo SPI del PIC.
El modo
SPI (0,0) con SMP=0 es una combinación que me ha funcionado con
todas las tarjetas usadas. Obviamente el micro (PIC) será el master SPI. Solo
nos hace falta decidir la velocidad del reloj.
En algunos
referencias se advierte que es preciso una conexión inicial a baja velocidad
(se cita un máximo de 400Khz) y solo cuando la tarjeta ha sido convenientemente
inicializada y ha habido una negociación pasaremos a la velocidad máxima
soportada. En otros lugares se indica que dicha restricción es sólo para
tarjetas MMC. Con las tarjetas que yo he probado (SD todas ellas) la
inicialización ha sido satisfactoria arrancando a la máxima velocidad, pero no
cuesta nada realizar el primer intercambio a velocidad baja y pasar a velocidad
alta tras la inicialización.
En nuestro
caso usaremos Fosc/64 como velocidad inicial y Fosc/4 como velocidad final.
En este
proyecto usaremos un cristal de 8 MHz y la opción HSPLL en la configuración.
Con esta opción un PLL multiplica por 4 la frecuencia del cristal, por lo que
el PIC está trabajando a 32 Mhz. Para habilitar esta opción basta hacer al
principio del programa:
#pragma config
OSC=HSPLL // PLL x 4
Con un
cristal "efectivo" de 32 MHz, la velocidad más lenta (Fosc/64)
corresponde a un reloj del SPI inicial de 500 Khz y una velocidad final (Fosc/4)
de 8 Mhz. La velocidad inicial se sale un poco del máximo recomendado, pero me
ha funcionado con las tarjetas probadas.
Usando las rutinas conocidas podemos inicializar el módulo SPI con la siguiente función:
// CS line
#define SD_CS
LATCbits.LATC2
#define SD_CS_dir TRISCbits.TRISC2
#define select_SD
SD_CS=0
#define deselect_SD SD_CS=1
void SD_spi_init(byte clock)
{
SD_CS_dir=0; deselect_SD; // Configure CS line as output and set it low.
spi_master(clock); //
Configure SPI as master and set clock
spi_mode(0,0,0); //
SPI mode (0,0). Sample incoming bits at mid period
spi_enable(); // Enable SPI
}
Como hicimos en el caso de la memoria flash, previamente definimos SD_CS y SD_CS_dir para informar al resto de las rutinas de que pin estamos usando como ChipSelect (CS) y su correspondiente bit de dirección TRIS.
Especificación del protocolo SPI
para tarjetas SD
En el
documento SanDisk
Secure Digital Card (Product Manual) podéis encontrar unas especificaciones
bastante completas del protocolo para comunicarse con tarjetas SD. En
particular hay que leer el capítulo 5 que contiene la definición del protocolo
SPI.
Nota: todo lo que viene a continuación es una
versión muy reducida del protocolo completo. Sigo muchos atajos (confiando por
ejemplo que se que tipo de tarjeta tengo, o que voltaje acepta) y como siempre
existe la posibilidad de que tenga errores. El protocolo completo es complejo y
es posible que si no se respeta estrictamente tengamos un código que funcione
en muchos casos pero falle para cierto tipo de tarjetas que exigen una
adherencia más estricta al protocolo. En
caso de duda o problemas no hay nada como leerse las especificaciones.
De hecho, si alguien desea
realmente enterarse de los detalles debería estar leyendo dicho capítulo 5 en
lugar de este documento.
Además de
en una somera lectura de las especificaciones, el código presentado está basado
en varias fuentes entre las que caben destacar:
Algunos de
estos códigos son para otros micros, pero la base del protocolo de comunicación
es esencialmente la misma. En general los programas citados anteriormente son
mucho más completos que el que presento aquí, considerando más tipos de
comandos, etc.
Una muy buena referencia del proceso de inicialización de una tarjeta SD puede encontrarse en
http://elm-chan.org/docs/mmc/mmc_e.html
Una muy buena referencia del proceso de inicialización de una tarjeta SD puede encontrarse en
http://elm-chan.org/docs/mmc/mmc_e.html
Envío de comandos a la tarjeta SD:
El
protocolo de comunicación entre PIC y tarjeta SD se basa en el envío de
comandos desde el PIC y la recepción de respuestas de la tarjeta. La secuencia
standard es:
- Comando de PIC a SD (un total de 6 bytes)
- Respuesta de SD a PIC (normalmente 1 o 2 bytes, casi siempre 1, a veces más dependiendo del comando usado). La tarjeta siempre responde, aunque podemos llegar a necesitar hasta 8 "peticiones" de bytes hasta que llegue las respuesta.
- Opcionalmente un bloque de datos (en uno u otro sentido) si el comando era de escritura/lectura de datos.
En este
apartado presentaremos el intercambio más sencillo consistente en Envio de Comando/Respuesta
sin datos. La figura siguiente ilustra este tipo de intercambios.
El comando
enviado es simplemente un código (1 byte) seguido de un argumento de 4 bytes y
de un CRC de un byte, para un total de 6 bytes. El uso de un CRC correcto es
opcional en el protocolo SPI y generalmente se ignora su valor (aunque hay que seguir
mandando dicho byte). La única excepción
es en los primeros comandos enviados para pasar al modo SPI. Como la tarjeta no está
todavía en modo SPI si que es preciso enviar un CRC valido.
El código
de comando es un valor de 6 bits (de 0 a 63). Antes de mandarlo se añade 01 en
los dos bits más significativos. Para ello haremos un OR con el valor 0b01000000=0x40. La tabla
siguiente refleja algunos de los comandos usados en el código:
#define CMD_GO_IDLE_STATE 0x00 // CMD00: R1
#define CMD_SEND_CSD 0x09 // CMD09: R1
#define CMD_SEND_CID 0x0A // CMD10: R1
#define CMD_SET_BLOCKLEN 0x10 // CMD16: R1 arg=BLOCKLEN
#define CMD_READ_SECTOR 0x11 // CMD17: R1 arg=data address
#define CMD_READ_MULT 0x12 // CMD18: R1 arg=data address
#define CMD_WRITE_SECTOR 0x18 // CMD24: R1 arg=data address
#define CMD_WRITE_MULT 0x19 // CMD25: R1 arg=data address
#define CMD_APP 0x37 // CMD55: R1
#define ACMD_SEND_OP_COND 0x29 // ACMD41 R1 (after CMD_APP_CMD)
// SDHC addemdum
#define CMD_SEND_OP_COND
0x01
#define CMD_SEND_IF_COND
0x08
Tras la
recepción de un comando (aunque puede que no inmediatamente) la tarjeta
responderá. Hay varios formatos de respuesta, dependiendo del comando, pero el
más común es el tipo R1, de tamaño 1 byte:
El bit más
significativo debe ser 0. Un 1 en esa posición indica que el byte recibido no
es una respuesta válida. De nuevo, la idea es que el pull-up en está línea la mantendrá
alta en ausencia de actividad por parte de la tarjeta. Exigir que el 1er bit
sea un 0 permite saber si la tarjeta está activa. El resto de los bits indican
distintos tipos de errores cuando están a 1. La excepción es el bit 0 que
indica que la tarjeta esta en IDLE_STATE y corriendo el proceso de inicialización.
En el código usamos los siguientes defines para identificar estos estados:
#define R1_IDLE 0x01 // Idle_State
#define R1_BAD 0x80 // MSbit no nulo
Las
respuestas de tipo R2 consisten en un byte adicional con información adicional (el 1er byte es identico a
R1). Hay también respuestas con un mayor número de bytes. En el código
de este tutorial solo trabajaremos con respuestas del tipo R1.
Vamos a
escribir una función que mande un comando, reciba la respuesta y compruebe que
todo va bien para poder proseguir. La función usada para mandar un comando de
tipo R1 es la siguiente (habría que escribir distintas funciones para enviar
comandos tipo R2, etc.:
/// send a R1 type command and wait for an answer
byte
send_command_R1(byte cmd, unsigned long arg)
{
byte*
ptr = (byte*)&arg; // Pointer to individual bytes of argument
byte
k,res,crc=0xFF;
if (cmd==CMD_GO_IDLE_STATE)
crc=0x95; // CMD00
--> crc needed = 0x95
if (cmd==CMD_SEND_IF_COND) crc=0x87; // CMD08 --> crc needed = 0x87
cmd
= (cmd & 0x3F) | 0x40; // Makes sure cmd is 6 bit and OR it with 0x40
// Wait up to 10 x 8 clocks until line is idle (0xFF)
k=10;
res=0; while ( (res!=0xFF) && (k--) )
{res=spi_rx(); Delay10TCYx(1);
}
// Send
command, argument and CRC (only needed for CMD00 and CMD08)
spi_tx(cmd); //
Command
for(k=0;k<4;k++) spi_tx(ptr[3-k]); //
Argument bytes: Most Significant Byte first
spi_tx(crc); ////
CRC (fake in all cases except for CMD00 or CMD08)
// Wait up to 10 bytes to get a R1 answer with a clear MSbit
res=0xFF;
k=10; while ( (res&R1_BAD) && (k--) )
res=spi_rx();
return res;
}
Algunos
comentarios sobre el código anterior:
- Antes de enviar el comando se verifica que la línea esta libre (alta). Se detecta al recibir 0xFF.
- Se manda el COMANDO + 4 bytes de argumento + CRC.
- El CRC enviado solo es correcto para dos comandos CMD00 y CMD08 que se mandan al principio. En todos los demás casos se usa un CRC=0xFF.
- Se espera hasta recibir una respuesta tipo R1, con el primer bit igual a 0. LLegamos a esperar hasta 10 peticiones de respuesta antes de desistir.
Inicialización
de la tarjeta en modo SPI
Una vez
que ya sabemos la configuración necesaria para el puerto SPI y como mandar
comandos básicos a la tarjeta, estamos en condiciones de inicializar una
tarjeta SD. Pasamos a describir los
pasos para conseguirlo.
Nota: la inicialización completa de una tarjeta SD es un proceso complicado si queremos cubrir todas las posibilidades. En http://elm-chan.org/docs/mmc/sdinit.png podéis encontrar un diagrama de flujo de los pasos a seguir. El proceso que presento aquí es una simplificación de ese gráfico que me funciona para todas las SD que he probado (SDSC v1.0, SDSC v2.0 y SDHC v2.0). Si estáis seguros de la tarjeta que váis a usar podríais simplificar aún más el código presentado. Si debemos manejar cualquier tarjeta que a la gente se le ocurra meter en la ranura, deberíamos implementar el protocolo completo.
Tras un power-up una tarjeta SD despierta en modo SDbus. Lo primero que hay que hacer es pasarla a modo SPI. El proceso que uso en mi código es el siguiente:
- Dejar un delay inicial de algunos ms para estar seguros de que se ha estabilizado la alimentación
- Inicializar SPI a la velocidad más lenta (Fosc/64)
- Con CS=1 (deselect_SD), mandar al menos 74 clocks a la tarjeta. Lo conseguiremos mandando 8 bytes (8x10=80) cualesquiera usando spi_clock().
- Mandarle el comando CMD00 que pone a la tarjeta en modo SPI, dejándola en IDLE_STATE mientras corre una especie de self-test. Comprobar que se recibe una respuesta tipo R1 (1 bytes) con valor 0x01 (idle state). Este comando requiere un CRC correcto (00x95).
- Si este comando funciona correctamente tratar de enviar un comando CMD_08 a la tarjeta con argumento 0x1AA y CRC = 0x87. Este paso es fundamental si se tiene una tarjeta SDHC y se puede obviar con otras tarjetas. En comandos sucesivos ya no es necesario enviar CRCs correctos.
- La especificación indica que ahora deberíamos preguntar sobre un cierto registro (OCR) y determinar el voltaje aceptado por la tarjeta y actuar en consecuencia. También hay una serie de pasos que permiten distinguir entre las antiguas tarjetas MMC y las más modernas SD. Si estamos seguros de que no hay problemas de voltaje y de que nuestra tarjeta es una SD y no una MMC, podemos saltarnos dichas comprobaciones y pasar directamente al siguiente paso.
- Si el comando CMD08 no fue aceptado, se envía el comando CMD_0x37 seguido del comando CMD_0x29. El comando CMD_0x37 (APP_CMD) es un "prefijo" que indica que el siguiente comando es un comando especifico de aplicación (APPLICATION COMMAND) en vez de un comando usual. El CMD_0x29 (ACMD_SEND_OP_COND) activa el proceso de inicialización de la tarjeta en modo SPI. Ambos comandos no precisan argumento (aunque siempre hay que mandar 4 bytes, p.e. 0x00 00 00 00) ni CRCs correctos.
- Si el comando CMD08 fue aceptado se envia el comando CMD_0x01 (CMD_SEND_OP_COND), de nuevo con argumento 0 y CRC cualquiera.
- Si 7 u 8 funcionaron la tarjeta está inicializada y lista para aceptar todo tipo de comandos en modo SPI. Como el proceso de auto-test puede llegar a tardar hasta 1 segundo es conveniente reintentar el envio de estos comando durante cierto tiempo antes de declarar un TIMEOUT.
- Trás la inicialización podemos usar el comando SET_BLOCKLEN (CMD16) para fijar el tamaño del bloque de datos (que se pasa como argumento). El tamaño "nativo" de las tarjetas SD es de 512 bytes. En operaciones de lectura se puede especificar un tamaño de bloque entre 1 y 512 bytes. Las operaciones de escritura deben hacerse en bloques de 512. Lo normal es usar el BLOCKLEN nativo de 512.
Una vez inicializada la tarjeta, volvemos a poner la máxima velocidad posible (Fosc/4) en el reloj del módulo SPI (si es que empezamos con una velocidad lenta) para agilizar las
transferencias de información. El código completo
de la inicialización queda:
#define N_trials 10
byte
init_SD(void)
{
byte k,res;
SD_spi_init(2); //
Slow SPI clock (Fosc/64)
deselect_SD; for (k=0;k<10;k++) spi_clock(); //80
clocks with CS=1 to start up card
select_SD;
for(k=0;k<N_trials;k++) //Try
with CMD_0x00 a few times
{
res=send_command_R1(CMD_GO_IDLE_STATE,0x00);
if (res==R1_IDLE) break; //
Great, card went into IDLE_STATE
Delay10KTCYx(25); //delay
}
if (res!=R1_IDLE) {deselect_SD; return 4;} //
Didn't reach IDLE_STATE
//
Check a few times for SDHC with CMD_0x08
for(k=0;k<N_trials;k++)
{
res=send_command_R1(CMD_SEND_IF_COND,0x1AA);
if (res==R1_IDLE) //
Great, CMD_08 accepted
{
spi_clock(); spi_clock(); spi_clock();
spi_clock(); // Shift 4 trailing bytes
break; //
}
Delay10KTCYx(25); //delay_ms
}
//
According to answer to CMD08 try CMD_SEND_OP, ACMD_SEND_OP, or return with
error
switch (res)
{
case 1: //SDHC
card --> send CMD_01
res=1; k=0; // Try
with CMD_SEND_OP a few times.
while(res
& (k<100))
{
res=send_command_R1(0x01,0x40000000); if (res&R1_BAD) continue;
k++; Delay10KTCYx(10); //delay_ms(20);
}
if (res) {deselect_SD; return 2;} //
Card failed when sending CMD_SEND_OP_COND after CMD08
break;
case 5: // CMD08 declared illegal command, maybe it is a
normal SD card
res=1; k=0; // Try
with CMD_APP+ACMD_SEND_OP a few times.
while(res
& (k<100))
{
res=send_command_R1(CMD_APP,0); if (res&R1_BAD) continue;
res=send_command_R1(ACMD_SEND_OP_COND,0);
k++; Delay10KTCYx(10); //delay_ms(20);
}
if (res) { deselect_SD; return 1; } // Card failed when sending ACMD_SEND_OP_COND
break;
otherwise: deselect_SD; return 3; break; //
Failed CMD08
}
res=send_command_R1(CMD_SET_BLOCKLEN,512L); // Set
block length = 512
deselect_SD;
SD_spi_init(0); //
Fastest SPI clock (Fosc/4)
return (0);
}
Lectura de registros CSD/CID de la tarjeta:
Si todo ha ido bien, la rutina init_SD devuelve un valor 0 (no error), indicando que la tarjeta está lista para aceptar todo tipo de comandos válidos y para enviar/recibir datos. A continuación veremos como leer nuestros primeros datos de la tarjeta, los registros CID (Card ID Data) y CSD (Card Specific Data), conteniendo interesante información de la tarjeta, como su velocidad de transferencia, capacidad, tamaño de sector, consumo eléctrico, fecha de fabricación, número de serie, etc.
Proceso de lectura de datos:
Para
proceder a leer el registro CSD es preciso implementar nuestro primer comando
de lectura de datos. Veremos en primer lugar como es un típico intercambio de
lectura.
Un comando de lectura se inicia con el mismo intercambio
CMD/Respuesta presentado antes. La diferencia es que si el comando tiene éxito,
la tarjeta transferirá un cierto bloque de datos a su buffer de salida y
procederá a enviarlos (cuando el master se lo pida). El número de datos depende del comando, pero el primer
dato enviado es siempre un Data Token (que en la mayoría de los casos corresponde a 0xFE) indicando que los datos pedidos
vienen a continuación. En vez del Data Token (0xFE) también podría recibirse un Error Token (0b 000xxxxx) donde los 5 bits menos significativos corresponden a banderas para diferentes errores. Por ejemplo podríamos haber pedido leer un sector fuera del rango de la tarjeta. Si se recibe un Error Token no se continua con la transmisión de datos esperada.
Un típico comando de lectura de datos tiene el siguiente diagrama:
Un típico comando de lectura de datos tiene el siguiente diagrama:
Al intercambio CMD/R le sigue la recepción de un bloque de datos. Dicho bloque está delimitado por un Data Token con valor 0xFE y terminado por un CRC de 16 bits (que puede ignorarse en modo SPI).
Como el
intercambio Command/Response ya esta cubierto en nuestra función send_command()
basta escribir una función que reciba los datos enviados y los guarde en un
buffer indicado por el usuario:
uint8 read_data_block(uint8*
buf, uint16 n) // Read data block
{
uint16
k;
uint8
res,crc;
res=0xFF; while(res==0xFF) res=spi_rx(); //
Wait for first data
if (res!=0xFE) return res; // Data token not found, return Error Token
for(k=0;k<n;k++) buf[k]=spi_rx(); // read n bytes
crc=spi_rx(); crc=spi_rx(); //
Read CRC (16 bits) and ignore it
return(0);
}
Lectura del registro CSD:
Veamos como usar la rutina
anterior para leer los registros CSD y CID. Ambos registros tienen 16 bytes y basta mandar el comando correspondiente para iniciar el proceso de transferencia de esos 16 bytes:
Los datos de ambos registros se guardan en el buffer suministrado por el usuario (con un espacio de al menos 16 bytes).
byte read_CSD(unsigned char* buf)
{
byte res;
select_SD;
res=send_command_R1(CMD_SEND_CSD,0);
if(res==0) res=read_data_block(buf,16);
deselect_SD;
return res;
}
byte read_CID(unsigned char* buf)
{
byte res;
select_SD;
res=send_command_R1(CMD_SEND_CID,0);
if(res==0) res=read_data_block(buf,16);
deselect_SD;
return res;
}
Los datos de ambos registros se guardan en el buffer suministrado por el usuario (con un espacio de al menos 16 bytes).
Para la
interpretación de ambos registros debemos dirigirnos a las especificaciones citadas
anteriormente (en particular el apartado 3.5.3).
Usando dichas especificaciones he escrito un par de funciones show_CID_data y show_CSD_data que vuelcan por el puerto serie algunos de los campos más interesantes de ambos registros. El registro CID contiene información sobre el fabricante, producto, número de serie y fecha de fabricación. El registro CSD lleva info muy importante sobre tamaño y número de sectores, capacidad de la tarjeta, limitaciones y forma de acceso, etc.
Veamos la salida para un par de tarjetas SD. La primera es una vieja tarjeta de 32 Mb de Panasonic. La segunda, una tarjeta más reciente de 8 Gbytes de SanDisk:
Panasonic 32 Mbytes:
SanDisk Ultra 8 Gbytes:
La información más interesante está en el CSD, a partir de la cual podemos saber el tamaño del sector (típicamente 512 bytes) y el número de sectores o capacidad de la tarjeta. La discrepancia entre los datos del fabricante (32 Mb y 8 Gb frente a los 29 Mb y 7580 Mb obtenidos) es que yo estoy usando 1Mbyte = 1024 Kb y no 1 Mbyte = 1000 Kb).
Otra diferencia muy importante es que la segunda tarjeta (como todas las SD mayores de 2 Gb) es de alta capacidad (SDHC). Esto será importante de cara a la lectura de datos. Las tarjetas más pequeñas admiten lecturas/escrituras parciales de datos (esto es, por debajo del amaño del sector). Esto nos permite mantener un buffer más pequeño en el PIC donde acumular los datos hasta que llega el momento de volcarlos a la tarjeta. Por el contrario en las tarjetas SDHC la lectura/escritura es sólo por bloques de 512 bytes.
De hecho, como veremos en el siguiente tutorial, cuando usemos los comandos para escribir/leer datos, veremos que en el caso de las tarjetas SDSC (Standard Capacity) cuando especifiquemos la dirección u offset a partir del cual se van a escribir los datos, se dan en unidades de bytes. Por el contrario, en tarjetas SDHC (High Capacity) la dirección de lectura/escritura se da en unidades de bloques.
Usando dichas especificaciones he escrito un par de funciones show_CID_data y show_CSD_data que vuelcan por el puerto serie algunos de los campos más interesantes de ambos registros. El registro CID contiene información sobre el fabricante, producto, número de serie y fecha de fabricación. El registro CSD lleva info muy importante sobre tamaño y número de sectores, capacidad de la tarjeta, limitaciones y forma de acceso, etc.
Veamos la salida para un par de tarjetas SD. La primera es una vieja tarjeta de 32 Mb de Panasonic. La segunda, una tarjeta más reciente de 8 Gbytes de SanDisk:
Panasonic 32 Mbytes:
SanDisk Ultra 8 Gbytes:
La información más interesante está en el CSD, a partir de la cual podemos saber el tamaño del sector (típicamente 512 bytes) y el número de sectores o capacidad de la tarjeta. La discrepancia entre los datos del fabricante (32 Mb y 8 Gb frente a los 29 Mb y 7580 Mb obtenidos) es que yo estoy usando 1Mbyte = 1024 Kb y no 1 Mbyte = 1000 Kb).
Otra diferencia muy importante es que la segunda tarjeta (como todas las SD mayores de 2 Gb) es de alta capacidad (SDHC). Esto será importante de cara a la lectura de datos. Las tarjetas más pequeñas admiten lecturas/escrituras parciales de datos (esto es, por debajo del amaño del sector). Esto nos permite mantener un buffer más pequeño en el PIC donde acumular los datos hasta que llega el momento de volcarlos a la tarjeta. Por el contrario en las tarjetas SDHC la lectura/escritura es sólo por bloques de 512 bytes.
De hecho, como veremos en el siguiente tutorial, cuando usemos los comandos para escribir/leer datos, veremos que en el caso de las tarjetas SDSC (Standard Capacity) cuando especifiquemos la dirección u offset a partir del cual se van a escribir los datos, se dan en unidades de bytes. Por el contrario, en tarjetas SDHC (High Capacity) la dirección de lectura/escritura se da en unidades de bloques.
hola antonio, estoy trabajando en el tema de las micro sd y según yo queda inicializada en modo SPI pero no puedo leer ni escribir en ella.. nunca me llega el token FE para comenzar la lectura.tienes algún correo para enviar mi codigo?
ResponderEliminarespero tu respuesta saludos
¿Has probado mi código con tu sistema? Eso podría valer para determinar si hay un problema en tu código o si podría haber un problema de hardware (aunque por lo que dices si que parece que funciona durante la inicialización).
EliminarSi que es verdad que, como comento, la implementación presentada es una versión simplificada del protocolo, pero a mi me ha funcionado en todas (5 o 6) tipos de tarjetas que he probado.
Es cierto que no lo he probado en una microSD, aunque eso no debería influir ya que tienen la misma funcionalidad (de ahí que usen adaptadores microSD-SD sin electronica adicional).
Un saludo, Antonio
No es necesario usar el contacto cs de la sd simplemente colocandolo a masa funciona ahorrando un bit del port. gauna sergio antonio ing Freescale
ResponderEliminarhttp://www.youtube.com/watch?v=6ust10hCjf0
ResponderEliminargracias por el aporte
ResponderEliminarHola Antonio, sera que tu has trabajado con la tarjeta de desarrollo TM4C123G con este mismo tipo de proyectos, es claro resaltar que es de sistemas embebidos, o algun contacto que sepa de ello, por otro lado que si tienes el codigo completo perdon por la pedirlo pero es que quiero trabajar sobre la realizacion de poder leer datos MP3 desde una SD con el protocolo SPI, gracias por su atencion.
ResponderEliminarAtt. Fernando
No conozco la tarjeta de desarrollo que mencionas. Respecto al código del proyecto lo tienes enlazado al inicio de la entrada. Lo que ocurre es que de cara a tu aplicación lo presentado aquí es sólo una parte. Tu proyecto constaría de 3 partes:
Eliminar1. Código de bajo nivel para leer sectores de una tarjeta SD usando SPI (esta entrada)
2. Ser capaz de usar un sistema de ficheros (algo simple como FAT16 o FAT32) para poder leer ficheros MP3 de una tarjeta SD. En una próxima entrada pensaba describir como manejar un FAT16 con un PIC.
3. Código para decodificar el formato comprimido MP3 y enviarlo al DAC correspondiente.
Un saludo, Antonio.
Buenas, estaba interesado en cambiar PERM_WRITE_PROTECT en el bit 13, porque mi memoria esta bloqueda en escritura y me gustaria arreglarlo. ¿Como se haría?
ResponderEliminarBuenas tardes, estoy con una SD Sandisk 1Gb, en lamisma puedo escribir, pero a la hora de leer y almacenar los datos en el buffer solo leo basura, esto lo corroboro leyendo, grabando en el burffer y luego reescribiendo la info en otra parte d ela memoria, hay alguna manera de ir descartando que problema tengo al leer, ya que sigo al pie de la letra la secuencia, envio CMD leer , corroboro que recibo el TOKEN 0XFE, y luego con un for de 512 repeticiendo voy guardando todo en una variable vector char, trabajo con el pic4550, no se si sera problema de ruido o me estoy saltando algun paso del protocolo
ResponderEliminarhttps://www.youtube.com/watch?v=UajDN5dpW3I
ResponderEliminarHola Antonio,
ResponderEliminarTengo un problema con mi tarjeta SD que creo es distinto a todo lo que explicas pero podrías ayudar.
Mi tarjeta se le ha activado PERM_WRITE_PROTECT (Se colocó en 1) y no he podido cambiar ese valor, quisiera saber si hay alguna forma electrónica o en software de cambiarlo, ya que pues es la tercera que le sucede lo mismo y no deseo invertir más dinero.
Agradecería una respuesta.
No se muy bien cual es el problema que mencionas pero el programa WinHex te permite acceder a toda la información de la tarjeta y puedes modificar cualquier dato de ella, creo que la dificultad seria identificar cual es ese valor, ¿ya probaste formateando la tarjeta?
EliminarPara nada es la forma cutre partir de regulador a 5v reducir con regulador 3,3v mientras el primer regulador este elegido para aguantar todo el consumo, para cutre están los montajes basados en divisores de resistencias y voltaje de 5v para todo, eso sí es cutre ya que toda la estabilidad general de los 3,3 de la tarjeta depende del consumo general e influye en la estabilidad de los divisores
ResponderEliminarEste comentario ha sido eliminado por el autor.
ResponderEliminarhola estoy tratando de iniciar una SDHC todo va bien hasta envio el cmd 0x08 y me devuelve 0x01 0x00 0x00 0x01 0xAA pero al enviar el cmd 0x01 y el resto de comandos la sd solo me responde 0x01 y no se que hace por favor necesito una mano con eso
ResponderEliminarHola!, tengo una duda... para usar SPI Y USART en el pic18f4550 como le hiciste? Ya que ambas comunicaciones utilizan el pin C7 para SDO y RX respectivamente???
ResponderEliminarHola que Tal... estoy intentando consultar la guia que dejaste en este link "SanDisk Secure Digital Card (Product Manual)" pero la pagina no se encuentra. por casualidad tienes el documento u otro link en donde lo pueda consultar??
ResponderEliminarHola, que tal?.
ResponderEliminarUn favor, estoy confuso sobre lá funcion: spi_clock(), no veo el código de ella. Podrias darme mas detalhes?.
Desde ya agradezco por lá ayuda.
Muy interesante, me gustaría saber como direccionar la memoria para escribir y leer datos
ResponderEliminar