Chiamare la stessa funzione temporizzata con valori differenti

Salve a tutti,
vorrei costruire dei ritmi usando un percussore, a partire dall'esempio base blinkWithoutDelay.
La mia domanda è: come posso far eseguire la funzione contemporaneamente più volte ma in maniera indipendente e passando intervalli di tempo differenti? Vorrei che tutte le variabili fossero all'interno di una "istanza della funzione" senza che le variabili si confondessero tra loro.
Devo creare tante funzioni e tante variabili quante sono i diversi ritmi che voglio sommare? o c'è un'altra via?

Metto qui uno pseudo-codice per cercare di spiegarmi meglio, spero si capisca cosa intendo:

unsigned long previousMillis = 0;  // will store last time LED was updated

void loop() {
  blinkWithoutDelay(1000);
  blinkWithoutDelay(1250);
}

void blinkWithoutDelay (int frequenza) {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= frequenza) {
    // ---> fai qualcosa, tipo aziona un solenoide!
    previousMillis = currentMillis;
  }
}

Grazie per qualsiasi suggerimento.
Ah, sto lavorando con una MKR zero.

Antonio

Se usi la stessa funzione per tutti e 3 i percussori, ogni successiva chiamata andrà a modificare di nuovo le variabili andando a interferire con la chiamata precedente.

Il modo migliore secondo me è fare una semplice classe che puoi istanziare quante volte vuoi passandogli ad esempio il pin dove è connesso il percussori e la durata del blink.

Qualcosa del genere oper intenderci:

Ma tu vuoi chiamare la stessa funzione per scopi differenti, oppure fare "fisicamente" la stessa cosa?

Mi spiego: alla fine il percussore Sarà uno solo ?

Per fare questo, in C++ (linguaggio usato da Arduino) puoi mettere la variabile previousMillis dentro alla funzione ma dichiarandola static

