Come strutturare un programma? (stroboscopio a LED da 100W) - I timer di Arduino

Ne ho ancora un paio in giro, di quelle ad U, ma erano per stroboscopi da discoteca, al massimo le potevi portare ad una ventina di lampi al secondo, ed anche cosi riducendo l'energia che ci buttavi dentro, oppure esplodevano in una trentina di secondi ... il problema e' fare delle misure decenti ed attendibili, data la brevita' del lampo di quelle lampade.

Ne avrei anche una o due, non ricordo bene, da 1kW, da qualche parte, recuperate dal rottame di una vecchissima fotocopiatrice ad alta velocita' Xerox molti anni fa , che usava 4 lampade lineari intorno alla finestra del documento per illuminarlo a lampi, invece del solito carrello con lampada alogena, pero' non ho piu i suoi condensatori speciali (erano dei "secchielli" di lamiera da 6000V 24uF l'uno, con i terminali in ceramica)

Roba da 800 joule... :upside_down_face:

Un po meno in realta', ma non molti ... erano caricati a circa 4500V o poco piu direttamente dalla rete attraverso un trasformatore elevatore enorme, sara' pesato 20 chili, gruppo raddrizzatore con due rami da 10 diodi in serie per parte, scheda trigger con trasformatorini di innesco ("ini" per dire, grossi come pacchetti di sigarette, 25kV di uscita) che immettevano l'impulso direttamente sulla linea di alimentazione attraverso alcuni condensatori a disco per EAT in serie (roba vecchia, niente elevatori switching, anche per via della velocita' di ricarica richiesta, la macchina in automatico con feeder e collatore poteva fotocopiare circa 4 pagine al secondo, che all'epoca erano una velocita' folle) ... purtroppo era massacrata e la maggior parte della roba non era recuperabile.

Se parli di quelle diametro 6mm altezza 47mm e larghezza 22mm ti posso confermare che dovrebbero essere da 50Watt , in quanto ne ho una che lavora da circa 12 anni alimentata a 600VDC e a 2 lampeggi al secondo il drive consuma circa 70 Watt , ho lo schema del drive e quindi stimo 50Watt solo la lampada

Il drive?...

Qui, per esempio:

leggo:
Lampada allo xenon,
60 lampi/min.

tensione anodo 270-500V
energia lampo 8Ws

Caratteristiche Tecniche Lampade a scarico

Tensione anodo nominale 350V
Tensione anodo min. 270V
Tensione anodo max. 500V
Energia lampo max. 8Ws
Velocità lampo max. 60/Min.
Tensione trigger min. 4kV
Durata minima 1 milione di lampi
Materiale vetro duro

VC=IT
C*V*V=VIT
CV^2=Ws
8Ws=C*500^2
C=8/250000=32uF

8Ws=80W*0,1s

quindi il LED da 100W (assorbiti), per una potenza luminosa simile a quella della lampada a scarica da 8Ws, dovrebbe rimanere acceso per 1/10s!...

