New Il tempo delle librerie è finito....

Eccomi ancora qua, a tentare di riaprire il post "Il tempo delle librerie è finito...." Il tempo delle librerie è finito.... - Software - Arduino Forum

Ci siamo conosciuti con E’ possibile sviluppare un metodo simile al comando “goto” ma più efficiente - Software - Arduino Forum, e sono qui per chiedere un aiuto.

Nel tempo trascorso sono riuscito a convertire il mio TimerSoft per l’IDE di arduino, ma non sono proprio soddisfatto del risultato ottenuto.

Riguardando il mio vecchio post sopra citato ed i suggerimenti offerti ho approfondito il codice di Standardoil, in particolare mi ha colpito la modalità con cui richiama le varie funzioni, che rappresenta quello che cercavo sin dall’inizio.

Ci ho studiato sopra, e aiutandomi anche con un po’ per deduzione sono entrato nelle variabili strutturate.
Ho smanettato il codice proposto, giusto per adattarlo alle mie esigenze e a titolo di test ero riuscito ad inizializzare l’array del timer anche direttamente da void setup().

timer[0].azione = led13;
timer[0].passo= 250;

Ora però la mia esigenza era quella di passare tali valori tramite una void.
A solo titolo esempio:
void TmrSoft (varAzione, varPasso)
timer[0].azione = varAzione;
timer[0].passo= varpasso;

Tralasciando ogni critica in merito alla sintassi descritta, chiedevo se sia fattibile realizzare qualcosa di equivalente applicando le regole corrette.
Non avendo una specifica preparazione, come è ovvio immaginare i miei tentativi non sono andati a buon fine.

Sarei grato di avere suggerimenti e/o link per relativa documentazione
Grazie

Nei parametri della funzione devi indicare il giusto tipo , void funzione(tipo parametro, tipo parametro).

Se l'array di struct è dichiarato fuori da ogni funzione è globale e sarà accessibile in tutto il programma, mentre se è dichiarato dentro una funzione lo devi passare come argomento alla funzione,
void funzione(tipo parametro, tipo parametro, tipo arrayStruct[] )

Pur seguendo il tuo suggerimento e precedenti mie esperienze su analoghe funzionalità non sono riuscito definire la corretta sintassi.

Il seguente listato è funzionante, ma come si vede alla funzione "AttivaTimer" riesco a passare solo il valore da assegnare al "timer[0].passo", mentre quello da assegnare "timer[0].azione" devo assegnarlo in modo diretto.

Hai qualche suggerimento in merito?

Ringrazio per contributo

// https://forum.arduino.cc/index.php?topic=622158.0

typedef void (* callback)(void); // callback è puntatore a funzione void che restituisce void

struct gearbox { // la singola struttura del singolo timer
  unsigned long int passo;  // passo in millisecondi, ovvio no?
  callback azione;      // l'azione da eseguire, che corrisponde a una funzione void nome(void)
  unsigned long int ultimointervento;    // ultimo tempo di intervento in millisecondi
};

// creo le funzioni di callback
void led13(void){
  digitalWrite(13, !digitalRead(13));
}

void dummy(void){   
}

gearbox timer[] = {{1000, dummy}, {3000, dummy}, {4500, dummy}};
#define TIMER sizeof(timer)/sizeof(timer[0])
// il numero degli elementi

// la sola funzione da richiamare nella loop per eseguire i timer
void eseguitimer(void) {
    for (int i = 0; i < TIMER; i++){
      if (millis() - timer[i].ultimointervento > timer[i].passo){
        timer[i].azione();
        timer[i].ultimointervento = millis();
      }//if
    }//for
}//void eseguitimer

void setup(void){
  pinMode(13, OUTPUT);
  AttivaTimer(1000);    
}

void loop(void) {
  eseguitimer();
}

void AttivaTimer(unsigned long VarPasso){
  timer[0].azione = led13;
  timer[0].passo= VarPasso;
}
// https://forum.arduino.cc/index.php?topic=622158.0

typedef void (* callback)(struct gearbox*); // callback è puntatore a funzione che prende gearbox come argomento che restituisce void

struct gearbox { // la singola struttura del singolo timer
  unsigned long int passo;  // passo in millisecondi, ovvio no?
  unsigned long int ultimointervento;    // ultimo tempo di intervento in millisecondi
  callback azione;      // l'azione da eseguire, che corrisponde a una funzione void nome(void)
};


