Come Arduino gestisce il PWM. Esempio pilotaggio CENT4UR™ con Arduino

Abbiamo visto che è possibile pilotare CENT4UR™ in modalità LAP: un singolo segnale PWM per canale consente di fermare, o far ruotare i motori in un verso o nell’altro a varie velocità. Dato che non è mia usanza spiattellare del codice senza spiegare da dove viene fuori, analizziamo come Arduino gestisce i segnali PWM hardware, dopodichè vediamo come è possibile pilotare due motori attraverso CENT4UR™ . In un prossimo articolo mi occuperò della stessa cosa ma con il chipKIT™ UNo32.

Il PWM su Arduino

Arduino possiede 3 moduli OC (Output Compare), che fanno capo a 6 uscite PWM (pin 3, 5, 6, 9, 10 e 11 : ogni modulo ha 2 uscite). I 3 moduli sono contraddistinti dalle sigle OC0, OC1 e OC2 e vengono gestiti ognuno da un diverso timer:

Pin ArduinoPin ATMega 328ModuloTimer
6PD6 (OC0A)OC0TCCR0
5PD5 (OC0B)
9PB1 (OC1A)OC1TCCR1
10PB2 (OC1B)
11PB3 (OC2A)OC2TCCR2
3PD3 (OC2B)

Ogni modulo, essendo pilotato da un diverso Timer, ha la possibilità di operare ad una frequenza diversa dagli altri. E’ ovvio che i due pin appartenenti allo stesso modulo opereranno sempre alla stessa frequenza pur potendo variare individualmente il duty cycle.

Normalmente chi utilizza Arduino, per poter sfruttare il PWM, è abituato ad utilizzare la seguente funzione:

analogWrite(pin, valore);

dove pin assume il valore 6, 5, 9, 10, 11 o 3, e valore è un numero tra 0 (duty cycle:0%) e 255 (duty cycle:100%). Molti, purtroppo, ignorano la questione della frequenza: ok sto impostando il duty cycle… ma a che frequenza sto lavorando? E soprattutto: sono vincolato ad una frequenza fissa oppure ho la possibilità di variarla?

Di default Arduino imposta i pin 9, 10, 11 e 3 (moduli OC1 e OC2) per lavorare a 488Hz, e i pin 6 e 5 (modulo OC0) a 976Hz.

Segnale PWM Arduino sui pin 3, 9, 10, 11
Segnale PWM Arduino sui pin 5 e 6

Queste frequenze così basse potrebbero andare bene per variare la luminosità di un led ma non certo per pilotare un motore o generare un segnale analogico, come purtroppo molti fanno. Ancora peggio quando su “certi siti” si vedono collegati addirittura i driver motori a due pin che generano frequenze diverse (per esempio ho visto in giro qualcuno che sul suo fantastico sito di elettronica di terza mano collegava i motori ai pin 3 e 5, che hanno due frequenze diverse). Il sistema per poter variare le frequenze alle quali lavorano i moduli Output Compare dell’ATmega328 è quello di agire nei registri di configurazione dei Timer che li controllano.

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 Timer0, produrranno risultati del tutto inaspettati.

I Timers (che nei documenti Atmel vengono più propriamente chiamati Timer/Counter) sull’ATmega328 hanno due registri di controllo: TCCRxA e TCCRxB (dove x è 0,1 o 2). Il registro A controlla più propriamente le modalità operative dei Timer (normale, Fast PWM, Phase-Correct PWM ecc, per le quali vi rimando ai link a fine articolo). Il registro B consente, tra le altre cose, di impostare il prescaler agendo sui bit 0,1 e 2, chiamati CS0, CS1 e CS2

La soluzione più rapida di tutte è quella di modificare unicamente il prescaler dei Timer lasciando invariate tutte le altre impostazioni, agendo nei registri TCCRxB, utilizzando la seguente comoda funzione disponibile sul playground di Arduino:

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;
  }
}

A dispetto di quello che potrebbe far capire il nome della funzione (setPwmFrequency), non andiamo ad impostare direttamente la frequenza del PWM bensì il divisore del prescaler 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.

I valori 32 e 128 per i pin 5,6,9 e 10, non sono disponibili perchè i Timers/Counter 0 e 1 hanno due impostazioni per i bit CS0÷CS2 che prevedono il clocking dei timer da una sorgente esterna, opzione non disponibile per il Timer/Counter2 che in compenso ha due valori di divisore in più. Vedi datasheet completo nei link.

Pilotaggio CENT4UR™ con Arduino

Ho detto prima che non intendo modificare la frequenza del PWM sui pin 6 e 5 dato che potrei avere bisogno di funzioni di ritardo o di utilizzare servocomandi. Per pilotare i miei motori userò la minima frequenza ultrasonica disponibile su Arduino: 31250Hz impostando il prescaler a 1 sul Timer/Counter1 (pin 9 e 10)

PWM Arduino a 31,25KHz

A tal scopo, facendo uso della funzione sopra esposta, nel setup imposto i pin per il PWM nel seguente modo:

// PWM sui pin 9 e 10 a 31,2KHz
setPwmFrequency(9, 1);
setPwmFrequency(10, 1);

Non c’è bisogno di impostare i pin del PWM come uscite dal momento che la funzione analogWrite già esegue questa operazione ogniqualvolta viene richiamata. Lo schema di collegamento per poter sfruttare l’esempio, allegato a fine articolo, con CENT4UR™ è il seguente:

Esempio collegamento CENT4UR su Arduino. Parte del disegno è stata realizzata con Fritzing
  • GND -> GND
  • PWA -> 9
  • PWB -> 10
  • SENA -> A1
  • SENB -> A2

Utilizzare quindi un trimmer o un potenziometro lineare da 10K e collegare il polo centrale su A0 di Arduino e le estremità a 5V e GND. La batteria (o la tensione di alimentazione di CENT4UR™/motori) va ovviamente scelta in base alla tensione di funzionamento dei motori. Fate riferimento al manuale operativo di CENT4UR™ per maggiori informazioni.

Ruotando il potenziometro si varia il valore di duty cycle. La lettura analogica fornisce un valore a 10bit (da 0 a 1023) mentre il valore di duty cyle è a 8 bit (da 0 a 255), per cui il valore letto dal potenziometro viene scalato a 8 bit utilizzando la funzione map di Arduino. Si passa dal valore 0 (motori a massima velocità in un verso), al valore 128 (motori fermi) per arrivare al valore 255 (motori a massima velocità nel verso opposto).

Aprendo il terminale seriale, impostato alla velocità di default di 9600bps, è possibile leggere il valore di duty cycle e l’assorbimento dei motori espresso in mA

CENT4UR™ è un driver motori di piccola/media potenza. Se ci tieni a settorezero.com, richiedi un PCB di CENT4UR™ e contribuisci a mantenere settorezero.com sempre attivo.

Downloads

Links

 

Se questo articolo ti è piaciuto, condividilo su un social:
Se l'articolo ti è piaciuto o ti è stato utile, potresti dedicare un minuto a leggere questa pagina, dove ho elencato alcune cose che potrebbero farmi contento? Grazie :)