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,