kopia lustrzana https://github.com/espressif/esp-idf
Merge branch 'doc/pcnt_overflow_compensate_v5.0' into 'release/v5.0'
pcnt: support accumulate the count value in the high/low limit event (v5.0) See merge request espressif/esp-idf!21170pull/10970/head
commit
302ecb0764
|
@ -61,6 +61,9 @@ typedef struct {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int low_limit; /*!< Low limitation of the count unit, should be lower than 0 */
|
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 high_limit; /*!< High limitation of the count unit, should be higher than 0 */
|
||||||
|
struct {
|
||||||
|
uint32_t accum_count: 1; /*!< Whether to accumulate the count value when overflows at the high/low limit */
|
||||||
|
} flags; /*!< Extra flags */
|
||||||
} pcnt_unit_config_t;
|
} pcnt_unit_config_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -84,6 +84,7 @@ struct pcnt_unit_t {
|
||||||
int unit_id; // allocated unit numerical ID
|
int unit_id; // allocated unit numerical ID
|
||||||
int low_limit; // low limit value
|
int low_limit; // low limit value
|
||||||
int high_limit; // high limit value
|
int high_limit; // high limit value
|
||||||
|
int accum_value; // accumulated count value
|
||||||
pcnt_chan_t *channels[SOC_PCNT_CHANNELS_PER_UNIT]; // array of PCNT channels
|
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
|
pcnt_watch_point_t watchers[PCNT_LL_WATCH_EVENT_MAX]; // array of PCNT watchers
|
||||||
intr_handle_t intr; // interrupt handle
|
intr_handle_t intr; // interrupt handle
|
||||||
|
@ -94,6 +95,9 @@ struct pcnt_unit_t {
|
||||||
pcnt_unit_fsm_t fsm; // record PCNT unit's driver state
|
pcnt_unit_fsm_t fsm; // record PCNT unit's driver state
|
||||||
pcnt_watch_cb_t on_reach; // user registered callback function
|
pcnt_watch_cb_t on_reach; // user registered callback function
|
||||||
void *user_data; // user data registered by user, which would be passed to the right callback function
|
void *user_data; // user data registered by user, which would be passed to the right callback function
|
||||||
|
struct {
|
||||||
|
uint32_t accum_count: 1; /*!< Whether to accumulate the count value when overflows at the high/low limit */
|
||||||
|
} flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct pcnt_chan_t {
|
struct pcnt_chan_t {
|
||||||
|
@ -186,6 +190,16 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re
|
||||||
int group_id = group->group_id;
|
int group_id = group->group_id;
|
||||||
int unit_id = unit->unit_id;
|
int unit_id = unit->unit_id;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
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,
|
||||||
|
TAG, "install interrupt service failed");
|
||||||
|
}
|
||||||
|
|
||||||
// some events are enabled by default, disable them all
|
// some events are enabled by default, disable them all
|
||||||
pcnt_ll_disable_all_events(group->hal.dev, unit_id);
|
pcnt_ll_disable_all_events(group->hal.dev, unit_id);
|
||||||
// disable filter by default
|
// disable filter by default
|
||||||
|
@ -196,12 +210,15 @@ esp_err_t pcnt_new_unit(const pcnt_unit_config_t *config, pcnt_unit_handle_t *re
|
||||||
pcnt_ll_set_low_limit_value(group->hal.dev, unit_id, config->low_limit);
|
pcnt_ll_set_low_limit_value(group->hal.dev, unit_id, config->low_limit);
|
||||||
unit->high_limit = config->high_limit;
|
unit->high_limit = config->high_limit;
|
||||||
unit->low_limit = config->low_limit;
|
unit->low_limit = config->low_limit;
|
||||||
|
unit->accum_value = 0;
|
||||||
|
unit->flags.accum_count = config->flags.accum_count;
|
||||||
|
|
||||||
// clear/pause register is shared by all units, so using group's spinlock
|
// clear/pause register is shared by all units, so using group's spinlock
|
||||||
portENTER_CRITICAL(&group->spinlock);
|
portENTER_CRITICAL(&group->spinlock);
|
||||||
pcnt_ll_stop_count(group->hal.dev, unit_id);
|
pcnt_ll_stop_count(group->hal.dev, unit_id);
|
||||||
pcnt_ll_clear_count(group->hal.dev, unit_id);
|
pcnt_ll_clear_count(group->hal.dev, unit_id);
|
||||||
pcnt_ll_enable_intr(group->hal.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id), false);
|
// enable the interrupt if we want to accumulate the counter in the ISR
|
||||||
|
pcnt_ll_enable_intr(group->hal.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id), to_install_isr);
|
||||||
pcnt_ll_clear_intr_status(group->hal.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id));
|
pcnt_ll_clear_intr_status(group->hal.dev, PCNT_LL_UNIT_WATCH_EVENT(unit_id));
|
||||||
portEXIT_CRITICAL(&group->spinlock);
|
portEXIT_CRITICAL(&group->spinlock);
|
||||||
|
|
||||||
|
@ -349,6 +366,11 @@ esp_err_t pcnt_unit_clear_count(pcnt_unit_handle_t unit)
|
||||||
pcnt_ll_clear_count(group->hal.dev, unit->unit_id);
|
pcnt_ll_clear_count(group->hal.dev, unit->unit_id);
|
||||||
portEXIT_CRITICAL_SAFE(&group->spinlock);
|
portEXIT_CRITICAL_SAFE(&group->spinlock);
|
||||||
|
|
||||||
|
// reset the accumulated count as well
|
||||||
|
portENTER_CRITICAL_SAFE(&unit->spinlock);
|
||||||
|
unit->accum_value = 0;
|
||||||
|
portEXIT_CRITICAL_SAFE(&unit->spinlock);
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,7 +379,11 @@ esp_err_t pcnt_unit_get_count(pcnt_unit_handle_t unit, int *value)
|
||||||
pcnt_group_t *group = NULL;
|
pcnt_group_t *group = NULL;
|
||||||
ESP_RETURN_ON_FALSE_ISR(unit && value, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
ESP_RETURN_ON_FALSE_ISR(unit && value, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
|
||||||
group = unit->group;
|
group = unit->group;
|
||||||
*value = pcnt_ll_get_count(group->hal.dev, unit->unit_id);
|
|
||||||
|
// the accum_value is also accessed by the ISR, so adding a critical section
|
||||||
|
portENTER_CRITICAL_SAFE(&unit->spinlock);
|
||||||
|
*value = pcnt_ll_get_count(group->hal.dev, unit->unit_id) + unit->accum_value;
|
||||||
|
portEXIT_CRITICAL_SAFE(&unit->spinlock);
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
@ -723,6 +749,16 @@ IRAM_ATTR static void pcnt_default_isr(void *args)
|
||||||
int event_id = __builtin_ffs(event_status) - 1;
|
int event_id = __builtin_ffs(event_status) - 1;
|
||||||
event_status &= (event_status - 1); // clear the right most bit
|
event_status &= (event_status - 1); // clear the right most bit
|
||||||
|
|
||||||
|
portENTER_CRITICAL_ISR(&unit->spinlock);
|
||||||
|
if (unit->flags.accum_count) {
|
||||||
|
if (event_id == PCNT_LL_WATCH_EVENT_LOW_LIMIT) {
|
||||||
|
unit->accum_value += unit->low_limit;
|
||||||
|
} else if (event_id == PCNT_LL_WATCH_EVENT_HIGH_LIMIT) {
|
||||||
|
unit->accum_value += unit->high_limit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL_ISR(&unit->spinlock);
|
||||||
|
|
||||||
// invoked user registered callback
|
// invoked user registered callback
|
||||||
if (on_reach) {
|
if (on_reach) {
|
||||||
pcnt_watch_event_data_t edata = {
|
pcnt_watch_event_data_t edata = {
|
||||||
|
|
|
@ -44,7 +44,8 @@ Install PCNT Unit
|
||||||
|
|
||||||
To install a PCNT unit, there's a configuration structure that needs to be given in advance: :cpp:type:`pcnt_unit_config_t`:
|
To install a PCNT unit, there's a configuration structure that needs to be given in advance: :cpp:type:`pcnt_unit_config_t`:
|
||||||
|
|
||||||
- :cpp:member:`pcnt_unit_config_t::low_limit` and :cpp:member:`pcnt_unit_config_t::high_limit` specify the range for the internal counter. Counter will back to zero when it crosses either limit value.
|
- :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.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
|
@ -223,17 +224,28 @@ Note, :cpp:func:`pcnt_unit_start` and :cpp:func:`pcnt_unit_stop` should be calle
|
||||||
Get Count Value
|
Get Count Value
|
||||||
~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
You can check current count value at any time by calling :cpp:func:`pcnt_unit_get_count`.
|
You can read current count value at any time by calling :cpp:func:`pcnt_unit_get_count`. The returned count value is a **signed** integer, where the sign can be used to reflect the direction.
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
The returned count value is a **signed** integer, where the sign can be used to reflect the direction. The internal counter will overflow when it reaches high or low limit, but this function doesn't compensate for that loss.
|
|
||||||
|
|
||||||
.. code:: c
|
.. code:: c
|
||||||
|
|
||||||
int pulse_count = 0;
|
int pulse_count = 0;
|
||||||
ESP_ERROR_CHECK(pcnt_unit_get_count(pcnt_unit, &pulse_count));
|
ESP_ERROR_CHECK(pcnt_unit_get_count(pcnt_unit, &pulse_count));
|
||||||
|
|
||||||
|
.. _pcnt-compensate-overflow-loss:
|
||||||
|
|
||||||
|
Compensate Overflow Loss
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
The internal hardware counter will be cleared to zero automatically when it reaches high or low limit. If you want to compensate for that count loss and extend the counter's bit-width, you can:
|
||||||
|
|
||||||
|
1. Enable :cpp:member:`pcnt_unit_config_t::accum_count` when installing the PCNT unit.
|
||||||
|
2. Add the high/low limit as the :ref:`pcnt-watch-points`.
|
||||||
|
3. Now, the returned count value from the :cpp:func:`pcnt_unit_get_count` function not only reflects the hardware's count value, but also accumulates the high/low overflow loss to it.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
:cpp:func:`pcnt_unit_clear_count` will reset the accumulated count value as well.
|
||||||
|
|
||||||
.. _pcnt-power-management:
|
.. _pcnt-power-management:
|
||||||
|
|
||||||
Power Management
|
Power Management
|
||||||
|
|
|
@ -45,6 +45,7 @@ PCNT 单元和通道分别用 :cpp:type:`pcnt_unit_handle_t` 与 :cpp:type:`pcnt
|
||||||
安装 PCNT 单元时,需要先完成配置 :cpp:type:`pcnt_unit_config_t`:
|
安装 PCNT 单元时,需要先完成配置 :cpp:type:`pcnt_unit_config_t`:
|
||||||
|
|
||||||
- :cpp:member:`pcnt_unit_config_t::low_limit` 与 :cpp:member:`pcnt_unit_config_t::high_limit` 用于指定内部计数器的最小值和最大值。当计数器超过任一限值时,计数器将归零。
|
- :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: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` 中,以供参考。
|
调用函数 :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` 中,以供参考。
|
||||||
|
|
||||||
|
@ -110,8 +111,8 @@ PCNT 单元和通道分别用 :cpp:type:`pcnt_unit_handle_t` 与 :cpp:type:`pcnt
|
||||||
|
|
||||||
.. _pcnt-watch-points:
|
.. _pcnt-watch-points:
|
||||||
|
|
||||||
配置观察点
|
PCNT 观察点
|
||||||
^^^^^^^^^^
|
^^^^^^^^^^^
|
||||||
|
|
||||||
PCNT 单元可被设置为观察几个特定的数值,这些被观察的数值被称为 **观察点**。观察点不能超过 :cpp:type:`pcnt_unit_config_t` 设置的范围,最小值和最大值分别为 :cpp:member:`pcnt_unit_config_t::low_limit` 和 :cpp:member:`pcnt_unit_config_t::high_limit`。当计数器到达任一观察点时,会触发一个观察事件,如果在 :cpp:func:`pcnt_unit_register_event_callbacks` 注册过事件回调函数,该事件就会通过中断通知您。关于如何注册事件回调函数,请参考 :ref:`pcnt-register-event-callbacks`。
|
PCNT 单元可被设置为观察几个特定的数值,这些被观察的数值被称为 **观察点**。观察点不能超过 :cpp:type:`pcnt_unit_config_t` 设置的范围,最小值和最大值分别为 :cpp:member:`pcnt_unit_config_t::low_limit` 和 :cpp:member:`pcnt_unit_config_t::high_limit`。当计数器到达任一观察点时,会触发一个观察事件,如果在 :cpp:func:`pcnt_unit_register_event_callbacks` 注册过事件回调函数,该事件就会通过中断通知您。关于如何注册事件回调函数,请参考 :ref:`pcnt-register-event-callbacks`。
|
||||||
|
|
||||||
|
@ -205,8 +206,8 @@ PCNT 单元的滤波器可滤除信号中的短时毛刺,:cpp:type:`pcnt_glitc
|
||||||
|
|
||||||
.. _pcnt-unit-io-control:
|
.. _pcnt-unit-io-control:
|
||||||
|
|
||||||
控制单元 IO
|
控制单元 IO 操作
|
||||||
^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
启用/停用及清零
|
启用/停用及清零
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^
|
||||||
|
@ -223,17 +224,28 @@ PCNT 单元的滤波器可滤除信号中的短时毛刺,:cpp:type:`pcnt_glitc
|
||||||
获取计数器数值
|
获取计数器数值
|
||||||
^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
通过调用 :cpp:func:`pcnt_unit_get_count` 可随时获取当前计数器的数值。
|
调用 :cpp:func:`pcnt_unit_get_count` 可随时获取当前计数器的数值。返回的计数值是一个 **带符号** 的整型数,其符号反映了计数的方向。
|
||||||
|
|
||||||
.. note::
|
|
||||||
|
|
||||||
返回的计数器数值是一个 **带符号** 的整数,符号代表计数方向。计数器的数值大于等于最大值或小于等于最小值时,计数器会溢出。
|
|
||||||
|
|
||||||
.. code:: c
|
.. code:: c
|
||||||
|
|
||||||
int pulse_count = 0;
|
int pulse_count = 0;
|
||||||
ESP_ERROR_CHECK(pcnt_unit_get_count(pcnt_unit, &pulse_count));
|
ESP_ERROR_CHECK(pcnt_unit_get_count(pcnt_unit, &pulse_count));
|
||||||
|
|
||||||
|
.. _pcnt-compensate-overflow-loss:
|
||||||
|
|
||||||
|
计数溢出补偿
|
||||||
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
PCNT 内部的硬件计数器会在计数达到高/低门限的时候自动清零。如果你想补偿该计数值的溢出损失,以期进一步拓宽计数器的实际位宽,你可以:
|
||||||
|
|
||||||
|
1. 在安装 PCNT 计数单元的时候使能 :cpp:member:`pcnt_unit_config_t::accum_count` 选项。
|
||||||
|
2. 将高/低计数门限设置为 :ref:`pcnt-watch-points`.
|
||||||
|
3. 现在,:cpp:func:`pcnt_unit_get_count` 函数返回的计数值就会包含硬件计数器当前的计数值,累加上计数器溢出造成的损失。
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
:cpp:func:`pcnt_unit_clear_count` 会复位该软件累加器。
|
||||||
|
|
||||||
.. _pcnt-power-management:
|
.. _pcnt-power-management:
|
||||||
|
|
||||||
电源管理
|
电源管理
|
||||||
|
|
|
@ -38,17 +38,9 @@ typedef struct {
|
||||||
bdc_motor_handle_t motor;
|
bdc_motor_handle_t motor;
|
||||||
pcnt_unit_handle_t pcnt_encoder;
|
pcnt_unit_handle_t pcnt_encoder;
|
||||||
pid_ctrl_block_handle_t pid_ctrl;
|
pid_ctrl_block_handle_t pid_ctrl;
|
||||||
int accumu_count;
|
|
||||||
int report_pulses;
|
int report_pulses;
|
||||||
} motor_control_context_t;
|
} motor_control_context_t;
|
||||||
|
|
||||||
static bool example_pcnt_on_reach(pcnt_unit_handle_t unit, const pcnt_watch_event_data_t *edata, void *user_ctx)
|
|
||||||
{
|
|
||||||
int *accumu_count = (int *)user_ctx;
|
|
||||||
*accumu_count += edata->watch_point_value;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void pid_loop_cb(void *args)
|
static void pid_loop_cb(void *args)
|
||||||
{
|
{
|
||||||
static int last_pulse_count = 0;
|
static int last_pulse_count = 0;
|
||||||
|
@ -60,7 +52,6 @@ static void pid_loop_cb(void *args)
|
||||||
// get the result from rotary encoder
|
// get the result from rotary encoder
|
||||||
int cur_pulse_count = 0;
|
int cur_pulse_count = 0;
|
||||||
pcnt_unit_get_count(pcnt_unit, &cur_pulse_count);
|
pcnt_unit_get_count(pcnt_unit, &cur_pulse_count);
|
||||||
cur_pulse_count += ctx->accumu_count;
|
|
||||||
int real_pulses = cur_pulse_count - last_pulse_count;
|
int real_pulses = cur_pulse_count - last_pulse_count;
|
||||||
last_pulse_count = cur_pulse_count;
|
last_pulse_count = cur_pulse_count;
|
||||||
ctx->report_pulses = real_pulses;
|
ctx->report_pulses = real_pulses;
|
||||||
|
@ -77,7 +68,6 @@ static void pid_loop_cb(void *args)
|
||||||
void app_main(void)
|
void app_main(void)
|
||||||
{
|
{
|
||||||
static motor_control_context_t motor_ctrl_ctx = {
|
static motor_control_context_t motor_ctrl_ctx = {
|
||||||
.accumu_count = 0,
|
|
||||||
.pcnt_encoder = NULL,
|
.pcnt_encoder = NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -99,6 +89,7 @@ void app_main(void)
|
||||||
pcnt_unit_config_t unit_config = {
|
pcnt_unit_config_t unit_config = {
|
||||||
.high_limit = BDC_ENCODER_PCNT_HIGH_LIMIT,
|
.high_limit = BDC_ENCODER_PCNT_HIGH_LIMIT,
|
||||||
.low_limit = BDC_ENCODER_PCNT_LOW_LIMIT,
|
.low_limit = BDC_ENCODER_PCNT_LOW_LIMIT,
|
||||||
|
.flags.accum_count = true, // enable counter accumulation
|
||||||
};
|
};
|
||||||
pcnt_unit_handle_t pcnt_unit = NULL;
|
pcnt_unit_handle_t pcnt_unit = NULL;
|
||||||
ESP_ERROR_CHECK(pcnt_new_unit(&unit_config, &pcnt_unit));
|
ESP_ERROR_CHECK(pcnt_new_unit(&unit_config, &pcnt_unit));
|
||||||
|
@ -124,10 +115,6 @@ void app_main(void)
|
||||||
ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_b, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));
|
ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_b, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));
|
||||||
ESP_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit, BDC_ENCODER_PCNT_HIGH_LIMIT));
|
ESP_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit, BDC_ENCODER_PCNT_HIGH_LIMIT));
|
||||||
ESP_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit, BDC_ENCODER_PCNT_LOW_LIMIT));
|
ESP_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit, BDC_ENCODER_PCNT_LOW_LIMIT));
|
||||||
pcnt_event_callbacks_t pcnt_cbs = {
|
|
||||||
.on_reach = example_pcnt_on_reach, // accumulate the overflow in the callback
|
|
||||||
};
|
|
||||||
ESP_ERROR_CHECK(pcnt_unit_register_event_callbacks(pcnt_unit, &pcnt_cbs, &motor_ctrl_ctx.accumu_count));
|
|
||||||
ESP_ERROR_CHECK(pcnt_unit_enable(pcnt_unit));
|
ESP_ERROR_CHECK(pcnt_unit_enable(pcnt_unit));
|
||||||
ESP_ERROR_CHECK(pcnt_unit_clear_count(pcnt_unit));
|
ESP_ERROR_CHECK(pcnt_unit_clear_count(pcnt_unit));
|
||||||
ESP_ERROR_CHECK(pcnt_unit_start(pcnt_unit));
|
ESP_ERROR_CHECK(pcnt_unit_start(pcnt_unit));
|
||||||
|
|
|
@ -39,18 +39,13 @@ static rmt_channel_handle_t s_rmt_chan;
|
||||||
static rmt_encoder_handle_t s_rmt_encoder;
|
static rmt_encoder_handle_t s_rmt_encoder;
|
||||||
static volatile uint32_t s_milliseconds;
|
static volatile uint32_t s_milliseconds;
|
||||||
|
|
||||||
static bool on_reach_watch_point(pcnt_unit_handle_t unit, const pcnt_watch_event_data_t *edata, void *user_ctx)
|
|
||||||
{
|
|
||||||
s_milliseconds += REF_CLOCK_PRESCALER_MS;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ref_clock_init(void)
|
void ref_clock_init(void)
|
||||||
{
|
{
|
||||||
// Initialize PCNT
|
// Initialize PCNT
|
||||||
pcnt_unit_config_t unit_config = {
|
pcnt_unit_config_t unit_config = {
|
||||||
.high_limit = REF_CLOCK_PRESCALER_MS * 1000,
|
.high_limit = REF_CLOCK_PRESCALER_MS * 1000,
|
||||||
.low_limit = -100, // any minus value is OK, in this case, we don't count down
|
.low_limit = -100, // any minus value is OK, in this case, we don't count down
|
||||||
|
.flags.accum_count = true, // accumulate the counter value
|
||||||
};
|
};
|
||||||
TEST_ESP_OK(pcnt_new_unit(&unit_config, &s_pcnt_unit));
|
TEST_ESP_OK(pcnt_new_unit(&unit_config, &s_pcnt_unit));
|
||||||
pcnt_chan_config_t chan_config = {
|
pcnt_chan_config_t chan_config = {
|
||||||
|
@ -63,13 +58,8 @@ void ref_clock_init(void)
|
||||||
TEST_ESP_OK(pcnt_channel_set_edge_action(s_pcnt_chan, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE));
|
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
|
// 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));
|
TEST_ESP_OK(pcnt_channel_set_level_action(s_pcnt_chan, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_KEEP));
|
||||||
// add watch point
|
// add watch point: high limit
|
||||||
TEST_ESP_OK(pcnt_unit_add_watch_point(s_pcnt_unit, REF_CLOCK_PRESCALER_MS * 1000));
|
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));
|
|
||||||
// enable pcnt
|
// enable pcnt
|
||||||
TEST_ESP_OK(pcnt_unit_enable(s_pcnt_unit));
|
TEST_ESP_OK(pcnt_unit_enable(s_pcnt_unit));
|
||||||
// start pcnt
|
// start pcnt
|
||||||
|
@ -110,8 +100,6 @@ void ref_clock_init(void)
|
||||||
.flags.eot_level = 1,
|
.flags.eot_level = 1,
|
||||||
};
|
};
|
||||||
TEST_ESP_OK(rmt_transmit(s_rmt_chan, s_rmt_encoder, &data, sizeof(data), &trans_config));
|
TEST_ESP_OK(rmt_transmit(s_rmt_chan, s_rmt_encoder, &data, sizeof(data), &trans_config));
|
||||||
|
|
||||||
s_milliseconds = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ref_clock_deinit(void)
|
void ref_clock_deinit(void)
|
||||||
|
@ -133,5 +121,6 @@ uint64_t ref_clock_get(void)
|
||||||
{
|
{
|
||||||
int microseconds = 0;
|
int microseconds = 0;
|
||||||
TEST_ESP_OK(pcnt_unit_get_count(s_pcnt_unit, µseconds));
|
TEST_ESP_OK(pcnt_unit_get_count(s_pcnt_unit, µseconds));
|
||||||
return 1000 * (uint64_t)s_milliseconds + (uint64_t)microseconds;
|
// because the PCNT is configured to always count up, it's impossible to get a negative value
|
||||||
|
return (uint64_t)microseconds;
|
||||||
}
|
}
|
||||||
|
|
Ładowanie…
Reference in New Issue