Il tempo delle librerie è finito....

Che è il mio solito titolo ad effetto per attirare l’attenzione
se vi ricordate il mio “tempo delle librerie” avevo organizzato una piccola libreria in ‘C’ per fare timer multipli
“quella cosa la” aveva alcuni problemini, andava sì, ma non era buona programmazione
ecco quindi che spronato da questa discussione

ed avanzando alcuni minuti ho buttato giù questa cosa qui, che non è una libreria, sono proprio righe da copiare…

per prima cosa siccome userò dei puntatori a fuznione me li dichiaro:

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

poi li uso per creare una struttura di timer, che chiamo gearbox (scatola degli ingranaggi, riduttore)

struct gearbox
{
    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
};
// la singola struttura del singolo timer

adesso devo creare tante funzioni di callback quante sono necessarie

// creo le funzioni di callback
void led13(void)
{
    // inverte lo stato del led 13
    digitalWrite(13, !digitalRead(13));
}

void alpasso(void)
{
    // ogni passo trasmette una stringa sulla seriale
    static int passo = 0;
    Serial.print("Funzione 1 eseguita: ");
    Serial.print(passo++);
    Serial.println(" volte");
}

void algradino(void)
{
    // ogni passo trasmette una stringa sulla seriale
    static int passo = 0;
    Serial.print("Numero esecuzioni funzione 2: ");
    Serial.println(passo++);
}

adesso creo un array di gearbox (singoli timer), inizializzandolo alla creazione

gearbox timer[] = {{1000, led13}, {2000, alpasso}, {1500, algradino}};
#define TIMER sizeof(timer)/sizeof(timer[0])
// il numero degli elementi

i trucchi:

  1. definisco la dimensione con una macro
  2. NON inizializzo il terzo membro della struttura, che quindi rimane naturalmente a zero
    naturalmente il primo membro è il “passo” del timer, e il secondo il nome della funzione da eseguire

adesso tutto il lavoro sporco:

// 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();
        }
    }
}

non difficile vero?
chi usa regolarmente millis ne ha viste fino alla nausea, temo

a questo punto rimangono solo la setup

void setup(void)
{
    Serial.begin(9600);
    Serial.println("Programma dimostrativo timer multipli");
    pinMode(13, OUTPUT);
}

e la loop

void loop(void)
{
    eseguitimer();
}

ops, non c’è nulla, è tutto fatto dalla eseguitimer, che in realtà richiama dei membri puntatore…
il bello del ‘C’: la concisione

[/code]

Senza voler fare pubblicità, ma se ti prendi il numero 229 (Ottobre 2018) di Elettronica In trovi ... uno scheduler cooperativo già bello che fatto ed ottimizzato per Arduino che è un ottimo spunto di parteza per future estensioni ;)

Guglielmo

non ne dubito, e non mi sono mai "voluto muovere" a quei livelli in realtà a me basta dare spunti....

Sostanzialmente il delay è sempre bandito, anche in questo caso, come pure in cooperative mode.

Suggerimento: se alla funzione utente (UF) gli passi un puntatore a gearbox in questa struct ci metti un bool ottieni che la UF può spegnere il timer, permettendo il cosi detto “un colpo solo”.

typedef enum timer_mode timer_mode_e;
enum timer_mode { Run, Stop };

typedef gearbox gearbox_t;
struct gearbox
{
    timer_mode_e mode;
    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
};
// la singola struttura del singolo timer

typedef void (* callback)(gearbox_t *gb );


// creo le funzioni di callback
void led13(gearbox_t *gb )
{
    // inverte lo stato del led 13
    digitalWrite(13, !digitalRead(13));
    gb->mode = Stop;  // un colpo solo
}

Ovviamente anche eseguitimer deve verificare se mode == Run o Stop

C’è da dire che al posto del puntatore “gb” basterebbe passare l’indice, ma mettiamo che
la l’array di gearbox non sia visibile, nascosto o che magari passiamo a malloc, in tal caso siamo già pronti.

Soluzione che non impedisce di usare millis come di consueto all’interno del loop per fare altro.

Altra info lo stack pointer è accessibile e manipolabile da C, purtroppo ci ho dedicato una oretta e mi sono arreso, troppi numeri da seguire in asm, comunque sapendo con precisione in che stato è lo stack dentro una funzione possiamo ripulire lo stack e fare in modo che chiamando una funzione questa no ritorni al chiamante ma ad esempio al main. Più in la molto più in la ci gioco un poco, ma se non prendo il libro asm in mano non vado da nessuna parte, ma solo per gusto del rischio ne vale la pena. :smiley:

Ciao.

Di fatto hai dato vita alla mia idea iniziale, io magari ci avrei messo un po' di più e l'avrei fatta un po' più alla buona ;) perchè ancora un po' crudo sono ma l'idea era la stessa fin dall'inizio.

sapevo di essermi ispirato a qualcuno, ma non ricordavo a chi.... non volevo togliere nulla a nessuno, sia chiaro socio.... i miei sono solo esercizi, così per provare invece, il significato del terzo membro della struttura, che se ignoato viene valorizzato a zero (ditemi che sono un genio....) banalmente il tempo dell'ultima esecuzione della callback relativa, ma se invece venisse inizializzato.... permetterebbe di programmare eventi di periodo uguale, ma "sfalsati" di fase esempio due cicli, durata 5 minuti ognuno, ma uno accade al secondo 0 dei minuti 0 5 10 15 ecc... il secondo accade al secondo 15 del minuto 1 6 11 16 ecc ecc basta mettere membro 1 pari a 5UL*60*1000 -> numero di millisecondi in 5 minuti ma per il primo ciclo un terzo membro di 0 per il secondo cicli un terzo membro di 75UL*1000 -> un minuto e 15 secondi

Standardoil: sapevo di essermi ispirato a qualcuno, ma non ricordavo a chi.... non volevo togliere nulla a nessuno, sia chiaro socio.... i miei sono solo esercizi, così per provare

Ma ci mancherebbe, altrimenti la mia idea iniziale sarebbe rimasta un'idea in un post sperso in mezzo al mare, così invece ha visto la luce.

Per rispondere a Maurotec hai pienamente ragione, aggiungere un membro boolean permette di avere one -shot e permette anche di avere cicli (non voglio chiamarli timer) che partono e si fermano a comando in prospettiva si potrebbe creare una classe gearbox, che ha la sua funzione di esecuzione come funzione membro, ha anche un membro di dis-attivazione e tutti i membri che servono per la regolazione - definizione rimarrebbe che debbo creare un array di oggetti gearbox e ciclarlo ma credo che se aggiungesimo nella classe un membro puntatore a successivo, e delle funzioni di aggiunta di successivo, rimozione (mi viene in mente lista linkata, non so se sbaglio) e se il membro di esecuzione al termine chiamasse il prossimo elemento della lista... potrei anciare nella loop solo il membro di esecuzione della sola prima istanza della gearbox, e da li acascata arrivano tutte le altre, quante che siano non ci interessa, potremmo anche aggiungere e/o togliere gearbox a runtime mii....... sono ormai completamente assorbito dal lato oscuro, penso in termini di classi e istanze e allocazione dinamica

debbo andarmi a confessare...... ->

EGO TE ABSOLVO...

->

ragazzi, ho l'assoluzione e la penitenza:

Torna a pensare in Basic, figliolo

per il momento vi saluto

10 system:

Invece creare una libreria basata su questo codice ne renderebbe facile l'utilizzo :)

Pensavo di inserire nella libreria la struttura e la funzione che calcola il tempo con millis(). Nel costruttore si indicano il numero di funzioni che si devono usare, e con allocazione dinamica si crea un array di struct.

Poi la libreria avrebbe il metodo add(tempo, e nome funzione), per inserire le varie funzioni.

... continuo a ripetere che state reinventando l'acqua calda!!! Di "scheduler coppoerativi" per Arduino ne trovate quanti ne volete ... anche alcuni scritti dal nostro caro Leo72 ed altri dal sottoscritto ... :D

Anzi, Leo72 ha fatto di meglio ... con la sua libreria LeOS2 ... dategliela un'occhiata (anche ai successivi aggiornamenti QUI e QUI) ;)

Guglielmo

Beh, Di acqua calda ne serve sempre, ad esempio per la doccia... Comunque io non voglio 'sostituire' o 'reinventare' nulla Mostro solo degli spunti, che se uno li legge, li comprende (letteralmente "prende con se") Eli ricorda... Poi magari le lucine di natale, o i semafori.... Li ricordate? Se li fa facilmente...

maubarzi: Ma ci mancherebbe, altrimenti la mia idea iniziale sarebbe rimasta un'idea in un post sperso in mezzo al mare, così invece ha visto la luce.

Non vorrei si intendesse che rivendico la paternità della cosa, semplicemente la mia idea iniziale era molto simile e mi fa piacere abbia preso un po' vita in questa implementazione che ne riporta alcuni tratti caratteristici.

