Leer bancos de memoria SD

No se si con el titulo me expreso bien pero mi propósito es leer una SD sector por sector desde el primero hasta el ultimo sin restricciones de algún tipo de formato de archivo.
mas o menos como leer un byte de memoria del arduino sin importar si se trada de una variable o una entrada a una función o metodo.

Es un tema interesante, pero para eso tendrías que estudiar el modo de operación de una tarjeta SD para con SPI; o si no, explorar en las entrañas de la librería SD.

Hasta el momento, lo que puedo asegurarte, es que las de 2 GB o menos, pueden ser leídos/escritos byte por byte; mientras que de 4 GB en adelante solo en bloques de 512 bytes (efectivamente equivale a un sector lógico).

Sería interesante incluso para mi, para hacer realidad una idea que me había dado un usuario llamado Metaconta; esa idea se trataba de crear archivos bajo un sistema de archivos propio, pero a la vez irreconocible para las computadoras y demás lectores comerciales (como reproductores de MP3),

Mira este post con las consideraciones de que es un post del 2011 y que su uso puede traerte algunos problemas de compatibilidad con el IDE que estes usando.

Agradezco sus respuestas y mi objetivo es ese mismo.
Hacer un sistema de archivos distinto uno para arduino pero que coexista con la libreria SD.
No tengo idea clara de como se maneja esto pero hay va.

Hasta donde se es que cada memoria desde la ram de una pc hasta la flash de una usb o de arduino o la rom o un disquet. Poseen una dirección de memoria Ejemplo de como lo veo yo.

Ejemplo.

Direccion dato char
de memoria
0000 0000 01010011 S
0000 0001 01110100 t
0000 0010 01110010 r
0000 0011 01101001 i
0000 0100 01101110 n
0000 0101 01100111 g
0000 0110 01000011 C
0000 0111 01000111 G
0000 1000 01000101 E

Entonces eso es lo que quiero.
Poder decirle a arduino que me diga lo que hay en la dirección 0000 0101 y me responda 01100111
que con el Cast respertivo me enterare que es el char g en caso de que fuere.

Trabajo con una memoria de 4Gb y tocaria leer el sector.

Con respecto al post que indica Surbyte.

#include "WProgram.h"

esta es la falla que tengo al intentar cargar el boceto
ya que en el header se incluye pero no en el actual IDE.

La funcion.

unsigned char SDCARDclass::readblock(unsigned long Rstartblock)

utiliza funciones o métodos que creo que están en WProgram.h

Ahora desentrañando
SPI.h
SPI.cpp

y por consiguiente la librería SD con sus 9 cabeceras y sus 6 archivos fuente
Es una tarea compleja para mis conocimientos actuales pero no imposible.

Olvida WProgram hasta donde yo se, puedes prescindir.
Solo comentala.

Otra sugerencia. Si miras SD.h seguramente existe el bajo nivel que permita un acceso a nivel de sectores.
Tal vez sean procedimientos privados para la clase asi que podrias crear una librería propia donde estos procedimientos sean públicos.

Aca otro hilo que merece leerse.

El anterior hilo que te sugerí esta pensado para IDE 1.0.X ojo!!! no me había percatado de ese detalle. Te dará muchos dolores de cabeza.

EDITO: He modificado la librería eliminado WProgram y agregando Arduino.h y compila.
Te dejo la prueba con la SD a ti.

La adjunto para que tengas el archivo ejemplo y las librerías todo para una prueba.

readwritesector.zip (5.63 KB)

Honestamente, yo uso una SD de 4 GB, y ese método no me funcionó; no escribe nada al sector indicado.
Probé con la clase Sd2Card que viene en la librería SD:

#include <SD.h>
Sd2Card card;

unsigned char buffer[512] ;  // this 512 bytes read from or written to sd card
const unsigned long sector = 10000;  // the sector we will write or read from - leave lower sectors


void setup(){
 Serial.begin(9600);   // initialize serial communication with computer
 memset(buffer, '?', 512); // Llenar un sector completo con signos de interrogación
 if (!card.init(SPI_FULL_SPEED, 4)) { Serial.println(F("ERROR")); return;}
 if (card.writeBlock(sector, buffer)) Serial.println(F("Lo logre!"));
 else Serial.println(F("NOPE"));
}

