unit_test: migrate to use pulse_cnt driver

This commit is contained in:
morris 2022-01-17 14:45:56 +08:00
parent 04b3f8b210
commit 0d920a47f7
6 changed files with 257 additions and 260 deletions

View File

@ -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

View File

@ -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

View File

@ -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<LEDC_TIMER_MAX-1; i++) {
for(int j=0; j<LEDC_SPEED_MODE_MAX; j++) {
for (int i = 0; i < LEDC_TIMER_MAX - 1; i++) {
for (int j = 0; j < LEDC_SPEED_MODE_MAX; j++) {
timer_duty_test(LEDC_CHANNEL_0, LEDC_TIMER_13_BIT, timer_list[i], speed_mode_list[j]);
}
}
@ -473,10 +392,88 @@ TEST_CASE("LEDC fade stop test", "[ledc]")
#if SOC_PCNT_SUPPORTED
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32S3)
#include "driver/pulse_cnt.h"
TEST_CASE("LEDC set and get frequency", "[ledc][test_env=UT_T1_LEDC][timeout=60][ignore]")
#define HIGHEST_LIMIT 10000
#define LOWEST_LIMIT -10000
static pcnt_unit_handle_t pcnt_unit;
static pcnt_channel_handle_t pcnt_chan;
static void setup_testbench(void)
{
pcnt_unit_config_t unit_config = {
.high_limit = HIGHEST_LIMIT,
.low_limit = LOWEST_LIMIT,
};
TEST_ESP_OK(pcnt_new_unit(&unit_config, &pcnt_unit));
pcnt_chan_config_t chan_config = {
.edge_gpio_num = PULSE_IO,
.level_gpio_num = -1,
};
TEST_ESP_OK(pcnt_new_channel(pcnt_unit, &chan_config, &pcnt_chan));
TEST_ESP_OK(pcnt_channel_set_level_action(pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
TEST_ESP_OK(pcnt_channel_set_edge_action(pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_HOLD));
}
static void tear_testbench(void)
{
TEST_ESP_OK(pcnt_del_channel(pcnt_chan));
TEST_ESP_OK(pcnt_del_unit(pcnt_unit));
}
// use PCNT to test the waveform of LEDC
static int wave_count(int last_time)
{
// The input ability of PULSE_IO is disabled after ledc driver install, so we need to reenable it again
PIN_INPUT_ENABLE(GPIO_PIN_MUX_REG[PULSE_IO]);
int test_counter = 0;
TEST_ESP_OK(pcnt_unit_clear_count(pcnt_unit));
TEST_ESP_OK(pcnt_unit_start(pcnt_unit));
vTaskDelay(pdMS_TO_TICKS(last_time));
TEST_ESP_OK(pcnt_unit_stop(pcnt_unit));
TEST_ESP_OK(pcnt_unit_get_count(pcnt_unit, &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)
{
int 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(real_freq, ledc_get_freq(speed_mode, timer));
}
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, 20);
frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 5000, 5000, 50);
frequency_set_get(ledc_ch_config.speed_mode, ledc_ch_config.timer_sel, 9000, 8992, 50);
}
TEST_CASE("LEDC set and get frequency", "[ledc][timeout=60][ignore]")
{
setup_testbench();
#if SOC_LEDC_SUPPORT_HS_MODE
timer_frequency_test(LEDC_CHANNEL_0, LEDC_TIMER_13_BIT, LEDC_TIMER_0, LEDC_HIGH_SPEED_MODE);
timer_frequency_test(LEDC_CHANNEL_0, LEDC_TIMER_13_BIT, LEDC_TIMER_1, LEDC_HIGH_SPEED_MODE);
@ -487,10 +484,12 @@ TEST_CASE("LEDC set and get frequency", "[ledc][test_env=UT_T1_LEDC][timeout=60]
timer_frequency_test(LEDC_CHANNEL_0, LEDC_TIMER_13_BIT, LEDC_TIMER_1, LEDC_LOW_SPEED_MODE);
timer_frequency_test(LEDC_CHANNEL_0, LEDC_TIMER_13_BIT, LEDC_TIMER_2, LEDC_LOW_SPEED_MODE);
timer_frequency_test(LEDC_CHANNEL_0, LEDC_TIMER_13_BIT, LEDC_TIMER_3, LEDC_LOW_SPEED_MODE);
tear_testbench();
}
TEST_CASE("LEDC timer set", "[ledc][test_env=UT_T1_LEDC]")
TEST_CASE("LEDC timer set", "[ledc]")
{
setup_testbench();
const ledc_mode_t test_speed_mode = TEST_SPEED_MODE;
ledc_channel_config_t ledc_ch_config = {
.gpio_num = PULSE_IO,
@ -513,7 +512,8 @@ TEST_CASE("LEDC timer set", "[ledc][test_env=UT_T1_LEDC]")
TEST_ESP_OK(ledc_timer_config(&ledc_time_config));
uint32_t freq_get;
uint32_t count;
int count;
#if SOC_LEDC_SUPPORT_REF_TICK
//set timer 0 as 250Hz, use REF_TICK
TEST_ESP_OK(ledc_timer_set(test_speed_mode, LEDC_TIMER_0, 1000, 10, LEDC_REF_TICK));
TEST_ESP_OK(ledc_timer_rst(test_speed_mode, LEDC_TIMER_0));
@ -521,6 +521,7 @@ TEST_CASE("LEDC timer set", "[ledc][test_env=UT_T1_LEDC]")
freq_get = ledc_get_freq(test_speed_mode, LEDC_TIMER_0);
count = wave_count(1000);
TEST_ASSERT_UINT32_WITHIN(10, count, freq_get);
#endif
//set timer 0 as 500Hz, use APB_CLK
TEST_ESP_OK(ledc_timer_set(test_speed_mode, LEDC_TIMER_0, 5000, 13, LEDC_APB_CLK));
@ -539,12 +540,14 @@ TEST_CASE("LEDC timer set", "[ledc][test_env=UT_T1_LEDC]")
TEST_ESP_OK(ledc_stop(test_speed_mode, LEDC_CHANNEL_0, !current_level));
vTaskDelay(1000 / portTICK_PERIOD_MS);
TEST_ASSERT_EQUAL_INT32( LEDC.channel_group[test_speed_mode].channel[LEDC_CHANNEL_0].conf0.idle_lv, !current_level);
tear_testbench();
}
TEST_CASE("LEDC timer pause and resume", "[ledc][test_env=UT_T1_LEDC]")
TEST_CASE("LEDC timer pause and resume", "[ledc]")
{
setup_testbench();
const ledc_mode_t test_speed_mode = TEST_SPEED_MODE;
int16_t count;
int count;
ledc_channel_config_t ledc_ch_config = {
.gpio_num = PULSE_IO,
.speed_mode = test_speed_mode,
@ -588,6 +591,7 @@ TEST_CASE("LEDC timer pause and resume", "[ledc][test_env=UT_T1_LEDC]")
TEST_ESP_OK(ledc_timer_rst(test_speed_mode, LEDC_TIMER_0));
vTaskDelay(100 / portTICK_PERIOD_MS);
TEST_ASSERT_UINT32_WITHIN(5, count, 5000);
tear_testbench();
}
static void ledc_cpu_reset_test_first_stage(void)
@ -603,17 +607,18 @@ static void ledc_cpu_reset_test_first_stage(void)
static void ledc_cpu_reset_test_second_stage(void)
{
int count;
TEST_ASSERT_EQUAL(ESP_RST_SW, esp_reset_reason());
int16_t count;
setup_testbench();
// reconfigure the GPIO again, as the GPIO output ability has been disabled during initialize pcnt peripheral
ledc_set_pin(PULSE_IO, TEST_SPEED_MODE, LEDC_CHANNEL_0);
count = wave_count(1000);
TEST_ASSERT_UINT32_WITHIN(5, count, TEST_PWM_FREQ);
TEST_ASSERT_UINT32_WITHIN(5, TEST_PWM_FREQ, count);
tear_testbench();
}
TEST_CASE_MULTIPLE_STAGES("LEDC software reset test",
"[ledc][test_env=UT_T1_LEDC]",
TEST_CASE_MULTIPLE_STAGES("LEDC continue work after software reset", "[ledc]",
ledc_cpu_reset_test_first_stage,
ledc_cpu_reset_test_second_stage);
#endif //!TEMPORARY_DISABLED_FOR_TARGETS(ESP32S2, ESP32S3)
#endif // SOC_PCNT_SUPPORTED

View File

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -15,12 +15,10 @@
#include "soc/rtc.h"
#if SOC_MCPWM_SUPPORTED
#include "soc/mcpwm_periph.h"
#include "driver/pcnt.h"
#include "driver/pulse_cnt.h"
#include "driver/mcpwm.h"
#include "driver/gpio.h"
#define TEST_PWMA_PCNT_UNIT (0)
#define TEST_PWMB_PCNT_UNIT (1)
#define TEST_PWMA_GPIO (2)
#define TEST_PWMB_GPIO (4)
#define TEST_FAULT_GPIO (21)
@ -41,6 +39,11 @@ const static mcpwm_io_signals_t sync_io_sig_array[] = {MCPWM_SYNC_0, MCPWM_SYNC_
const static mcpwm_capture_signal_t cap_sig_array[] = {MCPWM_SELECT_CAP0, MCPWM_SELECT_CAP1, MCPWM_SELECT_CAP2};
const static mcpwm_io_signals_t cap_io_sig_array[] = {MCPWM_CAP_0, MCPWM_CAP_1, MCPWM_CAP_2};
static pcnt_unit_handle_t pcnt_unit_a;
static pcnt_channel_handle_t pcnt_chan_a;
static pcnt_unit_handle_t pcnt_unit_b;
static pcnt_channel_handle_t pcnt_chan_b;
// This GPIO init function is almost the same to public API `mcpwm_gpio_init()`, except that
// this function will configure all MCPWM GPIOs into output and input capable
// which is useful to simulate a trigger source
@ -75,26 +78,9 @@ static esp_err_t test_mcpwm_gpio_init(mcpwm_unit_t mcpwm_num, mcpwm_io_signals_t
static void mcpwm_setup_testbench(mcpwm_unit_t group, mcpwm_timer_t timer, uint32_t pwm_freq, float pwm_duty,
unsigned long int group_resolution, unsigned long int timer_resolution)
{
// PWMA <--> 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

View File

@ -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")

View File

@ -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, &microseconds));
return 1000 * (uint64_t)s_milliseconds + (uint64_t)microseconds;
}