Ciao a tutti
Questa è una domanda che avrei dovuto fare molto tempo fa ma, d'altra parte, nemmeno è mai stato affrontato bene l'argomento per farsi venire il dubbio... ![]()
Vorrei realizzare uno stroboscopio con un leddone da 100W che alimenterò con un alimentatore per switch di rete da 48V 3A che ho trovato a 22€ da Amazon, un regolatore buck V/I e un mosfet on/off. Non uso un alimentatore 230V per LED perché credo che non gradiscano un carico intermittente e la tensione possa salire molto quando è senza carico, mentre il regolatore buck è limitato in tensione e corrente.
Concettualmente è molto semplice e con valori fissi già funziona, ma devo aggiungere le regolazioni di periodo e shutter (durata del lampo, impostando le frazioni di secondo come i tempi dell'otturatore delle macchine fotografiche).
Solitamente, quando premo il pulsante dell'encoder l'esecuzione del programma entra nel menu e si instrada verso le diverse impostazioni, bloccando tutto fino all'uscita dalle impostazioni stesse. Questo, di solito, l'ho ritenuto accettabile; quando è stato necessario che l'esecuzione del programma principale continuasse, in tutte le impostazioni ho richiamato continuamente la funzione che fa ciò che serve.
Se invece questa volta, approfittando della semplicità del programma (almeno finché non aggiungerò funzioni fino a occupare tutta la memoria disponibile, come il sincronismo esterno per cominciare...
), volessi impostarlo in maniera non bloccante, come dovrei fare?
L'idea è questa:
- Premo il pulsante:
- Mi fa regolare il periodo fra 2,0 secondi e 10ms (100Hz) con passi pari alla seconda delle due cifre significative (es.: 10-11-12...100-110-120ms, 1.0, 1.1, 1,2s...) o qualcosa di simile. Un potenziometro su un ingresso analogico, quando attivato, permetterà di fare una regolazione fine.
- Premo di nuovo il pulsante:
- Mi fa regolare la durata del lampo: 1/10s, ..., 1/100s, 1/125, 1/160, ..., 1/1000, 1/1250, ...).
- Premo di nuovo il pulsante:
- Esce dalle impostazioni.
Per ora ho cominciato così:
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>
LiquidCrystal lcd(8,9,10,11,12,13); // RS,EN,D4,D5,D6,D7
uint16_t frequenza=100; // Frequenza in Hz. Va da 2 a 1000.
uint16_t shutter=1000; // Ton=1/shutter; p.es.: 1/1000s=1ms=1000us. Va da 100 a 10.000.
// Shutter deve essere maggiore di 10*frequenza: 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.
uint32_t periodo; // Periodo in us.
uint32_t t_periodo; // Per prendere il tempo con micros() (con millis c'era jitter, sia perchè va a passi di 1ms,
// sia perché è interrotto dagli interrupt, mentre micros() non lo è).
uint16_t Ton; // Tempo di accensione in us (1/shutter, ma in us).
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_lettura_pulsante=0; // millis() per la lettura del pulsante ogni 200ms.
uint8_t stato=0;
// stato:
#define norm 0
#define setTon 1
#define setshutter 2
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);
lcd.begin(16,2);
periodo=1000000.0/frequenza+0.5; // In us.
if(shutter>=10*frequenza)
{
Ton=1000000.0/shutter+0.5; // Ton in us; +0.5 è per l'arrotondamento.
}
lcd.print(" Stroboscopio");
lcd.setCursor(6, 1); lcd.print(ver);
delay(2000);
lcd.clear();
}
void loop()
{
// ----- QUANDO E' TRASCORSO IL PERIODO FA IL LAMPO -----
if(micros()-t_periodo >= periodo)
{
t_periodo=micros();
lampo();
}
// ----- OGNI 200ms CONTROLLA IL PULSANTE; SE E' PREMUTO, INCREMENTA stato. -----
if(millis()-t_lett_pulsante >= 200) // Ogni 200ms legge il pulsante.
{
t_lett_pulsante=millis();
if(!(PIND||0b01000000)) // Se il pulsante dell'encoder su PD6 è premuto (livello basso)
{
stato+=1;
if(stato==3) stato=norm;
}
}
}
void lampo()
{
// I/O 76543210
PORTD|=0b10000000;
delayMicroseconds(Ton);
PORTD&=0b01111111;
}
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;
// Bip();
}
if (So==3&&S==2)
{E=-1; X=1;
// Bip();
}
if (S==0)
{E=0; X=0;}
So=S;
}
}
/*
Fis.I/O
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).
13 7 PD7 Uscita verso il gate dell'IRLZ44N via 100 Ohm.
14 8 PB0
15 9 PB1
16 10 PB2
17 11 PB3
18 12 PB4
19 13 PB5
---------------
Fis.I/OPorta
23 14 PC0 A0 Pulsante dell'encoder a GND con 100nF in parallelo.
24 15 PC1 A1 Encoder A a GND.
25 16 PC2 A2 Encoder B a GND.
26 17 PC3 A3
27 18 PC4 A4 SDA
28 19 PC5 A5 SCL
29 20 A6 (solo in Arduino Nano)
Fis
9 In XTAL 16MHz; compensatore a GND.
10 Out XTAL 16MHz; 22pF a GND.
7,20: Vcc
8,22: GND
EEPROM(1):
*/
Nelle impostazioni (per ora nel setup) è presente una verifica affinchè il duty cycle non superi il 10%, permettendo un modesto sistema di raffreddmento del LED.
Nota: qui ho riunito tutte le schede dell'IDE (cioè i file) di cui è composto ill programma.
Grazie
Gianluca