Go Down

Topic: Tercer reto de la semana (Read 934 times) previous topic - next topic

TonyDiana

¿Será posible crear una función que modifique el contenido de PROGMEM?

Yo creo que sí

¿Quién se anima a intentarlo?

"Si crees que puedes, tienes razón. Si crees que no puedes, también tienes razón" (Henry Ford: 1863 - 1947).

gatul

Si, se puede, con un par de servos, un Dremel y mucha puntería.
 :smiley-mr-green: :smiley-mr-green: :smiley-mr-green:

TonyDiana

JAJAJAJA, no en serio @gatul, que tu tienes más frescos los punteros que yo

Estoy seguro que se puede acceder por punteros a la memoria PROGMEM porque realmente es la volátil, sólo que constante para la ejecución del programa, pero por punteros que va a tener que poder.

Imagina poder cambiar en ejecución, desde una SD, por ejemplo, la configuración defecto o vete tu a saber qué.

A ver qué se nos ocurre

gatul

Tal vez se pueda hacer algo estudiando a fondo pgmspace.h
A mi, por ahora, me supera.

TonyDiana

Está duro el asunto


Code: [Select]
#include <Arduino.h>



// --- Extractor genérico de PROGMEM
String extraerPROGMEM( const char* const TABLA[], int INDICE) {
    char _temp[20];

    strcpy_P( _temp, (char *)pgm_read_word( &( TABLA[ INDICE ] )
                                          )
            );

return _temp;
};

const char K_COSA_00[] PROGMEM = "Alfa";
const char K_COSA_01[] PROGMEM = "Betta";
const char K_COSA_02[] PROGMEM = "Gamma";
const char K_COSA_03[] PROGMEM = "Epsilon";

// --- Puteros a las constantes en forma de array
const char* const K_COSA_TAB[] PROGMEM = {
                                K_COSA_00, K_COSA_01, K_COSA_02, K_COSA_03,
                                            };



// --- Receptores
char receptor[15];
    String receptor2;

void hazme(){
    for (int x=0; x<4; x++) {
        receptor2 = extraerPROGMEM(K_COSA_TAB, x);
        Serial.println(receptor2);
    };
};



/*
 *
 */
void setup() {
    Serial.begin(9600);
    Serial.println(); Serial.println(); Serial.println("Comenzando");


                                //long punteador = &K_COSA_TAB[0];
                                //Serial.println( punteador );

    char c_u[] = "ZRDUINO", *c_d, *u_d;
    c_d = c_u;
    Serial.println( *c_d );
    *c_d = 65;
    Serial.println( c_u );

    u_d = c_d;
    *u_d = 66;
    Serial.println( c_u );

                                    //Serial.println( (long)u_d );
    long spm = (long)u_d;
    u_d = spm;
    *u_d = 67;
    Serial.println( c_u );



    Serial.println(); Serial.println(); Serial.println("Tenemos");
    hazme();

    Serial.println(); Serial.println(); Serial.println("Nos vamos");
    c_d = K_COSA_01;
                                //Serial.println( (long)u_d );
                                //Serial.println( (int)*u_d );
    *c_d = 65;

    hazme();

    /* --- Esto ya aburre
    for (int x=0; x<4; x++) {
        // --- Extraer de PROGMEM (Complicado el uso)
        strcpy_P( receptor, (char *)pgm_read_word(
                                    &( K_COSA_TAB[ x ] )
                                            )
              );

        Serial.println(receptor);
    };
    Serial.println(); Serial.println(); Serial.println();
    */









};



/*
 *
 */
void loop() {

};



victorjam

Yo creo que no se podrá, al menos de forma trivial:

Code: [Select]

#define     PROGMEM   __ATTR_PROGMEM__
 
#define     PGM_P   const char *
 
#define     PGM_VOID_P   const void *
 
#define     PSTR(s)   ((const PROGMEM char *)(s))
 
#define     pgm_read_byte_near(address_short)   __LPM((uint16_t)(address_short))
 
#define     pgm_read_word_near(address_short)   __LPM_word((uint16_t)(address_short))
 
#define     pgm_read_dword_near(address_short)   __LPM_dword((uint16_t)(address_short))
 
#define     pgm_read_float_near(address_short)   __LPM_float((uint16_t)(address_short))
 