8W/S, cioe' se riuscissi a tenerla accesa u nsecondo, sarebbero 8W, dato che viaggiano ad impulsi molto piu brevi, la potenza istantanea del singolo picco puo essere molto piu alta per tempi molto piu brevi, per quello di solito le potenze dei sistemi pulsati (laser soprattutto, ma in genere) le indicano in Joule, prendendo in pratica l'energia che dai alla lampada dalla carica e capacita' del condensatore (nei flash fotografici, dagli 80/120uF caricati a 300V delle compattine usa-e-getta ai mostriciattoli da 400uF caricati a 450V della maggior parte dei professionali) e moltiplicandola per l'efficenza della lampada stessa (che pero' li non danno, e curiosamente, pare quasi impossibile trovare dei datasheet decenti per lampade flash da strobo, o almeno io non sono riuscito a trovarne, manco fossero segreti industriali)

Ricorda che puoi sovralimentarli, per duty-cycle molto brevi, quindi con i led puoi compensare, cosa che con le lampade a scarica non puoi fare (tranne in modo molto limitato con le pulse forming networks dedicate) ... inoltre per frequenze maggiori di un Hz (in realta' limitando il condensatore puoi facilmente arrivare a 10) il led e' molto piu efficente di una xenon.

Voglio dire, se ti servono ad esempio, 50 o 100 Hz, con un duty-cycle dell'1% ed una corrente di 10 volte quella di targa (ben dissipato) col led ce la fai, con la xenon ti tocca vendere un polmone per acquistare una lampada speciale Hamamatsu o Heraeus completa di alimentatore e modulo trigger :grin:

Credo che 8W*s sia la massima energia che le può essere fornita senza danneggiarsi, quindi con un condensatore da 32uF caricato alla masssima tensione di lavoro. Inoltre specificano 60 lampi al minuto, cioè uno al secondo... :frowning: Per tempi più brevi, ferma la massima tensione di alimentazione, bisogna usare condensatori di capacità minore e quindi minore energia.

Beh... Dovrei metterci un bel condensatore, perché ho previsto un alimentatore da 3Amax e, d'altra parte, sarebbe ridicolo usare un alimentatore da 30A (1kW!) per una potenza media di 100W (considerando un duty cycle del 10% max).
con una cdt ammessa di 1V sui 36V e una durata massima di 1/100s (10ms), VC=IT, C=IT/V:
30A*10ms/5V = 60.000uF 40V lavoro...
Beh...

La scheda di pilotaggio della lampada xenon

La lampada che hai riportato è da 16mm x 30mm ,piu' piccola di quella mia , quindi con una potenza inferiore e minor tensione anodica, la tensione di innesco però è simile

Piloto il relè della lampada con una scheda DMX che ho progettato 12 anni fa , facente parte di un sistema con 30 unita tra teste mobili, lampade rgb led , dimmer , par64, lasers, e 2 acceccatori 40watt led sempre DMX e sempre progettati da me , è vero che gli acceccatori sono più forti della strobo xenon ma quest'ultima dà dei lampi più netti (anche perchè i lampi della xenon sono generati dalla scheda driver mentre i lampi degli acceccatori sono ricavati dalla temporizzazione di ciclo della DMX512) comunque la xenon è più bella e "old stile"

comunque le schede dmx si possono vedere qui

3A, userei un 5 o 6 a, personalmente, per avere un minimo di limite, ma nonserve da 30 perche' non sono continui ma pulsati ... invece si, come condensatori abbonderei, il tuo pero' e' da 30V, non va bene, quei led hanno una tensione media di 36V, e non andrebbe bene neppure da 40V, troppo "al limite", ne userei uno da almeno 50 o meglio 65V (anche realizzato con un parallelo di piu condensatori se il problema e' l'ingombro), e se il duty-cycle non supera mai il 10% non e' detto che tu debba per forza dargli 10 volte la corrente di targa, quello sarebbe il limite con l'1%, ma anche solo 3 o 4 volte compenserebbe la minore luminosita' apparente con impulsi molto brevi (e scalderebbe pure di meno, dissipatore si, ventola probabilmente no :wink:), quindi potrebbero bastarti 3 condensatorini (ini :grin:) da 10000u 50V in parallelo.

Funziona! :slight_smile:

Io fossi in te eviterei di sottopormi alla terapia stroboscopica per lungo tempo o almeno avvisa i familiari di ciò così se notano qualche comportamento strano sanno il perché. :smiley:

PS: pensavo al PWM sopra i 20Khz che puoi regolare e poi lo spegnimento lo fai mettendo il un pin LOW così il mosfet non conduce. Ma in questo modo puoi regolare l'intensità con il PWM.

Ciao.

Per fare la maggior parte delle prove tengo in funzione solo il tic-tic, anche perché il LED che lampeggia, seppure a 1/10 della potenza (per ora lo alimento a 30V, 400mA), mi distrae.

Se vorrò regolare l'intensità, potrò farlo dal regolatore buck V/I che mi deve arrivare:

Si avevo letto, ma con il PWM ad esempio lo puoi regolare aumentandolo quando la durata del lampo è molto breve e via via lo riduci quando la durata del lampo si allunga.

Ciao.

1 Like

E' venuto così:

// Per base dei tempi di 50us:
#define prescaler 2 // 50us
#define presetTCNT1 65436 // 50us
#define div_periodo 192000.0 //50us
#define div_shutter 19200.0 //50us

// Per base dei tempi di 100us:
//#define prescaler 3 // 100us
//#define presetTCNT1 65511 // 100us
//#define div_periodo 100000.0 // 100us
//#define div_shutter 10000.0 //100us

// NOTA: Senza l'autoblocco, il salvataggio dell'impostazione effettuata avviene solo dopo aver premuto l'encoder!
const char *percorso=__FILE__; // Dalla macro __FILE__  prende il percorso del file in uso. Questa parte  di programma,
      char ver[10];            // però, si trova  nel file/cartella  dell'IDE c_setup, in cui non è scritta la versione
                               // del programma. La versione è scritta nel file principale, che ha lo stesso nome della
                               // cartella che contiene tutti i file del programma. Questa  riga, quindi, non può stare
                               // nel setup.
#include <LiquidCrystal.h>
#include <EEPROM.h>
LiquidCrystal lcd(17,15,13,12,11,10); // RS,EN,D4,D5,D6,D7
uint16_t frequenzax10=1000; // 10*frequenza in Hz. Va da 5 a 2000.
const uint8_t  val_freq[]={5, 8, 10, 12, 15, 18, 20, 25, 30, 35, 40};
int8_t ind_freq=0; // Indice per le frequenze (da 0 a ind_freq_max (28), considerando i sottomultipli e multipli da 0,5Hz a 200Hz).
#define ind_freq_max 28
const uint16_t val_shut[]={10000, 5000, 2000, 1000, 500, 250, 125, 60, 30, 15, 8, 4};
int8_t ind_shut=0; // Indice per gli shutter,
#define ind_shut_max 11
uint16_t shutter=10000; // Ton=1/shutter; p.es.: 1/1000s=1ms=1000us. Va da 100 a 10.000.
         // Shutter deve essere maggiore  di frequenzax10: per esempio, con f=10Hz (periodo=1/10s) shutter  deve essere 
         // maggiore di 100 (1/100s) per garantire che il duty cycle non superi il 10% e il LED non si surriscaldi.
volatile uint32_t periodo; // Periodo in us.
uint16_t Ton; // Tempo di accensione in us (1/shutter, ma in us).
volatile uint32_t cont_overflow=0; // Conta gli overflow di 50 o 100us ciascuno.
// uint32_t t_shutter=0; // Per la durata del lampo.

int E; // Risultato della routine encoder(): 1, -1, 0.
byte S; // Lettura dei due valori dell'encoder.
byte So;// Lettura precedente dell'encoder.
int X; // Usato in encoder() per evitare letture multiple.

uint32_t t_puls_prem;
// uint8_t stato=0;
// uint8_t statoPrec=0;
// bool ext=0;
uint32_t t_rot_encoder=0;

int8_t Bipon=0;
int8_t Ticon=0;
int8_t Autoblocco=0;
int8_t Posizione_di_blocco=0;
int8_t Autoshutter=0;
uint8_t esce_da_impostazioni=0;

byte triangolo[8]=
{B00000,
 B00100,
 B01100,
 B11100,
 B01100,
 B00100,
 B00000,
 B00000};

#define puls_prem !(PIND&0b01000000)


void setup()
{
char *ver_ext=strrchr(percorso,'v'); // Va a cercare l'ultima 'v' nel percorso.
byte n_car=strlen(ver_ext)-4; // Calcola la lunghezza escludendo .ino.
strncpy(ver, ver_ext, n_car); // Copia i primi n_car caratteri da ver_ext a ver.
ver[n_car]='\0'; // Mette il terminatore in fondo.

pinMode (4,INPUT_PULLUP); // Encoder: A.
pinMode (5,INPUT_PULLUP); // Encoder: B.
pinMode (6,INPUT_PULLUP); // Encoder: Pulsante.
pinMode (7, OUTPUT); // Uscita.
pinMode (8, OUTPUT); // Cicalino.
lcd.createChar(1,triangolo);
lcd.begin(16,2);
lcd.print("  Stroboscopio  ");
lcd.setCursor((16-strlen(ver))/2, 1); lcd.print(ver);
switch(presetTCNT1)
  {
  case 65511: lcd.print(" 100us"); break;
  case 65436: lcd.print(" 50us"); break;
  }
delay(1500);
lcd.setCursor(0,1); lcd.print("G.Giangreco 2022");
// Mia discussione: https://forum.arduino.cc/t/come-strutturare-un-programma-stroboscopio-a-led-da-100w/1062259
// Trattto da:
// https://forum.arduino.cc/t/using-the-hardware-counter-in-arduino/888482/11 (punto9).
TCCR1A = 0x0000;    // Up counter mode.
TCCR1B = 0x0000;    // TC1 is STOP.
TCNT1 = 65511;  // 0xC2F7=49911: preset value for 1 sec timeDelay.
         // Comincia a contare dal valore di TCNT1. Quando arriva a 65535+1 va in overflow.
         // Quindi: 1/16.000.000*1024*(65536-49911) = 1 secondo.
         //  TCNT1 = 65536 - T [s]/(PRESCALER/fCLOCK [Hz])
         //  TCNT1 = 65536 - T[us]/(PRESCALER/fCLOCK[MHz])
         // Per 1ms: 65536 -  1000/(   256   /    16     ) = 65473,5
       // Per 100us: 65536 -   100/(   256   /    16     ) = 65529,75
       // Per 100us: 65536 -   100/(    64   /    16     ) = 65511
       // Per  10us: 65536 -    10/(    64   /    16     ) = 65533,5
       // Per  10us: 65536 -    10/(     8   /    16     ) = 65516
       // Per  20us: 65536 -    20/(     8   /    16     ) = 65496
       // Per  50us: 65536 -    50/(     8   /    16     ) = 65436     

TCCR1B = prescaler; // Prescaler
                    //  1:   1
                    //  2:   8
                    //  3:  64
                    //  4: 256
                    //  5:1024                          
delay(2000);
lcd.clear();
Bip();
maskFreqShut();

ind_freq=EEPROM.read(0); // Legge l'indice della frequenza memorizzato.
if(ind_freq==0 || ind_freq>ind_freq_max) {ind_freq=13; EEPROM.write(0,13);} // Se nella EEPROM è memorizzato un valore non compatibile carica l'indice 13, corrispondente a 10Hz.
imposta_e_scrive_frequenza();

ind_shut=EEPROM.read(1); // Legge l'indice dello shutter memorizzato.
if(ind_shut==0 || ind_shut>ind_shut_max || val_shut[ind_shut]<calcola_frequenzax10(ind_freq)) {ind_shut=0; EEPROM.write(1,0);} // Se nella EEPROM è memorizzato un valore non compatibile carica l'indice 0, corrispondente a 1/10000s (100us).
imposta_e_scrive_shutter();

Bipon=EEPROM.read(2); // Legge se il Bip è attivo o no.
if(Bipon>1) {Bipon=1; EEPROM.write(2,1);}

Ticon=EEPROM.read(3); // Legge se il Tic è attivo o no.
if(Ticon>1) {Ticon=1; EEPROM.write(3,1);}

Autoblocco=EEPROM.read(4); // Legge se l'autoblocco è attivo o no.
if(Autoblocco>1) {Autoblocco=1; EEPROM.write(4,1);}

Posizione_di_blocco=EEPROM.read(5); // Legge se la posizione di blocco dopo frequenza e shutter è attiva o no.
if(Posizione_di_blocco>1) {Posizione_di_blocco=0; EEPROM.write(5,0);}

Autoshutter=EEPROM.read(6); // Legge se l'Autoshutter è attivo o no.
if(Autoshutter>1) {Autoshutter=0; EEPROM.write(6,0);}

//---------------------------------------------------
bitSet(TIMSK1, TOIE1); // Local part of interrupt logic is enabled; Fig-9.1.
bitSet(SREG, 7);   // Global part(I) of interrupt logic is enabled; Fig-9.1.
//---------------------------------------------------
}


void loop()
{
while(!puls_prem);
impostazioni_se_pressione_lunga();
lcd.setCursor(15,0); lcd.write(byte(1));
lcd.setCursor(15,1); lcd.print(' ');
while(puls_prem);
t_rot_encoder=millis();
while(!puls_prem) // ----- Impostazione della frequenza -----
  {
  encoder();
  if(E!=0)
    {
    t_rot_encoder=millis();
    if(!Autoshutter) // No Autoshutter.
      {
      if(ind_freq+E<=ind_freq_max && shutter>=calcola_frequenzax10(ind_freq+E)) // OK.
        {
        ind_freq+=E; delay(20); Bip();
        if(ind_freq>ind_freq_max) {noTone(8); ind_freq=28;}
        if(ind_freq< 0) {noTone(8); ind_freq=0;}
        imposta_e_scrive_frequenza();
        }
      else Biip(); // Altrimenti non fa nulla.
      }
    else // Autoshutter.
      {
      if(ind_freq+E<=ind_freq_max)
        {
        if(shutter< calcola_frequenzax10(ind_freq+E))
          {
          if(ind_shut>0) ind_shut-=1;
          imposta_e_scrive_shutter();
          }
        if(shutter>=calcola_frequenzax10(ind_freq+E)) // OK.
          {
          ind_freq+=E; delay(20); Bip();
          if(ind_freq>ind_freq_max) {noTone(8); ind_freq=ind_freq_max;}
          if(ind_freq< 0) {noTone(8); ind_freq=0;}
          imposta_e_scrive_frequenza();
          }
        }
      else Biip(); // Altrimenti non fa nulla.
      }
    }
  if(esce_da_impostazioni) // Serve per fare in modo che ritornando dalle impostazioni stia già bloccato.
    {
    goto fine;
    }
  else if(Autoblocco)
    {
    if(millis()-t_rot_encoder>5000)
      {
      Bip();
      EEPROM.update(0, ind_freq); // Salva l'indice della frequenza.
      lcd.setCursor(15,0); lcd.print(' ');
      return;
      }
    } 
  }
if(!Autoblocco) EEPROM.update(0, ind_freq); // Salva l'indice della frequenza.
impostazioni_se_pressione_lunga();
lcd.setCursor(15,0); lcd.write(' ');
lcd.setCursor(15,1); lcd.write(byte(1));
while(puls_prem);
t_rot_encoder=millis();
while(!puls_prem) // ----- Impostazione dello shutter -----
  {
  encoder();
  if(E!=0)
    {
    t_rot_encoder=millis();
    if(val_shut[ind_shut+E]>=frequenzax10)
      {
      ind_shut+=E; delay(20); Bip();
      if(ind_shut>ind_shut_max) {noTone(8); ind_shut=ind_shut_max;}
      if(ind_shut< 0) {noTone(8); ind_shut=0;}
      imposta_e_scrive_shutter();
      }
    }
  if(esce_da_impostazioni) // Serve per fare in modo che ritornando ddalle impostazioni stia già bloccato.
    {
    goto fine;
    }
  if(Autoblocco)
    {
    if(millis()-t_rot_encoder>5000)
      {
      Bip();
      EEPROM.update(1, ind_shut); // Salva l'indice della frequenza.
      lcd.setCursor(15,1); lcd.print(' ');
      return;
      }
    }
  }
if(!Autoblocco)
  {
  EEPROM.update(1, ind_shut); // Salva l'indice della frequenza.
  }
fine:
if(Posizione_di_blocco || esce_da_impostazioni)
  {
  esce_da_impostazioni=0;
  lcd.setCursor(15,0); lcd.print(' ');
  lcd.setCursor(15,1); lcd.print(' ');
  while(puls_prem);
  }
}


// ISRTOV1 Routine
ISR(TIMER1_OVF_vect)   // ISR Routine for TOV1 interrupt.
  {
  TCNT1 = presetTCNT1;  // Ricarica il valore.
  cont_overflow+=1; // Ogni unità corrisponde a 100us.
  if(cont_overflow>=periodo)
    {
    cont_overflow=0;
    PORTD|=0b10000000; // Lampo ON.
    PORTB|=(0b00000001 & Ticon); // Tic.
    }
  else if(cont_overflow>=Ton) // Anche Ton è in centinaia di us.
    { // I/O 76543210
    PORTD&=0b01111111; // Lampo OFF.
    PORTB&=(0b11111110 | ~Ticon); // Fine tic.
    }
  }

void maskFreqShut()
  {
  lcd.print("Frequenza:");
  lcd.setCursor(0,1); lcd.print("Shutter:1/");
  }

void maskExt()
  {
  lcd.print("Sinc esterno:");
  }

void imposta_e_scrive_frequenza()
  {
  frequenzax10=calcola_frequenzax10(ind_freq);
  char buf[3];
  if(frequenzax10<40) dtostrf(frequenzax10/10.0, 3, 1, buf);
  else dtostrf(frequenzax10/10, 3, 0, buf);
  lcd.setCursor(11,0); lcd.print(buf);
  periodo=div_periodo/frequenzax10+.5; // In unità di 50 o 100us.
  }

uint16_t calcola_frequenzax10(uint8_t indice)
  {
  uint16_t fx10;
  if(indice<11) fx10 = val_freq[indice];
  else if(indice<22) fx10=10*val_freq[indice-11];
  else fx10=100*val_freq[indice-22];
  return fx10;
  }

void imposta_e_scrive_shutter()
  {
  shutter=val_shut[ind_shut];
  char buf[5];
  dtostrf(shutter, 5, 0, buf);
  lcd.setCursor(10,1);
  lcd.print(buf);
  Ton=div_shutter/shutter+0.5; // Ton in unità di 50 o 100us; +0.5 è per l'arrotondamento.
  }


void impostazioni_se_pressione_lunga()
  {
  t_puls_prem=millis();
  while(puls_prem)
    {
    if(millis()-t_puls_prem>1000)
      {
      Bip();
      impostazioni();
      lcd.clear(); 
      maskFreqShut();
      imposta_e_scrive_frequenza();
      imposta_e_scrive_shutter();  
      return;
      }
    }
  lcd.clear(); 
  maskFreqShut();
  imposta_e_scrive_frequenza();
  imposta_e_scrive_shutter();
  }

void impostazioni()
  {
  lcd.clear();
  lcd.print("Bip? "); // ----- Impostazione del Bip -----
  if(Bipon) lcd.print("Si"); else lcd.print("No");
  while(puls_prem);
  while(!puls_prem)
    {
    encoder();
    if(E)
      {
      Bipon+=E; delay(20); Bip();
      if(Bipon<0) {noTone(8); Bipon=0;}
      if(Bipon>1) {noTone(8); Bipon=1;}
      lcd.setCursor(5, 0);
      if(Bipon) lcd.print("Si"); else lcd.print("No");
      }
    }
  EEPROM.write(2, Bipon);
  lcd.setCursor(0,1);
  lcd.print("Tic? "); // ----- Impostazione del Tic -----
  if(Ticon) lcd.print("Si"); else lcd.print("No");
  while(puls_prem);
  while(!puls_prem)
    {
    encoder();
    if(E)
      {
      Ticon+=E; delay(20); Bip();
      if(Ticon<0) {noTone(8); Ticon=0;}
      if(Ticon>1) {noTone(8); Ticon=1;}
      lcd.setCursor(5, 1);
      if(Ticon) lcd.print("Si"); else lcd.print("No");
      }
    }
  EEPROM.write(3, Ticon);
  lcd.clear();
  lcd.print("Autoblocco (5s)? "); // ----- Impostazione dell'Autoblocco -----
  lcd.setCursor(12, 1);
  if(Autoblocco) lcd.print("Si"); else lcd.print("No");
  while(puls_prem);
  while(!puls_prem)
    {
    encoder();
    if(E)
      {
      Autoblocco+=E; delay(20); Bip();
      if(Autoblocco<0) {noTone(8); Autoblocco=0;}
      if(Autoblocco>1) {noTone(8); Autoblocco=1;}
      lcd.setCursor(12, 1);
      if(Autoblocco) lcd.print("Si"); else lcd.print("No");
      }
    }
  EEPROM.write(4, Autoblocco);
  lcd.clear();
  lcd.print("Posizione"); // ----- Impostazione della Posizione di blocco -----
  lcd.setCursor(0, 1);
  lcd.print("di blocco? ");
  lcd.setCursor(11, 1);
  if(Posizione_di_blocco) lcd.print("Si"); else lcd.print("No");
  while(puls_prem);
  while(!puls_prem)
    {
    encoder();
    if(E)
      {
      Posizione_di_blocco+=E; delay(20); Bip();
      if(Posizione_di_blocco<0) {noTone(8); Posizione_di_blocco=0;}
      if(Posizione_di_blocco>1) {noTone(8); Posizione_di_blocco=1;}
      lcd.setCursor(11, 1);
      if(Posizione_di_blocco) lcd.print("Si"); else lcd.print("No");
      }
    }
  EEPROM.write(5, Posizione_di_blocco);
  lcd.clear();
  lcd.print("Autoshutter? "); // ----- Impostazione dell'Autoshutter -----
  if(Autoshutter) lcd.print("Si"); else lcd.print("No");
  while(puls_prem);
  while(!puls_prem)
    {
    encoder();
    if(E)
      {
      Autoshutter+=E; delay(20); Bip();
      if(Autoshutter<0) {noTone(8); Autoshutter=0;}
      if(Autoshutter>1) {noTone(8); Autoshutter=1;}
      lcd.setCursor(13, 0);
      if(Autoshutter) lcd.print("Si"); else lcd.print("No");
      }
    }
  EEPROM.write(6, Autoshutter);
  esce_da_impostazioni=1;
  }


void Bip() {if(Bipon) tone(8, 1000, 15);}
void Biip(){if(Bipon) tone(8, 1000, 100);}

void encoder()
  {
  //              PD 76543210
  // S=3-      PIND&B00000011 ; // Uso PD1 e PD0, perciò non devo scorrere a destra.
  // S=3-( PIND>>3)&B00000011 ; // Uso PD4 e PD3, quindi scorro a destra di 3 bit per farli diventare 1 e 0. 
  // S=3-((PIND>>2)&B00000011); // Uso PD3 e PD2, quindi scorro a destra di 2 bit per farli diventare 1 e 0. 
  // S=3-((PINC>>1)&B00000011); // Uso PC2 e PC1 (A2 e A1), quindi scorro a destra di 1 bit per farli diventare 1 e 0.
     S=3-((PIND>>4)&B00000011); // Uso PD5 e PD4, quindi scorro a destra di 4 bit per farli diventare 1 e 0.
  // Il valore binario di S rappresenta A e B. Il centrale dell'encoder è a massa, quindi faccio complemento a 3 (11).  
  S^=S>>1; // Faccio XOR (^) fra S (gray) e il suo bit 1, facendolo scorrere a Dx: AB XOR A,
           // ottenendo un binario che per ogni scatto fa 0-1-2-3-0 oppure 0-3-2-1-0.
  E=0;
  if (S!=So && S==0) X=0;
  if (X==0)
    {
    if (So==1&&S==2)
      {E=1; X=1;
      }
    if (So==3&&S==2)
      {E=-1; X=1;
      }
    So=S;  
    }
  }


/* 
Fis.I/OPorta
 1   - RS
 2   0 PD0
 3   1 PD1
 4   2 PD2 
 5   3 PD3 
 6   4 PD4 Encoder: A (con INPUT_PULLUP).
--
11   5 PD5 Encoder: B (con INPUT_PULLUP).
12   6 PD6 Encoder: Pulsante (con INPUT_PULLUP) verso GND con 100nF in parallelo.
13   7 PD7 Uscita verso il gate dell'IRLZ44N via 100 Ohm.
14   8 PB0 Uscita per cicalino passivo.
----------
15   9 PB1 
16  10 PB2    D7 LCD
17  11 PB3    D6 LCD
18  12 PB4    D5 LCD
19  13 PB5    D4 LCD
--
23  14 PC0 A0
24  15 PC1 A1 EN LCD
25  16 PC2 A2
26  17 PC3 A3 RS LCD
27  18 PC4 A4 SDA 
28  19 PC5 A5 SCL 
29  20     A6 (solo in Arduino Nano)
          
Fis
 9     PB6 XTAL 16MHz In : compensatore a GND. 
10     PB7 XTAL 16MHz Out: 22pF a GND.
21    ARef
7,20: Vcc
8,22: GND

EEPROM(0): Indice della frequenza memorizzato.
EEPROM(1): Indice dello shutter memorizzato.
EEPROM(2): Bipon.
EEPROM(3): Ticon.
EEPROM(4): Autoblocco.
EEPROM(5): Posizione di blocco.
EEPROM(6): Autoshutter.

*/

per un totale di 9748 byte (30%) dello spazio disponibile per i programmi e 402 byte (19%) occupato dalle variabili globali nella memoria dinamica,

Sto facendo qualche prova cambiando la base dei tempi, ma c'è qualcosa che interferisce... Stranamente, a 100us è tutto perfetto, salvo la granularità della variazione fine della frequenza, mentre già a 50us devo fare delle correzioni...
Le uniche librerie che uso sono LiquidCrystal.h e EEPROM.h e sono attive solo nei momenti in cui viene aggiornata una sctitta sul display (perché ho ruotato l'encoder) oppure sta scivendo su EEPROM poco dopo una regolazione.
Forse è millis()? Che prescaler usa?...
Ecco la storia delle modifiche (uso un frequenzimetro Racal-Dana a conteggio reciproco, che riesce quindi a misurare le frazioni di Hertz):

0.25_t_10us 20/12/22 Per aumentare la risoluzione effettiva della frequenza (a 200Hz la regolazione fine varia la frequenza ogni 2 scatti) ho provato a ridurre l'interrupt da 100us a 10us, ma stranamente la frequenza veniva più bassa di un fattore 0,83! Anche correggendo questo valore nel calcolo del periodo e di Ton con 830100.0/frequenzax10; anziché 1000000.0/frequenzax10; la frequenza e il periodo risentono della pressione del pulsante e del passaggio allo stato di blocco! Per esempio, con 100,01Hz in stato di blocco, si hanno 99,42Hz con il triangolino (freccetta) acceso e 97,65Hz mentre è anche premuto il pulsante!

0.25_t_20us 20/12/22 Con l'interrupt a 20us va un po' meglio, ma ancora si nota la variazione di frequenza premendo il pulsante, che con l'interrupt a 100us è irrilevante. Anche qui è necessario fare delle correzioni di frequenza, che a 100us non sono necessarie.

0.25_t_50us 20/12/22 Anche con l'interrupt a 50us, seppure vada un po' meglio, non è perfetto come a 100us!
Lo shutter a 1/2000s produce 520us e non c'è modo di portarlo finemente a 500us, perché i passi sono di 50us. A 100us, invece, si hanno 500us precisi!
Questo, però, nella regolazione fine della frequenza, produce frequenze molto più vicine a quelle che appaiono sul display. Probabilmente è il miglior compromesso.

0.26 21/12/22 Metto dei #define iniziali per selezionare la base dei tempi a 100us o a 50us, anziché conservare due versioni del programma.

Secondo me c'è qualche problema nell'aggiornamento dei registri del Timer che usi ...

millis(), su AVR classici, usa Timer0 e quindi interferenze non ne può creare salvo un interrupt ogni millisecondo che serve a far avanzare il contatore (e quando una ISR è in esecuzione, le altre restano in attesa, quindi, se capita che un altro timer richiami una ISR quando è in esecuzione quella del Timer0, è chiaro che ci sarà un ritrado di un certo numero di cicli macchina, fino al completamento della ISR attiva).

Comunque, sulla sequenza di come impostare un Timer e i valori, è molto valido il codice d'esempio che scrive QUESTO timer calculator on-line ... verifica ch i valori che tu usi siano congrui e che l'aggiornamento dei registri avvenga come si deve.

Guglielmo

Il Time Calculator utilizza l'Output Compare, mentre io conto gli overflow...
Entrambi usiamo il Timer 1.