Problemi con l'uso del millis()

Ho letto il bellissimo lavoro di Leo sui millis ed ho iniziato ad usare questa funzione, ma sto incontrando alcune difficoltà, apro questo Topic con l’intento di risolverle una per volta, così magari Leo potrà arricchire, se vuole, il suo testo.
Il primo quesito è questo: posso usare millis() al posto del delay() per creare una pausa? L’intento è ovviamente quello di non bloccare il micro col delay. Allora ho scritto queste righe:

digitalWrite(L1, HIGH);
    startTime = millis();
    while (L1_P > (millis()-startTime)){};
  digitalWrite(M_DIR, M_DIR_FLAG);

Sto usando la lib PcInt con ben 7 ingressi associati ad altrettanti interrupt, ma raramente lavorano assieme, anche se può capitare e devo prevederlo; quando un ingresso viene attivato deve assolutamente interrompere la routine in esecuzione e sostituirla con la propria e questo avviene a meno che non sono costretto ad inserire delle pause. Nel codice riportato devo attivare le uscite L1 e, dopo circa 300ms, M_DIR; startTime e L1_P sono unsigned long; la pausa funziona alla perfezione, ma purtroppo si comporta esattamente come il delay, cioè se durante i 300ms arriva un altro interrupt viene completamente ignorato; la mia idea è che sia il while a fare questo scherzo, ma come posso sostituire questa cosa?

Perché hai ricreato una funzione bloccante. Quel while che hai scritto fa esattamente ciò che fa delay, ossia mette il micro in un loop e ne esce solo quando la condizione è vera. Per un ciclo non bloccante devi legare l'operazione da eseguire ad una condizione verificata con if, in modo che se non è vera, la CPU possa passare oltre ed eseguire i controlli successivi

digitalWrite(L1, HIGH);
startTime = millis();
Do {
  If ((millis()-startTime > L1_p)){
    digitalWrite(M_DIR, M_DIR_FLAG);
     Break;
   }
  ......altro codice
} while(1);

Questa potrebbe essere una possibile soluzione (non far caso alle maiuscole, solo col tablet e mi sta correggendo tutto)

Quindi ho conferma di ciò che temevo, ora provo il tuo suggerimento. Grazie Leo!!!

Non era un timore, era una cosa scontata dato che se crei un loop il codice dal loop non esce. Il while è un loop anche se non contiene altro codice all’ interno del blocco che introduce quindi cicla finché la condizione d’uscita non diventa vera.presumo che tu usi un PCINT per cambiareuna variabile che usi nella digitalWrite, dovresti quindi tener conto anche del cambio di stato di questa variabile come condizione d’uscita dal while, forse

OK, allora questa sezione di codice controlla 4 uscite e viene avviata periodicamente, può dipendere da un interrupt oppure da alcune combinazioni di variabili, solo che mentre viene eseguita devo poterla interrompere se arriva un interrupt, tra alcuni dei sette previsti. Cercando di seguire le tue indicazioni ho fatto così:

digitalWrite(L1, HIGH); 
  startTime = millis();
    do {
        if ((millis()-startTime > L1_P))
          {
            digitalWrite(M_DIR, M_DIR_FLAG); 
          }
        if ((millis()-startTime > (L1_P + M_DIR_P)))
          {
            digitalWrite(LP, HIGH);
          }  
        if (digitalRead(SW13_PRE_LP_OPZ))   
          {
            if ((millis()-startTime) > (L1_P + M_DIR_P + PRE_LP_OFF))
              {
                break;
              } 
          }
        else
          {
            if ((millis()-startTime) > (L1_P + M_DIR_P + PRE_LP_ON))
             { 
               break;
             }
          }
       } while(1);

  digitalWrite(L2, HIGH);

In pratica attivo la prima uscita, poi valorizzo startTime, quindi inizia il loop do...while; ho dovuto sommare ad ogni tempo i precedenti, perché sono molto simili tra loro altrimenti non funzionava la sequenza.

Per distingure fra lo stato di attesa da 200 ms e quello da 4000 ms, puoi mettere un semplicissimo if prima del ciclo do..while:

if (digitalRead(SW13_PRE_LP_OPZ)) {
    attesaMillis = 200;
} else {
    attesaMillis = 4000;
}

Poi... non ho capito il resto dei due if. Se si escludono, usa un else in modo da dire: Se è vero fai X, altrimenti fai Y.

