Search code examples
arduinoesp32platformioarduino-esp32

Can't set individual PWM frequencies on ESP32 with Arduino framework (on Platformio)


I'm trying to setup 4 different PWMs (on GPIOs 4, 25, 26, 27) on a ESP32 using Arduino framework. I can't setup the frequencies on GPIOs 25 and 26, those end up receiving the same frequencies as the other 2 (i.e. GPIO 25 oscillates at the same frequency as GPIO 4, and GPIO 26 oscillates the same frequency as GPIO 27). Relevant code parts:

on the header file:

#define DC_IO_PORTS_QUANTITY 4

typedef enum DC_IO_NUM
{
    DC_IO0,
    DC_IO1,
    DC_IO2,
    DC_IO3

} DC_IO_NUM;

typedef enum DC_IO_PIN
{
    DC_IO0_PIN = 4,
    DC_IO1_PIN = 25,
    DC_IO2_PIN = 26,
    DC_IO3_PIN = 27

} DC_IO_PIN;
//...
void DC_IO_Init(uint8_t resolution, uint16_t freq); //initializes all PWMs with the same frequency and resolution, with 50% duty-cycle
void Set_DC_IO_Freq(DC_IO_NUM dc_iox, uint16_t freq);
//...

on the source file:

const DC_IO_NUM DC_IO_NUM_TABLE[DC_IO_PORTS_QUANTITY]={DC_IO0, DC_IO1, DC_IO2, DC_IO3};
const DC_IO_PIN DC_IO_PIN_TABLE[DC_IO_PORTS_QUANTITY]={DC_IO0_PIN, DC_IO1_PIN, DC_IO2_PIN, DC_IO3_PIN};

uint16_t full_duty_cycle=1;
uint16_t old_duty_cycle[DC_IO_PORTS_QUANTITY]={0};
uint8_t old_res=0;

void DC_IO_Init(uint8_t resolution, uint16_t freq)
{
    uint16_t half_duty_cycle;

    old_res=resolution;

    uint16_t bit_val=1;
    for(uint8_t k=1;k<resolution;k++)
    {
        bit_val *= 2;
        full_duty_cycle += bit_val;
    }

    half_duty_cycle = full_duty_cycle/2; //50%

    for(uint8_t i=0; i<DC_IO_PORTS_QUANTITY; i++)
    {
        old_duty_cycle[i] = half_duty_cycle;

        ledcSetup(DC_IO_NUM_TABLE[i], freq, old_res);
        ledcWrite(DC_IO_NUM_TABLE[i], old_duty_cycle[i]);
        ledcAttachPin(DC_IO_PIN_TABLE[i], DC_IO_NUM_TABLE[i]);
    }
    vTaskDelay(10);
}
//...
void Set_DC_IO_Freq(DC_IO_NUM dc_iox, uint16_t freq)
{
    ledcSetup(dc_iox, freq, old_res);
    ledcWrite(dc_iox, old_duty_cycle[dc_iox]);
    ledcAttachPin(DC_IO_PIN_TABLE[dc_iox], DC_IO_NUM_TABLE[dc_iox]);
    vTaskDelay(10);
}
//...

on the main.cpp setup():

DC_IO_Init(16, 100); //all pwms on 100Hz 16bits
Set_DC_IO_Freq(DC_IO0,50); //PWM0:=GPIO4 on 50Hz
Set_DC_IO_Freq(DC_IO2,200); //PWM2:=GPIO26 on 200Hz
Set_DC_IO_Freq(DC_IO3,300); //PWM3:=GPIO27 on 300Hz

However, on the scope I can see that PWM1 is on 50Hz and PWM2 is on 300Hz...


Solution

  • According to the ESP32 Technical Reference Manual, ver 4.5, 2021, page 382:

    Figure 14-1 shows the architecture of the LED_PWM controller. As can be seen in the figure, the LED_PWM controller contains eight high-speed and eight low-speed channels. There are four high-speed clock modules for the high-speed channels, from which one h_timerx can be selected. There are also four low-speed clock modules for the low-speed channels, from which one l_timerx can be selected.

    (emphasis is mine)

    There's a nice picture which shows 4 timers going to a mux which drives 8 pwm outputs, for both slow and fast clocks.

    So, I think you're seeing the best you can get - two different PWM rates.