]> granicus.if.org Git - esp-idf/commitdiff
driver(ledc): fixed ledc clock selection bug.
authorkooho <2229179028@qq.com>
Thu, 1 Nov 2018 04:23:11 +0000 (12:23 +0800)
committerkooho <2229179028@qq.com>
Tue, 6 Aug 2019 08:11:18 +0000 (16:11 +0800)
components/driver/include/driver/ledc.h
components/driver/ledc.c
components/driver/test/test_ledc.c
components/driver/test/test_pcnt.c
examples/mesh/internal_communication/main/mesh_light.c
examples/mesh/manual_networking/main/mesh_light.c
examples/peripherals/ledc/main/ledc_example_main.c
examples/peripherals/pcnt/main/pcnt_example_main.c

index 17875783fb6bc77fedaf4632e5cddd29ac7445b3..4d5593abb76670a24336c7d16189f0e22340456a 100644 (file)
@@ -51,6 +51,13 @@ typedef enum  {
     LEDC_APB_CLK,      /*!< LEDC timer clock divided from APB clock (80Mhz) */
 } ledc_clk_src_t;
 
+typedef enum {
+    LEDC_AUTO_CLK,        /*!< The driver will automatically select the source clock(REF_TICK or APB) based on the giving resolution and duty parameter when init the timer*/  
+    LEDC_USE_REF_TICK,    /*!< LEDC timer select REF_TICK clock as source clock*/
+    LEDC_USE_APB_CLK,     /*!< LEDC timer select APB clock as source clock*/
+    LEDC_USE_RTC8M_CLK,   /*!< LEDC timer select RTC8M_CLK as source clock. Only for low speed channels and this parameter must be the same for all low speed channels*/
+} ledc_clk_cfg_t;
+
 typedef enum {
     LEDC_TIMER_0 = 0, /*!< LEDC timer 0 */
     LEDC_TIMER_1,     /*!< LEDC timer 1 */
@@ -125,6 +132,9 @@ typedef struct {
     };
     ledc_timer_t  timer_num;               /*!< The timer source of channel (0 - 3) */
     uint32_t freq_hz;                      /*!< LEDC timer frequency (Hz) */
+    ledc_clk_cfg_t clk_cfg;                /*!< Configure LEDC source clock.
+                                                For low speed channels and high speed channels, you can specify the source clock using LEDC_USE_REF_TICK, LEDC_USE_APB_CLK or LEDC_AUTO_CLK.
+                                                For low speed channels, you can also specify the source clock using LEDC_USE_RTC8M_CLK, in this case, all low speed channel's source clock must be RTC8M_CLK*/
 } ledc_timer_config_t;
 
 typedef intr_handle_t ledc_isr_handle_t;
index ff992d14740b1b4747796f4b48de9a9a0a84a29f..f82efaf5e3abb5ef281458551382ed0102e78d1e 100644 (file)
@@ -19,6 +19,7 @@
 #include "soc/gpio_periph.h"
 #include "driver/ledc.h"
 #include "soc/ledc_periph.h"
+#include "soc/rtc.h"
 #include "esp_log.h"
 
 static const char* LEDC_TAG = "ledc";
@@ -51,12 +52,17 @@ static ledc_isr_handle_t s_ledc_fade_isr_handle = NULL;
 #define LEDC_VAL_NO_CHANGE        (-1)
 #define LEDC_STEP_NUM_MAX         (1023)
 #define LEDC_DUTY_DECIMAL_BIT_NUM (4)
+#define DELAY_CLK8M_CLK_SWITCH    (5)
+#define SLOW_CLK_CYC_CALIBRATE    (13)
 #define LEDC_HPOINT_VAL_MAX       (LEDC_HPOINT_HSCH1_V)
 #define LEDC_FADE_TOO_SLOW_STR    "LEDC FADE TOO SLOW"
 #define LEDC_FADE_TOO_FAST_STR    "LEDC FADE TOO FAST"
 static const char *LEDC_FADE_SERVICE_ERR_STR = "LEDC fade service not installed";
 static const char *LEDC_FADE_INIT_ERROR_STR = "LEDC fade channel init error, not enough memory or service not installed";
 