#define     pgm_read_ptr_near(address_short)   (void*)__LPM_word((uint16_t)(address_short))
 
#define     pgm_read_byte_far(address_long)   __ELPM((uint32_t)(address_long))
 
#define     pgm_read_word_far(address_long)   __ELPM_word((uint32_t)(address_long))
 
#define     pgm_read_dword_far(address_long)   __ELPM_dword((uint32_t)(address_long))
 
#define     pgm_read_float_far(address_long)   __ELPM_float((uint32_t)(address_long))
 
#define     pgm_read_ptr_far(address_long)   (void*)__ELPM_word((uint32_t)(address_long))
 
#define     pgm_read_byte(address_short)    pgm_read_byte_near(address_short)
 
#define     pgm_read_word(address_short)    pgm_read_word_near(address_short)
 
#define     pgm_read_dword(address_short)    pgm_read_dword_near(address_short)
 
#define     pgm_read_float(address_short)    pgm_read_float_near(address_short)
 
#define     pgm_read_ptr(address_short)    pgm_read_ptr_near(address_short)
 
#define     pgm_get_far_address(var)

const void *     memchr_P (const void *, int __val, size_t __len)
 
int     memcmp_P (const void *, const void *, size_t) __ATTR_PURE__
 
void *     memccpy_P (void *, const void *, int __val, size_t)
 
void *     memcpy_P (void *, const void *, size_t)
 
void *     memmem_P (const void *, size_t, const void *, size_t) __ATTR_PURE__
 
const void *     memrchr_P (const void *, int __val, size_t __len)
 
char *     strcat_P (char *, const char *)
 
const char *     strchr_P (const char *, int __val)
 
const char *     strchrnul_P (const char *, int __val)
 
int     strcmp_P (const char *, const char *) __ATTR_PURE__
 
char *     strcpy_P (char *, const char *)
 
int     strcasecmp_P (const char *, const char *) __ATTR_PURE__
 
char *     strcasestr_P (const char *, const char *) __ATTR_PURE__
 
size_t     strcspn_P (const char *__s, const char *__reject) __ATTR_PURE__
 
size_t     strlcat_P (char *, const char *, size_t)
 
size_t     strlcpy_P (char *, const char *, size_t)
 
size_t     strnlen_P (const char *, size_t)
 
int     strncmp_P (const char *, const char *, size_t) __ATTR_PURE__
 
int     strncasecmp_P (const char *, const char *, size_t) __ATTR_PURE__
 
char *     strncat_P (char *, const char *, size_t)
 
char *     strncpy_P (char *, const char *, size_t)
 
char *     strpbrk_P (const char *__s, const char *__accept) __ATTR_PURE__
 
const char *     strrchr_P (const char *, int __val)
 
char *     strsep_P (char **__sp, const char *__delim)
 
size_t     strspn_P (const char *__s, const char *__accept) __ATTR_PURE__
 
char *     strstr_P (const char *, const char *) __ATTR_PURE__
 
char *     strtok_P (char *__s, const char *__delim)
 
char *     strtok_rP (char *__s, const char *__delim, char **__last)
 
size_t     strlen_PF (uint_farptr_t src)
 
size_t     strnlen_PF (uint_farptr_t src, size_t len)
 
void *     memcpy_PF (void *dest, uint_farptr_t src, size_t len)
 
char *     strcpy_PF (char *dest, uint_farptr_t src)
 
char *     strncpy_PF (char *dest, uint_farptr_t src, size_t len)
 
char *     strcat_PF (char *dest, uint_farptr_t src)
 
size_t     strlcat_PF (char *dst, uint_farptr_t src, size_t siz)
 
char *     strncat_PF (char *dest, uint_farptr_t src, size_t len)
 
int     strcmp_PF (const char *s1, uint_farptr_t s2) __ATTR_PURE__
 
int     strncmp_PF (const char *s1, uint_farptr_t s2, size_t n) __ATTR_PURE__
 
int     strcasecmp_PF (const char *s1, uint_farptr_t s2) __ATTR_PURE__
 
int     strncasecmp_PF (const char *s1, uint_farptr_t s2, size_t n) __ATTR_PURE__
 
char *     strstr_PF (const char *s1, uint_farptr_t s2)
 
size_t     strlcpy_PF (char *dst, uint_farptr_t src, size_t siz)
 
