giovedì 18 agosto 2022

ESP32 PWM PIN

ESP32 PWM PINS

Con Arduino IDE e la scheda ESP32 la funzione analogWrite() lavora in modo standard, tuttavia disponiamo di nuove funzioni più flessibili che ci permettono di configurare parametri come la frequenza e risoluzione del segnale PWM generato. 

Se una frequenza di 1000Hz e risoluzione di 8-bit ci bastano possiamo usare analogWrite() come abbiamo sempre fatto. In caso contrario qui a seguire vediamo le nuove API per configurare e gestire il generatore di PWM. 

Quali sono i pins PWM su ESP32?

A differenza delle board arduino UNO, NANO, MEGA ecc che hanno un numero limitato di pin PWM, su ESP32 tutti i pin possono essere collegati ad un canale PWM, tranne quei pin specificati "only input" che sono D35(GPIO35), D34(GPIO34), VN(GPIO39) e VP(GPIO36). Abbiamo a disposizione però solo 16 canali PWM e ci sono 21 pins ed ognuno lo possiamo assegnare ai 16 canali del generatore PWM. Come vedremo è possibile assegnare ad un canale PWM più di un pin se necessario.

Le API ledc

Tre sono le principali funzioni da conoscere: ledcSetup(), ledcAttachPin() e ledcWrite(). Qui di seguito sono documentate tutte con tanto di esempio al simulatore online wokwi.

ledcSetup()

Questa funzione è da chiamare nel setup() per configurare il generatore PWM. Essa prende 3 argomenti descritti di seguito:

prototipo:
double ledcSetup(uint8_t ch, double freq, uint8_t res)
  • ch - è il canale, ci sono 16 canali da 0÷15 per ESP32.
  • freq - è la frequenza del segnale PWM.
  • res - è la risoluzione espressa in bit: 8, 9, 10 ecc. 1÷14-bit (1÷20-bit per ESP32).
  • Restituisce la frequenza freq se la configurazione è andata a buon fine, diversamente restituisce 0 ad indicare che nulla è stato configurato.

Esempio: 

ledcSetup(0, 1000, 8);  // canale 0, f = 1000Hz, risoluzione 8-bit

ledcAttachPin()

Tutti i pin possono generare un segnle PWM, ad eccezione di: GPIO34, GPIO35, GPIO36 e GPIO39. Questa funzione serve ad assegnare il pin al canale ch. Vedi anche ledcDetachPin().

prototipo:
void ledcAttachPin(uint8_t pin, uint8_t ch)

  • pin - qualunque pin ad eccezione di: GPIO34, GPIO35, GPIO36 e GPIO39.
  • ch è il canale usato prima nella funzione ledcSetup()

Esempio: 

ledcAttachPin(10, 0);  // pin 10 assegnato al canale 0
ledcAttachPin(11, 0);  // pin 11 anche esso assegnato al canale 0

Questo esempio mette in evidenza il fatto che più pin possono essere associati ad un canale.

ledcWrite()

Attenzione che adesso per modificare il pwm non dobbiamo agire sul pin ma sul canale che abbiamo in precedenza associato al pin tramite la funzione ledcAttachPin().

prototipo:
void ledcWrite(uint8_t ch, uint32_t pwmValue)
  • ch è il canale a cui  abbiamo associato il pin.
  • pwmValue è il valore che vogliamo dare al PWM, sarà nel range 0÷255 se abbiamo configurato la risoluzione a 8-bit.

Esempio: 

ledcWrite(0, 127);  // assegna un PWM di valore 127 al canale 0

ledcDetachPin()

Dopo avere scollegato il pin dal canale PWM lo possiamo usare normalmente come un GPIO. Vedi ledcAttachPin().

prototipo:
void ledcDetachPin(uint8_t pin)
  • pin è il pin da scollegare dal canale a cui era connesso.

Esempio: 

ledcDetachPin(10);  // scollega pin 10 dal canale a cui era connesso

ledcChangeFrequency()

Questa funzione è simile a ledcSetup(). Essa permette di modificare la frequenza e risoluzione del segnale PWM generato dal canale ch:

prototipo:
double ledcChangeFrequency(uint8_t ch, double freq, uint8_t res)
  • ch - è il canale, ci sono 16 canali da 0÷15 per ESP32.
  • freq - è la frequenza del segnale PWM.
  • res - è la risoluzione espressa in bit: 8, 9, 10 ecc. 1÷14-bit (1÷20-bit per ESP32).
  • Restituisce la frequenza freq se la configurazione è andata a buon fine, diversamente restituisce 0 ad indicare che nulla è stato configurato.