+//This value will be calibrated when in use.
+static uint32_t s_ledc_slow_clk_8M = 0;
+
 static void ledc_ls_timer_update(ledc_mode_t speed_mode, ledc_timer_t timer_sel)
 {
     if (speed_mode == LEDC_LOW_SPEED_MODE) {
@@ -71,6 +77,23 @@ static IRAM_ATTR void ledc_ls_channel_update(ledc_mode_t speed_mode, ledc_channe
     }
 }
 
+//We know that CLK8M is about 8M, but don't know the actual value. So we need to do a calibration.
+static bool ledc_slow_clk_calibrate(void)
+{
+    //Enable CLK8M for LEDC
+    SET_PERI_REG_MASK(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_DIG_CLK8M_EN_M);
+    //Waiting for CLK8M to turn on
+    ets_delay_us(DELAY_CLK8M_CLK_SWITCH);
+    uint32_t cal_val = rtc_clk_cal(RTC_CAL_8MD256, SLOW_CLK_CYC_CALIBRATE);
+    if(cal_val == 0) {
+        ESP_LOGE(LEDC_TAG, "CLK8M_CLK calibration failed");
+        return false;
+    }
+    s_ledc_slow_clk_8M = 1000000ULL * (1 << RTC_CLK_CAL_FRACT) * 256 / cal_val;
+    ESP_LOGD(LEDC_TAG, "Calibrate CLK8M_CLK : %d Hz", s_ledc_slow_clk_8M);
+    return true;
+}
+
 static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel, ledc_intr_type_t type)
 {
     LEDC_ARG_CHECK(speed_mode < LEDC_SPEED_MODE_MAX, "speed_mode");
@@ -219,6 +242,60 @@ esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags,
     return ret;
 }
 
+// Setting the LEDC timer divisor with the given source clock, frequency and resolution.
+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");
@@ -227,6 +304,7 @@ esp_err_t ledc_timer_config(const ledc_timer_config_t* timer_conf)
     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);
@@ -236,38 +314,7 @@ esp_err_t ledc_timer_config(const ledc_timer_config_t* timer_conf)
         ESP_LOGE(LEDC_TAG, "invalid timer #%u", timer_num);
         return ESP_ERR_INVALID_ARG;
     }
-    esp_err_t ret = ESP_OK;
-    uint32_t precision = ( 0x1 << duty_resolution );  // 2**depth
-    // Try calculating divisor based on LEDC_APB_CLK
-    ledc_clk_src_t timer_clk_src = LEDC_APB_CLK;
-    // div_param is a Q10.8 fixed point value
-    uint64_t div_param = ( (uint64_t) LEDC_APB_CLK_HZ << 8 ) / freq_hz / precision;
-    if (div_param < 256) {
-        // divisor is too low
-        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);
-        ret = ESP_FAIL;
-    }
-    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;
-        if (div_param < 256 || div_param > LEDC_DIV_NUM_HSTIMER0_V) {
-            ESP_LOGE(LEDC_TAG, "requested frequency and duty resolution can not be achieved, try increasing freq_hz or duty_resolution. div_param=%d",
-                (uint32_t ) div_param);
-            ret = ESP_FAIL;
-        }
-    } else {
-        if (speed_mode == LEDC_LOW_SPEED_MODE) {
-            //for now, we only select 80mhz for slow clk of LEDC low speed channels.
-            LEDC.conf.slow_clk_sel = 1;
-        }
-    }
-    // set timer parameters
-    ledc_timer_set(speed_mode, timer_num, div_param, duty_resolution, timer_clk_src);
-    // reset timer
-    ledc_timer_rst(speed_mode, timer_num);
-    return ret;
+    return ledc_set_timer_div(timer_num, timer_num, timer_conf->clk_cfg, freq_hz, duty_resolution);
 }
 
 esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel)
