fix(PCNT): Add zero input gpio enable flag

The default zero input gpio num is 0. Users need to set it additionally
when not use zero input signal. It may become a breaking change.
Add an enable flag to avoid it.
This commit is contained in:
Chen Jichang
2023-08-16 15:06:51 +08:00
parent 52bca70b1a
commit d7d87813ad
7 changed files with 180 additions and 99 deletions

View File

@@ -63,15 +63,8 @@ typedef struct {
int high_limit; /*!< High limitation of the count unit, should be higher than 0 */
int intr_priority; /*!< PCNT interrupt priority,
if set to 0, the driver will try to allocate an interrupt with a relative low priority (1,2,3) */
#if SOC_PCNT_SUPPORT_ZERO_INPUT
int zero_input_gpio_num; /*!< GPIO number used by the clear signal, the default active level is high, input mode with pull down enabled. Set to -1 if unused */
#endif
struct {
uint32_t accum_count: 1; /*!< Whether to accumulate the count value when overflows at the high/low limit */
#if SOC_PCNT_SUPPORT_ZERO_INPUT
uint32_t invert_zero_input: 1; /*!< Invert the zero input signal and set input mode with pull up.*/
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */
#endif
} flags; /*!< Extra flags */
} pcnt_unit_config_t;
@@ -146,6 +139,34 @@ esp_err_t pcnt_del_unit(pcnt_unit_handle_t unit);
*/
esp_err_t pcnt_unit_set_glitch_filter(pcnt_unit_handle_t unit, const pcnt_glitch_filter_config_t *config);
#if SOC_PCNT_SUPPORT_CLEAR_SIGNAL
/**
* @brief PCNT clear signal configuration
*/
typedef struct {
int clear_signal_gpio_num; /*!< GPIO number used by the clear signal, the default active level is high, input mode with pull down enabled */
struct {
uint32_t invert_clear_signal: 1; /*!< Invert the clear input signal and set input mode with pull up */
uint32_t io_loop_back: 1; /*!< For debug/test, the signal output from the GPIO will be fed to the input path as well */
} flags; /*!< clear signal config flags */
} pcnt_clear_signal_config_t;
/**
* @brief Set clear signal for PCNT unit
*
* @note The function of clear signal is the same as `pcnt_unit_clear_count()`. High-level Active
*
* @param[in] unit PCNT unit handle created by `pcnt_new_unit()`
* @param[in] config PCNT clear signal configuration, set config to NULL means disabling the clear signal
* @return
* - ESP_OK: Set clear signal successfully
* - ESP_ERR_INVALID_ARG: Set clear signal failed because of invalid argument
* - ESP_ERR_INVALID_STATE: Set clear signal failed because set clear signal repeatly or disable clear signal before set it
* - ESP_FAIL: Set clear signal failed because of other error
*/
esp_err_t pcnt_unit_set_clear_signal(pcnt_unit_handle_t unit, const pcnt_clear_signal_config_t *config);
#endif
/**
* @brief Enable the PCNT unit
*

View File

@@ -87,7 +87,7 @@ struct pcnt_unit_t {
int unit_id; // allocated unit numerical ID
int low_limit; // low limit value
int high_limit; // high limit value
int zero_input_gpio_num; // which gpio clear signal input
int clear_signal_gpio_num; // which gpio clear signal input
int accum_value; // accumulated count value
pcnt_chan_t *channels[SOC_PCNT_CHANNELS_PER_UNIT]; // array of PCNT channels
pcnt_watch_point_t watchers[PCNT_LL_WATCH_EVENT_MAX]; // array of PCNT watchers
@@ -235,6 +235,7 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re
unit->high_limit = config->high_limit;
unit->low_limit = config->low_limit;
unit->accum_value = 0;
unit->clear_signal_gpio_num = -1;
unit->flags.accum_count = config->flags.accum_count;
// clear/pause register is shared by all units, so using group's spinlock
@@ -253,29 +254,6 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re
unit->watchers[i].event_id = PCNT_LL_WATCH_EVENT_INVALID; // invalid all watch point
}
#if SOC_PCNT_SUPPORT_ZERO_INPUT
// GPIO configuration
gpio_config_t gpio_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), // also enable the output path if `io_loop_back` is enabled
.pull_down_en = true,
.pull_up_en = false,
};
if (config->zero_input_gpio_num >= 0) {
if (config->flags.invert_zero_input) {
gpio_conf.pull_down_en = false;
gpio_conf.pull_up_en = true;
}
gpio_conf.pin_bit_mask = 1ULL << config->zero_input_gpio_num;
ESP_GOTO_ON_ERROR(gpio_config(&gpio_conf), err, TAG, "config zero GPIO failed");
esp_rom_gpio_connect_in_signal(config->zero_input_gpio_num,
pcnt_periph_signals.groups[group_id].units[unit_id].clear_sig,
config->flags.invert_zero_input);
}
unit->zero_input_gpio_num = config->zero_input_gpio_num;
#endif // SOC_PCNT_SUPPORT_ZERO_INPUT
ESP_LOGD(TAG, "new pcnt unit (%d,%d) at %p, count range:[%d,%d]", group_id, unit_id, unit, unit->low_limit, unit->high_limit);
*ret_unit = unit;
return ESP_OK;
@@ -299,11 +277,11 @@ esp_err_t pcnt_del_unit(pcnt_unit_handle_t unit)
ESP_RETURN_ON_FALSE(!unit->channels[i], ESP_ERR_INVALID_STATE, TAG, "channel %d still in working", i);
}
#if SOC_PCNT_SUPPORT_ZERO_INPUT
if (unit->zero_input_gpio_num >= 0) {
gpio_reset_pin(unit->zero_input_gpio_num);
#if SOC_PCNT_SUPPORT_CLEAR_SIGNAL
if (unit->clear_signal_gpio_num >= 0) {
gpio_reset_pin(unit->clear_signal_gpio_num);
}
#endif // SOC_PCNT_SUPPORT_ZERO_INPUT
#endif // SOC_PCNT_SUPPORT_CLEAR_SIGNAL
ESP_LOGD(TAG, "del unit (%d,%d)", group_id, unit_id);
// recycle memory resource
@@ -311,6 +289,40 @@ esp_err_t pcnt_del_unit(pcnt_unit_handle_t unit)
return ESP_OK;
}
#if SOC_PCNT_SUPPORT_CLEAR_SIGNAL
esp_err_t pcnt_unit_set_clear_signal(pcnt_unit_handle_t unit, const pcnt_clear_signal_config_t *config)
{
ESP_RETURN_ON_FALSE(unit, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
pcnt_group_t *group = unit->group;
int group_id = group->group_id;
int unit_id = unit->unit_id;
if (config) {
gpio_config_t gpio_conf = {
.intr_type = GPIO_INTR_DISABLE,
.mode = GPIO_MODE_INPUT | (config->flags.io_loop_back ? GPIO_MODE_OUTPUT : 0), // also enable the output path if `io_loop_back` is enabled
.pull_down_en = true,
.pull_up_en = false,
};
if (config->flags.invert_clear_signal) {
gpio_conf.pull_down_en = false;
gpio_conf.pull_up_en = true;
}
gpio_conf.pin_bit_mask = 1ULL << config->clear_signal_gpio_num;
ESP_RETURN_ON_ERROR(gpio_config(&gpio_conf), TAG, "config zero signal GPIO failed");
esp_rom_gpio_connect_in_signal(config->clear_signal_gpio_num,
pcnt_periph_signals.groups[group_id].units[unit_id].clear_sig,
config->flags.invert_clear_signal);
unit->clear_signal_gpio_num = config->clear_signal_gpio_num;
} else {
ESP_RETURN_ON_FALSE(unit->clear_signal_gpio_num >= 0, ESP_ERR_INVALID_STATE, TAG, "zero signal not set yet");
gpio_reset_pin(unit->clear_signal_gpio_num);
unit->clear_signal_gpio_num = -1;
}
return ESP_OK;
}
#endif // SOC_PCNT_SUPPORT_CLEAR_SIGNAL
esp_err_t pcnt_unit_set_glitch_filter(pcnt_unit_handle_t unit, const pcnt_glitch_filter_config_t *config)
{
pcnt_group_t *group = NULL;

View File

@@ -21,9 +21,6 @@ TEST_CASE("pcnt_unit_install_uninstall", "[pcnt]")
.low_limit = -100,
.high_limit = 100,
.intr_priority = 0,
#if SOC_PCNT_SUPPORT_ZERO_INPUT
.zero_input_gpio_num = -1,
#endif
};
pcnt_unit_handle_t units[SOC_PCNT_UNITS_PER_GROUP];
int count_value = 0;
@@ -91,9 +88,6 @@ TEST_CASE("pcnt_channel_install_uninstall", "[pcnt]")
pcnt_unit_config_t unit_config = {
.low_limit = -100,
.high_limit = 100,
#if SOC_PCNT_SUPPORT_ZERO_INPUT
.zero_input_gpio_num = -1,
#endif
};
pcnt_chan_config_t chan_config = {
.edge_gpio_num = TEST_PCNT_GPIO_A, // only detect edge signal in this case
@@ -182,9 +176,6 @@ TEST_CASE("pcnt_multiple_units_pulse_count", "[pcnt]")
pcnt_unit_config_t unit_config = {
.low_limit = -100,
.high_limit = 100,
#if SOC_PCNT_SUPPORT_ZERO_INPUT
.zero_input_gpio_num = -1,
#endif
};
pcnt_unit_handle_t units[2];
for (int i = 0; i < 2; i++) {
@@ -250,9 +241,6 @@ TEST_CASE("pcnt_quadrature_decode_event", "[pcnt]")
pcnt_unit_config_t unit_config = {
.low_limit = -100,
.high_limit = 100,
#if SOC_PCNT_SUPPORT_ZERO_INPUT
.zero_input_gpio_num = -1,
#endif
};
printf("install pcnt unit\r\n");
@@ -376,9 +364,6 @@ TEST_CASE("pcnt_zero_cross_mode", "[pcnt]")
pcnt_unit_config_t unit_config = {
.low_limit = -100,
.high_limit = 100,
#if SOC_PCNT_SUPPORT_ZERO_INPUT
.zero_input_gpio_num = -1,
#endif
};
printf("install pcnt unit\r\n");
@@ -471,9 +456,6 @@ TEST_CASE("pcnt_virtual_io", "[pcnt]")
pcnt_unit_config_t unit_config = {
.low_limit = -100,
.high_limit = 100,
#if SOC_PCNT_SUPPORT_ZERO_INPUT
.zero_input_gpio_num = -1,
#endif
};
pcnt_chan_config_t chan_config = {
.edge_gpio_num = TEST_PCNT_GPIO_A, // only detect edge signal in this case
@@ -520,23 +502,24 @@ TEST_CASE("pcnt_virtual_io", "[pcnt]")
TEST_ESP_OK(pcnt_del_unit(unit));
}
#if SOC_PCNT_SUPPORT_ZERO_INPUT
#if SOC_PCNT_SUPPORT_CLEAR_SIGNAL
TEST_CASE("pcnt_zero_input_signal", "[pcnt]")
{
pcnt_unit_config_t unit_config = {
.low_limit = -1000,
.high_limit = 1000,
.zero_input_gpio_num = TEST_PCNT_GPIO_Z,
.flags.io_loop_back = true,
};
printf("install pcnt unit\r\n");
pcnt_unit_handle_t unit = NULL;
TEST_ESP_OK(pcnt_new_unit(&unit_config, &unit));
pcnt_glitch_filter_config_t filter_config = {
.max_glitch_ns = 1000,
pcnt_clear_signal_config_t clear_signal_config = {
.clear_signal_gpio_num = TEST_PCNT_GPIO_Z,
.flags.io_loop_back = true,
};
TEST_ESP_OK(pcnt_unit_set_glitch_filter(unit, &filter_config));
TEST_ESP_OK(pcnt_unit_set_clear_signal(unit, &clear_signal_config));
printf("install pcnt channels\r\n");
pcnt_chan_config_t chan_config = {
@@ -571,11 +554,30 @@ TEST_CASE("pcnt_zero_input_signal", "[pcnt]")
TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value));
printf("count_value=%d\r\n", count_value);
TEST_ASSERT_EQUAL(0, count_value); // 0 after rst_sig
TEST_ASSERT_EQUAL(0, count_value); // 0 after zero signal
printf("remove zero signal\r\n");
TEST_ESP_OK(pcnt_unit_set_clear_signal(unit, NULL));
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, pcnt_unit_set_clear_signal(unit, NULL));
// trigger 10 rising edge on GPIO
test_gpio_simulate_rising_edge(TEST_PCNT_GPIO_A, 10);
TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value));
printf("count_value=%d\r\n", count_value);
TEST_ASSERT_EQUAL(10, count_value);
printf("simulating zero input signal\r\n");
TEST_ESP_OK(gpio_set_level(TEST_PCNT_GPIO_Z, 1));
TEST_ESP_OK(gpio_set_level(TEST_PCNT_GPIO_Z, 0));
TEST_ESP_OK(pcnt_unit_get_count(unit, &count_value));
printf("count_value=%d\r\n", count_value);
TEST_ASSERT_EQUAL(10, count_value); // 10 with no zero signal
TEST_ESP_OK(pcnt_del_channel(channel));
TEST_ESP_OK(pcnt_unit_stop(unit));
TEST_ESP_OK(pcnt_unit_disable(unit));
TEST_ESP_OK(pcnt_del_unit(unit));
}
#endif // SOC_PCNT_SUPPORT_ZERO_INPUT
#endif // SOC_PCNT_SUPPORT_CLEAR_SIGNAL

View File

@@ -459,7 +459,7 @@ config SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE
bool
default y
config SOC_PCNT_SUPPORT_ZERO_INPUT
config SOC_PCNT_SUPPORT_CLEAR_SIGNAL
bool
default y

View File

@@ -262,7 +262,7 @@
#define SOC_PCNT_CHANNELS_PER_UNIT 2
#define SOC_PCNT_THRES_POINT_PER_UNIT 2
#define SOC_PCNT_SUPPORT_RUNTIME_THRES_UPDATE 1
#define SOC_PCNT_SUPPORT_ZERO_INPUT 1 /*!< Support encoder with Zero phase input */
#define SOC_PCNT_SUPPORT_CLEAR_SIGNAL 1 /*!< Support clear signal input */
/*--------------------------- RMT CAPS ---------------------------------------*/
#define SOC_RMT_GROUPS 1U /*!< One RMT group */