// creo le funzioni di callback
void led13(struct gearbox *me){
  digitalWrite(13, !digitalRead(13));
  me->ultimointervento = millis(); // all'interno della callback ho accesso a gearbox tramire il puntatore "me"
  // questo significa che puoi anche modificare il membro azione
  // me->azione =  
}
struct gearbox gbox = {1000, 0, led13 };

void checkTimer(struct gearbox *me) {
    if (me->azione == NULL)
        return;   // si assicura che il puntatore non sia vuoto
    if (millis() - me->ultimointervento > me->passo) 
        me->azione(me);    // come vedi passo come argomento "me".  
}

void setup(void){
  pinMode(13, OUTPUT);
  
}

void loop(void) {
  checkTimer(&gbox); 
}

Attenzione non testato. Alle volte serve un timer one shot, cioè solo una volta, allora dentro la callback
assegni a me->azione = NULL;

Sto lavorando senza gli array e quindi un solo timer per volta per rendere più semplice il codice.

Spero che questo ti dia spunto per ciò che vuoi realizzare.

Ciao.

Ti ringrazio per l'aiuto e l'opportunità di valutare metodi alternativi, ma al momento sto cercando la corretta sintassi per passare ad una void un puntatore.

Un esempio, giusto per chiarire il concetto:

// Una funzione generica
  void led13(void){
    digitalWrite(13, !digitalRead(13));
  }


// La funzione da richiamare per inizializzare e fa partire il timer
  void AttivaTimer(unsigned long VarPasso, ????? VarVoid){
    timer[0].azione = VarVoid;
    timer[0].passo= VarPasso;
  }


//  Comando generico da inserire nel codice per passare
//   il ritardo e la funzione da richiamare al termine del conteggio

  AttivaTimer (100,led13)

Il mio problema è cosa anteporre a "VarVoid" (vedi ?????) per dichiarare questa variabile come puntatore di una void, ammesso si possa farlo ....

Non capisco cosa vuoi fare ...
una funzione di tipo 'void' NON ritorna nessun valore (altrimenti non sarebbe void, ma sarebbe con un tipo di ritorno), quindi, ovviamnete, NON puoi passare il 'valore' di ritorno di una funzione che NON ritorna nulla.

Diverso è il caso in cui tu vuoi passare ad una funzione (f1) il puntatore ad un altra funzione (f2), in modo che poi, durante la sua esecuzione, (f1) possa richiamare la funzione (f2) che gli è stata passata.

Quindi, descrivi chiaramemte A PAROLE ed IN ITALIANO quello che vuoi fare e NON con strani esempi in 'C' e vediamo di capire se si può fare.

Guglielmo

// La funzione da richiamare per inizializzare e fa partire il timer
  void AttivaTimer(unsigned long VarPasso, callback VarVoid) {
    timer[0].azione = VarVoid;
    timer[0].passo= VarPasso;
  }

Ciao.

Vi leggo sempre, ma scrivo poco, molto di rado. Stavolta ci ho pensato seriamente di rispondere per "togliere qualche castagna dal fuoco". Ma ho visto con piacere che ne siete usciti
Ho anche avuto piacere che i miei scritti sono ancora consultati. Grazie

Ho appena testato "callback" ed ora tutto funziona come speravo.

Non ho parole per ringraziare dell'aiuto!

p.s.
nel frattempo avevo scaricato qualche guida sul C, ma ho trovato poco in merito ai puntatori delle void, conoscete qualche link in merito?

Non esistoo puntatori a delle "VOID" perché void non é una funzione ma solo un "tipo di varabile" che segna che una funzione non riceve o ritorna valori.

L' uso dei puntatori su delle funzioni "VOID" é lo stesso di altre funioni "INT", "BYTE", "LONG" ecc con eccezione che non ha un valore di ritorno.

Forse dovresti informarti cosa significa "VOID".

Ciao Uwe

uwefed:
Non esistoo puntatori a delle "VOID" perché void non é una funzione ma solo un "tipo di varabile" che segna che una funzione non riceve o ritorna valori.

Infatti ... più o meno quanto ho detto nel mio post #5 a cui l'OP NON ha risposto ... ::slight_smile:

Guglielmo

ho l'impressione che molti pensino che void sia la "parola magica" che "crea" la funzione
e da qui void diventa sinonimo di funzione
un po' come in altri linguaggi serve scrivere

FUNCTION nome(lista argomenti) AS tipo

servirà sfatare questo mito, temo

