Voy a compartirles una experimento que hace poco hice. Intenté ejecutar este código:
#include <SPI.h>
#include <SdFat.h> // Esta no es la librería SD que viene en la IDE, OJO
SdFat sd;
volatile boolean dumpTriggered = false;
void setup() {
pinMode(2, INPUT_PULLUP);
Serial.begin(9600);
while (!Serial) {}
if (!sd.begin(4)) {
Serial.println("SD fail");
return;
}
Serial.println("SD ok");
attachInterrupt(digitalPinToInterrupt(2), dump, FALLING);
char dummy[5]; // A ver en que lugar del volcado me encuentro este "array"
while (true) { // Ciclo infinito. De esta manera pruebo que la interrupción en verdad funciona.
for (byte i = 0; i < 5; i++) {
dummy[i] += 1;
}
if (dumpTriggered) { // Una manera de identificar cuándo se desencadenó la interrupción
Serial.println("Dump triggered!");
dumpTriggered = false;
}
}
}
void loop() {
// Nada. Aquí no hay nada que mirar.
}
void dump() { // He aquí la ISR
if (sd.exists("Dump2.bin")) {
sd.remove("Dump2.bin");
}
File dump = sd.open("Dump2.bin", FILE_WRITE);
byte *posmem = 0;
do {
dump.write(*posmem);
*posmem++;
} while ((int)posmem < 2048); // Necesito una explicación en esta línea
dump.close();
dumpTriggered = true;
}
Y note que... ¡El archivo de la ISR sí se creó! Por lo tanto, ¡SPI sí funciona en una ISR! 8)
Pero ahora me quedó la siguiente duda:
while ((int)posmem < 2048);
"posmem" es un puntero... de tipo byte? Entonces:
El tipo de dato significa lo que se debe leer en el puntero? O que la dirección de memoria se guarda con un byte?
Es realmente necesario "castearlo" a int a la hora de comparar? Digo... cómo sobrepasó el número 255 si se supone que es de tipo byte?
Hola.
Permite primero que me remonte un poco atrás.
Realmente la instrucción más confusa de mi código anterior fue esta:
Serial.println(*posmem++, HEX);
Aunque creo que hace lo esperado, caí en un error frecuente entre los programadores de C, que es condensar la sintaxis a costa de perder claridad a cambio de nada (el programa resultante es el mismo).
Realmente debí haber escrito esa línea así:
Serial.println(*posmem, HEX);
posmem++;
Todo esto lo digo porque en tu código, inducido por mi fallo mencionado, luego has puesto esto:
*posmem++;
Ahora vamos al meollo:
posmem es un puntero a byte. Esto significa que *posmem devolverá el byte apuntado por dicho puntero, mientras que posmem devolverá directamente el puntero, es decir el número de posición de memoria en sí. Hago un cast a integer para evitar errores o warnings del compilador, pues aunque para nosotros podríamos entender una posición de memoria como un entero, para el compilador son datos distintos (arduino creo que usa 14 bits para direcciones, y otro sistema puede utilizar punteros de 16, 32 o 64 bits). Entonces mi incremento es sobre el puntero, que avanzará una posición, mientras que tu incremento lo es sobre el byte almacenado en esa posición.
Lo que hago en la función es definir un puntero a byte y decirle que apunte literalmente a la posición 0 de memoria. Entonces voy imprimiendo byte y avanzando a la siguiente posición de memoria, hasta la 2048 (habrá que ver el límite de memoria de cada modelo de arduino).
Entonces, si todos los punteros son datos iguales, ¿por qué se definen con un tipo acompañado?
Pues para que el compilador nos dé la murga cuando, por ejemplo queramos leer un byte en memoria a través de un puntero que habíamos dicho que era para int. Podremos aún hacerlo si es que así lo habíamos planeado indicándoselo al compilador mediante un cast. También sirve para que cuando incrementemos/decrementemos ese puntero el compilador lo haga según el tamaño de variable apuntada, o bien para si queremos utilizar dicho puntero como un array de ese tipo de variable.
Perdón por el rollo, pero ya que me he liado lo dejo e igual a alguien le aprovecha :).
noter:
Realmente debí haber escrito esa línea así:
Serial.println(*posmem, HEX);
posmem++;
Todo esto lo digo porque en tu código, inducido por mi fallo mencionado, luego has puesto esto:
*posmem++;
Gracias por el dato. Mi error sí fue inducido como tu lo dices, pero separé las líneas más que todo porque el archivo resultante salía de 2047 bytes y no de 2048 como se esperaba.
Entonces mi incremento es sobre el puntero, que avanzará una posición, mientras que tu incremento lo es sobre el byte almacenado en esa posición.
Y casualmente, aún así los datos ni se repetían, ni se alteraban (por ejemplo, los strings en memoria se copiaron correctamente en el volcado)
(habrá que ver el límite de memoria de cada modelo de arduino).
Que pasará entonces, si apunto más allá de la memoria? Retornará a la posición 0? O el programa se colgará?
o bien para si queremos utilizar dicho puntero como un array de ese tipo de variable.
Pero porque no buscas una placa para hacer debugging con hardware si tan importante es?
El mismo ATMEL STUDIO usando STK500 o STK600 permite hacer esto, si mal recuerdo.
Pues efectivamente, Lucario448, contra lo que yo pensaba, la instrucción *posmem++ que usabas también incrementa el puntero. Para incrementar el contenido, habría que meter entre paréntesis
(*posmem)++;
pues al parecer la precedencia del incremento es superior a la de puntero.
En cuanto a lo que comenta surbyte, estoy de acuerdo en el sentido de que si se quiere hacer un volcado de memoria limpio, lo mejor es realizarlo desde fuera, bien con el debugger, o mediante simulación. Lo que no significa que con este experimento no podamos apreciar algunas cosillas.
surbyte:
Porque justamente se van a Pisar posiciones de memoria que de otro modo no estarían en uso.
Correcto. La ISR es una función; por lo tanto, siempre va a necesitar un espacio adicional en la pila ("stack").
El otro detalle es que en la ISR se crea un objeto File, lo cual hace que el montón ("heap") también sea alterado después de desencadenar la interrupción y antes de empezar a leer la memoria.
Soy consciente de todo lo anterior; sin embargo, considero que con este método de volcado de memoria RAM estoy satisfecho.
Por ejemplo: abrí el archivo resultante con un editor hexadecimal; y descubrí que los strings usados en los Serial.print se guardaron en la memoria... dos veces? Solo algunos se guardaron tres veces, pero creo que simplemente eran remanentes de la pila.
Adjunto el archivo del volcado resultante (comprimido en zip) por si quieren echarle un ojo.
Para abrirlo, yo uso XVI32, un editor hexadecimal de archivos gratuito. La uso porque se pueden ver los bytes en forma hexadecimal y como caracteres ASCII, al mismo tiempo.
Por esta última razón, no creo que sea difícil ubicar los strings del código.