Slow PWM's with ESP32-S3

MCUism
Posts: 56
Joined: Mon Jul 15, 2019 9:25 am

Slow PWM's with ESP32-S3

Postby MCUism » Sat Jan 11, 2025 5:15 pm

Hi all,

I have ESP32-S3 and I am trying to generate some slow PWM's around 500Hz to 1Khz for a LED driver project. I am reading mixed things about number of PWM's possible, some say only 8 channels are possible but this github repo says 16 are possible https://github.com/khoih-prog/ESP32_PWM ... SP32S3_DEV

But datasheet says 8 PWM's + 2 MCPWMs. Which one is correct? I need atleast 9 PWM"S to be generated and I want to know is it really possible to generate 9 pwms on esp32-s3 WROOM specifically or not?

Any examples or references would be highly appreciated.

Thanks

MicroController
Posts: 1955
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Slow PWM's with ESP32-S3

Postby MicroController » Sat Jan 11, 2025 7:07 pm

The library you found uses timer interrupts to generate PWM signals in software.
The S3's LEDC hardware indeed has only 8 channels, the MCPWM has additional 3 channels (x2 outputs). So yes, generating 8+3=11 PWM signals via hardware is possible. (Even more if you're willing to (ab)use the RMT.)
The S3 also provides 8 "Sigma Delta Modulated Output" channels which enable you to generate up to 8 PDM outputs at a resolution of 8 bits (effectively approximating an 8-bit DAC output).

MCUism
Posts: 56
Joined: Mon Jul 15, 2019 9:25 am

Re: Slow PWM's with ESP32-S3

Postby MCUism » Sat Jan 11, 2025 8:05 pm

Thanks alot for the response, so I'll use the all 8 native PWMs and then I'll use one PDM channel using this code https://github.com/espressif/esp-idf/bl ... ple_main.c

Can you also point me to an example if MCPWM can be used as a normal PWM signal?

would that be fine? Also I have another feature I need to implement, by adjusting the PWM's to my LED drivers I will arrive at a brightness or color right. Now I want to generate a 200mS flash of this color. What's the best way to switch off and on the PWM channels this quickly?

I tried with an arduino code, but looks like only if the delay is above 500mS I am able to see the PWM switching on and off, below this delay PWM is off. I'll have to test again but wanted to hear your recommendation.

mikemoy
Posts: 634
Joined: Fri Jan 12, 2018 9:10 pm

Re: Slow PWM's with ESP32-S3

Postby mikemoy » Sun Jan 12, 2025 6:47 am

Ran your question
an example if MCPWM can be used as a normal PWM signal?
through AI, and this is what It shot back.


Yes, you can use the MCPWM module to control a LED via PWM on the ESP32, even though MCPWM is typically used for motor control. When controlling a LED, you typically only care about generating a standard PWM signal to adjust the brightness of the LED.

Here’s how you can do this in the ESP-IDF framework:

Steps to control an LED using MCPWM
Initialize MCPWM and set it up for PWM control.

Configure a frequency for PWM (e.g., 1 kHz).
Set the duty cycle to control the brightness of the LED.
Connect the MCPWM output to the GPIO pin where your LED is connected (make sure the pin supports PWM functionality).

Adjust the duty cycle to control the brightness of the LED. A duty cycle of 0% means the LED is off, and 100% means the LED is fully on.

Example Code to control an LED using MCPWM
Here’s a simplified example of controlling an LED with PWM using the MCPWM module in ESP-IDF:

Code: Select all

#include "driver/mcpwm.h"
#include "esp_system.h"
#include "driver/gpio.h"

#define LED_GPIO_PIN 18 // The GPIO pin where the LED is connected

void app_main() {
    // Initialize MCPWM for PWM output
    mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, LED_GPIO_PIN); // Set GPIO18 as MCPWM0A

    // Configure the MCPWM parameters (frequency and duty cycle)
    mcpwm_config_t pwm_config;
    pwm_config.frequency = 1000;          // 1kHz PWM frequency
    pwm_config.duty_cycle_a = 50;         // 50% duty cycle for MCPWM0A (this will control brightness)
    pwm_config.duty_cycle_b = 0;          // No signal on MCPWM0B (not used)
    pwm_config.motor_direction = MCPWM_DIR_CW; // Not necessary for LED, just a placeholder
    mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); // Initialize MCPWM

    // Now the LED should be blinking with 50% brightness at 1kHz frequency

    // If you want to dynamically change the brightness:
    while (1) {
        // Gradually increase and decrease the LED brightness
        for (int duty = 0; duty <= 100; duty++) {
            mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, duty); // Change duty cycle
            vTaskDelay(10 / portTICK_PERIOD_MS);
        }

        for (int duty = 100; duty >= 0; duty--) {
            mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, duty); // Change duty cycle
            vTaskDelay(10 / portTICK_PERIOD_MS);
        }
    }
}

MCUism
Posts: 56
Joined: Mon Jul 15, 2019 9:25 am

Re: Slow PWM's with ESP32-S3

Postby MCUism » Sun Jan 12, 2025 7:52 pm

mikemoy wrote:
Sun Jan 12, 2025 6:47 am
Ran your question
an example if MCPWM can be used as a normal PWM signal?
through AI, and this is what It shot back.

Code: Select all

#include "driver/mcpwm.h"
    // Now the LED should be blinking with 50% brightness at 1kHz frequency

    // If you want to dynamically change the brightness:
    while (1) {
        // Gradually increase and decrease the LED brightness
        for (int duty = 0; duty <= 100; duty++) {
            [b]mcpwm_set_duty[/b](MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, duty); // Change duty cycle
            vTaskDelay(10 / portTICK_PERIOD_MS);
        }

        for (int duty = 100; duty >= 0; duty--) {
           [b] mcpwm_set_duty[/b](MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, duty); // Change duty cycle
            vTaskDelay(10 / portTICK_PERIOD_MS);
        }
    }
}
Hahaha I already tried, looks like I could do it, but the code is not correct. According to the esp-idf API guide there's no API called mcpwm_set_duty. Thisexample tells to use mcpwm_set_comparator_value but I don't understand how to use it for my application. I'm kind of a noob to esp-idf sorry for basic questions.

MicroController
Posts: 1955
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Slow PWM's with ESP32-S3

Postby MicroController » Mon Jan 13, 2025 12:30 pm

A basic PWM generator uses a timer/counter and one (or two) comparators.
The timer/counter counts up to a certain max value, then resets to 0 and goes on counting up. The time it takes for the timer/counter to complete one of these cycles defines the PWM frequency.

The most simple way to create a PWM signal based on the timer/counter is by doing two things: 1. when the counter resets to 0, set an output to HIGH; 2. when the counter value matches the comparator's value, set the output to LOW.

Done in this way, the duty cycle of the PWM signal is defined by the comparator's set value between 0 (=0%) and the counter's max value (=100%).

With the MCPWM, you can 'build' your own PWM generator from building blocks: a timer/counter, a comparator, and two 'actions': One action is to set the output HIGH when the counter goes to 0 (it's "empty"), the other action is to set the output LOW when the counter matches the comparator.

MCUism
Posts: 56
Joined: Mon Jul 15, 2019 9:25 am

Re: Slow PWM's with ESP32-S3

Postby MCUism » Tue Jan 14, 2025 2:42 pm

Thanks for your guidance, for anyone who's coming here later here's the code that worked perfectly fine for me. Just load the code and press 'o' to start the pwm and press 'f' to stop the pwm. The duty cycle is 50%, you can change it when needed.

Code: Select all

#include "driver/mcpwm.h"

#define GPIO_PWM0A_OUT 22
const uint8_t doPress = GPIO_PWM0A_OUT;  // PWM

void setup() {
    Serial.begin(115200);
    
    mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, doPress);
    mcpwm_config_t pwm_config = {};
    pwm_config.frequency = 1000;
    pwm_config.cmpr_a = 0;
    pwm_config.cmpr_b = 0;
    pwm_config.counter_mode = MCPWM_UP_COUNTER;
    pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
    mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config);

}

static void pwm_set_duty(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num, float duty_cycle) {
  mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_B);
  mcpwm_set_duty(mcpwm_num, timer_num, MCPWM_OPR_A, duty_cycle);
  mcpwm_set_duty_type(mcpwm_num, timer_num, MCPWM_OPR_A, MCPWM_DUTY_MODE_0);
}

void loop() {

      if(Serial.available()>0)
      {
        char in = Serial.read();
        if(in == 'o')
        {
          pwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, 50);
        }
        if(in == 'f')
        {
          pwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, 0);
        }
      }

  }
 

Who is online

Users browsing this forum: No registered users and 58 guests