delayMicroseconds

Salve, delayMicroseconds(10) ferma Arduino per 10 microsecondi, giusto? Se è così come realizzare la stessa pausa senza fermare Arduino? Un pò come si fa con millis() al posto di delay().

Al posto di millis si può usare micros
https://www.arduino.cc/reference/en/language/functions/time/micros/

void zero_crosss_int()  //function to be fired at the zero crossing to dim the light
{
  /*
  Firing angle calculation : 1 full 50Hz wave =1/50=20ms 
  Every zerocrossing thus: (50Hz)-> 10ms (1/2 Cycle) 
  For 60Hz => 8.33ms (10.000/120)
  10ms=10000us
  (10000us - 10us) / 255 = 40
  255 è la quantità di passi necessari per comprire l'intera variazione da spento ad acceso
  per non avere sfarfallii, impostare 10 come massima luminosità e 222 per spento
  */ 
  int dimtime = (40*dimming);   
  if (micros()-attesa > dimtime) {   
  //delayMicroseconds(dimtime);    // Wait till firing the TRIAC  	
  digitalWrite(pin_giorno, HIGH);   // Fire the TRIAC
  attesa=micros();
  }
  delayMicroseconds(10);         // triac On propogation delay 
				 // (for 60Hz use 8.33) Some Triacs need a longer period
  digitalWrite(pin_giorno, LOW);    // No longer trigger the TRIAC (the next zero crossing will swith it off) TRIAC
}

Ho provato a sostituire il delayMicroseconds con micros() ma non funziona. Penso che in questo caso non possa essere sostituito. Quando l'onda passa per lo zero, grazie all'interrupt viene eseguita questa routine, che provvede ad attivare l'uscita pin_giorno dopo il tempo dimtime e lo spegne dopo 10 microsecondi

Lo sketch completo è quello di seguito. In pratica varia la luminosità tramite l'encoder.

#include <Wire.h>
#include <PCF8574_HD44780_I2C.h> //  librerie per display LCD ompatibili con R1 v4

// Definizione encoder
#define Clk 13
#define Dt 12
#define PIN_PULSANTE 8
#define PREMUTO ==LOW                         // Pulsante premuto
#define NON_PREMUTO ==HIGH                    // Pulsante non premuto
#define PULSANTE digitalRead(PIN_PULSANTE)    // Definisce la lettura del pulsante.
//  ---------


 #define pin_zero 2       // Pin 2 per zero crossing
 #define pin_giorno 14    // Pin 14 per Giorno

// Definizione variabili per i menù
int posizione = 0;        // Indica la posizione della freccia nel display
int scelta = 0;           // Indica quale opzione del menù si sceglie premendo il pulsante
bool tasto =0;            // 0 = pulsante non premuto, 1 = pulsante premuto
int direzione = 0;        // Indica il verso di rotazione dell'encoder, -1 o +1
int prevClk;
int prevDt;
int visualizza_menu =0;    // In base al valore viene visualizzato il menù corrispondente contenuto nell'array richiamato
int dimming = 100;  // Dimming level (10-222)  10 = ON, 222 = OFF
unsigned long attesa;

// Definizione caratteristiche display
PCF8574_HD44780_I2C lcd(0x27,20,4); // Indirizzo 27, 20 colonne e 4 righe
// Pin A4 = SDA
// Pin A5 = SCL

void setup() {

  // Dichiarazione funzionamento Pin 
  pinMode(Clk, INPUT);
  pinMode(Dt, INPUT);
  pinMode(PIN_PULSANTE, INPUT_PULLUP);
  prevClk = digitalRead(Clk);
  prevDt = digitalRead(Dt);
  
  pinMode(pin_giorno,OUTPUT);  // Pin 14 in uscita per Giorno

  // Inizializzazione del display
  lcd.init(); // Inizializza LCD                     
  lcd.backlight(); // Attiva retroilluminazione
  lcd.clear();  // Cancella lo schermo
  Serial.begin(9600);
  lcd.setCursor(3, 2); 
  lcd.print("Varia luminosità");
  attachInterrupt(pin_zero, zero_crosss_int, RISING);  // Choose the zero cross interrupt # from the table above
  attesa = micros();
}

void loop() {
  leggi_Encoder();
  lcd.setCursor(16, 2); 
  lcd.print(scelta);
  
  if (scelta>222) scelta=10;
  if (scelta<10) scelta=222;
  dimming=scelta;
 
}

