From 0d920a47f7ce3fb3474e940ef22f9aa3b3498de8 Mon Sep 17 00:00:00 2001 From: morris Date: Mon, 17 Jan 2022 14:45:56 +0800 Subject: [PATCH] unit_test: migrate to use pulse_cnt driver --- .gitlab/ci/target-test.yml | 14 -- components/driver/include/driver/ledc.h | 8 +- components/driver/test/test_ledc.c | 201 +++++++++--------- components/driver/test/test_pwm.c | 158 ++++++++------ .../components/test_utils/CMakeLists.txt | 5 + .../test_utils/ref_clock_impl_rmt_pcnt.c | 131 +++++------- 6 files changed, 257 insertions(+), 260 deletions(-) diff --git a/.gitlab/ci/target-test.yml b/.gitlab/ci/target-test.yml index 7926974c6c..b884ad3ffb 100644 --- a/.gitlab/ci/target-test.yml +++ b/.gitlab/ci/target-test.yml @@ -692,13 +692,6 @@ UT_008: - UT_T1_GPIO - psram -UT_012: - extends: .unit_test_esp32_template - tags: - - ESP32_IDF - - UT_T1_LEDC - - psram - UT_014: extends: .unit_test_esp32_template tags: @@ -782,13 +775,6 @@ UT_036: - UT_T1_PSRAMV0 - psram -# ToDo: re-enable this job when ESP32-S2 LEDC runner installed -# UT_037: -# extends: .unit_test_esp32s2_template -# tags: -# - ESP32S2_IDF -# - UT_T1_LEDC - UT_038: extends: .unit_test_esp32s2_template parallel: 2 diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index 0e231b249c..f6f1c0a150 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -132,7 +132,9 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel); /** * @brief Set LEDC output gpio. - * @deprecated This function is redundant, please use ledc_channel_config to set gpio pins. + * + * @note This function only routes the LEDC signal to GPIO through matrix, other LEDC resources initialization are not involved. + * Please use `ledc_channel_config()` instead to fully configure a LEDC channel. * * @param gpio_num The LEDC output gpio * @param speed_mode Select the LEDC channel group with specified speed mode. Note that not all targets support high speed mode. @@ -142,8 +144,8 @@ esp_err_t ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel); * - ESP_OK Success * - ESP_ERR_INVALID_ARG Parameter error */ -esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel) - __attribute__((deprecated("use ledc_channel_config instead"))); +esp_err_t ledc_set_pin(int gpio_num, ledc_mode_t speed_mode, ledc_channel_t ledc_channel); + /** * @brief LEDC stop. * Disable LEDC output, and set idle level diff --git a/components/driver/test/test_ledc.c b/components/driver/test/test_ledc.c index b6ada1e6f7..cce243cc0d 100644 --- a/components/driver/test/test_ledc.c +++ b/components/driver/test/test_ledc.c @@ -6,10 +6,6 @@ /* LEDC tested by PCNT in some case * PCNT can get the LEDC waveform frequency * - * test environment of UT_T1_LEDC: - * 1. connect GPIO18 with GPIO4 - * 2. connect GPIO5 to 3.3v (in case of it is pulled down by default) - * * some calculation related with duty: * real duty = duty/2^duty_resolution */ @@ -24,15 +20,13 @@ #include "freertos/queue.h" #include "unity.h" #include "soc/ledc_periph.h" +#include "soc/gpio_periph.h" +#include "soc/io_mux_reg.h" #include "esp_system.h" #include "driver/ledc.h" #include "driver/gpio.h" -#define PULSE_IO 18 -#define PCNT_INPUT_IO 4 -#define PCNT_CTRL_FLOATING_IO 5 -#define HIGHEST_LIMIT 10000 -#define LOWEST_LIMIT -10000 +#define PULSE_IO 18 #define TEST_PWM_FREQ 2000 @@ -116,81 +110,6 @@ static void timer_duty_test(ledc_channel_t channel, ledc_timer_bit_t timer_bit, timer_duty_set_get(ledc_ch_config.speed_mode, ledc_ch_config.channel, (1 << 13) - 2); } -#if SOC_PCNT_SUPPORTED -#include "driver/pcnt.h" // TODO: C3 doesn't have PCNT peripheral - -#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32S3) -//no runners - -// use PCNT to test the waveform of LEDC -static int16_t wave_count(int last_time) -{ - int16_t test_counter; - pcnt_config_t pcnt_config = { - .pulse_gpio_num = PCNT_INPUT_IO, - .ctrl_gpio_num = PCNT_CTRL_FLOATING_IO, - .channel = PCNT_CHANNEL_0, - .unit = PCNT_UNIT_0, - .pos_mode = PCNT_COUNT_INC, - .neg_mode = PCNT_COUNT_DIS, - .lctrl_mode = PCNT_MODE_REVERSE, - .hctrl_mode = PCNT_MODE_KEEP, - .counter_h_lim = HIGHEST_LIMIT, - .counter_l_lim = LOWEST_LIMIT, - }; - TEST_ESP_OK(pcnt_unit_config(&pcnt_config)); - - // initialize first - TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0)); - TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0)); - vTaskDelay(100 / portTICK_PERIOD_MS); - TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0)); - TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter)); - - vTaskDelay(last_time / portTICK_PERIOD_MS); - TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter)); - return test_counter; -} - -// the PCNT will count the frequency of it -static void frequency_set_get(ledc_mode_t speed_mode, ledc_timer_t timer, uint32_t freq_hz, int16_t real_freq, int16_t error) -{ - int16_t count; - TEST_ESP_OK(ledc_set_freq(speed_mode, timer, freq_hz)); - count = wave_count(1000); - TEST_ASSERT_INT16_WITHIN(error, count, real_freq); - TEST_ASSERT_EQUAL_INT32(ledc_get_freq(speed_mode, timer), real_freq); -} - -static void timer_frequency_test(ledc_channel_t channel, ledc_timer_bit_t timer_bit, ledc_timer_t timer, ledc_mode_t speed_mode) -{ - ledc_channel_config_t ledc_ch_config = { - .gpio_num = PULSE_IO, - .speed_mode = speed_mode, - .channel = channel, - .intr_type = LEDC_INTR_DISABLE, - .timer_sel = timer, - .duty = 4000, - .hpoint = 0, - }; - ledc_timer_config_t ledc_time_config = { - .speed_mode = speed_mode, - .duty_resolution = timer_bit, - .timer_num = timer, - .freq_hz = 5000, - .clk_cfg = LEDC_USE_APB_CLK, - }; - TEST_ESP_OK(ledc_channel_config(&ledc_ch_config)); - TEST_ESP_OK(ledc_timer_config(&ledc_time_config)); - frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 100, 100, 2); - frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 5000, 5000, 5); - frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 9000, 8993, 5); -} - -#endif // !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32S3) - -#endif // SOC_PCNT_SUPPORTED - TEST_CASE("LEDC channel config wrong gpio", "[ledc]") { ledc_channel_config_t ledc_ch_config = initialize_channel_config(); @@ -324,8 +243,8 @@ TEST_CASE("LEDC set and get duty", "[ledc]") { ledc_timer_t timer_list[4] = {LEDC_TIMER_0, LEDC_TIMER_1, LEDC_TIMER_2, LEDC_TIMER_3}; ledc_mode_t speed_mode_list[LEDC_SPEED_MODE_MAX] = SPEED_MODE_LIST; - for(int i=0; i PCNT UNIT0 - pcnt_config_t pcnt_config = { - .pulse_gpio_num = TEST_PWMA_GPIO, - .ctrl_gpio_num = -1, // don't care level signal - .channel = PCNT_CHANNEL_0, - .unit = TEST_PWMA_PCNT_UNIT, - .pos_mode = PCNT_COUNT_INC, - .neg_mode = PCNT_COUNT_DIS, - .lctrl_mode = PCNT_MODE_KEEP, - .hctrl_mode = PCNT_MODE_KEEP, - .counter_h_lim = 10000, - .counter_l_lim = -10000, - }; - TEST_ESP_OK(pcnt_unit_config(&pcnt_config)); mcpwm_io_signals_t mcpwm_a = pwma[timer]; TEST_ESP_OK(test_mcpwm_gpio_init(group, mcpwm_a, TEST_PWMA_GPIO)); - // PWMB <--> PCNT UNIT1 - pcnt_config.pulse_gpio_num = TEST_PWMB_GPIO; - pcnt_config.unit = TEST_PWMB_PCNT_UNIT; - TEST_ESP_OK(pcnt_unit_config(&pcnt_config)); + mcpwm_io_signals_t mcpwm_b = pwmb[timer]; TEST_ESP_OK(test_mcpwm_gpio_init(group, mcpwm_b, TEST_PWMB_GPIO)); @@ -111,14 +97,53 @@ static void mcpwm_setup_testbench(mcpwm_unit_t group, mcpwm_timer_t timer, uint3 TEST_ESP_OK(mcpwm_init(group, timer, &pwm_config)); } -static uint32_t mcpwm_pcnt_get_pulse_number(pcnt_unit_t pwm_pcnt_unit, int capture_window_ms) +static void pcnt_setup_testbench(void) { - int16_t count_value = 0; - TEST_ESP_OK(pcnt_counter_pause(pwm_pcnt_unit)); - TEST_ESP_OK(pcnt_counter_clear(pwm_pcnt_unit)); - TEST_ESP_OK(pcnt_counter_resume(pwm_pcnt_unit)); + // PWMA <--> PCNT UNIT0 + pcnt_unit_config_t unit_a_config = { + .high_limit = 10000, + .low_limit = -10000, + }; + TEST_ESP_OK(pcnt_new_unit(&unit_a_config, &pcnt_unit_a)); + pcnt_chan_config_t chan_a_config = { + .edge_gpio_num = TEST_PWMA_GPIO, + .level_gpio_num = -1, // don't care level signal + }; + TEST_ESP_OK(pcnt_new_channel(pcnt_unit_a, &chan_a_config, &pcnt_chan_a)); + TEST_ESP_OK(pcnt_channel_set_edge_action(pcnt_chan_a, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD)); + TEST_ESP_OK(pcnt_channel_set_level_action(pcnt_chan_a, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP)); + + // PWMB <--> PCNT UNIT1 + pcnt_unit_config_t unit_b_config = { + .high_limit = 10000, + .low_limit = -10000, + }; + TEST_ESP_OK(pcnt_new_unit(&unit_b_config, &pcnt_unit_b)); + pcnt_chan_config_t chan_b_config = { + .edge_gpio_num = TEST_PWMB_GPIO, + .level_gpio_num = -1, // don't care level signal + }; + TEST_ESP_OK(pcnt_new_channel(pcnt_unit_b, &chan_b_config, &pcnt_chan_b)); + TEST_ESP_OK(pcnt_channel_set_edge_action(pcnt_chan_b, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD)); + TEST_ESP_OK(pcnt_channel_set_level_action(pcnt_chan_b, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP)); +} + +static void pcnt_tear_testbench(void) +{ + TEST_ESP_OK(pcnt_del_channel(pcnt_chan_a)); + TEST_ESP_OK(pcnt_del_channel(pcnt_chan_b)); + TEST_ESP_OK(pcnt_del_unit(pcnt_unit_a)); + TEST_ESP_OK(pcnt_del_unit(pcnt_unit_b)); +} + +static uint32_t pcnt_get_pulse_number(pcnt_unit_handle_t pwm_pcnt_unit, int capture_window_ms) +{ + int count_value = 0; + TEST_ESP_OK(pcnt_unit_clear_count(pwm_pcnt_unit)); + TEST_ESP_OK(pcnt_unit_start(pwm_pcnt_unit)); usleep(capture_window_ms * 1000); - TEST_ESP_OK(pcnt_get_counter_value(pwm_pcnt_unit, &count_value)); + TEST_ESP_OK(pcnt_unit_stop(pwm_pcnt_unit)); + TEST_ESP_OK(pcnt_unit_get_count(pwm_pcnt_unit, &count_value)); printf("count value: %d\r\n", count_value); return (uint32_t)count_value; } @@ -163,24 +188,26 @@ static void mcpwm_start_stop_test(mcpwm_unit_t unit, mcpwm_timer_t timer) { uint32_t pulse_number = 0; + pcnt_setup_testbench(); mcpwm_setup_testbench(unit, timer, 1000, 50.0, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ); // Period: 1000us, 1ms // count the pulse number within 100ms - pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMA_PCNT_UNIT, 100); + pulse_number = pcnt_get_pulse_number(pcnt_unit_a, 100); TEST_ASSERT_INT_WITHIN(2, 100, pulse_number); - pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMB_PCNT_UNIT, 100); + pulse_number = pcnt_get_pulse_number(pcnt_unit_b, 100); TEST_ASSERT_INT_WITHIN(2, 100, pulse_number); TEST_ESP_OK(mcpwm_set_frequency(unit, timer, 100)); - pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMB_PCNT_UNIT, 100); + pulse_number = pcnt_get_pulse_number(pcnt_unit_b, 100); TEST_ASSERT_INT_WITHIN(2, 10, pulse_number); // stop timer, then no pwm pulse should be generating TEST_ESP_OK(mcpwm_stop(unit, timer)); usleep(10000); // wait until timer stopped - pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMA_PCNT_UNIT, 100); + pulse_number = pcnt_get_pulse_number(pcnt_unit_a, 100); TEST_ASSERT_INT_WITHIN(2, 0, pulse_number); - pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMB_PCNT_UNIT, 100); + pulse_number = pcnt_get_pulse_number(pcnt_unit_b, 100); TEST_ASSERT_INT_WITHIN(2, 0, pulse_number); + pcnt_tear_testbench(); } TEST_CASE("MCPWM start and stop test", "[mcpwm]") @@ -229,6 +256,7 @@ static void mcpwm_carrier_test(mcpwm_unit_t unit, mcpwm_timer_t timer, mcpwm_car { uint32_t pulse_number = 0; + pcnt_setup_testbench(); mcpwm_setup_testbench(unit, timer, 1000, 50.0, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ); mcpwm_set_signal_high(unit, timer, MCPWM_GEN_A); mcpwm_set_signal_high(unit, timer, MCPWM_GEN_B); @@ -239,14 +267,15 @@ static void mcpwm_carrier_test(mcpwm_unit_t unit, mcpwm_timer_t timer, mcpwm_car TEST_ESP_OK(mcpwm_carrier_oneshot_mode_enable(unit, timer, os_width)); vTaskDelay(pdMS_TO_TICKS(100)); - pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMA_PCNT_UNIT, 10); + pulse_number = pcnt_get_pulse_number(pcnt_unit_a, 10); TEST_ASSERT_INT_WITHIN(50, 2500, pulse_number); usleep(10000); - pulse_number = mcpwm_pcnt_get_pulse_number(TEST_PWMB_PCNT_UNIT, 10); + pulse_number = pcnt_get_pulse_number(pcnt_unit_b, 10); TEST_ASSERT_INT_WITHIN(50, 2500, pulse_number); TEST_ESP_OK(mcpwm_carrier_disable(unit, timer)); TEST_ESP_OK(mcpwm_stop(unit, timer)); + pcnt_tear_testbench(); } TEST_CASE("MCPWM carrier test", "[mcpwm]") @@ -382,33 +411,37 @@ TEST_CASE("MCPWM timer GPIO sync test", "[mcpwm]") } } -static void mcpwm_swsync_test(mcpwm_unit_t unit) { +// used only in this area but need to be reset every time. mutex is not needed +// store timestamps captured from ISR callback +static uint64_t cap_timestamp[3]; +// control the start of capture to avoid unstable data +static volatile bool log_cap; + +// cb function, to update capture value +// only log when channel1 comes at first, then channel2, and do not log further more. +static bool test_mcpwm_capture_callback(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_channel, const cap_event_data_t *edata, + void *user_data) +{ + if (log_cap && (cap_timestamp[1] == 0 || cap_timestamp[2] == 0)) { + if (cap_channel == MCPWM_SELECT_CAP1 && cap_timestamp[1] == 0) { + cap_timestamp[1] = edata->cap_value; + } + if (cap_channel == MCPWM_SELECT_CAP2 && cap_timestamp[1] != 0) { + cap_timestamp[2] = edata->cap_value; + } + } + return false; +} + +static void mcpwm_swsync_test(mcpwm_unit_t unit) +{ const uint32_t test_sync_phase = 20; - // used only in this area but need to be reset every time. mutex is not needed - // store timestamps captured from ISR callback - static uint64_t cap_timestamp[3]; + cap_timestamp[0] = 0; cap_timestamp[1] = 0; cap_timestamp[2] = 0; - // control the start of capture to avoid unstable data - static volatile bool log_cap; log_cap = false; - // cb function, to update capture value - // only log when channel1 comes at first, then channel2, and do not log further more. - bool capture_callback(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_channel, const cap_event_data_t *edata, - void *user_data) { - if (log_cap && (cap_timestamp[1] == 0 || cap_timestamp[2] == 0)) { - if (cap_channel == MCPWM_SELECT_CAP1 && cap_timestamp[1] == 0) { - cap_timestamp[1] = edata->cap_value; - } - if (cap_channel == MCPWM_SELECT_CAP2 && cap_timestamp[1] != 0) { - cap_timestamp[2] = edata->cap_value; - } - } - return false; - } - // configure all timer output 10% PWM for (int i = 0; i < 3; ++i) { mcpwm_setup_testbench(unit, i, 1000, 10.0, MCPWM_TEST_GROUP_CLK_HZ, MCPWM_TEST_TIMER_CLK_HZ); @@ -420,7 +453,7 @@ static void mcpwm_swsync_test(mcpwm_unit_t unit) { mcpwm_capture_config_t conf = { .cap_edge = MCPWM_POS_EDGE, .cap_prescale = 1, - .capture_cb = capture_callback, + .capture_cb = test_mcpwm_capture_callback, .user_data = NULL, }; TEST_ESP_OK(test_mcpwm_gpio_init(unit, MCPWM_CAP_0, TEST_SYNC_GPIO_0)); @@ -477,7 +510,8 @@ typedef struct { TaskHandle_t task_hdl; } test_capture_callback_data_t; -static bool test_mcpwm_intr_handler(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_sig, const cap_event_data_t *edata, void *arg) { +static bool test_mcpwm_intr_handler(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_sig, const cap_event_data_t *edata, void *arg) +{ BaseType_t high_task_wakeup = pdFALSE; test_capture_callback_data_t *cb_data = (test_capture_callback_data_t *)arg; vTaskNotifyGiveFromISR(cb_data->task_hdl, &high_task_wakeup); @@ -497,10 +531,10 @@ static void mcpwm_capture_test(mcpwm_unit_t unit, mcpwm_capture_signal_t cap_cha TEST_ESP_OK(test_mcpwm_gpio_init(unit, cap_io, TEST_CAP_GPIO)); mcpwm_capture_config_t conf = { - .cap_edge = MCPWM_POS_EDGE, - .cap_prescale = 1, - .capture_cb = test_mcpwm_intr_handler, - .user_data = &callback_data + .cap_edge = MCPWM_POS_EDGE, + .cap_prescale = 1, + .capture_cb = test_mcpwm_intr_handler, + .user_data = &callback_data }; TEST_ESP_OK(mcpwm_capture_enable_channel(unit, cap_channel, &conf)); // generate an posage diff --git a/tools/unit-test-app/components/test_utils/CMakeLists.txt b/tools/unit-test-app/components/test_utils/CMakeLists.txt index 1fd457f492..af19410c15 100644 --- a/tools/unit-test-app/components/test_utils/CMakeLists.txt +++ b/tools/unit-test-app/components/test_utils/CMakeLists.txt @@ -4,6 +4,11 @@ set(srcs "ccomp_timer.c" "test_utils.c") if(CONFIG_IDF_TARGET_ESP32) + # ESP32's timer group doesn't have XTAL clock source, + # so we can't implement a timekeeping that can work during DFS + # but we can work around that by combining RMT and PCNT + # where PCNT can count the pulses generated by RMT, and RMT is clocked from REF_TICK + # REF_TICK won't be affected by DFS list(APPEND srcs "ref_clock_impl_rmt_pcnt.c") else() list(APPEND srcs "ref_clock_impl_timergroup.c") diff --git a/tools/unit-test-app/components/test_utils/ref_clock_impl_rmt_pcnt.c b/tools/unit-test-app/components/test_utils/ref_clock_impl_rmt_pcnt.c index ea32a2af8a..3afd31d796 100644 --- a/tools/unit-test-app/components/test_utils/ref_clock_impl_rmt_pcnt.c +++ b/tools/unit-test-app/components/test_utils/ref_clock_impl_rmt_pcnt.c @@ -20,41 +20,65 @@ */ #include "sdkconfig.h" +#include "unity.h" #include "test_utils.h" #include "freertos/FreeRTOS.h" #include "esp_intr_alloc.h" #include "esp_private/periph_ctrl.h" #include "driver/rmt.h" +#include "driver/pulse_cnt.h" #include "soc/gpio_sig_map.h" #include "soc/gpio_periph.h" #include "soc/soc_caps.h" #include "hal/rmt_types.h" #include "hal/rmt_hal.h" #include "hal/rmt_ll.h" -#include "hal/pcnt_hal.h" -#include "hal/pcnt_ll.h" #include "esp_rom_gpio.h" #include "esp_rom_sys.h" -#define REF_CLOCK_RMT_CHANNEL 0 // RMT channel 0 -#define REF_CLOCK_PCNT_UNIT 0 // PCNT unit 0 -#define REF_CLOCK_PCNT_CHANNEL 0// PCNT channel 0 -#define REF_CLOCK_GPIO 21 // GPIO used to combine RMT out signal with PCNT input signal - +#define REF_CLOCK_RMT_CHANNEL 0 // RMT channel 0 +#define REF_CLOCK_GPIO 21 // GPIO used to combine RMT out signal with PCNT input signal #define REF_CLOCK_PRESCALER_MS 30 // PCNT high threshold interrupt fired every 30ms -static void IRAM_ATTR pcnt_isr(void *arg); - -static intr_handle_t s_intr_handle; -static portMUX_TYPE s_lock = portMUX_INITIALIZER_UNLOCKED; -static volatile uint32_t s_milliseconds; static rmt_hal_context_t s_rmt_hal; -static pcnt_hal_context_t s_pcnt_hal; +static pcnt_unit_handle_t s_pcnt_unit; +static pcnt_channel_handle_t s_pcnt_chan; +static volatile uint32_t s_milliseconds; + +static bool on_reach_watch_point(pcnt_unit_handle_t unit, pcnt_watch_event_data_t *edata, void *user_ctx) +{ + s_milliseconds += REF_CLOCK_PRESCALER_MS; + return false; +} void ref_clock_init(void) { - assert(s_intr_handle == NULL && "ref clock already initialized"); + // Initialize PCNT + pcnt_unit_config_t unit_config = { + .high_limit = REF_CLOCK_PRESCALER_MS * 1000, + .low_limit = -100, // any minus value is OK, in this case, we don't count down + }; + TEST_ESP_OK(pcnt_new_unit(&unit_config, &s_pcnt_unit)); + pcnt_chan_config_t chan_config = { + .edge_gpio_num = REF_CLOCK_GPIO, + .level_gpio_num = -1, + .flags.io_loop_back = true, + }; + TEST_ESP_OK(pcnt_new_channel(s_pcnt_unit, &chan_config, &s_pcnt_chan)); + // increase count on both edges + TEST_ESP_OK(pcnt_channel_set_edge_action(s_pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE)); + // don't care level change + TEST_ESP_OK(pcnt_channel_set_level_action(s_pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP)); + // add watch point + TEST_ESP_OK(pcnt_unit_add_watch_point(s_pcnt_unit, REF_CLOCK_PRESCALER_MS * 1000)); + // register watch event + pcnt_event_callbacks_t cbs = { + .on_reach = on_reach_watch_point, + }; + TEST_ESP_OK(pcnt_unit_register_event_callbacks(s_pcnt_unit, &cbs, NULL)); + // start pcnt + TEST_ESP_OK(pcnt_unit_start(s_pcnt_unit)); // Route RMT output to GPIO matrix esp_rom_gpio_connect_out_signal(REF_CLOCK_GPIO, RMT_SIG_OUT0_IDX, false, false); @@ -71,18 +95,11 @@ void ref_clock_init(void) }; rmt_ll_enable_drive_clock(s_rmt_hal.regs, true); -#if SOC_RMT_SUPPORT_XTAL - rmt_ll_set_group_clock_src(s_rmt_hal.regs, REF_CLOCK_RMT_CHANNEL, RMT_BASECLK_XTAL, 39, 0, 0); // XTAL(40MHz), rmt_sclk => 1MHz (40/(1+39)) -#elif SOC_RMT_SUPPORT_REF_TICK rmt_ll_set_group_clock_src(s_rmt_hal.regs, REF_CLOCK_RMT_CHANNEL, RMT_BASECLK_REF, 0, 0, 0); // select REF_TICK (1MHz) -#endif rmt_hal_tx_set_channel_clock(&s_rmt_hal, REF_CLOCK_RMT_CHANNEL, 1000000, 1000000); // counter clock: 1MHz rmt_ll_tx_enable_idle(s_rmt_hal.regs, REF_CLOCK_RMT_CHANNEL, true); // enable idle output rmt_ll_tx_set_idle_level(s_rmt_hal.regs, REF_CLOCK_RMT_CHANNEL, 1); // idle level: 1 rmt_ll_tx_enable_carrier_modulation(s_rmt_hal.regs, REF_CLOCK_RMT_CHANNEL, true); -#if !CONFIG_IDF_TARGET_ESP32 - rmt_ll_tx_set_carrier_always_on(s_rmt_hal.regs, REF_CLOCK_RMT_CHANNEL, true); -#endif rmt_hal_set_carrier_clock(&s_rmt_hal, REF_CLOCK_RMT_CHANNEL, 1000000, 500000, 0.5); // set carrier to 500KHz rmt_ll_tx_set_carrier_level(s_rmt_hal.regs, REF_CLOCK_RMT_CHANNEL, 1); rmt_ll_enable_mem_access(s_rmt_hal.regs, true); @@ -92,77 +109,25 @@ void ref_clock_init(void) rmt_ll_tx_enable_loop(s_rmt_hal.regs, REF_CLOCK_RMT_CHANNEL, false); rmt_ll_tx_start(s_rmt_hal.regs, REF_CLOCK_RMT_CHANNEL); - // Route signal to PCNT - esp_rom_gpio_connect_in_signal(REF_CLOCK_GPIO, PCNT_SIG_CH0_IN0_IDX, false); - if (REF_CLOCK_GPIO != 20) { - PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[REF_CLOCK_GPIO]); - } else { - PIN_INPUT_ENABLE(PERIPHS_IO_MUX_GPIO20_U); - } - - // Initialize PCNT - periph_module_enable(PERIPH_PCNT_MODULE); - pcnt_hal_init(&s_pcnt_hal, REF_CLOCK_PCNT_UNIT); - - pcnt_ll_set_edge_action(s_pcnt_hal.dev, REF_CLOCK_PCNT_UNIT, REF_CLOCK_PCNT_CHANNEL, - PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE); - pcnt_ll_set_level_action(s_pcnt_hal.dev, REF_CLOCK_PCNT_UNIT, REF_CLOCK_PCNT_CHANNEL, - PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP); - pcnt_ll_disable_all_events(s_pcnt_hal.dev, REF_CLOCK_PCNT_UNIT); - pcnt_ll_set_high_limit_value(s_pcnt_hal.dev, REF_CLOCK_PCNT_UNIT, REF_CLOCK_PRESCALER_MS * 1000); - pcnt_ll_enable_high_limit_event(s_pcnt_hal.dev, REF_CLOCK_PCNT_UNIT, true); - - // Enable PCNT and wait for it to start counting - pcnt_ll_start_count(s_pcnt_hal.dev, REF_CLOCK_PCNT_UNIT); - pcnt_ll_clear_count(s_pcnt_hal.dev, REF_CLOCK_PCNT_UNIT); - - esp_rom_delay_us(10000); - - // Enable interrupt s_milliseconds = 0; - ESP_ERROR_CHECK(esp_intr_alloc(ETS_PCNT_INTR_SOURCE, ESP_INTR_FLAG_IRAM, pcnt_isr, NULL, &s_intr_handle)); - pcnt_ll_clear_intr_status(s_pcnt_hal.dev, BIT(REF_CLOCK_PCNT_UNIT)); - pcnt_ll_enable_intr(s_pcnt_hal.dev, 1 << REF_CLOCK_PCNT_UNIT, true); } -static void IRAM_ATTR pcnt_isr(void *arg) +void ref_clock_deinit(void) { - portENTER_CRITICAL_ISR(&s_lock); - pcnt_ll_clear_intr_status(s_pcnt_hal.dev, BIT(REF_CLOCK_PCNT_UNIT)); - s_milliseconds += REF_CLOCK_PRESCALER_MS; - portEXIT_CRITICAL_ISR(&s_lock); -} + // Deinitialize PCNT + TEST_ESP_OK(pcnt_unit_stop(s_pcnt_unit)); + TEST_ESP_OK(pcnt_unit_remove_watch_point(s_pcnt_unit, REF_CLOCK_PRESCALER_MS * 1000)); + TEST_ESP_OK(pcnt_del_channel(s_pcnt_chan)); + TEST_ESP_OK(pcnt_del_unit(s_pcnt_unit)); -void ref_clock_deinit() -{ - assert(s_intr_handle && "ref clock deinit called without init"); - - // Disable interrupt - pcnt_ll_enable_intr(s_pcnt_hal.dev, 1 << REF_CLOCK_PCNT_UNIT, false); - esp_intr_free(s_intr_handle); - s_intr_handle = NULL; - - // Disable RMT + // Deinitialize RMT rmt_ll_tx_enable_carrier_modulation(s_rmt_hal.regs, REF_CLOCK_RMT_CHANNEL, false); periph_module_disable(PERIPH_RMT_MODULE); - - // Disable PCNT - pcnt_ll_stop_count(s_pcnt_hal.dev, REF_CLOCK_PCNT_UNIT); - periph_module_disable(PERIPH_PCNT_MODULE); } -uint64_t ref_clock_get() +uint64_t ref_clock_get(void) { - portENTER_CRITICAL(&s_lock); int microseconds = 0; - microseconds = pcnt_ll_get_count(s_pcnt_hal.dev, REF_CLOCK_PCNT_UNIT); - uint32_t milliseconds = s_milliseconds; - uint32_t intr_status = pcnt_ll_get_intr_status(s_pcnt_hal.dev); - if (intr_status & BIT(REF_CLOCK_PCNT_UNIT)) { - // refresh counter value, in case the overflow has happened after reading cnt_val - microseconds = pcnt_ll_get_count(s_pcnt_hal.dev, REF_CLOCK_PCNT_UNIT); - milliseconds += REF_CLOCK_PRESCALER_MS; - } - portEXIT_CRITICAL(&s_lock); - return 1000 * (uint64_t)milliseconds + (uint64_t)microseconds; + TEST_ESP_OK(pcnt_unit_get_count(s_pcnt_unit, µseconds)); + return 1000 * (uint64_t)s_milliseconds + (uint64_t)microseconds; }