Continuo nell’aggiornamento delle esercitazioni di base per gli studenti di 3′ dell’ITIS (Elettronica ed Automazione) e per gli utenti che iniziano con Arduino.
Utilizzeremo un display LCD 16×2 di tipo I2C su cui visualizzeremo mediante una barra di avanzamento, la quantità di luminosità impostata per il LED, mediante un potenziometro.
Di seguito viene indicato il codice i base, all’interno i commenti che ne dettagliano il funzionamento di ogni parte:
/*
Prof. Maffucci Michele
https://www.maffucci.it
Ver.1 - 27.12.21
Controllo di luminosità LED con
visualizzazione intensità mediante una
barra di avanzamento su display 16x2
*/
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
byte pinPot = A0; // pin analogico 0 a cui connettere il potenziometro per controllare la luminosità
byte pinLed = 3; // pin PWM a cui connettere il LED
int analogVal = 0; // variabile in cui memorizzare il valore impostato dal potenziometro
int luminosita = 0; // variabile in cui memorizzare la luminosità
byte barraAvanzamento = 0; // indice barra avanzamento
// Per maggiori informazioni sulla realizzazione di caratteri speciali:
// https://www.maffucci.it/2020/01/18/disegnare-caratteri-personalizzati-con-arduino-per-un-lcd-16x2/
// Carattere personalizzato per disegnare la barraAvanzamento di avanzamento
byte iconaBarra[8] = {
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
B11111,
};
// inizializzazione della libreria in cui è descritta la modalità di utilizzo dei pin
// impostazione dell'indirizzo dell'LCD 0x27 di 16 caratteri e 2 linee
LiquidCrystal_I2C lcd(0x27, 16, 2);
//-----------------------------
void setup()
{
lcd.begin(); // inizializzazione dell'LCD
lcd.backlight(); // attivazione della retroilluminazione
// Inpostazione ad OUTPUT del pin a cui connettiamo il LED
pinMode(pinLed, OUTPUT);
// Cancella il display
lcd.clear();
// Stampa il messaggio sulla prima riga del display
lcd.print("Luminosita' LED");
//Creazione del carattere per la barra di avanzamento
lcd.createChar(0, iconaBarra);
}
// Per maggiori informazioni sull'uso del display 16x2 I2C:
// https://www.maffucci.it/2019/01/25/utilizzo-delllcd-16x2-hitachi-hd44780-1602-con-modulo-i2c-pcf8574t/
//-----------------------------
void loop() {
// Cancella il display
lcd.clear();
// Stampa il messsaggio sulla prima riga
lcd.print("Luminosita' LED");
//Posiziona il cursore nella seconda riga, prima colonna
lcd.setCursor(0,1);
// Lettura del valore impostato dal potenziometro
analogVal = analogRead(pinPot);
// Conversione del valore analogico impostato con il potenziometro
// in Duty Cicle per impostare la luminosità del LED
luminosita=map(analogVal, 0, 1024, 0, 255);
// Impostazione della luminosità del LED
analogWrite(pinLed, luminosita);
// Conversione della luminosità in quantità di caratteri della barra da stampare
barraAvanzamento=map(luminosita, 0, 255, 0, 15);
// Stampa la barra di avanzamento
for (byte i = 0; i < barraAvanzamento; i++)
{
lcd.setCursor(i, 1);
lcd.write(byte(0));
}
// leggero ritardo di 500 ms per visualizzare la barra
delay(500);
}
Proposta esercizi
Esercizio 1
Nell’esempio proposto viene utilizzato un delay() finale per permettere la visualizzazione dei caratteri sul display. Sostituire il delay() ed utilizzare l’istruzione millis() per valutare il tempo trascorso e controllare la stampa dei caratteri sul display.
Esercizio 2
Modificare il programma precedente raddoppiando il numero di caratteri che rappresenta la barra di avanzamento.
Esercizio 3
Modificare l’esercizio proposto variando la barra di avanzamento in funzione dell’approssimarsi al valore massimo o minimo della luminosità.
Esercizio 4
Modificare l’esercizio proposte inserendo un buzzer che emette un suono la cui frequenza varia in funzione dell’intensità luminosa del LED.
Abbiamo visto a lezione in cosa consiste la Modulazione di Larghezza di Impulso, in altro modo conosciuta come PWM (Pulse Width Modulation). Questo tipo di modulazione permette ad un sistema digitale di controllare dispositivi di tipo analogico in corrente continua facendo variare in modo continuo la potenza erogata, quindi moduliamo la larghezza di un impulso, ovvero la durata temporale di una serie di impulsi che regolerà l’attivazione e disattivazione del motore. Con il PWM agiamo non sulla tensione di alimentazione per controllare la velocità del motore, ma bensì sul tempo, per maggiori informazioni vi rimando alle slide: Alfabeto Arduino – Lezione 2 in cui come esempio viene controllata l’intensità luminosa di LED. (Per i miei studenti fate riferimento al libro di testo e agli appunti di teoria).
Il transistor nell’immagine che segue viene fatto funzionare in modalità ON-OFF e quindi possiamo assimilarlo ad un interruttore che si apre e si chiude in corrispondenza del segnale presente sulla base del transistor. Se il segnale sulla base è alto il transistor sarà in conduzione e il motore potrà essere alimentato (si chiude verso massa il collegamento del motore). Se il segnale sulla base del transistor è a livello basso il transistor è assimilabile ad un interruttore aperto per cui il motore risulta non alimentato (non si ha collegamento a massa). Quindi al motore verrà applicata una tensione continua proporzionale al duty cycle e quindi variando il duty cycle varieremo la velocità di rotazione del motore.
La generazione dell’onda quadra, che controlla la velocità del motore, viene eseguita da Arduino. Il diodo presente nel circuito è chiamato diodo di ricircolo ed è inserito in parallelo al motore (carico induttivo) per sopprimere i transienti elettrici ed è collegato in modo tale che non conduce quando il carico (il motore) viene alimentato.
Quando il motore viene disattivato rapidamente si avrà un picco di tensione in senso inverso perché l’induttore tenderà a mantenere costante la corrente che circola al suo interno, in questa situazione però il diodo sarà polarizzato direttamente e tutta la corrente scorrerà dall’induttore al diodo e l’energia che era stata immagazzinata dall’induttore viene dissipata in calore dalla componente resistiva dell’induttore. Inserendo il diodo si eviterà di applicare una sovratensione sul collettore del transistor evitando quindi che si danneggi.
Controlliamo con Arduino il movimento del motore
Lista Componenti
Arduino UNO R3
Resistore da 1 K Ohm
Dido: 1N4001
Transistor: 2N2222
Condensatore: 0,1 microF
Pinout transistor 2N2222
Circuito 1 di montaggio: alimentazione del motore prelevata da Arduino
Circuito 2 di montaggio: alimentazione del motore esterna ad Arduino
Esempio 1 Si faccia riferimento al circuito 1. Realizziamo uno sketch che permette la regolazione della velocità del motore inserendo un numero compreso tra 0 e 9. Il valore 0 ferma il motore, il valore 9 permette di muovere il motore alla velocità massima, valori intermedi movimenteranno il motore ad una velocità proporzionale al numero inserito.
Valori fuori dall’intervallo impostato restituiscono un messaggio di errore.
Per la realizzazione di questo sketch si utilizza la funzione isDigit() che restituisce TRUE verifica se il carattere inviato sulla Serial Monitor è un numero, altrimenti restituisce FALSE.
/*
Prof. Maffucci Michele
Controllo motore a spazzola:
accensione, spegnimento, controllo velocità.
All'avvio del programma il motore è spento.
La velocità del motore è impostata
con un valore compreso tra 0 e 9
Valori non numerici restituiscono
un messaggio di errore
Data: 19.01.2021
*/
// driver del motore collegato al pin 6
const byte pinMotore = 6; // motor driver is connected to pin 3
// per stampare una sola volta il messaggio sulla Serial Monitor
bool abilitaMessaggio = 0;
void setup()
{
// inizializzazione della serial monitor
Serial.begin(9600);
// inizializzazione OUTPUT del pin 6
pinMode(pinMotore, OUTPUT);
}
void loop()
{
// consente di visualizzare sulla Serial Monitor
// una sola stampa delle stringa
if (abilitaMessaggio == 0) {
// ritardo che evita la doppia stampa del messaggio
delay(200);
Serial.println("Controllo velocità motore");
Serial.println("Inserire la velocità (0 - 9)");
Serial.println();
abilitaMessaggio = 1;
}
// Controlla se è disponibile almeno un carattere sulla seriale
// La Serial.available() restituisce
// 1 se presente un cattere,
// 0 se non è presente un carattere
// per maggior informazioni sull'uso di parseInt() consultare il link:
// https://wp.me/p4kwmk-4Ah
if (Serial.available()) { // Viene controllato se è disponibile un carattere
// definizione di una variabile di tipo char in cui memorizzare
// il carattere inviato ad Arduino mediante la Serial Monitor
char carattere = Serial.read();
// La funzione isDigit restituisce TRUE se se il carattere
// inviato sulla Serial Monitor è un numero altrimenti restituisce FALSE
if (isDigit(carattere)) // verifica se è un numero
{
// mappiamo l'intervallo dei caratteri da '0' a '9'
// in un valore compreso tra 0 e 255, intervallo di valori
// del Duty Cycle
int vel = map(carattere, '0', '9', 0, 255);
analogWrite(pinMotore, vel);
Serial.print("Valore del Duty Cycle: ");
Serial.println(vel);
Serial.println("--------------------------");
Serial.print("Velocità impostata: ");
Serial.println(carattere);
Serial.println("==========================");
}
else
{
// nel caso in cui il carattere inserito non è un numero
// viene restituito un messaggio e stampa il carattere
Serial.println();
Serial.println("******************************");
Serial.print("Carattere non riconosciuto: ");
Serial.println(carattere);
Serial.println("******************************");
Serial.println();
}
}
}
Esempio 2 Si faccia riferimento al circuito 1. Realizziamo uno sketch che permette la regolazione della velocità del motore inserendo un numero compreso tra 0 e 9. Il valore 0 ferma il motore, il valore 9 permette di muovere il motore alla velocità massima, valori intermedi movimenteranno il motore ad una velocità proporzionale al numero inserito.
Valori fuori dall’intervallo impostato fermano il motore.
Per la realizzazione di questo sketch si utilizza la funzione Serial.parseInt() che legge i caratteri sulla seriali e restituisce la loro rappresentazione numerica (tipo long). I caratteri che non sono numeri interi (o con segno meno) vengono ignorati.
Nel dettaglio
I caratteri iniziali che non sono cifre o sono numeri negativi vengono ignorati;
L’analisi si interrompe quando non sono stati letti caratteri per un valore di tempo di timeout che può essere configurato oppure viene letta una non cifra;
Se non sono state lette cifre valide quando si verifica il timeout (vedere Serial.setTimeout ()), viene restituito 0; Serial.parseInt () eredita dalla classe Stream.
/*
Prof. Maffucci Michele
Controllo motore a spazzola:
accensione, spegnimento, controllo velocità.
All'avvio del programma il motore è spento.
La velocità del motore è impostata
con un valore compreso tra 0 e 9
Valori non numerici fermano il motore.
Viene utilizzata la funzione Serial.parseInt() per leggere
Data: 19.01.2021
*/
// driver del motore collegato al pin 6
const byte pinMotore = 6; // motor driver is connected to pin 3
// per stampare una sola volta il messaggio sulla Serial Monitor
bool abilitaMessaggio = 0;
void setup()
{
// inizializzazione della serial monitor
Serial.begin(9600);
// inizializzazione OUTPUT del pin 6
pinMode(pinMotore, OUTPUT);
}
void loop()
{
// consente di visualizzare sulla Serial Monitor
// una sola stampa delle stringa
if (abilitaMessaggio == 0) {
// ritardo che evita la doppia stampa del messaggio
delay(200);
Serial.println("Controllo velocità motore");
Serial.println("Inserire la velocità (0 - 9)");
Serial.println();
abilitaMessaggio = 1;
}
// Controlla se è disponibile almeno un carattere sulla seriale
// La Serial.available() restituisce
// 1 se presente un cattere,
// 0 se non è presente un carattere
// per maggior informazioni sull'uso di parseInt() consultare il link:
// https://wp.me/p4kwmk-4Ah
if (Serial.available()) { // Viene controllato se è disponibile un carattere
// definizione di una variabile di tipo char in cui memorizzare
// il carattere inviato ad Arduino mediante la Serial Monitor
// per maggior informazioni sull'uso di parseInt() consultare il link:
// https://wp.me/p4kwmk-4Ah
int valore = Serial.parseInt();
// La funzione isDigit verifica se il carattere inviato è un numero
// e restituisce TRUE se il carattere è un numero altrimenti restituisce FALSE
// mappiamo l'intervallo dei caratteri da '0' a '9'
// in un valore compreso tra 0 e 255, intervallo di valori
// del Duty Cycle
if (valore >= 0 && valore <= 9) {
if (valore == 0) {
Serial.println();
Serial.println("************************************");
Serial.println("MOTORE FERMO");
Serial.println("Valore inserito 0 o fuori intervallo");
Serial.println("************************************");
Serial.println();
}
int vel = map(valore, 0, 9, 0, 255);
analogWrite(pinMotore, vel);
Serial.print("Valore del Duty Cycle: ");
Serial.println(vel);
Serial.println("--------------------------");
Serial.print("Velocità impostata: ");
Serial.println(valore);
Serial.println("==========================");
}
}
}
Esercizio 1 Aggiungere al circuito 1 due pulsanti che permettono di aumentare o diminuire la velocità del motore. Fare in modo che la velocità impostata del motore sia proporzionale ai valori numerici interi nell’intervallo tra 0 e 9, così come fatto negli esempi precedenti. Ad ogni pressione del pulsante P1 si incrementa di una unità il valore della velocità. Ad ogni pressione del pulsante P2 si decrementa la velocità del motore i una unità. All’avvio di Arduino il motore è spento. Mostrare la velocità impostata sulla serial monitor.
Esercizio 2
Aggiungere all’esercizio precedente un pulsante P3 di emergenza che alla pressione ferma il motore. Se è stata azionata l’emergenza i pulsanti P1 e P2 di incremento e decremento non funzionano. Per poter riattivare il sistema bisogna premere nuovamente P3.
Scrivo questo post ad integrazione della lezione: Arduino – lezione 06: modulazione di larghezza di impulso (PWM) che sto utilizzando con i miei studenti di 4′ informatica per illustrare le modulazioni di tipo digitali. L’obiettivo è quello di mostrare sull’oscilloscopio come varia il Duty Cycle di un’onda quadra su un pin di tipo PWM di Arduino utilizzato per impostare l’intensità luminosa di un LED mediante una regolazione applicata attraverso un trimmer connesso al pin A0 di Arduino.
Oltre alla visualizzazione sull’oscilloscopio si desidera, come riscontro, la stampa sulla Serial Monitor dei seguenti valori:
Tensione in input sul pin A0
Valore restituito dalla funzione analogRead() – (tra 0 e 1023)
Valore restituito dall’analogWrite – (tra 0 e 254)
Valore percentuale del Duty Cycle (tra 0% e 100%)
Il circuito da realizzare con l’indicazione delle connessioni all’oscilloscopio è il seguente:
Sul canale X verrà visualizzata l’onda quadra in uscita dal pin 11 il cui Duty Cycle sarà regolato agendo sul trimmer.
Sul canale Y verrà visualizzata la tensione continua in input sul pin A0, che sarà convertita dal convertitore Analogico Digitale di Arduino in un valore compreso tra 0 e 1023 (risoluzione di 10 bit). Ricordo che tale conversione sarà fatta con l’istruzione analogRead(pin).
Poiché uno degli obiettivi è quello di visualizzare la tensione rilevata sul pin A0, ricordo che tale misurazione viene fatta utilizzando la funzione analogRead(pin) che legge il valore di tensione (compreso tra 0 e 5V) applicato sul piedino analogico ‘pin’ con una risoluzione di 10 bit e la converte in un valore numerico compreso tra 0 e 1023, corrispondente quindi ad un intervallo di 1024 valori, pertanto ogni intervallo corrisponde ad un valore di tensione Vu di:
Per sapere quindi il valore di tensione rilevato (nell’intervallo tra 0V e 5V) sarà sufficiente moltiplicare la tensione unitaria Vu per il valore restituito dalla funzione analogRead(pin), valore quantizzato indicato con Vq compreso tra 0 e 1023:
Sapendo che Vu corrisponde a 4,88 mV
possiamo anche scrivere che:
Questa formula sarà inserita all’interno dello sketch.
Di seguito la schermata dell’oscilloscopio che visualizza la situazione indicata dai dati stampati sulla Serial Monitor:
Vmax(2) indica la tensione in ingresso ad A0 (la piccola discrepanza tra valore indicato sull’oscilloscopio e la stampa sulla Serial Monitor dipende dalle approssimazioni di calcolo).
Vmax(1) indica il valore di picco della tensione sul pin 11.
La spiegazione del funzionamento dello sketch sono dettagliate nei commenti:
/* Prof. Michele Maffucci
03.06.2019
Regolazione luminosità LED mediante
trimmer, si utilizza la funzione map
Stampa sulla seriale:
- del valore di tensione sul pin A0
- del valore restituito dall'analogRead
- del valore restituito dall'analogWrite
- del valore del Duty Cycle %
Questo codice è di dominio pubblico
*/
// pin analogico su cui inviare la tensione analogica (pin A0)
int misura = 0;
// pin a cui è connesso il LED
int pinLed = 11;
// variabile in cui conservare il valore inserito su A0
long val = 0;
// variabile in cui memorizzare il Duty Cycle
int inputVal = 0;
const long VoltRiferimento = 5.0; // valore di riferimento
void setup(){
Serial.begin(9600); // inizializzazione della comunicazione seriale
pinMode(pinLed, OUTPUT); // definizione di ledPin come output
}
void loop(){
// analogRead leggerà il valore su A0 restituendo un valore tra 0 e 1023
// per approfondimenti si consulti il link: http://wp.me/p4kwmk-1Qd
val = analogRead(misura);
// analogWrite() accetta come secondo parametro (PWM) valori tra 0 e 254
// pertanto "rimappiamo" i valori letti da analogRead() nell'intervallo
// tra 0 e 254 usando la funzione map
// per approfondimenti si consulti il link: http://wp.me/p4kwmk-1Tu
inputVal = map(val, 0, 1023, 0, 254);
// accendiamo il LED con un valore del Duty Cycle pari a val
analogWrite(pinLed,inputVal);
// Tensione inviata sul pin analogico A0.
// Valore in virgola mobile.
float volt = (VoltRiferimento/1024.0)*val;
// visualizzazione il valore della tensione su A0,
// del valore restituito dalla analogRead,
// del valore restituito dall'analogWrite
// e del Duty Cycle %
// per approfondimenti sull'uso di String si consulti il link: https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/
Serial.println(String("Tensione su A0: ") + volt + "V" + String("; ") + "analogRead: " + val + String("; ") + String("Valore analogWrite: ") + inputVal + String("; ") + String("Duty Cycle %: ") + (inputVal/255.0)*100 + String("%;"));
delay(500); // stampa una strina di valori ogni mezzo secondo
}
analogRead(pin)
Legge un valore di tensione applicato al piedino analogico ‘pin’ con una risoluzione di 10 bit. La funzione restituisce un valore compreso tra 0 e 1023.
value = analogRead(pin); // imposta 'value' uguale al
// valore letto su 'pin'
// dalla funzione analogRead
Nota: i pin analogici a differenza di quelli digitali non hanno bisogno di essere dichiarati come pin di INPUT o OUTPUT.
analogWrite(pin, value)
Cambia il duty cycle (nella funizione: ‘value’) della modulazione di ampiezza di impulso (PWM: Pulse Width Modulation) su uno dei ‘pin’ contrassegnati dall’etichetta PWM.
Sull’attuale Arduino UNO su cui è montato un ATmega 328, il PWM è abilitato sui piedini 3,5,6,9,10 e 11.
Il valore del duty cycle può essere specificato da una variabile o una costante con un valore compreso tra 0 e 255.
analogWrite(pin, value); // scrive il valore 'value'
// sul 'pin' analogico
Un valore di 0 genera in uscita una tensione continua di 0 volt sul pin specificato nella funzione; un valore di 255 genera una tensione continua 5 volt sul pin specificato nella funzione. Per valori compresi tra 0 e 255, il valore in uscita varierà rapidamente tra 0 e 5 volt. Più alto sarà il valore di ‘value’ più spesso su ‘pin’ si avrà una tensione di 5 volt. Ad esempio un valore di ‘value’ pari a 64 genera un segnale in cui per tre quarti del periodo dell’onda il segnale sarà a 0 volt e per un quarto del periodo dell’onda sarà a 5 volt. Se ‘value’ è posto a 128 avremo un segnale che per metà del periodo sarà a 0 volte e per la restante metà del periodo sarà a 5 volt. Se ‘value’ è posto a 192 avremo 0 volt per un quarto del periodo e 5 volt per i restanti tre quarti del periodo.
Poiché analogWrite è una funzione hardware, sul pin avremo un onda quadra dopo una chiamata della funzione analogWrite e questa verrà continuativamente emessa in background fino alla successiva chiamata della analogWrite (o chiamata della digitalRead o digitalWrite sullo stesso pin).
L’esempio che segue legge un valore analogico da un pin di ingresso analogico, converte il valore dividendolo per 4, e fornisce un segnale PWM sul pin PWM specificato:
int led = 10; // al pin 10 è collegato un LED
// in cui in serie è posta un
// resistore da 220 Ohm
int pin = 0; // inseriamo un potenziometro sul pin 0
int value; // valore che sarà letto
void setup(){} // non è necessaria nessuna configurazione
void loop()
{
value = analogRead(pin); // imposta 'value' al
// valore letto su 'pin'
value /= 4; // dividendo per 4 si converte
// il valore letto compreso tra
// 0 e 1023 in un valore
// compreso tra 0 e 255
analogWrite(led, value); // il valore del PWM
// viene assegnato al led
}
Nella precedente lezione abbiamo visto come progettare un controllo presenza in due stanze adiacenti in cui abbiamo utilizzato due pulsanti per simulare due sensori PIR (uno per ogni stanza) e due led per simulare l’accensione delle lampade nelle due stanze.
L’ultimo sketch consentiva di silmulare la seguente situazione per entrambe le stanze:
entro nella stanza
PIR avverte la mia presenza
si accende la luce
ritardo lo spegnimento della luce per darmi il tempo di uscire dalla stanza
In questa e nella successiva lezione vorrei giungere alla realizzazione di un comportamento più vicino alla realtà:
entro nella stanza
PIR avverte la mia presenza
si accende in fade la luce
esco dalla stanza
il PIR non rileva più la mia presenza
si spegne in fade la luce
Suddivido la soluzione del problema in due sottoproblemi che risolverò in questa e nella successiva lezione:
funzione fade
funzione controllo presenza
Realizziamo la funzione fade
Se ricordate nella lezione 2 abbiamo realizzato il famoso “blink” lo sketch che accendeva e spegneva in modo continuativo un LED.
Realizziamo nuovamente il circuito ed eseguite lo sketch allegato:
// Esempio 01: fare lampeggiare un LED
#define LED 13 // LED collegato al pin digitale 13
void setup() {
pinMode(LED, OUTPUT); // imposta il pin digitale come output
}
void loop() {
digitalWrite(LED, HIGH); // accende il LED
delay(1000); // aspetta 1 secondo
digitalWrite(LED, LOW); // spegne il LED
delay(1000); // aspetta 1 secondo
}
Provate a ridurre drasticamente il ritardo di accensione e spegnimento, arrivate a 10 millisecondi, quasi non dovreste più pecepire lampeggiare il LED e inoltre dovreste aver notato che la luminosità del LED è diminuita.
// Esempio 02: modulazione di larghezza di impulso (PWM)
#define LED 13 // LED collegato al pin digitale 13
void setup() {
pinMode(LED, OUTPUT); // imposta il pin digitale come output
}
void loop() {
digitalWrite(LED, HIGH); // accende il LED
delay(10); // aspetta 10 millisecondi
digitalWrite(LED, LOW); // spegne il LED
delay(10); // aspetta 10 millisecondi
}
Il motivo per cui si vede illuminare di meno il diodo LED è dovuta alla modulazione di larghezza di impulso in inglese Pulse Width Modulation – PWM, che detta in modo meno tecnico vuol dire che se facciamo lampeggiare un diodo LED ad una frequenza sufficientemente elevata e se cambiamo il rapporto tra il tempo in cui sta acceso ed il tempo in cui sta spento, il nostro occhio non percepirà il lampeggiare del LED ed inoltre a secondo del rapporto del tempo di accensione e spegnimento potremo regolare la luminosità del LED.
Possiamo dire che il PWM è una tecnica per ottenere risultati analogici con mezzi digitali.
Un po’ di teoria: il Duty cycle
Il duty cycle di un onda quadra/rettangolare e il rapporto tra la durata (in secondi) del segnale quando è “alto” ed il periodo totale del segnale. In altre parole è un numero che esprime quant’è la parte di periodo in cui il segnale è alto.
Facendo riferimento al disegno la formula che esprime il duty cycle è:
τ/T
dove T è il periodo e τ la parte di periodo in cui il segnale è alto.
Dalla formula potete subito notare che τ può variare da un valore minimo di 0 a un valore massimo pari a T, ciò implica che il valore del duty cycle varia da 0 a 1:
in entrambi i casi siamo in presenza di segnali continui.
Dalla formula possiamo comprendere quindi che il duty cycle è sempre un valore che varia tra 0 e 1.
Il duty cycle è spesso rappresentato in percentuale, D% e per ottenere la percentuale è sufficiente moltiplicare per 100 il rapporto τ/T, dire quindi che il D%=30% vuol dire che per il 30% del periodo totale il segnale si trova a livello alto, come conseguenza possiamo subito dire che il segnale sarà a livello basso per il restante 70% del periodo.
Dire quindi che il duty cycle è del 50% vuol dire che nel periodo T il segnale si mantiene alto per T/2 e per il restante T/2 a livello basso in questo caso siamo quindi in presenza di un’onda quadra.
Passiamo ora alla pratica
Poniamoci nelle seguenti 3 condizioni:
50% LED acceso e 50% LED spento
luminosità al 50%
25% LED acceso e 75% LED spento
luminosità al 25%
75% LED acceso e 25% LED spento
luminosità al 75%
Per realizzare le tre condizioni impostate scegliamo un periodo sufficientemente breve tale da non percepire il lampeggiare del LED:
Poniamo un periodo T = 20ms
10ms acceso e 10ms spento
5 ms acceso e 15ms spento
75 ms acceso e 5ms spento
// Esempio 03: modulazione di larghezza di impulso (PWM) - on: 10ms - off: 10ms
#define LED 13 // LED collegato al pin digitale 13
void setup() {
pinMode(LED, OUTPUT); // imposta il pin digitale come output
}
void loop() {
digitalWrite(LED, HIGH); // accende il LED
delay(10); // aspetta 10 millisecondi
digitalWrite(LED, LOW); // spegne il LED
delay(10); // aspetta 10 millisecondi
}
// Esempio 04: modulazione di larghezza di impulso (PWM) - on: 5ms - off: 15ms
#define LED 13 // LED collegato al pin digitale 13
void setup() {
pinMode(LED, OUTPUT); // imposta il pin digitale come output
}
void loop() {
digitalWrite(LED, HIGH); // accende il LED
delay(5); // aspetta 5 millisecondi
digitalWrite(LED, LOW); // spegne il LED
delay(15); // aspetta 15 millisecondi
}
// Esempio 05: modulazione di larghezza di impulso (PWM) - on: 15ms - off: 5ms
#define LED 13 // LED collegato al pin digitale 13
void setup() {
pinMode(LED, OUTPUT); // imposta il pin digitale come output
}
void loop() {
digitalWrite(LED, HIGH); // accende il LED
delay(15); // aspetta 15 millisecondi
digitalWrite(LED, LOW); // spegne il LED
delay(5); // aspetta 5 millisecondi
}
Dal filmato potete notare la variazione di luminosità nelle tre condizioni:
Con un po’ di esperimenti noterete che la tecnica di variazione della luminosità, utilizzando il ritardo “delay()”, non è il metodo migliore in quanto, non appena inseite altri sensori ad Arduino, oppure inviate dei dati alla seriale, il LED tremolerà nell’attesa che termini la lettura del sensore o la scrittura sulla seriale.
Come evitare questo problema?
Arduino UNO offre la possibilità di usare i pin 3, 5, 6, 9, 10, 11 l’istruzione: analogWrite(), istruzione che consente appunto di far lampeggiare il LED o governare un motore elettrico mentre lo sketch esegue altre istruzioni.
Sintassi:
analogWrite(pin, valore)
dove:
pin: è il piedino su cui inviamo il segnale, per Arduino UNO i pin 3, 5, 6, 9, 10, 11
valore: è il duty cycle compreso tra 0 (sempre off) a 255 (sempre on)
La funzione non restituisce nessun valore.
Quindi se utilizziamo la funzione analogWrite() per il controllo della luminosità del LED e scriviamo:
analogWrite(11, 0)
il LED collegato al pin 11 avrà una luminosità dello 0% (duty cycle 0%)
// Esempio 06: utilizzo della funzione analgoWrite() - LED spento - (duty cycle 0%)
#define LED 11 // LED collegato al pin digitale 11
void setup() {
pinMode(LED, OUTPUT); // imposta il pin digitale come output
}
void loop() {
analogWrite(LED, 0); // accende il LED
}
analogWrite(11, 64)
il LED collegato al pin 11 avrà una luminosità del 25% (duty cycle 25%)
// Esempio 07: utilizzo della funzione analgoWrite() - LED spento - (duty cycle 25%)
#define LED 11 // LED collegato al pin digitale 11
void setup() {
pinMode(LED, OUTPUT); // imposta il pin digitale come output
}
void loop() {
analogWrite(LED, 64); // accende il LED
}
analogWrite(11, 128)
il LED collegato al pin 11 avrà una luminosità del 50% (duty cycle 50%)
// Esempio 08: utilizzo della funzione analgoWrite() - LED spento - (duty cycle 50%)
#define LED 11 // LED collegato al pin digitale 11
void setup() {
pinMode(LED, OUTPUT); // imposta il pin digitale come output
}
void loop() {
analogWrite(LED, 128); // accende il LED
}
analogWrite(11, 191)
il LED collegato al pin 11 avrà una luminosità del 75% (duty cycle 75%)
// Esempio 09: utilizzo della funzione analgoWrite() - LED spento - (duty cycle 75%)
#define LED 11 // LED collegato al pin digitale 11
void setup() {
pinMode(LED, OUTPUT); // imposta il pin digitale come output
}
void loop() {
analogWrite(LED, 191); // accende il LED
}
analogWrite(11, 255)
il LED collegato al pin 11 avrà una luminosità del 100% (duty cycle 100%)
// Esempio 10: utilizzo della funzione analgoWrite() - LED spento - (duty cycle 100%)
#define LED 11 // LED collegato al pin digitale 11
void setup() {
pinMode(LED, OUTPUT); // imposta il pin digitale come output
}
void loop() {
analogWrite(LED, 255); // accende il LED
}
Nel filmato allegato potete notare la differenza di luminosità nelle 5 condizioni sopra illustrate.
Imparato come variare la luminosità del LED usando la funzione analogWrite(), vediamo come realizzare il fade del LED ovvero l’accensione e lo spegnimento graduale, attenzione che questo modo di procedere sarà utile anche quando dovremo imparare a variare la velocità di un motorino elettrico.
Lo schetch che vi allego è già presente negli esempi disponibili dal menù: File -> Examples -> 3.Analog -> Fading ne ho variato alcune parti:
// Esempio 11: Fading
#define LED 11 // LED collegato al pin digitale 11
int valoreFade = 0; // variabile usata per contare in avanti e indietro
void setup() {
pinMode(LED, OUTPUT); // imposta il pin digitale come output
}
void loop() {
// procede ciclicamente da 0 a 254 (fade in -> aumento luminosità)
for (valoreFade = 0 ; valoreFade < 255; valoreFade++) { analogWrite(LED, valoreFade); //impostiamo la luminosità del LED delay(10); // aspettiamo 10ms per percepire la viariazione di luminosità, //perché analogWrite è istantaneo } // procede ciclicamente da 255 a 1 (fade out -> diminuzione della luminosità)
for(valoreFade = 255 ; valoreFade > 0; valoreFade--) {
analogWrite(LED, valoreFade); //impostiamo la luminosità del LED
delay(10);
// aspettiamo 10ms per percepire la viariazione di luminosità,
//perché analogWrite è istantaneo
}
}
Analizzaimo il codice.
Prendiamo in analisi la prima parte del loop(). All’interno troviamo un primo ciclo for:
...
// procede ciclicamente da 0 a 254 (fade in -> aumento luminosità)
for (valoreFade = 0 ; valoreFade < 255; valoreFade++) {
analogWrite(LED, valoreFade); //impostiamo la luminosità del LED
delay(10);
// aspettiamo 10ms per percepire la viariazione di luminosità,
//perché analogWrite è istantaneo
}
...
Ad ogni ciclo incrementiamo la variabile valoreFade di 1 (valoreFade++) partendo da 0 fino a 254. valoreFade viene utilizzata in analogWrite(LED, valoreFade) per variare il valore del duty cycle ed incrementare la luminosità del LED. Poichè l’azione di analogWrite(LED, valoreFade) è immediata per percepire visivamente la variazione di luminosità introduciamo un piccolo ritardo di 10ms con delay(10). Il ciclo terminerà non appena la condizione valoreFade < 255 non è più vera, cioè valoreFade non più minore di 255.
Il secondo ciclo for consente di diminuire la luminosità:
...
// procede ciclicamente da 255 a 1 (fade out -> diminuzione della luminosità)
for(valoreFade = 255 ; valoreFade > 0; valoreFade--) {
analogWrite(LED, valoreFade); //impostiamo la luminosità del LED
delay(10);
// aspettiamo 10ms per percepire la viariazione di luminosità,
//perché analogWrite è istantaneo
}
...
Ad ogni ciclo la variabie valoreFade viene decrementata di 1 (valoreFade–) facendo decrescere valoreFade da 255 a 1 e di conseguenza la luminosità del LED. Il ciclo terminerà quando la condizione valoreFade > 0 non è più vera.
Usciti da questo secondo ciclo si ripartirà con il primo ciclo for che permetterà nuovamente l’aumento della luminosità del LED.
Nella prossima lezione vedreme come interagire mediante pulsanti sull’intensità luminosità del LED.