Newsgroup: it.hobby.elettronica.digitale
Titolo: Tecniche per dimmerare un LED con un microcontrollore

A differenza di lampade 230Vac, dimmerare la luminosità di un LED è 
semplicissimo. Basta accenderlo completamente solo per una frazione di 
tempo mediante tecnica PWM.
Esistono altri modi, per esempio il pilotaggio con una corrente 
variabile? A parte la maggiore complessità circuitale, immagino che la 
relazione luminosità/corrente non sia lineare, né facilmente ricavabile, 
quindi il compito si complica anche da un punto di vista del software.
Tornando alla tecnica PWM e supponendo di poter collegare il LED 
direttamente ad un pin di un micro (con resistenza di limitazione), mi 
chiedo quale deve essere la frequenza PWM.
Non vorrei usare frequenze troppo alte (che non darebbero fastidio 
all'effetto visivo), perchè devo moltiplicare tale frequenza per (N-1), 
dove N è il numero dei livelli di luminosità che voglio realizzare.
In pratica divido il periodo (1/F_PWM) in (N-1) parti e ottengo N 
possibilità:
- 0 volte acceso / N-1 volte spento
- 1 volta acceso / N-2 volte spento
- ...
- N-1 volte acceso / 0 volte spento
La frequenza con cui devo impostare lo stato del LED è quindi:
   F = (N-1) * F_PWM
In questo periodo (1/F) devo poter fare tutti i calcoli necessari per 
stabilire se il LED deve essere acceso oppure no (ovviamente di LED ne 
ho tanti e gli algoritmi non banalissimi).
Se diminuisco la frequenza ho più tempo per fare i miei calcoli, ma 
rischio di vedere "pulsare" il LED (tipo stella) piuttosto che vederlo 
stabile alla luminosità desiderata.
Quindi mi chiedo: qual è la mimina frequenza necessaria per dimmerare la 
luminosità di un LED con PWM senza vedere la "pulsazione"?
Qualcuno ha mai fatto qualcosa del genere e qualche misura?

 - N-1 volte acceso / 0 volte spento
Mi sembra complicato pensarla cosï¿œ. La frequenza del PWM ï¿œ legata alla 
frequenza di clock divisa da un prescaler (nel caso piï¿œ semplice). Il 
duty-cycle ha una certa risoluzione (fissa o in funzione della frequenza).
Quindi prima decidi che risoluzione ti serve e che puoi avere su tuo hw 
considerando l'eventuale correzione logaritmica, poi ti scegli una 
frequenza sensata.
Di solito qualche centinaia di Hz sono sufficienti: non vedi alcun 
tremolio e se usi mos non ï¿œ cosï¿œ traumatico accenderli.
Marco

 tremolio e se usi mos non è così traumatico accenderli.
Quindi utilizzando PWM con frequenza 1kHz vado alla grande. Ottimo!

 sceglierò potrò, per esempio, avere solo alcuni valori di N e non tutti.
N è il numero di divisioni del DC, quindi diciamo 2^B con B numero di 
bit del PWM. Non so che micro usi, ma con gli AVR ad esempio una volta 
stabilito B hai *tutti* i valori da 0 a 2^B-1.
Marco
(*) intendo la soluzione di controllare i led in tensione. Il DCDC 
continua a darmi problemi ma questo non è legato ai led.

 Quindi utilizzando PWM con frequenza 1kHz vado alla grande. Ottimo!
Anche con 70 (settanta) Hz, giusto per stare un po' piu' veloci della 
persistenza retinica :)
Claudio_F

 persistenza retinica :)
Ancora meglio... farò qualche prova.

 stabilito B hai *tutti* i valori da 0 a 2^B-1.
Siccome voglio pilotare molti LED (circa 32), non posso utilizzare 32 
PWM. Cosï¿œ stavo pensando di creare le forme d'onda rettangolari mediante 
interrupt del timer.
Se decido che la frequenza del PWM ï¿œ 100Hz e che ogni LED puï¿œ avere 6 
duty-cycle (0%, 20%, 40%, 60%, 80%, 100%), mi basterï¿œ far scattare un 
interrupt ogni 1/100*5=2ms.
Ogni 2ms devo impostare i livelli alto/basso delle singole uscite 
collegate ai LED, in base ai giochi di luce che voglio realizzare.
Penso che in 2ms ho tutto il tempo che voglio per calcolarmi lo stato 
delle uscite al prossimo interrupt...