Era una prova :blush: comunque ho corretto il codice nel post precedente, ora funziona ma, secondo me, abbiamo solo reso la cosa più elegante :sweat_smile: Infatti se provo a dare un interrupt lo ignora comunque; d'altra parte il do...while(1) comunque costringe il micro a stare lì dentro finché non incontra un break, che però io posso dare solo quando il ciclo è completo, altrimenti appena ho il true alla seconda uscita esco e mi salta direttamente alla quarta. Che ne pensi?

Mi sa che mi conviene aggiungere una verifica dello stato degli ingressi ad ogni passaggio, in caso di un qualsiasi IN attivo dò il break ma dovrei anche eseguire manualmente la funzione legata all'interrupt, un casino :fearful:

Fai un sistema a stati e lascia ciclare il void loop...

[quote author=Michele Menniti link=msg=2017690 date=1419678432] ... controlla 4 uscite e viene avviata periodicamente ... [/quote]

Non sono sicuro di aver capito bene se fa solo cicli, o se deve anche eseguire istruzioni, ma non si possono fare semplicemente dei cicli di if nidificati nel void(loop) principale senza usare i do..while ? ... per non bloccare tutto ?

EDIT: o magari richiamati come funzioni ?

Grazie degli interventi, mi pare giusto fornire ulteriori elementi. Ho quattro attuatori e sette sensori; in assenza di attivazione dei sensori non deve succedere mai nulla, quando un sensore si attiva istantaneamente agisce sempre sui quattro attuatori, secondo tre possibili combinazioni; la logica di interscambio dei sensori è estremamente complessa ma fondamentalmente ogni sensore, appena attivato, deve svolgere la sua funzione, che può dipendere anche dallo stato di altri sensori, se attivati in contemporanea o immediatamente prima o dopo. Ogni sensore l'ho legato ad un interrupt software (mediante la lib PcInt), ed ogni interrupt avvia una void che fa il debounce (altro problema da risolvere) e che valorizza una variabile. Solo a questo punto interviene la void loop che contiene soltanto uno switch case che, in base al valore della variabile, lancia una void con la propria logica; in tal modo ho separato gli interrupt dalle void esecutive. Ogni void inevitabilmente termina con una delle tre combinazioni di attuatori, quindi lanciando la relativa void tra le tre disponibili, ma la cosa non è automatica, perché la void del sensore può essere legata anche a tutte e tre le combinazioni, sempre scegliendone una in base alla logica. Allo stato attuale devo risolvere in modo efficace la questione del debounce perché non mi funziona bene (ma ne parliamo più avanti) e volevo poter intervenire in una delle tre void degli attuatori in qualsiasi momento. Ora mi rendo conto che in realtà quello che è importante è solo l'ultimo passaggio (attivazione di L2) e quindi posso anche fare a meno di preoccuparmi, visto che a quel punto la void è finita :sweat_smile: quindi direi che per ora va bene così. Nel post seguente spiego l'altra problematica.....

Il problema debounce sull'interrupt l'ho risolto. Sintetizzo la situazione di un solo interrupt, tanto la cosa vale per tutti e sette:

const byte K1 = 5;
const int ANTI_DEBOUNCE = 200;
unsigned long inizioImpulso;
void setup() {
pinMode(K1, INPUT_PULLUP);
 PCintPort::attachInterrupt(K1, debounceK1, FALLING);
}
void loop() {

 switch(routine)
   {
     case 5: //sensore K1
        if ((millis() - inizioImpulso) > ANTI_DEBOUNCE) //cicla finché è TRUE
         { 
           if (digitalRead(K1)==LOW) //ricontrolla lo stato del sensore
             {
               COMMAND(); //in caso positivo esegue la routine
               routine = 0; //interrompe il ciclo di verifica
               break;
             }
         }
        else
        {
          break;
        }
   }
}
void debounceK1(){
    routine = 5;
    inizioImpulso = millis(); //memorizzo l'istante di attivazione
  }

Così finalmente funziona alla perfezione, avevo sbagliato la gestione dei millis(), ancora sono "crudo" :confused:

Però non sento più Leo.... volevo il suo parere sulla questione Do...While, a prescindere dal fatto di aver superato la problematica.

Stara' smaltendo l'eccesso di bollicine ... :D

Scusa se te lo chiedo, ma perche' non hai usato degli antirimbalzo hardware (rete RC) eliminando il problema di quella parte alla radice ?