void leggi_Encoder(){
   
  while(PULSANTE PREMUTO ){} // Gira finchè non viene rilasciato il pulsante
  direzione = 0;
  tasto = 1;
  while(direzione==0 && tasto == 1){
  tasto = PULSANTE; 
   int currClk = digitalRead(Clk);
   int currDt = digitalRead(Dt);
   if (currClk != prevClk) {
     if (0 == currClk) { 
       direzione = 1 - ((currDt == currClk) << 1);
     }
     prevClk = currClk;
     prevDt = currDt;
    }
  } // Fine while
  if (direzione > 0) {
    scelta++;
    posizione++;
  } else if (direzione < 0) {
     scelta--;
     posizione--;
  }
  while(PULSANTE PREMUTO ){} // Gira finchè non viene rilasciato il pulsante
}

//the interrupt function must take no parameters and return nothing
void zero_crosss_int()  //function to be fired at the zero crossing to dim the light
{
  /*
  Firing angle calculation : 1 full 50Hz wave =1/50=20ms 
  Every zerocrossing thus: (50Hz)-> 10ms (1/2 Cycle) 
  For 60Hz => 8.33ms (10.000/120)
  10ms=10000us
  (10000us - 10us) / 255 = 40
  255 è la quantità di passi necessari per comprire l'intera variazione da spento ad acceso
  per non avere sfarfallii, impostare 10 come massima luminosità e 222 per spento
  */ 
  int dimtime = (40*dimming);   
  
  delayMicroseconds(dimtime);    // Wait till firing the TRIAC  	
  digitalWrite(pin_giorno, HIGH);   // Fire the TRIAC
  
  delayMicroseconds(10);         // triac On propogation delay 
				 // (for 60Hz use 8.33) Some Triacs need a longer period
  digitalWrite(pin_giorno, LOW);    // No longer trigger the TRIAC (the next zero crossing will swith it off) TRIAC
}

Non si può fare così. In una ISR delay e delayMicroseconds non funzionano, e millis/micros non avanzano. Bisogna agganciare tutta l'esecuzione del loop allo zerocrossing. Questo comporta riscrivere in modo non bloccante la lettura dell'encoder (quindi senza quei while di attesa) e anche scrivere sul display il minimo possibile, non ad ogni ciclo.

Non esattamnete Claudio ... quello che NON avanza è millis (e quindi la relativa funzione delay) perché è gestito tramite un interrupt, micros invece NON è gestito tramite l'interrupt del timer e funziona correttamente anche nelle ISR.

Guglielmo

1 Like

Ecco, questo è il sorgente di micros():

unsigned long micros() {
	unsigned long m;
	uint8_t oldSREG = SREG, t;
	
	cli();
	m = timer0_overflow_count;
#if defined(TCNT0)
	t = TCNT0;
#elif defined(TCNT0L)
	t = TCNT0L;
#else
	#error TIMER 0 not defined
#endif

#ifdef TIFR0
	if ((TIFR0 & _BV(TOV0)) && (t < 255))
		m++;
#else
	if ((TIFR & _BV(TOV0)) && (t < 255))
		m++;
#endif

	SREG = oldSREG;
	
	return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}

... come vedi non usa una ISR ma si basa sull'accesso diretto ai registri di Timer0 ... che comunque continua a camminare :wink:

C'è comunque un limite ... dato che l'interrupt del Timer0 non ha effetto (nella ISR l'interrupt è disabilitato), NON c'è l'incremento di 'timer0_overflow_count' il che limita la durata che si può misurare con micros(); se ben ricordo, si arriva ad un massimo alcune centinaia di µsec ... poi i valori non sono più esatti ... :roll_eyes:

Guglielmo

1 Like

Lo sketch esposto sopra funziona, se io giro l'encoder la luminosità varia. Quello che non funziona è togliere il delayMicroseconds() mettendo micros().
Tu mi dirai: perchè vuoi sostituirlo se funziona?
Perchè vorrei comandare più lampade (con luminosità diverse) nello stesso istante usando sempre lo stesso interrupt.
Tu mi dirai: ci sono delle librerie apposite che lo fanno già. Lo so, ma con Arduino R4 non funzionano.

Tutto quanto ho detto sopra è valido per Arduino classici AVR, per la UNO R4, basata su MCU Renesas, cambiano parecchie cose ... :roll_eyes:

@Claudio_FF: Basti considerare che, su Renesas ...

The Interrupt Controller Unit (ICU) controls which event signals are linked to the Nested Vector Interrupt Controller (NVIC), Data Transfer Control (DTC), and Direct Memory Access Controller (DMAC) modules.

Guglielmo

Dato che lo sketch proposto sopra funziona, non si può fare un sistema per accendere più lampade contemporaneamente?

Problema interessante. Prima di tutto dobbiamo capire se anche micros funziona. Al posto di:

delayMicroseconds(dimtime);
digitalWrite(pin_giorno, HIGH);
delayMicroseconds(10);
digitalWrite(pin_giorno, LOW);

Provare a scrivere:

t = micros();
while (micros()-t < dimtime);
digitalWrite(pin_giorno, HIGH);
t = micros();
while (micros()-t < 10);
digitalWrite(pin_giorno, LOW);

Dopo di che forse si riesce a fare tutto nella ISR, scrivendo N temporizzatori software non bloccanti, ciascuno col proprio dimtime... lasciando il resto delle regolazioni in un loop con letture bloccanti.

Come detto (post #7), per tempi non eccssivamente lunghi, non c'è alcun problema :wink:

Guglielmo

Domani vedo se funziona

Dopo di che una logica per la ISR con due dimmer contemporanei (estendibile a N) potrebbe essere questa:

inizio = micros

attivo1 = 1
fire1 = 0
attivo2 = 1
fire2 = 0

ripeti finché attivo1 or attivo2:

    trascorso = micros - inizio

    se attivo1:
        se non fire1:
            se trascorso >= dimming1:
                fire1 = 1
                t1 = micros
        altrimenti:
            se micros - t1  >= 10:
                fire1 = 0
                attivo1 = 0

    se attivo2:
        se non fire2:
            se trascorso >= dimming2:
                fire2 = 1
                t2 = micros
        altrimenti:
            se micros - t2  >= 10:
                fire2 = 0
                attivo2 = 0

Ti ringrazio per le indicazioni ma ancora devo provare, il tutto è collegato all'ESP32, per provare dovrei smontare da lì e collegare ad Arduino, lo faccio appena risolvo con ESP32.
Però le tue indicazioni mi hanno fatto pensare. Dato che l'ISR viene chiamata ogni volta che l'onda passa per lo zero, quindi ogni 10ms, se si usa il sistema non bloccante, la prima volta che viene chiamata l'ISR, la lampada non verrà accesa finchè non passa il tempo prestabilito, dato che non è bloccante, continua tornando al loop, però poi non tornerà all'ISR fino al prossimo passaggio per lo zero, quindi la lampada non si accenderà mai, non so se mi sono spiegato.

Si è una questione di design. O fai tutta la gestione degli impulsi dentro la ISR, o la ISR dovrebbe solo "dare il via" a dei processi non bloccanti nel loop (che devono concludere il loro lavoro entro i 10 ms). Per come è impostata la lettura encoder attuale, questa seconda via non è percorribile. Il precedente pseudocodice, per N impulsi gestiti in parallelo, è da intendersi tutto dentro la ISR.

Però la seconda scelta lo stesso non è percorribile perchè mentre varia la luminosità delle lampade, accadono altre cose, come l'aggiornamento del display ogni secondo, accendersi e spegnersi di led e altro.
Mi chiedo: le librerie sono sempre del codice, giusto? Se con la libreria si riesce a fare, si dovrebbe farlo anche col codice normale.

Le librerie sono codice scritto da altri utenti, spesso per risolvere esigenze personali, messo poi gratuitamente, ma senza alcuna garanzia, a disposizioni di tutti.

Se l'utente a tempo, documenta in tutto o in parte la libreria, se non ha tempo, la libreria non è neanche documentata.

Ovviamnete, il codice che si trova in una libreria, può anche essere scritto al di fuori di essa ... sempre codice Arduino è.

Guglielmo

Sono andato su GitHub e ho visto che c'era una versione aggiornata della libreria. l'ho installata e ha funzionato. Solo che sfarfallava, leggendo nelle indicazioni c'è scritto che in questo caso si deve modificare una linea:
Se riscontri problemi di sfarfallio dovuti al rumore sulla rete elettrica, puoi provare ad abilitare (decommentare) #define FILTER_INT PERIOD all'inizio del filethyristor.cpp.
dopo averlo fatto ha funzionato perfettamente.

È uno di quei casi in cui le temporizzazioni devono avere priorità su tutto il resto. Non basta, quindi, usare un interrupt per la sincronizzazione con il passaggio per lo zero: quell'interrupt deve far partire un timer, impostato con il tempo che desideri, alla fine del quale esce un impulso (o va alto e l'impulso lo produce un condensatore).