index d0e35851d8251350ca2c6c677292030d16b929b9..b676c4232adaceb2535099b3aad386ec0839cfcd 100644 (file)
@@ -86,6 +86,7 @@ static void timer_frequency_test(ledc_channel_t channel, ledc_timer_bit_t timer_
         .bit_num = timer_bit,
         .timer_num = timer,
         .freq_hz = 5000,
+        .clk_cfg = LEDC_AUTO_CLK,
     };
     TEST_ESP_OK(ledc_channel_config(&ledc_ch_config));
     TEST_ESP_OK(ledc_timer_config(&ledc_time_config));
@@ -126,6 +127,7 @@ static void timer_duty_test(ledc_channel_t channel, ledc_timer_bit_t timer_bit,
         .bit_num = timer_bit,
         .timer_num = timer,
         .freq_hz = 5000,
+        .clk_cfg = LEDC_AUTO_CLK,
     };
     TEST_ESP_OK(ledc_channel_config(&ledc_ch_config));
     TEST_ESP_OK(ledc_timer_config(&ledc_time_config));
@@ -188,6 +190,7 @@ TEST_CASE("LEDC error log channel and timer config", "[ledc][test_env=UT_T1_LEDC
     ledc_time_config.duty_resolution = LEDC_TIMER_13_BIT;
     ledc_time_config.timer_num = LEDC_TIMER_0;
     ledc_time_config.freq_hz = 5000;
+    ledc_time_config.clk_cfg = LEDC_AUTO_CLK;
 
     ledc_timer_config_t temp_timer_config = ledc_time_config;
     TEST_ESP_OK(ledc_timer_config(&ledc_time_config));
@@ -228,6 +231,7 @@ TEST_CASE("LEDC normal channel and timer config", "[ledc][test_env=UT_T1_LEDC]")
         .bit_num = LEDC_TIMER_13_BIT,
         .timer_num = LEDC_TIMER_0,
         .freq_hz = 5000,
+        .clk_cfg = LEDC_AUTO_CLK,
     };
     ledc_timer_config_t temp_time_config = ledc_time_config;
 
@@ -297,6 +301,7 @@ TEST_CASE("LEDC timer set", "[ledc][test_env=UT_T1_LEDC]")
         .bit_num = 13,
         .timer_num = LEDC_TIMER_0,
         .freq_hz = 5000,
+        .clk_cfg = LEDC_AUTO_CLK,
     };
     TEST_ESP_OK(ledc_timer_config(&ledc_time_config));
 
@@ -347,6 +352,7 @@ TEST_CASE("LEDC timer pause and resume", "[ledc][test_env=UT_T1_LEDC]")
         .duty_resolution = LEDC_TIMER_13_BIT,
         .timer_num = LEDC_TIMER_0,
         .freq_hz = 5000,
+        .clk_cfg = LEDC_AUTO_CLK,
     };
     TEST_ESP_OK(ledc_timer_config(&ledc_time_config));
 
@@ -392,6 +398,7 @@ TEST_CASE("LEDC fade with time(logic analyzer)", "[ledc][ignore]")
         .duty_resolution = LEDC_TIMER_13_BIT,
         .timer_num = LEDC_TIMER_0,
         .freq_hz = 5000,
+        .clk_cfg = LEDC_AUTO_CLK,
     };
     TEST_ESP_OK(ledc_timer_config(&ledc_time_config));
 
@@ -429,6 +436,7 @@ TEST_CASE("LEDC fade with step(logic analyzer)", "[ledc][ignore]")
         .duty_resolution = LEDC_TIMER_13_BIT,
         .timer_num = LEDC_TIMER_0,
         .freq_hz = 5000,
+        .clk_cfg = LEDC_AUTO_CLK,
     };
     TEST_ESP_OK(ledc_timer_config(&ledc_time_config));
 