In realtà so che è stracarico di lavoro con pessimi turni, mettici la famiglia nel poco tempo libero ed ecco svanito il Mod ::) però il suo parere mi serve, perché se ho ragione io significa che non esiste una soluzione software a quel tipo di problema, forse ricorrendo ai due interrupt hw del micro... ma a me ne servirebbero al minimo 3, con priorità assoluta su tutto, magari è una questione interessante da approfondire.

Perché ti scusi? :) Quando ho pubblicato l'articolo sul capacimetro digitale ho scritto un'intera sezione dedicata ai circuiti antirimbalzo, dall'RC all'RC+porte logiche, per finire con gli integrati specifici di nuova generazione. La risposta è la più banale: risparmio circuitale e certezza teorica di risolvere quanto basta col software; che poi la cosa si sia rivelata ostica non potevo prevederlo; naturalmente sono ancora in fase prototipale su millefori, quindi posso fare di tutto e di più, e non è detto che non debba, visto che sto usando dei diversi tipi di pulsanti ed interruttori per simulare i sensori. Per il momento però la soluzione che ho trovato sperimentalmente (l'ho postata sopra) funziona piuttosto bene, semmai vedremo quando collegherò il circuito definitivo ai sensori reali cosa succederà :cold_sweat: Nel prossimo post spiego il terzo e ....forse....ultimo problema che sto affrontando.

La soluzione software è quella che ti ho indicato. Ho realizzato un sistema che gestisce diverse temporizzazioni e nel frattempo si accorge se è stato tolto un componente e richiama la funzione corrispondente, senza neanche usare gli interrupt. Il trucco consiste nell'evitare while ed altro, ad ogni loop verifica la presenza di quel componente e poi con dei semplici if verifica se millis ha superato previousmillisx del valore intervalx, dove x va da 1 a ... quello che serve a seconda di quanti timer devi gestire. Anche nelle sub (funzioni) richiamate, è assente ogni while - delay - until ...

paulus1969: Fai un sistema a stati e lascia ciclare il void loop...

@Michele, è codice che poi devi pubblicare per non esperti ? Secondo me questa è l'idea più pratica nel caso di questo tipo di problematiche. Però non è semplice, soprattutto per i non software-isti. Vagli a spiegare una macchina a stati.

Come gestire degli aventi a tempo prestabilito in modo indipendente dal funzionamento circuitale?
Come ho detto mi trovo tre possibili combinazioni di attuatori, sulla base dei sensori interessati e della logica di funzionamento. Ho tre diverse necessità di azioni a tempo:
1 - appena scatta una data combinazione deve partire un conteggio di xxx secondi, al termine del quale devo attivare un’altra delle due rimanenti combinazioni
2 - appena scatta una combinazione, se è stata determinata da alcuni sensori (e non da altri), deve partire un conteggio di xxx secondi, al termine del quale devo attivare un’altra delle due rimanenti combinazioni
3 - se attivo un sensore, che di norma lavora rarissimamente, ma quando lavora può farlo in modo massiccio, deve partire un conteggio, al termine del quale la combinazione di attuatori che ha avviato si deve interrompere.
Il problema si risolverebbe con tre banalissimi delay, se non fosse che durante i count-down, il circuito deve poter funzionare liberamente resettando il o i conteggi (i primi due facilmente si trovano ad operare assieme, anche se il primo che termina resetta entrambi).

Ho trovato un’intera pagina sul Reference dedicata alle librerie Timing; la MsTimer2 è perfetta per me, purtroppo funziona nel primo caso, in quanto la combinazione di attuatori è del tipo tutti OFF contemporaneamente; invece nel secondo caso, quando alla fine deve eseguire il primo codice che ho postato (quello dei quattro attuatori accesi in sequenza), appena incontra un’azione col millis() si inchioda il micro (devo resettare), forse il millis è gestito dal Timer2?

Ho provato a dare un’occhiata alle altre librerie, ma mi sembrano un tantino complicate per le mie conoscenze, anche perché prevedono un sacco di possibili utilizzi… :disappointed_relieved:

Suggerimenti? Io intanto continuo a sistemare la logica generale, che ancora va affinata…
Leggo ora nuovi interventi e vi rispondo subito.

Perché non dai un’occhiata alla libreria LeOs2, usa il timer del watchdog pertanto non spreca risorse, ti permette di eseguire eventi a tuo piacere con temporizzazioni in frazioni di 16 ms, io la uso in varie applicazioni per Arduino che ho scritto e mi ci trovo benissimo.