Esempio: 

double result = ledcChangerequency(0, 1000, 10);  // canale 0, f = 1000Hz, risoluzione 10-bit
if (!result) Serial.println("change freq error");

ledcRead()

La funzione è l'opposto di ledcWrite(). Essa restituisce il valore del PWM assegnato al canale ch tramite ledcWrite().

prototipo:
uint32_t ledcRead(uint8_t ch)

ledcReadFreq()

La funzione restituisce la frequenza a cui lavora il canale ch configurato in precedenza tramite ledcSetup() o ledcChangeFrequency()

prototipo:
double ledcReadFreq(uint8_t ch)

ledcWriteTone()

Questa funzione genera un segnale PWM sul canale ch, alla frequenza freq con un duty cycle del 50%.

prototipo:
double ledcWriteTone(uint8_t ch, double freq)
  • ch - è il canale, ci sono 16 canali da 0÷15 per ESP32.
  • freq - è la frequenza del segnale PWM.
  • Restituisce la frequenza freq se la configurazione è andata a buon fine, diversamente restituisce 0 ad indicare che nulla è stato configurato.

Esempio: 

double result = ledcWriteTone(0, 1000);  // canale 0, f = 1000Hz,
if (!result) Serial.println("change freq error");

ledcWriteNote() 

Questa funzione opera in modo simile alla funzione ledcWriteTone() ma al posto di specificare la frequenza specifichiamo la nota e l'ottava. 

note_t type:
typedef enum {
         NOTE_C, NOTE_Cs, NOTE_D, NOTE_Eb, NOTE_E, NOTE_F, NOTE_Fs, NOTE_G,             NOTE_Gs, NOTE_A, NOTE_Bb, NOTE_B
} note_t;
 
prototipo:
double ledcWriteNote(uint8_t ch, note_t note, uint8_t oct)
  • ch - è il canale, ci sono 16 canali da 0÷15 per ESP32.
  • note - è una delle note specificate in note_t type.
  • oct è l'ottava tra 1 e 8.
  • Restituisce la frequenza freq se la configurazione è andata a buon fine. diversamente restituisce 0 ad indicare che nulla è stato configurato.

Esempio: 

double result = ledcWriteTone(0, NOTE_C, 1);  // canale 0, DO prima ottava
if (!result) Serial.println("change freq error"); 

Uno sketch di esempio

Lo sketch di esempio si limita ad incrementare il pwm disponibile sui pins 19 e 21 entrambe assegnati al canale 0. Ricordiamo che al massimo ci sono 16 canali disponibile per cui al massimo possiamo avere 16 pin PWM indipendenti. Per comodità il codice viene scritto nella funzione setup() e voi dovete solo aggiungere la funzione loop() vuota.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
 #define LEDBLUE   21  
 #define LEDRED    19  
     
 void setup() {
      Serial.begin(115200);
      double result = ledcSetup(0, 875, 11); // ch 0, f 875Hz, res 11-bit
      if (!result) Serial.println("errore");
      Serial.println(result);
     
      ledcAttachPin(LEDRED, 0); // attach LEDRED on ch 0
      ledcAttachPin(LEDBLUE, 0); // attach LEDBLUE on ch 0
     
      for (uint16_t i=0; i<2048; i++) {
          delay(15);
          ledcWrite(0, i);
          if (i == 2047) Serial.println("Ok");
      }
}

La riga 6 configura il canale 0 del generatore PWM. La riga 7 verifica che la configurazione richiesta sia andata a buon fine, diversamente stampa la scritta errore. La riga 8 stampa la variabile result che in questo caso vale 874 anziché 875Hz come specificato. Alle righe 10 e 11 assegniamo al canale 0 i pin 19 e 21.

Il ciclo for (riga 13÷17) incrementa la variabile i ogni 15 millesimi di secondo, fino a che il valore di i è uguale al massimo valore permesso dalla risoluzione a 11-bit cioè 2047 e quindi stampa OK sul terminale. 

Lo sketch al simulatore wokwi lo trovate cliccando il seguente link: esp32_pwm.ino.

Licenza Creative Commons
Quest'opera è distribuita con Licenza Creative Commons Attribuzione - Condividi allo stesso modo 4.0 Internazionale