@@ -470,6 +478,7 @@ TEST_CASE("LEDC memory test", "[ledc][test_env=UT_T1_LEDC]")
         .duty_resolution = LEDC_TIMER_13_BIT,
         .timer_num = LEDC_TIMER_0,
         .freq_hz = 5000,
+        .clk_cfg = LEDC_AUTO_CLK,
     };
     TEST_ESP_OK(ledc_timer_config(&ledc_time_config));
 
index ae5656a846988a0ad9371ab1f0f2b62a7e5a23f1..af4e58fa6733e14abbb1396ed16f5bb08fc66466 100644 (file)
@@ -55,6 +55,7 @@ static void produce_pulse(void)
         .timer_num  = LEDC_TIMER_1,
         .duty_resolution = LEDC_TIMER_10_BIT,
         .freq_hz = 1,
+        .clk_cfg = LEDC_AUTO_CLK,
     };
     ledc_timer_config(&ledc_timer);
 
@@ -160,6 +161,7 @@ static void count_mode_test(gpio_num_t ctl_io)
         .timer_num  = LEDC_TIMER_1,
         .duty_resolution = LEDC_TIMER_10_BIT,
         .freq_hz = 100,
+        .clk_cfg = LEDC_AUTO_CLK,
     };
     ledc_timer_config(&ledc_timer);
 
index 58a66e1527a1d5c22800d60b128b874a3d4cb151..b1f4c6705c4e4ed5a0b3b425308b980c3d7b6948 100644 (file)
@@ -42,7 +42,8 @@ esp_err_t mesh_light_init(void)
         .bit_num = LEDC_TIMER_13_BIT,
         .freq_hz = 5000,
         .speed_mode = LEDC_HIGH_SPEED_MODE,
-        .timer_num = LEDC_TIMER_0
+        .timer_num = LEDC_TIMER_0,
+        .clk_cfg = LEDC_AUTO_CLK,
     };
     ledc_timer_config(&ledc_timer);
 
index 47805038172a0955f18ac2032f03f6b658b537f9..e181be41bf710708650f3935c6c8ce8aa020ee9f 100644 (file)
@@ -42,7 +42,8 @@ esp_err_t mesh_light_init(void)
         .bit_num = LEDC_TIMER_13_BIT,
         .freq_hz = 5000,
         .speed_mode = LEDC_HIGH_SPEED_MODE,
-        .timer_num = LEDC_TIMER_0
+        .timer_num = LEDC_TIMER_0,
+        .clk_cfg = LEDC_AUTO_CLK,
     };
     ledc_timer_config(&ledc_timer);
 
index 3a0cd5e97bff1fce135346b15e5dc3f1474a208b..88dbe6714ee61746161e4a58ac92fa7c5b1e1954 100644 (file)
@@ -63,7 +63,8 @@ void app_main(void)
         .duty_resolution = LEDC_TIMER_13_BIT, // resolution of PWM duty
         .freq_hz = 5000,                      // frequency of PWM signal
         .speed_mode = LEDC_HS_MODE,           // timer mode
-        .timer_num = LEDC_HS_TIMER            // timer index
+        .timer_num = LEDC_HS_TIMER,            // timer index
+        .clk_cfg = LEDC_AUTO_CLK,              // Auto select the source clock
     };
     // Set configuration of timer0 for high speed channels
     ledc_timer_config(&ledc_timer);
index 89a3703634e529c4d4706d1db2700a69163443ab..a8d1fb32d7f1c2b5effaf616523cee20082517b5 100644 (file)
@@ -100,6 +100,7 @@ static void ledc_init(void)
     ledc_timer.timer_num        = LEDC_TIMER_1;
     ledc_timer.duty_resolution  = LEDC_TIMER_10_BIT;
     ledc_timer.freq_hz          = 1;  // set output frequency at 1 Hz
+    ledc_timer.clk_cfg = LEDC_AUTO_CLK;
     ledc_timer_config(&ledc_timer);
 
     // Prepare and then apply the LEDC PWM channel configuration