void loop() {

}

Lo subí, introduje la tarjeta, y el monitor serie me responde con "Lo logre!"; esa es una buena señal.
Abro el programa HxD para hacer una lectura en crudo de la tarjeta, y miren justamente lo que encontré en el sector 10000:

En resumidas cuentas: funciona aún en SDHC (4 - 32 GB).

Creo que con este descubrimiento ya tenemos algo de progreso.

Ahora, lo único que me falta por saber, es cómo determinar la capacidad de la tarjeta en sí; sin tabla de particiones ni sistemas de archivos. Mi idea es convertir (a nivel de software) una tarjeta SD en una EEPROM, o al menos imitar el funcionamiento de la clase File (manipular toda la tarjeta como un archivo)

Uhm, el proyecto suena interesante, varias veces la EEPROM se me ha quedado corta.
Si piensas liberarlo como biblioteca, (y si quieres, obvio :P) te puedo dar una mano.

¿Has trabajado con git/github alguna vez?

Saludos!

Actualizo:

Lucario448:
Ahora, lo único que me falta por saber, es cómo determinar la capacidad de la tarjeta en sí; sin tabla de particiones ni sistemas de archivos.

Creo que ya lo tengo. No había notado que Sd2Card tiene una función llamada cardSize; retorna la cantidad de sectores que tiene la tarjeta; por lo tanto:

unsigned long capacidadEnBytes = card.cardSize() * 512; // Si retorna cero, un error ha ocurrido

El detalle es lidiar con más de 4 GB, ya que es hasta donde puede direccionar un unsigned long en AVR.
Manejarlo sector por sector no sería un problema, hasta 2 TB sería posible; pero mi implementación se supone que debe convertir operaciones "sector por sector" en operaciones "byte por byte".

PD: ¿alguien sabe cómo especificar herencia a una clase en C++? La idea es que mi implementación pueda ser usada igual que con Serial.

ferminolaiz:
varias veces la EEPROM se me ha quedado corta.

¿Hablas en serio? Pensé que con 1024 bytes se almacenaba bastante información. Si lo que sueles almacenar son cadenas de caracteres, no habría duda de por qué...

ferminolaiz:
Si piensas liberarlo como biblioteca, (y si quieres, obvio :P) te puedo dar una mano.

Agradezco tu ayuda entonces, porque alguna no me caería mal :slight_smile:

ferminolaiz:
¿Has trabajado con git/github alguna vez?

Sinceramente no, pero he oído decir que es una forma fácil de trabajar código en equipo.

Como estaba diciendo antes, estoy entre imitar EEPROM o File/Serial (estas dos últimas lo que tienen en común, es que se pueden usar las funciones establecidas por la clase Stream; entre ellas, el muy famoso print y println).

El heredar de otra clase es algo así:

class ClaseHija : public ClasePadre
{
    // ...
};

public indica que todos los métodos heredados mantienen la misma accesibilidad (public, public; protected, protected). Si lo cambiaras a protected, todos los miembros públicos se convertirían en protegidos. Lo mismo con private; convertiría los miembros públicos y protegidos a privados.

Un detalle con el que me encontré trabajando en espacios reducidos es que la clase Stream utiliza bastante espacio (insisto, en relación a los recursos disponibles si uno trabaja con controladores pequeños).

En cuanto a la EEPROM, rectifico, una vez me quedé corto en la práctica, efectivamente, almacenando strings; las demás fueron en el papel, almacenando configuraciones y una serie de instrucciones :stuck_out_tongue:

Para manejar tarjetas mayores a 4GB, se me ocurren dos opciones. Una, que el usuario especificara con qué sector quiere trabajar, y otra (que parece menos lógica pero me parece más eficiente): asumiendo que el tamaño de todas las tarjetas mayores a 4GB son multiplos de 4GB (nunca vi una de 6GB, corijanme si me equivoco), uno podría dividir la tarjeta en "sub tarjetas", o sectores de 4GB, y que la biblioteca se encargue manejar las direcciones que correspondan.

Saludos!

