espressif / esp-idf

Espressif IoT Development Framework. Official development framework for Espressif SoCs.
Apache License 2.0
13.7k stars 7.3k forks source link

ESP32-S3 MCPWM has a separate prescaler for each timer, buf IDF code doesn't use them. (IDFGH-13989) #14815

Open eriksl opened 4 days ago

eriksl commented 4 days ago

Answers checklist.

IDF version.

5.3.1

Espressif SoC revision.

ESP32-S3 0.1

Operating System used.

Linux

How did you build your project?

Command line with idf.py

If you are using Windows, please specify command line type.

None

Development Kit.

LilyGO S3 T7

Power Supply used.

USB

What is the expected behavior?

I expected to be able to run different timers within one MCPWM group to be able to run at different frequencies. I have selected APB frequency for timer0 and APB / 16 for timer1, so there should be no problem. The technical reference manual says (register overview) there is a common prescaler for all timers and a specific prescaler for each timer, so it should be possible. This is also what the text says, although the diagram of the MCPWM module is indecisive on this matter.

What is the actual behavior?

These reports and abort called:

E (10) mcpwm: mcpwm_set_prescale(242): group prescale conflict, already is 2 but attempt to 1
E (10) mcpwm: mcpwm_new_timer(125): set prescale failed

Besides the first report being very cryptic, it looks like the code simply doesn't take the timer-specific prescaler into account. Or there is none and the documentation is incorrect.

Steps to reproduce.

  1. create a timer on mcpwm group 0, frequency is 80 MHz (1x APB prescaler)
  2. create a second timer on mcpwm group, frequency is 5 MHz (16x APB prescaler)

Nothing more required than this.

When you create both timers with the same frequencies it works. If the timers are on separate groups, it works as well.

Debug Logs.

E (10) mcpwm: mcpwm_set_prescale(242): group prescale conflict, already is 2 but attempt to 1
E (10) mcpwm: mcpwm_new_timer(125): set prescale failed

More Information.

I am forced to use MCPWM for my lighting ("LED") purposes as the "LEDPWM" has a resolution of only 14 bits, which is really too little. I don't understand why the "fast LEDPWM" channels where dropped (with the high resolution timers). I am now investigating ways to achieve similar PWM resolutions. The 16 bit width of MCPWM is step one, but I'd really like even more. I am thinking of having the cpu updating the PWM "value" on the fly after one or more timer overflow interrupts, but it sounds complex to implement. On the other hand, it may even be done on the LEDPWM module.

Other possible paths:

I am interested in any interesting hints to workaround this omission.

suda-morris commented 3 days ago

Didn't reproduce the issue with the following init code:

TEST_CASE("mcpwm timer with different resolutions", "[mcpwm]")
{
    mcpwm_timer_config_t timer_config = {
        .group_id = 0,
        .clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
        .resolution_hz = 80 * 1000 * 1000,
        .period_ticks = 20 * 1000,
        .count_mode = MCPWM_TIMER_COUNT_MODE_UP,
    };
    mcpwm_timer_handle_t timer0, timer1;
    printf("create mcpwm timers\r\n");
    TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer0));
    timer_config.resolution_hz = 5 * 1000 * 1000;
    TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer1));

    printf("delete timers\r\n");
    TEST_ESP_OK(mcpwm_del_timer(timer0));
    TEST_ESP_OK(mcpwm_del_timer(timer1));
}
eriksl commented 3 days ago

That's interesting. I have tested it more than once. I have run the same code on one and on two mcpwm groups, using two groups it works like a charm, on one group it doesn't.

eriksl commented 3 days ago

I think I know where's the difference. I am always using a 16 bit (max) resolution timer.

Please try with:

mcpwm_timer_config_t timer_config =
{
    .group_id = 0,
    .clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
    .resolution_hz = FREQUENCY,
    .count_mode = MCPWM_TIMER_COUNT_MODE_UP,
    .period_ticks = TIMER_TICKS,
    .intr_priority = 0,
    .flags =
    {
        .update_period_on_empty = 0,
        .update_period_on_sync = 0,
    },
};

where either

This is where the problem occurs.

suda-morris commented 1 day ago

@eriksl this can be workaround by set the higher resolution first then the smaller resolution.

e.g.

TEST_CASE("mcpwm timer with different resolutions", "[mcpwm]")
{
    mcpwm_timer_config_t timer_config = {
        .group_id = 0,
        .clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT,
        .resolution_hz = 160 * 1000 * 1000,
        .period_ticks = 65535,
        .count_mode = MCPWM_TIMER_COUNT_MODE_UP,
    };
    mcpwm_timer_handle_t timer0, timer1;
    printf("create mcpwm timers\r\n");
    TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer0));
    timer_config.resolution_hz = 10 * 1000 * 1000;
    TEST_ESP_OK(mcpwm_new_timer(&timer_config, &timer1));

    printf("delete timers\r\n");
    TEST_ESP_OK(mcpwm_del_timer(timer0));
    TEST_ESP_OK(mcpwm_del_timer(timer1));
}