mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(pcnt): support set interrupt priority(v5.0)
This commit is contained in:
parent
7065c87963
commit
03ec7a0c5d
@ -61,6 +61,8 @@ typedef struct {
|
||||
typedef struct {
|
||||
int low_limit; /*!< Low limitation of the count unit, should be lower than 0 */
|
||||
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) */
|
||||
struct {
|
||||
uint32_t accum_count: 1; /*!< Whether to accumulate the count value when overflows at the high/low limit */
|
||||
} flags; /*!< Extra flags */
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -41,11 +41,13 @@
|
||||
#endif
|
||||
|
||||
#if CONFIG_PCNT_ISR_IRAM_SAFE
|
||||
#define PCNT_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
|
||||
#define PCNT_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
|
||||
#else
|
||||
#define PCNT_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_LOWMED | ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
|
||||
#define PCNT_INTR_ALLOC_FLAGS (ESP_INTR_FLAG_INTRDISABLED | ESP_INTR_FLAG_SHARED)
|
||||
#endif
|
||||
|
||||
#define PCNT_ALLOW_INTR_PRIORITY_MASK ESP_INTR_FLAG_LOWMED
|
||||
|
||||
#define PCNT_PM_LOCK_NAME_LEN_MAX 16
|
||||
|
||||
static const char *TAG = "pcnt";
|
||||
@ -63,6 +65,7 @@ struct pcnt_platform_t {
|
||||
|
||||
struct pcnt_group_t {
|
||||
int group_id; // Group ID, index from 0
|
||||
int intr_priority; // PCNT interrupt priority
|
||||
portMUX_TYPE spinlock; // to protect per-group register level concurrent access
|
||||
pcnt_hal_context_t hal;
|
||||
pcnt_unit_t *units[SOC_PCNT_UNITS_PER_GROUP]; // array of PCNT units
|
||||
@ -181,6 +184,10 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re
|
||||
ESP_GOTO_ON_FALSE(config->low_limit < 0 && config->high_limit > 0 && config->low_limit >= PCNT_LL_MIN_LIN &&
|
||||
config->high_limit <= PCNT_LL_MAX_LIM, ESP_ERR_INVALID_ARG, err, TAG,
|
||||
"invalid limit range:[%d,%d]", config->low_limit, config->high_limit);
|
||||
if (config->intr_priority) {
|
||||
ESP_RETURN_ON_FALSE(1 << (config->intr_priority) & PCNT_ALLOW_INTR_PRIORITY_MASK, ESP_ERR_INVALID_ARG,
|
||||
TAG, "invalid interrupt priority:%d", config->intr_priority);
|
||||
}
|
||||
|
||||
unit = heap_caps_calloc(1, sizeof(pcnt_unit_t), PCNT_MEM_ALLOC_CAPS);
|
||||
ESP_GOTO_ON_FALSE(unit, ESP_ERR_NO_MEM, err, TAG, "no mem for unit");
|
||||
@ -190,10 +197,27 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re
|
||||
int group_id = group->group_id;
|
||||
int unit_id = unit->unit_id;
|
||||
|
||||
// if interrupt priority specified before, it cannot be changed until `pcnt_release_group_handle()` called
|
||||
// so we have to check if the new priority specified consistents with the old one
|
||||
bool intr_priority_conflict = false;
|
||||
portENTER_CRITICAL(&group->spinlock);
|
||||
if (group->intr_priority == -1) {
|
||||
group->intr_priority = config->intr_priority;
|
||||
} else if (config->intr_priority != 0) {
|
||||
intr_priority_conflict = (group->intr_priority != config->intr_priority);
|
||||
}
|
||||
portEXIT_CRITICAL(&group->spinlock);
|
||||
ESP_GOTO_ON_FALSE(!intr_priority_conflict, ESP_ERR_INVALID_STATE, err, TAG, "intr_priority conflict, already is %d but attempt to %d", group->intr_priority, config->intr_priority);
|
||||
|
||||
// to accumulate count value, we should install the interrupt handler first, and in the ISR we do the accumulation
|
||||
bool to_install_isr = (config->flags.accum_count == 1);
|
||||
if (to_install_isr) {
|
||||
int isr_flags = PCNT_INTR_ALLOC_FLAGS;
|
||||
if (group->intr_priority) {
|
||||
isr_flags |= 1 << (group->intr_priority);
|
||||
} else {
|
||||
isr_flags |= PCNT_ALLOW_INTR_PRIORITY_MASK;
|
||||
}
|
||||
ESP_GOTO_ON_ERROR(esp_intr_alloc_intrstatus(pcnt_periph_signals.groups[group_id].irq, isr_flags,
|
||||
(uint32_t)pcnt_ll_get_intr_status_reg(group->hal.dev), PCNT_LL_UNIT_WATCH_EVENT(unit_id),
|
||||
pcnt_default_isr, unit, &unit->intr), err,
|
||||
@ -224,6 +248,7 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re
|
||||
|
||||
unit->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
unit->fsm = PCNT_UNIT_FSM_INIT;
|
||||
|
||||
for (int i = 0; i < PCNT_LL_WATCH_EVENT_MAX; i++) {
|
||||
unit->watchers[i].event_id = PCNT_LL_WATCH_EVENT_INVALID; // invalid all watch point
|
||||
}
|
||||
@ -409,6 +434,11 @@ esp_err_t pcnt_unit_register_event_callbacks(pcnt_unit_handle_t unit, const pcnt
|
||||
if (!unit->intr) {
|
||||
ESP_RETURN_ON_FALSE(unit->fsm == PCNT_UNIT_FSM_INIT, ESP_ERR_INVALID_STATE, TAG, "unit not in init state");
|
||||
int isr_flags = PCNT_INTR_ALLOC_FLAGS;
|
||||
if (group->intr_priority) {
|
||||
isr_flags |= 1 << (group->intr_priority);
|
||||
} else {
|
||||
isr_flags |= PCNT_ALLOW_INTR_PRIORITY_MASK;
|
||||
}
|
||||
ESP_RETURN_ON_ERROR(esp_intr_alloc_intrstatus(pcnt_periph_signals.groups[group_id].irq, isr_flags,
|
||||
(uint32_t)pcnt_ll_get_intr_status_reg(group->hal.dev), PCNT_LL_UNIT_WATCH_EVENT(unit_id),
|
||||
pcnt_default_isr, unit, &unit->intr),
|
||||
@ -688,6 +718,7 @@ static pcnt_group_t *pcnt_acquire_group_handle(int group_id)
|
||||
s_platform.groups[group_id] = group; // register to platform
|
||||
// initialize pcnt group members
|
||||
group->group_id = group_id;
|
||||
group->intr_priority = -1;
|
||||
group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED;
|
||||
// enable APB access pcnt registers
|
||||
periph_module_enable(pcnt_periph_signals.groups[group_id].module);
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -25,11 +25,18 @@ TEST_CASE("pcnt_unit_install_uninstall", "[pcnt]")
|
||||
int count_value = 0;
|
||||
|
||||
printf("install pcnt units and check initial count\r\n");
|
||||
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
|
||||
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP - 1; i++) {
|
||||
TEST_ESP_OK(pcnt_new_unit(&unit_config, &units[i]));
|
||||
TEST_ESP_OK(pcnt_unit_get_count(units[i], &count_value));
|
||||
TEST_ASSERT_EQUAL(0, count_value);
|
||||
}
|
||||
|
||||
// unit with a different intrrupt priority
|
||||
unit_config.intr_priority = 3;
|
||||
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, pcnt_new_unit(&unit_config, &units[SOC_PCNT_UNITS_PER_GROUP - 1]));
|
||||
unit_config.intr_priority = 0;
|
||||
TEST_ESP_OK(pcnt_new_unit(&unit_config, &units[SOC_PCNT_UNITS_PER_GROUP - 1]));
|
||||
|
||||
// no more free pcnt units
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_NOT_FOUND, pcnt_new_unit(&unit_config, &units[0]));
|
||||
|
||||
@ -43,9 +50,12 @@ TEST_CASE("pcnt_unit_install_uninstall", "[pcnt]")
|
||||
// invalid glitch configuration
|
||||
filter_config.max_glitch_ns = 500000;
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, pcnt_unit_set_glitch_filter(units[0], &filter_config));
|
||||
|
||||
pcnt_event_callbacks_t cbs = {
|
||||
.on_reach = NULL,
|
||||
};
|
||||
printf("enable pcnt units\r\n");
|
||||
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
|
||||
TEST_ESP_OK(pcnt_unit_register_event_callbacks(units[i], &cbs, NULL));
|
||||
TEST_ESP_OK(pcnt_unit_enable(units[i]));
|
||||
}
|
||||
|
||||
|
@ -46,6 +46,11 @@ To install a PCNT unit, there's a configuration structure that needs to be given
|
||||
|
||||
- :cpp:member:`pcnt_unit_config_t::low_limit` and :cpp:member:`pcnt_unit_config_t::high_limit` specify the range for the internal hardware counter. The counter will reset to zero automatically when it crosses either the high or low limit.
|
||||
- :cpp:member:`pcnt_unit_config_t::accum_count` sets whether to create an internal accumulator for the counter. This is helpful when you want to extend the counter's width, which by default is 16bit at most, defined in the hardware. See also :ref:`pcnt-compensate-overflow-loss` for how to use this feature to compensate the overflow loss.
|
||||
- :cpp:member:`pcnt_unit_config_t::intr_priority` sets the priority of the timer interrupt. If it is set to ``0``, the driver will allocate an interrupt with a default priority. Otherwise, the driver will use the given priority.
|
||||
|
||||
.. note::
|
||||
|
||||
Since all PCNT units share the same interrupt source, when installing multiple PCNT units make sure that the interrupt priority :cpp:member:`pcnt_unit_config_t::intr_priority` is the same for each unit.
|
||||
|
||||
Unit allocation and initialization is done by calling a function :cpp:func:`pcnt_new_unit` with :cpp:type:`pcnt_unit_config_t` as an input parameter. The function will return a PCNT unit handle only when it runs correctly. Specifically, when there are no more free PCNT units in the pool (i.e. unit resources have been used up), then this function will return :c:macro:`ESP_ERR_NOT_FOUND` error. The total number of available PCNT units is recorded by :c:macro:`SOC_PCNT_UNITS_PER_GROUP` for reference.
|
||||
|
||||
|
@ -46,6 +46,11 @@ PCNT 单元和通道分别用 :cpp:type:`pcnt_unit_handle_t` 与 :cpp:type:`pcnt
|
||||
|
||||
- :cpp:member:`pcnt_unit_config_t::low_limit` 与 :cpp:member:`pcnt_unit_config_t::high_limit` 用于指定内部计数器的最小值和最大值。当计数器超过任一限值时,计数器将归零。
|
||||
- :cpp:member:`pcnt_unit_config_t::accum_count` 用于设置是否需要软件在硬件计数值溢出的时候进行累加保存,这有助于“拓宽”计数器的实际位宽。默认情况下,计数器的位宽最高只有 16 比特。请参考 :ref:`pcnt-compensate-overflow-loss` 了解如何利用此功能来补偿硬件计数器的溢出损失。
|
||||
- :cpp:member:`pcnt_unit_config_t::intr_priority` 设置中断的优先级。如果设置为 ``0``,则会分配一个默认优先级的中断,否则会使用指定的优先级。
|
||||
|
||||
.. note::
|
||||
|
||||
由于所有 PCNT 单元共享一个中断源,安装多个 PCNT 单元时请确保每个单元的中断优先级 :cpp:member:`pcnt_unit_config_t::intr_priority` 一致。
|
||||
|
||||
调用函数 :cpp:func:`pcnt_new_unit` 并将 :cpp:type:`pcnt_unit_config_t` 作为其输入值,可对 PCNT 单元进行分配和初始化。该函数正常运行时,会返回一个 PCNT 单元句柄。没有可用的 PCNT 单元时(即 PCNT 单元全部被占用),该函数会返回错误 :c:macro:`ESP_ERR_NOT_FOUND`。可用的 PCNT 单元总数记录在 :c:macro:`SOC_PCNT_UNITS_PER_GROUP` 中,以供参考。
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user