ferminolaiz:
Un detalle con el que me encontré trabajando en espacios reducidos es que la clase Stream utiliza bastante espacio

¿Entonces qué sería más eficiente (en espacio)? ¿Heredar o simplemente implementar las funciones desde cero?
De ser la segunda... sería un desafío entonces; lo digo por funciones como print, println, find, findUntil, parseInt, parseFloat. Las que no mencioné, son fáciles para mi de recrear.

ferminolaiz:
En cuanto a la EEPROM, rectifico, una vez me quedé corto en la práctica, efectivamente, almacenando strings

Ja, lo sabía.
Cuando la memoria es poca, los strings se vuelven demasiado grandes. Si es para almacenar constantes, esas irían en la memoria flash; lo que podría ir en la EEPROM, son instrucciones que indiquen cual string imprimir (instrucciones de uno o unos cuantos bytes).

Ejemplo: para mostrar las configuraciones actuales en una LCD, en vez de almacenar las cadenas en la EEPROM, se hacen en la memoria flash; esta primera lo que contendría, es el ID asociado a esa configuración y cadena de caracteres.

ferminolaiz:
Para manejar tarjetas mayores a 4GB, se me ocurren dos opciones. Una, que el usuario especificara con qué sector quiere trabajar, y otra (que parece menos lógica pero me parece más eficiente): asumiendo que el tamaño de todas las tarjetas mayores a 4GB son multiplos de 4GB (nunca vi una de 6GB, corijanme si me equivoco), uno podría dividir la tarjeta en "sub tarjetas", o sectores de 4GB, y que la biblioteca se encargue manejar las direcciones que correspondan.

De hecho, yo tenía pensado combinar ambas. El direccionamiento por sectores siempre será absoluto, mientras que el direccionamiento por bytes será relativo si se detecta una tarjeta >4 GB. ¿Relativo a qué?, en caso de que preguntes; será relativo a algo que llamaré "número de banco", que es lo mismo a tu concepto de "sub-tarjeta".

Esta técnica tampoco es nueva, también se utilizaba en los cartuchos de las consolas de videojuegos en la época de los 80; en inglés se le llamaba "bank-switching", el cual consistía en "expandir" a nivel de software, el mapa de memoria que tenía el microprocesador con el cartucho (el cual era efectivamente por bytes). Frecuentemente está técnica era utilizada para adicionar memoria RAM a la que traía la consola por sí sola, o cuando el juego era muy avanzado y se almacenaba en una ROM más grande que la que puede mapear nativamente el microprocesador.

Entonces eso haré: una especie de "bank-switching" para que el direccionamiento a nivel de bytes pueda ir más allá de 4 GB. Como a nivel de sectores siempre será absoluto, dependiendo en la posición en la que se encuentre, ese cambio se realizaría automáticamente.

Y por último, es cierto que la capacidad crece "en múltiplos de 4", aunque en realidad lo hace en "potencias de 2"; ¿o si no por cuál otra razón las capacidades crecen en una secuencia de 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 (1 GB o 1 TB dependiendo de la unidad en que lo hayas visualizado)...?

Lucario448:
¿Entonces qué sería más eficiente (en espacio)? ¿Heredar o simplemente implementar las funciones desde cero?
De ser la segunda... sería un desafío entonces; lo digo por funciones como print, println, find, findUntil, parseInt, parseFloat. Las que no mencioné, son fáciles para mi de recrear.

Lo que haría sería agregar las funciones que realmente se necesiten, pero todo depende de qué se quiera hacer. Si uno pretende recrear una EEPROM, habría que implementar las funciones que permiten manipular tipos de datos mayores a 1 byte (como hace la lib de arduino). Sino, si la idea es manejar strings, puede que sea mejor heredar de Stream.
O, de última, hacer una base y una extensión que puede manejar strings.

Lucario448:
Ja, lo sabía.
Cuando la memoria es poca, los strings se vuelven demasiado grandes. Si es para almacenar constantes, esas irían en la memoria flash; lo que podría ir en la EEPROM, son instrucciones que indiquen cual string imprimir (instrucciones de uno o unos cuantos bytes).

