Come risolvere fischio Motore PWM Pin Arduino

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)

ArduinoFrequenzaPWM

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.