void blinkWithoutDelay (int frequenza) 
{ static unsigned long previousMillis = 0;  ...

Però come ti è stato detto prima, prova a spiegare meglio cosa devi fare.

Se non vuoi scomodare le classi, fai più funzioni o blocchi, ognuno con la propria temporizzazione:

uint16_t periodo_1=500;
uint16_t periodo_2=1000;
uint16_t periodo_3=2000;
uint32_t t_1;
uint32_t t_2;
uint32_t t_3;

void setup()
{
// Imposta gli ingressi con pullup interno e le uscite.
...
}

void loop()
{
if(millis()-t_1>=periodo_1)
  {
  t_1+=periodo_1;
  // Fai questo.
  }

if(millis()-t_2>=periodo_2)
  {
  t_2+=periodo_2;
  // Fai quest'altro.
  }

if(millis()-t_3>=periodo_3)
  {
  t_3+=periodo_3;
  // Fai quest'altro ancora.
  }
}

In ordine crescente di "sensatezza":

  1. creare tante funzioni (le variabili possono essere globali, ciascuna con un nome diverso, oppure static interne alla funzione.
  2. creare una sola funzione a cui passare un indice con cui accedere a un insieme di array, o a un array di struct, oppure passare alla funzione un puntatore a una struct (quindi gli array o le struct contengono le variabili di volta in volta in gioco)
  3. creare una classe da cui ottenere tutte le istanze che servono, ciascuna con le sue variabili.

La prima soluzione è la più lunga, dispersiva, è facile confondersi con le variabili. L'ultima è la più compatta e con meno potenziali effetti collaterali. Quella intermedia è ciò che si farebbe in C puro dove le classi non esistono.

Noi votiamo per una versione semplificata della 2

Nel pomeriggio proviamo a mettere un esempio

Si potrebbe fare una cosa simile:

uint16_t periodo[]={500, 1000, 2000};
uint32_t t[3];

void temporizzazione (uint8_t n)
{
if(millis()-t[n]>=periodo[n])
  {
  t[n]+=periodo[n];
  fai_questo (n);
  }
}

void fai_questo (uint8_t n)
{
switch(n)
  {
  case 0:
  // ...
  break;
  case 1:
  // ...
  break;
  case 2:
  // ...
  break;
  }
}

void setup()
{
// Imposta gli ingressi con pullup interno e le uscite.
...
}

void loop()
{
temporizzazione (0);
temporizzazione (1);
temporizzazione (2);
// ...
}

Ciao a tutti e grazie mille per le vostre risposte. Cerco di spiegarmi.
volendo creare diversi pattern ritmici, vorrei evitare di scrivere una funzione per ognuno, cioè è la soluzione che sto usando in questo momento perché più semplice per me che sono noob, ma mi crea molta confusione se le funzioni cominciano ad essere più di tre e lo trovo scomodo.

@ducecoder i percussori sono due, ma vorrei poter chiamare la stessa funzione anche sullo stesso percussore, in modo da ottenere ad esempio una sovrapposizione tra ritmi binari e ternari con un unico attuatore.

@Datman, scusa, non sono sicuro di capire, ma anche qui dovrei chiamare con un nome diverso ogni variabile all'interno di un case, giusto?

@Claudio_FF La seconda soluzione per me è arabo, scusate. Mi ci vorrà studio e molte prove prima di poterla prendere in considerazione.

La soluzione che "a naso" mi parrebbe giusta è quella suggerita da @cotestatnt ma prima di pronunciarmi vado a studiare l'esempio che ha postato. Credo che se ci fosse una classe dove posso specificare ad esempio la frequenza della pulsazione, il pin di riferimento e il numero di pulsazioni da eseguire sarei a posto e potrei chiamarla più volte facendo diverse prove.

@nid69ita vuoi dire che se la variabile previousMillis è static mantiene valori diversi ad ogni chiamata? Non mi è molto chiaro e non credo sia la mia soluzione (sempre "a naso") ma ci provo subito, anche perché è la più veloce.

Ancora grazie!

Antonio

come prima cosa devi pensare al singolo oggetto percussore e non a multi oggetti e trovare/capire/descrivere il suo funzionamento.
da quel che riporti capisco che tale percussore debba/possa avere nell'arco di un tot tempo diversi andamenti...per esempio in un tempo ciclo completo di 10 secondi farà:
1 secondo ON
1 secondo OFF
0.5 secondi ON
0,5 secondi OFF
0,5 secondi ON
0,5 secondi OFF
2 secondi ON
2 secondi OFF
1 secondo ON
1 secondo OFF
quindi un andamento del tipo, "normale", "accelerato", "lento", "normale".
ho capito giusto?
se è così la tua funzione finale di gestione percussore dovrà essere in po' più articolata...e modi per farlo sono diversi...come già hanno suggerito.

Però ripensandoci...

Credo di aver capito la domanda tanto quanto il libro sulla gravità quantistica di Rovelli :face_with_raised_eyebrow:

start OT...mi è venuta in mente "vite parallele" di Battiato...end OT

Si è esatto, ma se usi la stessa funzione con più percussori, le variabili su cui vai a lavorare DEVONO essere diverse e non basta renderle statiche altrimenti quando richiami la funzione la seconda volta per il secondo percussore vai ad interferire con quello che avevo fatto la prima volta.

Se usi l'approccio delle classi, nessuno ti vieta di creare due istanze della classe che lavorano in modo indipendente sullo stesso pin di uscita.
La stessa cosa si può fare anche con l'approccio 2 suggerito da @Claudio_FF, ma secondo me l'eleganza e la "compiutezza" della classe è imbattibile in questo contesto.

1 Like

Che variabile?... All'interno dei case devi mettere ciò che deve fare quando è attivo.

:grin: scusami @Claudio_FF intendo questo:

pseudo-codice:

blinkWithoutDelay(1000); // un colpo ogni secondo
blinkWithoutDelay(1500); // un colpo ogni secondo e mezzo

in questo modo (ad esempio!) avrei un pattern ritmico di questo tipo:

A:   x         x         x         x         x         x         x
B:   x              x              x              x              x
    ________________________________________________________________
=    x         x    x    x         x         x    x    x         x

Qui ci sono due livelli ritmici che danno un pattern, ma vorrei poter fare anche più livelli, passando i diversi tempi di intervallo alle funzioni senza doverne fare una per ogni tipo di ritmo.
Secondo me @cotestatnt ha capito, poi io più cerco di spiegarmi e più incasino le cose :roll_eyes:

(OT: lo pseudo codice va taggato o meglio di no?)

ma i "ritmi" vengono eseguiti contemporaneamente o prima uno e poi l'altro?
altra domanda...immginando che "!" stia per stato ON... lo spazio tra due "!" è stato OFF?...quindi un "! !" vuon dire ON/OFF/ON...giusto?

Ci pare di ricordare qualcosa di simile nei tempi passati

Guardiamo

O meglio: io guardo, frere maxim qui dice: lascia che cerchi lui
Ma io non sono il fratello

Thanks! era quello che mi serviva. mi metto al lavoro.

Quello che ho scritto al #8 va bene.
Potresti anche farlo con gli interrupt, ma è un po' più complesso, seppure esistano degli ottimi generatori di codice come questo:
https://www.arduinoslovakia.eu/application/timer-calculator

criptico.. non colgo la citazione, sorry :slight_smile: