Programming The ESP32 In C - PWM First Example
Written by Harry Fairhead   
Wednesday, 05 February 2025
Article Index
Programming The ESP32 In C - PWM First Example
ESP32 PWM
Duty Cycle and Phase
A First Example
Changing the PWM

Duty Cycle and Phase

The software lets you set the duty cycle resolution in terms of the number of bits, the duty cycle as a count and hpoint as a count. This makes it slightly difficult to see what is going on and in particular why you might want to set hpoint at all. Suppose you select duty_res to be 8 bits, that makes the timer roll over at 28-1= 255 and if you set the hpoint to 0 then the output goes high as soon as the timer starts counting. If you set the duty cycle to 127, then lpoint, computed as hpoint+duty cycle is 127. The result is the output goes high at count=0, goes low at count=127 and goes high at the rollover after 255. This is an output with a 50% duty cycle.

 pwmledc5

Now consider what happens if you set hpoint to 127. Now when the count starts the output stays low until the count reaches 127. The lpoint, again computed as hpoint+duty cycle is now 127+127, i.e. 254, which means the output goes low right at the end of the count, just before the rollover.

pwmledc6

You can see that moving the hpoint has not affected the duty cycle, but has changed where the pulse starts going high. That is, hpoint sets the phase. If you set hpoint too close to the overflow, the lpoint that completes the pulse will be in the next clock cycle. That is, the position of lpoint is given by:

lpoint = (duty cycle + hpoint) mod  overflow count

This means you can change the phase from zero to 360 degrees. Notice that PWM channels always start from a low state and this means if hpoint isn’t 0 the very first low may be longer than the following pulses. You can see an example of this later.

Setting up the PWM

Setting up a PWM source has two steps, configuring a timer and then configuring a channel. For the former you can use:

ledc_timer_config(ptimer_conf)

where ptimer_config is a pointer to a struct, timer_config, with the following fields:

  • speed_mode One of:

				LEDC_HIGH_SPEED_MODE
				LEDC_LOW_SPEED_MODE
		 		LEDC_SPEED_MODE_MAX
  • duty_resolution Either LEDC_TIMER_n_BIT,
    where n is 1 to 20 or LEDC_TIMER_BIT_MAX

  • timer_num Number between 0 and LEDC_TIMER_MAX-1

  • freq_hz Timer frequency in Hz

  • clk_cfg Source clock, one of:

LEDC_REF_TICK (1MHz)

LEDC_APB_CLK (80MHz)

LEDC_SCLK (8MHz)

LEDC_AUTO_CLK auto select

  • deconfigure If true deconfigure the timer, pausing the timer first

Once you have a timer setup the next step is to configure a channel using:

ledc_channel_config(pchan_conf)

where pchan_conf is a pointer to a struct with the following fields:

  • gpio_num GPIO line to use for the output

  • speed_mode One of:

			LEDC_HIGH_SPEED_MODE
			LEDC_LOW_SPEED_MODE	
  • channel A number between 0 and LEDC_CHANNEL_MAX-1

  • intr_type Fade interrupt enable or disable

  • timer_sel Timer source for channel
       (0 - LEDC_TIMER_MAX-1)

  • duty Range of settings is [0, (2**duty_resolution)]

  • hpoint Range is [0, (2**duty_resolution)-1]

  • flags.output_invert Enable (1) or disable (0) GPIO output

 



Last Updated ( Wednesday, 05 February 2025 )