Ejemplo: para mostrar las configuraciones actuales en una LCD, en vez de almacenar las cadenas en la EEPROM, se hacen en la memoria flash; esta primera lo que contendría, es el ID asociado a esa configuración y cadena de caracteres.

Eran strings modificables por el usuario :stuck_out_tongue: (Esos tiempos en donde no tenía un módulo SD y aun no me había armado uno propio..)

Lucario448:
Y por último, es cierto que la capacidad crece "en múltiplos de 4", aunque en realidad lo hace en "potencias de 2"; ¿o si no por cuál otra razón las capacidades crecen en una secuencia de 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024 (1 GB o 1 TB dependiendo de la unidad en que lo hayas visualizado)...?

Lo sé, lo escribí muy al pasar jajaja

Saludos!

================
Edito, el quote me traicionó, espero haberlo llegado a editar a tiempo.

ferminolaiz:
Sino, si la idea es manejar strings, puede que sea mejor heredar de Stream.

Es que... si los find y parse no fueran tan necesarios, entonces implemento sólo lo que necesito.

ferminolaiz:
O, de última, hacer una base y una extensión que puede manejar strings.

Esa no la entendí... ::slight_smile:

ferminolaiz:
Eran strings modificables por el usuario :stuck_out_tongue:

Entiendo, ¿pero tantos o tan grandes eran?

ferminolaiz:
Edito, el quote me traicionó, espero haberlo llegado a editar a tiempo.

Eso explica los 4 párrafos repetidos antes de la corrección.

¿Como hacen cast string a long o UL?
Ya probé el boceto y sale lo logre pero leyendo la SD con e programa HxD en el sector 10000 que es el que se supone estaría escrito, no hay nada.
Como escucho al SPI de arduino.
Hay alguna funcion como Serial.available y Serial.read o similar.

Leyendo por hay encontre este PDF.
Manejo de SD con Pic

¿Para qué serían el parse y/o find?

Con lo de una clase base me refiero a una que emule el funcionamiento de una EEPROM, para que el usuario la pueda utilizar como quiera; y otra clase que implemente, a partir del anterior, el manejo de strings.

Sobre lo otro, eran una serie de menúes modificables por completo.

Saludos!

StringCGE:
¿Como hacen cast string a long o UL?

Con un array de char, es con atol. Con un objeto String, con la función toInt.

StringCGE:
Ya probé el boceto y sale lo logre pero leyendo la SD con e programa HxD en el sector 10000 que es el que se supone estaría escrito, no hay nada.

Oh, es que había olvidado ponerle una parte que compruebe que efectivamente se haya escrito al sector.

Si no aparecen los signos de interrogación, se me ocurre que posiblemente hayas seleccionado "Disco lógico" en vez de "Disco físico".

StringCGE:
Como escucho al SPI de arduino.
Hay alguna funcion como

Serial.available

y

Serial.read

o similar.

Si hereda de Stream debería implícitamente. Según los ejemplos, todo apunta a que en cada transfer se recibe la respuesta.
No lo sé, por eso es que nunca alcanzo a entender SPI

Veo que lo haces a tu manera, aunque... ¿recuerdas cuando te mencioné sobre desentrañar la librería SD? Pues gracias a eso, es que tengo un punto de partida para una librería que está en camino; la cuál permitirá manipular una tarjeta SD como una EEPROM/archivo gigante.

ferminolaiz:
¿Para qué serían el parse y/o find?

Los "parsers" se usan para convertir un número de texto a binario (variable), leyendo directamente desde el "stream".
El "find" se usa para recorrer el "stream" hasta encontrar un byte (o secuencia de estos) que coincida(n) con el parámetro dado.

ferminolaiz:
Con lo de una clase base me refiero a una que emule el funcionamiento de una EEPROM, para que el usuario la pueda utilizar como quiera; y otra clase que implemente, a partir del anterior, el manejo de strings.

Bueno o malo, más bien acabé juntando todo en una sola clase.

Daré un adelanto de las funciones que tendrá mi librería (hasta el momento).