Standardoil: rimarrebbe che debbo creare un array di oggetti gearbox e ciclarlo ma credo che se aggiungesimo nella classe un membro puntatore a successivo, e delle funzioni di aggiunta di successivo, rimozione (mi viene in mente lista linkata, non so se sbaglio) e se il membro di esecuzione al termine chiamasse il prossimo elemento della lista...

avevo pensato anche io ad una struttura di questo tipo, per evitare l'array puro da scorrere interamente per vedere se ci sono elementi da eseguire, ma poi mi sono detto che gli elementi schedulati non dovrebbero essere tanti, una decina e non un centinaio o un migliaio, e quindi, forse, si fa prima a ciclare che a tenere allineata una tale struttura ottimizzata. L'aggiunta dinamica di gearbox potrebbe comunque essere fatta a prescindere dal fatto che ci sia un array fisso o una lista linkata o un albero, o altro, tanto si passa sempre per un array di oggetti che deve avere un limite massimo impostabile per non dover fare a pugni con la memoria. Quindi, se si vuole tenere il codice semplice da capire si tiene l'array semplice, se servono prestazioni spinte e si ha voglia di ottimizzare si studia un modo per "ordinare" gli elementi tra di loro. Un esercizio "scolastico" potrebbe essere quello di tenere l'array ordinato mediante algoritmo di quick sort dopo ogni nuova aggiunta/rischedulazione...

gpb01: ... continuo a ripetere che state reinventando l'acqua calda!!!

Si, vero, però reinventare l'acqua calda aiuta a tenere la mente allenata. Penso che nessuno abbia avuto l'intenzione di scrivere una libreria per sbaragliare il mercato, ma sia più un esercizio per tenersi allenati e per mettersi alla prova. Non lo trovo affatto sbagliato, anzi lo trovo molto stimolante.

Concorquoto

maubarzi: Si, vero, però reinventare l'acqua calda aiuta a tenere la mente allenata. ... ... Non lo trovo affatto sbagliato, anzi lo trovo molto stimolante.

non sono d'accordo ... molto più stimolante impegnare la mente a studiare cose nuove, che cose trite e ritrite!

Ad esempio, forse vie è sfuggito, dato che siete molto concentrati sulle vecchie architetture, ma, sia Microchip, che Arduino con alcune sue schede, stanno spingendo i nuovi "megaAVR 0-series" (es. ATmega4809) ... bestiole piuttosto potenti e complesse che, dei vecchi AVR che tutti conosciamo, hanno ben poco ... ::)

Ecco, se vuoi veramente tenere la mente allenata, scaricati il "megaAVR 0-series Manual (DS40002015A)" e ... ... comincia a studiare le nuove MCU che, sia il ATmega4808 che il ATmega4809, ultimamente li stanno mettendo su varie schede (come, ad esempio, Arduino UNO WiFi REV2) e ... sono tutto meno che banali ;)

Guglielmo

Ma non è mica per allenare la mia, di mente... Ma per stimolare i novizi...

Non è vero, bluffavo...

Io mica ci arrivo a quel livello che dici tu

Standardoil: Ma non è mica per allenare la mia, di mente...

NON parlavo con te ma, come vedi dal "quote", rispondevo a maubarzi.

Guglielmo

Standardoil: Ma per stimolare i novizi...

... ora invece rispono a te ... "aspetta e spera" ... :stuck_out_tongue_closed_eyes: :stuck_out_tongue_closed_eyes: :stuck_out_tongue_closed_eyes:

Eppure ormai li conosci ... ;)

Guglielmo

Standardoil: Non è vero, bluffavo...

Io mica ci arrivo a quel livello che dici tu

quotocordo (quoto e concordo ;))

Io pensavo alla mia di mente e vi ricordo che al C mi ci sono avvicinato da pochi mesi e pure ad Arduino, per cui sono ancora un bambino in questo mondo e sto scoprendo solo ora l'esistenza dell'acqua calda e dei metodi artigianali per ottenerla, poi per le centrali nucleari c'è tempo ;) ma ci arriverò? Mettere su una centrale nucleare come hobby? mai dire mai... ma... Ho quotato il socio e non Guglielmo per il detto: se non riesci a convincerli allora confondili :P

gpb01: ... ora invece rispono a te ... "aspetta e spera Guglielmo [/quote] io non intendo aspettarli Intendo mandarceli A cercare, per cominciare...

Standardoil: Intendo mandarceli ...

... quello ormai è da parecchio che anche io lo faccio :smiling_imp:

Guglielmo