Ducembarr:
ho l'impressione che molti pensino che void sia la "parola magica" che "crea" la funzione .....

Purtroppo ... debbo concordare al 100% ... molti pochi hanno le idee chiare sulle funzioni ... :confused:

Guglielmo

Per quanto mi riguarda il puntatore è un indirizzo ad una specifica posizione di memoria.
In quella posizione di memoria possiamo trovare il contenuto di una variabile, l'inizio di una stringa o come nel caso trattato l'inizio di una routine che per comodità chiamo Void.
In assembler la cosa è molto semplice da gestire, cosa che non si può dire del C, sopratutto per chi è alle prime armi.

Frequentando il forum mi ero fatto l'idea che non si potesse manipolare tali puntatori.
Solo analizzando il listato di "Standardoil" ho capito che con specifiche direttive, che al momento ignoravo, si possono accedere a tali indirizzi.

Ad ogni modo un grazie a tutti.

lelebum:
... l'inizio di una routine che per comodità chiamo Void.

... cosa che è sbagliata e crea confusione.

void è un tipo dato (vuoto, ovvero nulla) come lo sono byte, int, long, ecc. quindi nulla a che vedere con il concetto di "funzione" che si chiama "funzione" e basta :slight_smile:

Una funzione può non ritornare nulla e si dice che il ritorno è void, o può ritornare un valore e si deve specificare il tipo del valore che ritorna.

Idem, una funzione può non accettare nulla in ingresso come parametri e quindi, nella sua definizione, tra le parentesi si mette (void), altrimenti si mette il tipo dei vari parametri che accetta in ingresso.

Guglielmo

La sintassi certamente non è la più intuitiva, quindi serve una guida che spieghi bene i puntatori. Anche la parola void che potrebbe significare nulla assume il significato di generico quando si vuole creare un puntatore generico.

typedef void* void_ptr;

Il codice sopra ridefinisce un tipo di dato, il cui nome è void_ptr. I puntatori generici possono essere
passati come argomenti, ma non è possibile usarli direttamente senza prima castarli ad un tipo di dato ben definito nel codice. lo posso castare a(char*), (struct *miaStruct) o anche a (callback) ecc.

Dove callback è il nome del tipo di dato definito con typedef come segue:

typedef void (* callback)(void);

Ora se callback restituisse e prendesse qualcosa diverso da void si potrebbe trasformare in:

typedef void* (* callback)(void*);

void * led1(void*);

callback myCallback = led1;

Basta aggiungere * per cambiare il significato, cioè prima non prende e non restituisce nulla, dopo
prende e restituisce un puntatore generico. Questo non si può certo dire sia una sintassi chiarissima ed
inequivocabile.

Oppure in modo equivalente usando il tipo void_ptr

typedef void_ptr (* callback)(void_ptr);

void_ptr led1(void_ptr);

callback myCallback = led1;

dentro la funzione led1:

void_ptr led1(void_ptr gptr) {
    uint8_t * p = (uint8_t *) gptr;
    // ora grazie al cast posso usare [i]gptr[/i] attraverso [i]p[/i]

    return (void_ptr) p; // cast di puntatore [i]p[/i] a puntatore generico
}

Altra cosa con i puntatori:

struct state_t {
    uint8_t state;
};

struct state_t m_state;

typedef void (*stateFunc)( struct state_t*); // non restituisce ma prende puntatore a struttura di nome       
                                                                //[i]state_t[/i]

void state1(struct state_t *s_state );   // il prototipo della funzione 

stateFunc loopFunc = state1;           // alla variabile [i]loopFunc[/i] di tipo stateFunc è assegnata la funzione
                                                      // [i]state1[/i]

void loop () {

      loopFunc(&m_state);       //  chiamata a funzione, verra chiamata la funzione state1, ma da
                                            // dentro la funzione chiamata posso assegnare a loopFunc = altraFunc. 
}

C'è da prendere precauzioni nei confronti di loopFunc al fine di evitare la chiamata quando loopFunc == NULL.

Ciao.

Const void * eyes = wall;

Occhi vuoti che fissano costantemente il muro
Non ti sembra di aver esagerato come prima lezione sui puntatori?

Const void * eyes = wall;

:smiley:

Non voleva essere una lezione, poi mi sono lasciato prendere la mano. Volevo mostrare come la sintassi può confondere le idee. Sempre void c'è scritto ma basta un "*" in più e tutto cambia significato.

Ciao.