Premessa:
Arduino possiede 3 moduli OC (Output Compare), che sono il riferimento delle 6 uscite PWM (pin 3, 5, 6, 9, 10 e 11 : ogni modulo ha 2 uscite).
LISTA DEI MATERIALI:
I 3 moduli sono contraddistinti dalle sigle OC0, OC1 e OC2 e vengono gestiti ognuno da un diverso timer:
6 PD6(OC0A) OC0 TCCR0
5 PD5(OC0B)
9 PB1(OC1A) OC1 TCCR1
10 PB2(OC1B)
11 PB3(OC2A) OC2 TCCR2
3 PD3(OC2B)
un segnale PWM consente di fermare, ruotare e variare la velocità dei motori oppure diminuire, aumentare l’intensità di un led. Di default Arduino imposta i pin 9, 10, 11 e 3 (moduli OC1 e OC2) per lavorare a circa 490Hz, e i pin 6 e 5 (modulo OC0) a 976Hz. Di certo ciò permette di gestire la luminosità di un led senza avere grossi problemi, ma se volessimo pilotare un motore a queste basse frequenze riscontreremo un fischio continuo di sottofondo. L’uomo è in grado di udire suoni la cui frequenza è compresa dai 20 ai 20.000 Hz. Tale gamma di suoni è chiamata campo (o intervallo) di udibilità dello spettro delle frequenze sonore. I suoni la cui frequenza è al di sotto dei 20 Hz sono chiamati infrasuoni, i suoni la cui frequenza supera i 20.000 Hz sono chiamati ultrasuoni. Quindi facendo due calcoli ci troviamo in una frequenza “udibile” (490 e 976 Hz). Partiamo col dire che per la maggior parte delle applicazioni è sconveniente variare la frequenza operativa del modulo OC0 (pin 6 e 5) dal momento che questo si appoggia al Timer0, al quale fanno riferimento anche le routine di ritardo: ci sistemiamo il PWM su questi pin, ma poi le varie funzioni delay(), millis(), libreria servo e probabilmente molte altre funzioni che fanno uso del Timer 0, produrranno risultati del tutto inaspettati. Atmel consiglia come soluzione di modificare il prescaler dei Timer agendo nei registri TCCRxB:
void setup() { setPwmFrequency(9, 1); } void loop(){ //corpo del codice } void setPwmFrequency(int pin, int divisor) { byte mode; if(pin == 5 || pin == 6 || pin == 9 || pin == 10) { switch(divisor) { case 1: mode = 0x01; break; case 8: mode = 0x02; break; case 64: mode = 0x03; break; case 256: mode = 0x04; break; case 1024: mode = 0x05; break; default: return; } if(pin == 5 || pin == 6) { TCCR0B = TCCR0B & 0b11111000 | mode; } else { TCCR1B = TCCR1B & 0b11111000 | mode; } } else if(pin == 3 || pin == 11) { switch(divisor) { case 1: mode = 0x01; break; case 8: mode = 0x02; break; case 32: mode = 0x03; break; case 64: mode = 0x04; break; case 128: mode = 0x05; break; case 256: mode = 0x06; break; case 1024: mode = 0x7; break; default: return; } TCCR2B = TCCR2B & 0b11111000 | mode; } }
La funzione setPwmFrequency imposta il divisore del prescaler della frequenza PWM associato al timer che controlla il pin.
Per i pin 9,10, 11 e 3 si parte dalla frequenza di base di 31250Hz, per cui su tali pin impostando il parametro divisor sul valore 64, ad esempio, otterremo una frequenza PWM pari a 31250/64 = 488Hz (la frequenza di default).
La frequenza di base per i pin 6 e 5 è invece 62500Hz, per cui impostando anche qui divisor a 64, ad esempio, otteniamo 62500/64=976Hz (di nuovo la frequenza di default!). I valori validi per il parametro divisor sono: 1, 8, 64, 256 e 1024. Per i pin 3 e 11 sono disponibili anche i valori 32 e 128 oltre a quelli elencati prima.
Quindi ritornando al nostro esempio impostando la frequenza al nostro pwm per controllare il motore:
setPwmFrequency(9, 1);
Avremo una frequenza di 31250/1 = 31250 Hz
Tale frequenza non rientra nel range del nostro campo di udibilità, ma rientra nell’intervallo della gamma ultrasuoni e quindi il fischio del motore non sarà udibile dalle nostre orecchie.