int     memcmp_PF (const void *, uint_farptr_t, size_t) __ATTR_PURE__
 
static size_t     strlen_P (const char *s)


Ninguna de la funciones de pgmspace te permite la escritura en flash, solo la lectura.

Y creo que tiene sentido. La flash está pensada para la memoria de programa de forma que cuando "programas" el micro lo haces por bloques. La memoria flash no soporta muchos ciclos de escritura, aun cuando soporte infinitas lecturas. Si fuera fácilemente escribible todos los programadores intentarían usarla como una "RAM", reduciendo notablemente la vida del dispositivo. Para hacer eso tenemos el EEPROM y también hay que tener cuidado...

Así que en resumidas cuentas, seguro que hay una forma de poder escribir en la flash, aunque no creo solo sea un simple byte, poniendo el micro en algún modo especial (900 páginas de datasheet, ni el quijote es tan soporifero..), con lo que además de sacrificar la vida util del avr complicamos el programa. Podrias echar un vistazo al bootloader que obviamente es capaz de acceder a la flash, pero creo que es un tiempo perdido y no merece la pena. Si tu arduino se queda sin memoria, usa uno mas grande.

gatul

Tal cual, hay una secuencia para escribir la flash, que sí, esta organizada en páginas.
Primero se borra la página, después se escribe.
Se puede simular la escritura de una parte de la página copiando la seccion que no cambia en el buffer de la pagina completa (un collage, digamos) pero todo se hace a bajo nivel y siempre vía comunicación serie (en cualquiera de sus variantes) si no entiendo mal.
Así que no hay forma.

surbyte

La unica alternativa que investigué (no la sabía) es usar la zona del Bootloader.
Investiga como se escribe en el bootloader y ahi tienes una posible solución, con el cuidado debido por supuesto.

TonyDiana

Eso último me da mucho miedo la verdad, sí se que con PROGMEM a pelo no se puede, pero pensaba que con punteros lo lograría, creo que queda en el cajón del purgatorio (donde los proyectos esperan el cielo o el infierno)

TonyDiana

(900 páginas de datasheet, ni el quijote es tan soporifero..),
Cuidado lo que dices del quijote  ;)

gatul

#10
Nov 14, 2020, 07:44 pm Last Edit: Nov 14, 2020, 07:50 pm by gatul
Yo creo, si mal no entendí, que el bootloader mismo se reescribe cuando se graba la primera página (siempre que se quiera mantener el ya grabado, obviamente).
Sería algo así: se carga el buffer de escritura con lo que sea dejando un área  para el bootloader en blanco, se inserta (copia) el bootloader en el buffer, se borra la página, se escribe el contenido del buffer en la página.
Hay otra secuencia de escritura que es: se borra la página, se carga el buffer, se escribe. Pero en este caso seria "destructiva" porque no hay posibilidad de insertar el bootloader (o lo que sea segun la página que se escribe) ya que como la página fue borrada ya no se puede copiar nada al buffer.
Lo mismo se haría con cualquier parte de lo ya escrito en una página y se quisiera conservar.
Algo así explica el datasheet, en el que hay ejemplos de bootloaders, y dicho sea de paso, no tiene 900 páginas, tiene menos de 700.
Jaja

TonyDiana

No se trata, para mi, de hacer algo tan complicado, sino algo muy sencillo, que permita, por ejemplo, que es lo que se me había ocurrido, tener una serie de mensajes fijos y poderlos cambiar por el contenido de un archivo de una SD, de manera que el aparato funciona sin la tarjeta de manera estándar, por si te la olvidas, para no tener que ocupar siempre la memoria RAM, pero bueno, ya pensaré otra solución.

gatul

Es que para lograr lo "sencillo" tienes que hacer lo "complicado". Jejé

Igual creo que ahora entendí mejor tu idea pero no creo que se pueda cambiar el puntero facilmente porque tenes que meterte con registros que se acceden a bajo nivel. Tal vez en assembler y estudiando bien el datasheet...


¿No te gusta la carpintería? Así dejas esas neuronas en paz por unos días.    :smiley-mr-green:

TonyDiana

Me encanta la carpintería, pero prefiero evitar al alemán (el Alzheimer) refregando a mis neuronas en retos, jejejej  ;D

gatul


Go Up