Mi intrometto brevemente per segnalare un algoritmo che preparai tempo
addietro e che forse può essere utile nel caso si voglia "giocare" con
LED RGB (o led separati rossi, verdi e blu ;)
La tecnica utilizzata è chiaramente quella del PWM e il sorgente è
scritto in C per la piccola piattaforma di sviluppo Arduino (o clone),
ma può essere facilmente adattata ad altri sistemi:
//--- prototipo funzione
void HSVtoRGB( float h, float s, float v );
struct tipo_rgb{  //definisce la struttura dati RGB
  unsigned char r; //red - rosso
  unsigned char g; //green - verde
  unsigned char b; //blue - blu
}
led[2], pin[2]; //definisce i due array utilizzati: i valori RGB e le
relative uscite
//variabili di uso generale
byte x;
int  h = 0;
float s, v;
void setup()
{
  pin[0].r = 3;  //assegna le uscite ai colori del primo led
  pin[0].g = 6;
  pin[0].b = 5;
  pin[1].r = 9;  //assegna le uscite ai colori del secondo led
  pin[1].g = 11;
  pin[1].b = 10;
  for( x=0; x<2; x++){  //imposta i pin come uscite
    pinMode(pin[x].r, OUTPUT);
    pinMode(pin[x].g, OUTPUT);
    pinMode(pin[x].b, OUTPUT);
  }
}
void loop()
{
  (h > 359) ? h = 0 : h++;  //ciclo della tonalita
  v = 0.5;			//luminosita
  s = 1;			  //saturazione
  HSVtoRGB( 0, (float)(h), s, v );
  HSVtoRGB( 1, (float)(360 - h), s, v );
  for( x=0; x<2; x++){  //aggiorna le uscite
    analogWrite(pin[x].r, 255 - led[x].r);
    analogWrite(pin[x].g, 255 - led[x].g);
    analogWrite(pin[x].b, 255 - led[x].b);
  }
  delay(10);
}
/* HSVtoRGB
  (cc) 2008-2010 Kokiua (alias L.M.) from Sorbolo (Parma) - Italy
  Creative Commons - 
http://creativecommons.org/licenses/by-nc-sa/3.0/
  algoritmo rielaborato partendo da da 
http://www.cs.rit.edu/~ncs/color/t_convert.html
  da RGB a HSB (Hue/Tonalita' - Saturation/Saturazione - Brightness/
Luminosita')
  HSB diventa HSV definendo Brightness = Value per distinguere la
variabile B di RGB
  i valori RGB che tornano sono nel range da 0-255
  i valori in entrata sono:
  h = [0.0-360.0] ( Tonalita' in gradi)
  s = [0.0-1.0] (Saturazione in % dove 1 = 100%)
  v = [0.0-1.0] (Luminosita' in % dove 1 = 100%)
 */
void HSVtoRGB( unsigned char x, float h, float s, float v )
{
  int i;
  float f, p, q, t, r, g, b;
  if( s == 0 ) {
    r = g = b = v;   // achromatic (grey)
    return;
  } else {
    h /= 60;			// sector 0 to 5
    i = floor( h );
    f = h - i;			// factorial part of h
    p = v * ( 1 - s );
    q = v * ( 1 - s * f );
    t = v * ( 1 - s * ( 1 - f ) );
    switch( i ) {
    case 0: r = v; g = t; b = p; break;
    case 1: r = q; g = v; b = p; break;
    case 2: r = p; g = v; b = t; break;
    case 3: r = p; g = q; b = v; break;
    case 4: r = t; g = p; b = v; break;
    default: r = v; g = p; b = q;  break; // case 5:
    }
  }
  led[x].r = 255 * r;	//converto i valori RGB dal range 0.0-1.0 a
0-255
  led[x].g = 255 * g;
  led[x].b = 255 * b;
}
n.b. nell'esempio i valori in uscita sono in logica negativa (1
spento, 0 acceso) ed è è per questo che viene sottratto il valore a
255 prima di metterlo in uscita. E' anche importante ricordare che per
avere un risultato uniforme e corretto i valori di luminosità di
ciascun colore devono essere corretti e le resistenze di limitazione
devono essere calcolate sulla base del tipo di led utilizzato, della
tensione e del colore (esistono comode pagine che fanno il calcolo
anche su internet, ad esempio; 
http://ledcalculator.net/default.aspx?lang=it-IT
). E per una descrizione più approfondita dell'algoritmo rimando al
mio articolo originale: 
http://kokiua.wordpress.com/tutorial/hsv-e-meglio-di-rgb/
Spero di essere stato utile e buon divertimento :-)

 persistenza retinica :)
Anche qui dipende, IMHO. Se i led sono in movimento 70 Hz potrebbero non 
essere sufficienti.
Marco

 Spero di essere stato utile e buon divertimento :-)
Grazie della segnalazione. Non sto utilizzando LED RGB, ma l'idea di 
descrivere il colore tramite HSV anzichč RGB per fare effetti luminosi 
non č male.
Come PWM cosa utilizzavi? Un PWM hardware del microcontrollore?

 Come PWM cosa utilizzavi? Un PWM hardware del microcontrollore?
Come ti ho già indicato, nell'esempio ho usato un piccolo Arduino
(
http://www.arduino.cc/
 ), ovvero Atmel ATmega328.
Comunque a prescindere dal sistema, sul uP di solito si usano i timer
che (se non previsto diversamente dal costruttore) devono essere
opportunamente presettati sia sulla sorgente del clock (per la
frequenza di base) che sul prescaler che dovrà essere reimpostato su
interrupt. Teoricamente con un solo timer è possibile gestire
contemporaneamente molti canali pwm (anche 32 o più), mettendo in
interrupt un tempo base e comparando i vari valori di duty cicle per
le varie uscite digitali. Ma questo non sempre è possibile soprattutto
quando il clock di sistema è lento e/o il micro deve fare altre cose
(altrimenti è sempre impegnato a generare PWM ;).
Poi esistono ic dedicati alla gestione di pwm che possono essere
controllati via i2c o simile (es 
http://www.nxp.com/documents/data_sheet/PCA9685.pdf
), e, quando non già incluso, come driver di led di potenza puoi
utilizzare i soliti uln200x (con uln2008 si gestiscono sino a 7 led
ciascuno di parecchi ma senza problemi).
Tutto dipende da cosa devi fare perchè, come giustamente ti ha fatto
notare Marco Trapanese, a seconda delle applicazioni i risultati
possono cambiare e di tanto (es: un led in pwm in movimento che lo si
vede a tratti se la frequenza del pwm è troppo bassa o, nel caso
inverso, con una luminosità troppo bassa perchè poco luminoso per il
breve lampo previsto dal pwm)
Riassumendo: tutto dipende dall'applicazione, dal uP che hai scelto,
dai led (consumo, luminosità) ... ma se si tratta solo di curiosità di
sperimentare, credo che nelle risposte ricevute ci sia già molto da
verificare ;-)