Page 1 of 2

LED controller PWM timer compare values

Posted: Sat Jun 11, 2022 2:23 pm
by dg9ngf
Hello,

As I've seen it, the LED controller sets up PWM signals on up to 8 output channels. All channels turn on at the beginning of the shared PWM period, so all simultaneously, and then turn off when their individual compare value has been reached in the internal timer.

Is there a way to turn on channels at different times, or compare values? Or at least invert the output so that a channel isn't on during the time from LOW to CMP, but from CMP to HIGH.

I'd like to distribute the channel on-times more evenly to even the load on the PSU and have a less flickering light. One of my main scenarios is a dual-white (CCT) lighting that combines cold and warm white with 2 channels. When the light is on at maximum single brightness (100% of one channel), it doesn't flicker at all. But if it's distributed over two colours, it starts flickering again. This can be avoided if both colours (channels) simply alternate instead of partially overlap. Then it's never dark, just the colour changes very quickly according to the desired colour temperature.

I can achieve the output inverting on AVR MCUs with a flag and it works very well. But I'm using an ESP32 for a bigger application with more channels.

Re: LED controller PWM timer compare values

Posted: Sun Jun 12, 2022 12:44 am
by username
Just checking, have you read through this ?
https://docs.espressif.com/projects/esp ... ntrol-ledc

Re: LED controller PWM timer compare values

Posted: Sun Jun 12, 2022 12:22 pm
by Sprite
For what it's worth, you can invert the GPIOs used in the GPIO matrix to get the inversion effect you want. You could also have a look at the MCPWM peripheral; while it has less outputs, it does have phase angle features.

Re: LED controller PWM timer compare values

Posted: Sun Jun 12, 2022 2:53 pm
by dg9ngf
Yes, I've read that before and looked at it again. Do you have a certain part of that page in mind? Because it doesn't anwer my question.

MCPWM (Motor PWM) has only 3 channels? That's as much as the ATtiny1614 has. I'm using ESP32 for the 8 channels.

So I assume that it's not possible to use custom compare values. But the output inverting is already a good solution. Is it this?

Code: Select all

GPIO.func_out_sel_cfg[my_gpio_num].inv_sel = invert ? 1 : 0;

Re: LED controller PWM timer compare values

Posted: Mon Jun 13, 2022 2:53 am
by Sprite
The ESP32 has two MCPWM units, so I think you should get 6 channels. (Which is still less than 8, to be fair.)
Yes, that code would do the trick I think.

Re: LED controller PWM timer compare values

Posted: Tue Jun 14, 2022 11:23 am
by dg9ngf
So it does work, but initialisation is not smooth. Depending on the order of instructions, the inverted channels will briefly flash up at boot, or they will remain on, and some are even different than the others.

Code: Select all

ledc_channel.channel = LEDC_CHANNEL_6;
ledc_channel.gpio_num = LED6_GPIO;
ledc_channel_config(&ledc_channel);

GPIO.func_out_sel_cfg[LED7_GPIO].inv_sel = 1;
ledc_channel.channel = LEDC_CHANNEL_7;
ledc_channel.gpio_num = LED7_GPIO;
ledc_channel_config(&ledc_channel);
ledc_set_duty(LEDC_HIGH_SPEED_MODE, 7, PWM_MAXDUTY);
What can I do to keep all LEDs off at all times during initialisation and use the inverted channels normally later? I will provide corresponding inverted duty values, the initial value should be the maximum so that the pin remains completely off at startup.

Re: LED controller PWM timer compare values

Posted: Tue Jun 14, 2022 12:32 pm
by dg9ngf
This is a version that sets the correct initial state, but the short flash of all inverted channels remains. This may sound small but it feels frightening if a powerful room light does that in the dark.

Code: Select all

// Configure LED timers
ledc_timer_config_t ledc_timer = {
	.duty_resolution = PWM_RESOLUTION,
	.freq_hz = 2500,
	.speed_mode = LEDC_HIGH_SPEED_MODE,
	.timer_num = LEDC_TIMER_0,
	.clk_cfg = LEDC_AUTO_CLK,
};
ledc_timer_config(&ledc_timer);

// Configure LED channels
ledc_channel_config_t ledc_channel = {
	.channel    = LEDC_CHANNEL_0,
	.duty       = 0,
	.gpio_num   = LED0_GPIO,
	.speed_mode = LEDC_HIGH_SPEED_MODE,
	.hpoint     = 0,
	.timer_sel  = LEDC_TIMER_0
};
ledc_channel_config(&ledc_channel);

// Other channels...

ledc_channel.channel = LEDC_CHANNEL_6;
ledc_channel.gpio_num = LED6_GPIO;
ledc_channel.duty = 0;
ledc_channel_config(&ledc_channel);

ledc_channel.channel = LEDC_CHANNEL_7;
ledc_channel.gpio_num = LED7_GPIO;
ledc_channel.duty = PWM_MAXDUTY;
ledc_channel_config(&ledc_channel);
GPIO.func_out_sel_cfg[LED7_GPIO].inv_sel = 1;

Re: LED controller PWM timer compare values

Posted: Wed Jun 15, 2022 1:35 am
by Sprite
You could possibly get around this by initializing all channels to an unused GPIO, starting the LEDC channels (including inverting the ones you need inverted), then manually routing the signals to the correct GPIOs using something like this:

Code: Select all

        gpio_hal_iomux_func_sel(GPIO_PIN_MUX_REG[gpio], PIN_FUNC_GPIO);                                   
        gpio_set_direction(gpio, GPIO_MODE_OUTPUT);                                                       
        esp_rom_gpio_connect_out_signal(gpio, signal_idx, out_inv, oen_inv); 

Re: LED controller PWM timer compare values

Posted: Thu Sep 08, 2022 9:35 pm
by dg9ngf
Sorry for the long delay, the project was stalled.

Could you please explain how to use the two functions?

I found the header for gpio_hal_iomux_func_sel here but can't figure out what the parameters mean.

I also found the header for esp_rom_gpio_connect_out_signal here but can't figure out what all parameters mean. First is the GPIO, third should probably be true for me. And the others?

Is this stuff documented anywhere or is it hidden internal magic that may change at any time?

And should these functions be called before or after ledc_channel_config? If some before/after, which when?

Re: LED controller PWM timer compare values

Posted: Fri Sep 09, 2022 12:39 am
by Sprite
Okay, so I typed a whole bunch of text here as an answer, which I'll still include as reference below, but then I noticed you can tell the LED driver to invert the channel by setting output_invert to true. I'll still include the answer as it's more generic and works for drivers other than the LEDC:
I found the header for gpio_hal_iomux_func_sel here but can't figure out what the parameters mean.
It sets up the IOMUX for the selected GPIO pin to make it act as a 'standard' GPIO. The IOMUX can be used to route some high-speed signals to the IO pin, but that's not what we want here. ('pin name' is GPIO number here, func is the function selected by the iomux)
I also found the header for esp_rom_gpio_connect_out_signal here but can't figure out what all parameters mean. First is the GPIO, third should probably be true for me. And the others?
This sets up the GPIO matrix. First argument is indeed the GPIO. Second one is the signal to route to that GPIO, you can get those from here. (The LEDC driver probably has that setup already, but the function still needs the proper signal as an argument.) Third is if the output needs to be inverted; you want that to be true. Fourth is if the output enable needs to be inverted; that should be false.

Is this stuff documented anywhere or is it hidden internal magic that may change at any time?

And should these functions be called before or after ledc_channel_config? If some before/after, which when?
That API should be stable, as it directly refers to the underlying hardware. You want to call it after configuring the driver, because otherwise the driver will overwrite the settings.