pgmspace e puntatori

Durante il tempo libero ho cercato varie soluzioni per realizzare una collezione di dati non omogenea, ho provato quasi di tutto e solo una soluzione mi ha colpito, si per vari motivi ma uno in particolare mi porta ancora a pensare alle possibili applicazioni.

Io ho usato un array di struct:

int16_t setpoint_pb0 = 3000;
int16_t setpoint_pb0_max_value = 32767;
int8_t  setpoint_pb0_min_value = -50;
uint8_t diff_pb0 = 2;

struct data_type_t  {
    uint8_t type;
    void *ptr_data;
};

struct data_type_t data_types[] PROGMEM = {
    {INT16 | VIEW_AS_DOUBLE ,   &setpoint_pb0},
    {INT16 | VIEW_AS_DOUBLE ,   &setpoint_pb0_max_value},
    {INT8                   ,   &setpoint_pb0_min_value},
    {UINT8                  ,   &diff_pb0}
    .....

INT16, INT8 VIEW_AS_DOUBLE sono macro utili per assegnare un valore a data_type.type, VIEW_AS_DOUBLE indica che il dato
anche se è un int16 deve essere visualizzato sul display 7x3 segment con il punto decimale.

La cosa interessante è che data_type.ptr_data contiene un puntatore o meglio un indirizzo di memoria RAM, grazie a &setpoint_pb0. Questo è possibile perché le variabili sono globali e il loro indirizzo viene risolto prima di creare il contenuto in flash. Altra cosa interessante è la possibilita per ptr_data di prendere un puntatore a funzione.

Se al posto di usare la flash usiamo la EEprom possiamo anche cambiare ptr_data per farlo puntare altrove, cioè se in eeprom ci salviamo l'indirizzo di setup() e in type ci salviamo un bit code identificativo possiamo castare a qualunque cosa prevista.

if (data_type.type = FNC_SETUP)
     (setup)(*data_type.ptr_data)(); // sempre che il cast sia giusto

La cosa bella sarebbe non avere la necessità di castare, ma purtroppo non vedo soluzione.

Per gestire i double ho evitato del tutto di usare questo tipo, anche per temperatura che viene ricavate in centesimi di grado e salvata in un int16_t e quando deve essere visualizzata accendo solo il punto decimale del display nel posto giusto.

Che ne pensate di sta cosa, quale possibile applicazione pratica ci può essere nel salvare puntatori in flash o EEprom?

Ciao.

Aspetta, tu salvi dati in EEPROM con PROGMEM? Non mi torna.

Non mi aspettavo alcuna risposta prima di 20 minuti e stavo per chiudere, meno male perchè allora mi sono spiegato male.

No in eeprom puoi metterci indirizzi di memoria FLASH e memoria RAM, così come in RAM salvi il puntatore ad indirizzo FLASH io ho fatto il contrario mi sono salvato in FLASH degli indirizzi di memoria RAM e mi sono appuntato come interpretarli grazie a data_type.type.

La cosa bella e che se leggo da eeprom la stessa collezione di dati non omogenea per scriverla in RAM non mi serve neanche fare il cast.

IN FLASH IN RAM IN EEPROM
640 (is addr of g) &g=640 25 (is value)
642 (is addr of h) &h=642 2 (is value)

Quindi leggo da flash l'indirizzo di g e accedo a g presente in ram, stessa cosa dovrebbe essere possibile con i puntatori a funzione.

Ah si in effetti se in EEprom scrivo un indirizzo presente in flash come un puntatore a funzione poi posso richiamare questa funzione risalendoci dall'indirizzo.

Ciao.

Ok. Comincio a capire "come", ma non capisco "perché" fai questo... :sweat_smile:

Ok, il perché è richiesto dalla applicazione e dalla efficienza.

Ho una collezione di dati non omogenea come possono essere tutte le variabili globali normarlmente usate dentro un programma. Come fai ad accedere sequenzialmente a tutte queste variabili sparse per il programma pigiando dei tasti?

Risposta:
Salvi l'indirizzo in un array di struct presente in flash, e quindi leggi da flash una struct per volta e grazie a questa sai che all'indirizzo xxx c'è un intero con segno.

Metti che a tutte queste variabili ci vuoi dare un altro valore preimpostato preso da EEprom o da flash, bene fai il compia incolla
leggi da EEprom e grazie a ptr_data accedi alla variabile in memoria e non serve neanche sapere il tipo.

Cosi è più chiaro?

Ok. Grazie della spiegazione.

mi iscrivo per solidarieta' :slight_smile:
purtroppo solo morale

non ho mai torturato troppo arduino..non mi quadra tanto (setup)(*data_type.ptr_data)();..magari si può fare,ma non so..invece dovrebbe andare

typedef void (*tipoFun)(void);
  ((tipoFun)a)();

forse può dare problemi se gli passi il metodo di una classe(ma forse no,probab arduino,dato che non sfrutta tanto il polimorfismo,non usa i fat pointer)..invece coi funtori dovrebbe andare anche un goccetto più veloce..

Si in C++ ci sono anche i funtori e la cosa può funzionare anche con questi ma rimane il problema del casting. Il fatto è che ptr_data è un puntatore a void e prima di usarlo per chiamare funzione bisogna castarlo e finisce la speranza di dinamicità, d'altronde si tratta di C che è tutto altro che dinamico.

Però è possibile usare un puntatore a void per leggere e scrivere nella memoria, per questo mi è possibile cambiare i valori di tutte le variabili globali i cui indirizzi sono stati salvati in flash.

Però questa è una imposizione richiesta dal C/C++ ma non da asm con il quale dovrebbe bastare l'indirizzo e la chiamata __call o sbaglio.

Ciao.

ho sbagliato a scrivere :cold_sweat: ..per castare da void puoi scrivere

typedef void (*tipoFun)(void);
  ((tipoFun)data_type.ptr_data)();

, supponendo che te fai un assegnamento del tipo data_type.ptr_data=miaFunzione ..si,l'assembler di solito non è schizzinoso..ti parlavo dei funtori perchè di solito danno la possibilità al compilatore di fare espansioni inline, ma il passaggio tramite un puntatore generico in effetti può precludere questa ottimizzazione..

tipoFun puoi lasciarlo scritto così o dare un altro nome, è uguale..

Non poi male fare cast a puntatore a funzione che non prende e non restituisce nulla, una certa flessibilità c'è poi per curiosità scrivo un programmino che salva in eeprom i puntatori di 5 funzioni ognuna delle quali stampa in seriale qualcosa di senso compiuto se eseguite in ordine opportuno, poi inverto l'ordine dei dati in eeprom e vediamo cosa accade.

Io ho fatto l'esempio della funzione "setup" perchè attinente ad Arduino, ma io non la uso perchè scrivo solo C con un IDE generico e non uso le funzioni tipiche di Arduino, per cui con i funtori non posso provare. In ogni caso la coperta è sempre corta e se espandi inline occupi più FLASH ma guadagni in velocità specie se chiami il funtore ripetutamente, guadagni in leggibiità e usabilità, ma quano le risorse flash sono limitate pensi a farci stare dentro più codice possibile sacrificando la leggibilità e usabilità.

Se ti va puoi provare a fare tu l'esperimento con i funtori, anche perchè non ho idea di come accroccarlo un esempio valido.

Ciao.

col mio cast, il compilatore non riesce a fare espansioni inline, tranne se alla variabile assegni una sola funzione,e non hai if(..)puntat=funzione1;else puntat=funzione2..