// Initializers
  bool begin(unsigned char cs); // Intenta inicializar la tarjeta SD. Retorna false si falla o no logra reconocer la capacidad.
  bool begin(unsigned char cs, unsigned char mode); // Lo mismo solo que se le puede especificar la velocidad del bus SPI. Las opciones están en Sd2Card.h

  // Read/write operations
  int read(); // Lee un byte desde la poscición en la que está.
  int peek(); // Lee un byte desde la poscición en la que está, pero sin moverse.
  unsigned int readBytes(unsigned char* buff, unsigned int len); // Lee un conjunto de bytes hasta len y los guarda en buff.
  unsigned int readBytesUntil(char terminator, unsigned char* buff, unsigned int len); // Lee un conjunto de bytes hasta len o encontrar el terminador, y los guarda en buff.
  String readString(); // Crea un objeto String desde la poscición en la que está el puntero, hasta encontrarse con el caracter NULL ('\0').
  String readStringUntil(char terminator); // Crea un objeto String desde la poscición en la que está el puntero, hasta encontrarse con el caracter NULL ('\0') o el terminador.
  
  bool write(int b); // Escribe un byte en la posición en la que se encuentre.
  unsigned int write(unsigned char* buff, unsigned int len); // Escribe un conjunto de bytes provenientes de buff, hasta len.
  unsigned int writeString(String s); // Escribe el contenido del objeto String, y le agrega el teriminador; así sigue las reglas de readString().
  bool flush(); // La escritura a la tarjeta SD es asincrónica, por eso esta función garantiza que los cambios se actualicen a la tarjeta (si retorna true).

  // Addressing operations
  bool seek(unsigned long pos); // Mueve el puntero a una posición específica. Si la tarjeta es mayor a 4 GB, esta posición es relativa según el número de banco que se esté trabajando.
  bool seekSector(unsigned long sect); // Mueve el puntero a un sector específico y lo alinia al primer byte de este. Este tipo de direccionamiento siempre es absoluto.
  bool changeBank(unsigned char selection); // Selecciona el banco de memoria a trabajar. El puntero automáticamente se alinia al primer sector y byte de este. La operación falla si la tarjeta es igual o menor a 4 GB.

  unsigned long position(); // Retorna la posición actual del puntero, en bytes. De nuevo, es relativo al banco que se esté trabajando.
  unsigned long sector(); // Retorna el número de sector que actualmente está trabajando.
  unsigned long size(); // Retorna la capacidad de la tarjeta SD, en número de sectores.
  unsigned char bank(); // Retorna el número de banco que actualmente está trabajando. Siempre retornará cero si la tarjeta es igual o menor a 4 GB.

  // Public verifiers
  unsigned long available(); // Retorna la cantidad de bytes que quedan para leer/escribir sin riesgo de llegar a un desbordamiento de banco o alcanzar el final de la tarjeta.
  unsigned long availableSectors(); // Retorna la cantidad de sectores que quedan antes de alcanzar el final de la tarjeta.
  unsigned char availableBanks(); // Retorna la cantidad de bancos en la que se ha dividido la tarjeta SD, para su direccionamiento a nivel de bytes.

Faltan los put y get que vienen en EEPROM, pero más adelante las implementaré. Ahora no porque ahora estoy en proceso de implentar la función bank().

Cuando lo tenga listo, lo posteo y ahí entre los que quieran, lo analizan y lo prueban para encontrar cualquier error o característica faltante que no me haya enterado.

Como selecciono entre disco lógico y disco fisico.
Estoy confundiéndome mas.

que es exactamente esto

Detalle Direccion

SDFile SD.h

*_file; SD.h

sync(); FILE.cpp

No se de donde salen

typedef SDLib::File SDFile;

y esto me marea aun mas SDLib es una clase que tiene a una funcion/metodo File

SdFile::open()

que seria algo asi

SDLIB::FILE::open()

lo único que quiero es tener la capacidad de escribir y leer en la eeprom.... SD XD.

y sacar unicamente las funciones que me permiten esto para que el programa no sea tan pesado ya que mi arduino uno se esta quedando corto de memoria.

Si puedo ver cuantas direcciones de memoria tiene una SD lo del sistema de archivos sera liberdad de quien quiera hacerse uno propio.

Otra cosa.

uint8_t Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {
chipSelectHigh();
  return true;
}

Sin contar lo que retorna Fail solo me queda esto

Que me lleva a esto.

