ESP32使用LED模块输出pwm脉冲时注意事项

本文详细解析了LEDC模块中PWM频率设置的旧算法与新算法,对比了两种算法在计算PWM频率和分辨率时的不同处理方式。强调了LEDC_LOW_SPEED_MODE模式下只能使用8M时钟的限制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

输出频率和分辨率要符合一定的规则:一定要注意!

旧算法:

esp_err_t ledc_set_freq(ledc_mode_t speed_mode, ledc_timer_t timer_num, uint32_t freq_hz)
{
    LEDC_ARG_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "speed_mode");
    portENTER_CRITICAL(&ledc_spinlock);
    esp_err_t ret = ESP_OK;
    uint32_t clock_divider = 0;
    uint32_t duty_resolution = LEDC.timer_group[speed_mode].timer[timer_num].conf.duty_resolution;
    uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[timer_num].conf.tick_sel;
    uint32_t precision = (0x1 << duty_resolution);
    if (timer_source_clk == LEDC_APB_CLK) {
        clock_divider = ((uint64_t) LEDC_APB_CLK_HZ << 8) / freq_hz / precision;
    } else {
        clock_divider = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision;
    }
    if (clock_divider <= 256 || clock_divider > LEDC_DIV_NUM_HSTIMER0) {
        ESP_LOGE(LEDC_TAG, "div param err,div_param=%u", clock_divider);
        ret = ESP_FAIL;
    }
    LEDC.timer_group[speed_mode].timer[timer_num].conf.clock_divider = clock_divider;
    ledc_ls_timer_update(speed_mode, timer_num);
    portEXIT_CRITICAL(&ledc_spinlock);
    return ret;
}

 

 

新算法:

static esp_err_t ledc_set_timer_div(ledc_mode_t speed_mode, ledc_timer_t timer_num, ledc_clk_cfg_t clk_cfg, int freq_hz, int duty_resolution)
{
    uint32_t div_param = 0;
    uint32_t precision = ( 0x1 << duty_resolution );
    ledc_clk_src_t timer_clk_src = LEDC_APB_CLK;

    // Calculate the divisor
    // User specified source clock(RTC8M_CLK) for low speed channel
    if ((speed_mode == LEDC_LOW_SPEED_MODE) && (clk_cfg == LEDC_USE_RTC8M_CLK)) {
        if(s_ledc_slow_clk_8M == 0) {
            if (ledc_slow_clk_calibrate() == false) {
                goto error;    
            }
        }
        div_param = ( (uint64_t) s_ledc_slow_clk_8M << 8 ) / freq_hz / precision;
    } else {
        // Automatically select APB or REF_TICK as the source clock.
        if (clk_cfg == LEDC_AUTO_CLK) {
            // Try calculating divisor based on LEDC_APB_CLK
            div_param = ( (uint64_t) LEDC_APB_CLK_HZ << 8 ) / freq_hz / precision;
            if (div_param > LEDC_DIV_NUM_HSTIMER0_V) {
                // APB_CLK results in divisor which too high. Try using REF_TICK as clock source.
                timer_clk_src = LEDC_REF_TICK;
                div_param = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision;
            } else if (div_param < 256) {
                // divisor is too low
                goto error;
            }
        // User specified source clock(LEDC_APB_CLK_HZ or LEDC_REF_TICK)
        } else {
            timer_clk_src = (clk_cfg == LEDC_USE_APB_CLK) ? LEDC_APB_CLK : LEDC_REF_TICK;
            uint32_t sclk_freq = (clk_cfg == LEDC_USE_APB_CLK) ? LEDC_APB_CLK_HZ : LEDC_REF_CLK_HZ;
            div_param = ( (uint64_t) sclk_freq << 8 ) / freq_hz / precision;
        }
    }
    if (div_param < 256 || div_param > LEDC_DIV_NUM_LSTIMER0_V) {
        goto error;
    }
    // For low speed channels, if RTC_8MCLK is used as the source clock, the `slow_clk_sel` register should be cleared, otherwise it should be set.
    if (speed_mode == LEDC_LOW_SPEED_MODE) {
        LEDC.conf.slow_clk_sel = (clk_cfg == LEDC_USE_RTC8M_CLK) ? 0 : 1;
    }
    //Set the divisor
    ledc_timer_set(speed_mode, timer_num, div_param, duty_resolution, timer_clk_src);
    // reset the timer
    ledc_timer_rst(speed_mode, timer_num);
    return ESP_OK;
error:
    ESP_LOGE(LEDC_TAG, "requested frequency and duty resolution can not be achieved, try reducing freq_hz or duty_resolution. div_param=%d",
        (uint32_t ) div_param);
    return ESP_FAIL;
}
 

 

esp_err_t ledc_timer_config(const ledc_timer_config_t* timer_conf)
{
    LEDC_ARG_CHECK(timer_conf != NULL, "timer_conf");
    uint32_t freq_hz = timer_conf->freq_hz;
    uint32_t duty_resolution = timer_conf->duty_resolution;
    uint32_t timer_num = timer_conf->timer_num;
    uint32_t speed_mode = timer_conf->speed_mode;
    LEDC_ARG_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "speed_mode");
    LEDC_ARG_CHECK(!((timer_conf->clk_cfg == LEDC_USE_RTC8M_CLK) && (speed_mode != LEDC_LOW_SPEED_MODE)), "Only low speed channel support RTC8M_CLK");
    periph_module_enable(PERIPH_LEDC_MODULE);
    if (freq_hz == 0 || duty_resolution == 0 || duty_resolution >= LEDC_TIMER_BIT_MAX) {
        ESP_LOGE(LEDC_TAG, "freq_hz=%u duty_resolution=%u", freq_hz, duty_resolution);
        return ESP_ERR_INVALID_ARG;
    }
    if (timer_num > LEDC_TIMER_3) {
        ESP_LOGE(LEDC_TAG, "invalid timer #%u", timer_num);
        return ESP_ERR_INVALID_ARG;
    }
    return ledc_set_timer_div(speed_mode, timer_num, timer_conf->clk_cfg, freq_hz, duty_resolution);
}

注意:LEDC_LOW_SPEED_MODE模式只支持8M时钟。

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值