void Sd2Card::chipSelectHigh(void) {
  digitalWrite(chipSelectPin_, HIGH);
#ifdef USE_SPI_LIB
  if (chip_select_asserted) {
    chip_select_asserted = 0;
    SDCARD_SPI.endTransaction();
  }
#endif
}

SDCARD_SPI.transfer(b);

return SDCARD_SPI.transfer(0xFF);

SDCARD_SPI.begin();

Le estoy siguiendo el paso pero la verdad estoy mareado en esto.

Gracias por el aporte.

StringCGE:
Como selecciono entre disco lógico y disco fisico.

Discos físicos.png

StringCGE:
que es exactamente esto

Detalle Direccion

SDFile SD.h

*_file; SD.h

sync(); FILE.cpp

Uno y dos hacen referencia al objeto File; el tercero es básicamente flush.

StringCGE:
typedef SDLib::File SDFile;

y esto me marea aun mas SDLib es una clase que tiene a una funcion/metodo File

SdFile::open()

que seria algo asi

SDLIB::FILE::open()

Creo que es tenía que ver con el atributo friendly, que es como "maquillar" una clase para hacerla más fácil de usar.

StringCGE:
lo único que quiero es tener la capacidad de escribir y leer en la eeprom.... SD XD.

Paciencia, que ya tengo la "beta" de la librería.

StringCGE:
y sacar unicamente las funciones que me permiten esto para que el programa no sea tan pesado ya que mi arduino uno se esta quedando corto de memoria.

Ese de hecho es un tema del que necesito ayuda, porque quisiera saber qué se le puede quitar a Sd2Card; solo uso las funciones init, cardSize, readBlock y writeBlock de esta clase.

StringCGE:
Si puedo ver cuantas direcciones de memoria tiene una SD lo del sistema de archivos sera liberdad de quien quiera hacerse uno propio.

Sd2Card y mi librería de hecho permiten eso; mediante la lectura de un registro en la tarjeta SD se obtiene su capacidad en sectores. Cuando leí la de una de supuestamente 4 GB, la cantidad de sectores apunta a que es de 3.75 GB ._.

StringCGE:
Otra cosa.

uint8_t Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src) {

chipSelectHigh();
  return true;
}



Sin contar lo que retorna Fail solo me queda esto

Ese es parte del "corazón" de mi librería.

StringCGE:
Le estoy siguiendo el paso pero la verdad estoy mareado en esto.

Lo sé, a mi también.

Solo que apenas me di cuenta que ahí está justo lo que necesito, me puse manos a la obra con la librería esa.

Qué por cierto ya la tengo lista para hacer pruebas, lo llamaría "en beta" porque no es la versión definitiva hasta que pasar por el arduo proceso de probar y probar. Al ser así, tampoco tiene forma de librería, por ahora son solo varios archivos en una sola carpeta.
Recuerden que el .ino es el que manda a subir.

readwritesector.zip (15.9 KB)

Lo de los 3.75 GB es por cuestión de unidades.

Técnicamente son 4GB, y 3.7x GiB. si los bloques se dividieran de a 500 bytes, debería dar 4GB; el hecho es que windows desde sus inicios creó una confusión interesante en torno a esto :stuck_out_tongue:

Toma un tiempo acostumbrarse a pensarlo así (personalmente aún no termino de aceptarlo), pero bueno, tal parece que así es..

Saludos!

Oh sí, la confusión del prefijo decimal vs prefijo binario.

NOTA IMPORTANTE: para los que los que quieran hacer pruebas con lo que les acabo de dar en post anterior, deben cambiar esta línea en el archivo SdRaw.cpp:

if (!card.init(cs, mode)) return false;

Por:

if (!card.init(mode, cs)) return false;

Sin esta correción, la tarjeta nunca se inicializa.

Ahora este es mi problema.

Lo que quiero hacer es comunicarme directamente a la SD y con transfer lo hago y lo que se me complica es el hecho de poder escribir en el serial un valor HEX y que le llegue a una variable tipo uint8_t ese valor en HEX.

Ejemplo

Monitor serial 0xFF

Arduino Serial.readString() capturo la cadena

Arduino ?????????

De cadena a HEX no se como hacer cast.