example: migrate to use pulse_cnt driver

This commit is contained in:
morris 2022-01-17 14:46:28 +08:00
parent 0d920a47f7
commit d234f2f769
9 changed files with 202 additions and 374 deletions

View File

@ -2,8 +2,7 @@
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/common_components/pid_ctrl"
"$ENV{IDF_PATH}/examples/peripherals/pcnt/rotary_encoder/components")
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/examples/common_components/pid_ctrl")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mcpwm_brushed_dc_control)

View File

@ -9,8 +9,8 @@
#include "freertos/task.h"
#include "freertos/queue.h"
#include "driver/gptimer.h"
#include "driver/pulse_cnt.h"
#include "driver/mcpwm.h"
#include "rotary_encoder.h"
#include "pid_ctrl.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
@ -23,7 +23,9 @@
#define BDC_MCPWM_GENA_GPIO_NUM 7
#define BDC_MCPWM_GENB_GPIO_NUM 15
#define BDC_MCPWM_FREQ_HZ 1500
#define BDC_ENCODER_PCNT_UNIT 0
#define BDC_ENCODER_PCNT_HIGH_LIMIT 100
#define BDC_ENCODER_PCNT_LOW_LIMIT -100
#define BDC_ENCODER_PHASEA_GPIO_NUM 36
#define BDC_ENCODER_PHASEB_GPIO_NUM 35
@ -45,7 +47,8 @@ static int expect_pulses = 300;
static int real_pulses;
typedef struct {
rotary_encoder_t *encoder;
pcnt_unit_handle_t hall_pcnt_encoder;
int accumu_count;
QueueHandle_t pid_feedback_queue;
} motor_control_timer_context_t;
@ -54,6 +57,13 @@ typedef struct {
pid_ctrl_block_handle_t pid_ctrl;
} motor_control_task_context_t;
static bool example_pcnt_on_reach(pcnt_unit_handle_t unit, pcnt_watch_event_data_t *edata, void *user_ctx)
{
motor_control_timer_context_t *ctx = (motor_control_timer_context_t *)user_ctx;
ctx->accumu_count += edata->watch_point_value;
return false;
}
static void brushed_motor_set_duty(float duty_cycle)
{
/* motor moves in forward direction, with duty cycle = duty % */
@ -75,9 +85,12 @@ static bool motor_ctrl_timer_cb(gptimer_handle_t timer, const gptimer_alarm_even
static int last_pulse_count = 0;
BaseType_t high_task_awoken = pdFALSE;
motor_control_timer_context_t *user_ctx = (motor_control_timer_context_t *)arg;
rotary_encoder_t *encoder = user_ctx->encoder;
pcnt_unit_handle_t pcnt_unit = user_ctx->hall_pcnt_encoder;
int cur_pulse_count = 0;
pcnt_unit_get_count(pcnt_unit, &cur_pulse_count);
cur_pulse_count += user_ctx->accumu_count;
int cur_pulse_count = encoder->get_counter_value(encoder);
int delta = cur_pulse_count - last_pulse_count;
last_pulse_count = cur_pulse_count;
xQueueSendFromISR(user_ctx->pid_feedback_queue, &delta, &high_task_awoken);
@ -143,6 +156,8 @@ static void register_pid_console_command(void)
void app_main(void)
{
static motor_control_timer_context_t my_timer_ctx = {};
QueueHandle_t pid_fb_queue = xQueueCreate(BDC_PID_FEEDBACK_QUEUE_LEN, sizeof(int));
assert(pid_fb_queue);
@ -160,15 +175,40 @@ void app_main(void)
ESP_ERROR_CHECK(mcpwm_init(BDC_MCPWM_UNIT, BDC_MCPWM_TIMER, &pwm_config));
printf("init and start rotary encoder\r\n");
rotary_encoder_config_t config = {
.dev = (rotary_encoder_dev_t)BDC_ENCODER_PCNT_UNIT,
.phase_a_gpio_num = BDC_ENCODER_PHASEA_GPIO_NUM,
.phase_b_gpio_num = BDC_ENCODER_PHASEB_GPIO_NUM,
pcnt_unit_config_t unit_config = {
.high_limit = BDC_ENCODER_PCNT_HIGH_LIMIT,
.low_limit = BDC_ENCODER_PCNT_LOW_LIMIT,
};
rotary_encoder_t *speed_encoder = NULL;
ESP_ERROR_CHECK(rotary_encoder_new_ec11(&config, &speed_encoder));
ESP_ERROR_CHECK(speed_encoder->set_glitch_filter(speed_encoder, 1));
ESP_ERROR_CHECK(speed_encoder->start(speed_encoder));
pcnt_unit_handle_t pcnt_unit = NULL;
ESP_ERROR_CHECK(pcnt_new_unit(&unit_config, &pcnt_unit));
pcnt_glitch_filter_config_t filter_config = {
.max_glitch_ns = 1000,
};
ESP_ERROR_CHECK(pcnt_unit_set_glitch_filter(pcnt_unit, &filter_config));
pcnt_chan_config_t chan_a_config = {
.edge_gpio_num = BDC_ENCODER_PHASEA_GPIO_NUM,
.level_gpio_num = BDC_ENCODER_PHASEB_GPIO_NUM,
};
pcnt_channel_handle_t pcnt_chan_a = NULL;
ESP_ERROR_CHECK(pcnt_new_channel(pcnt_unit, &chan_a_config, &pcnt_chan_a));
pcnt_chan_config_t chan_b_config = {
.edge_gpio_num = BDC_ENCODER_PHASEB_GPIO_NUM,
.level_gpio_num = BDC_ENCODER_PHASEA_GPIO_NUM,
};
pcnt_channel_handle_t pcnt_chan_b = NULL;
ESP_ERROR_CHECK(pcnt_new_channel(pcnt_unit, &chan_b_config, &pcnt_chan_b));
ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_a, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE));
ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_a, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));
ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_b, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_DECREASE));
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_LOW_LIMIT));
pcnt_event_callbacks_t pcnt_cbs = {
.on_reach = example_pcnt_on_reach,
};
ESP_ERROR_CHECK(pcnt_unit_register_event_callbacks(pcnt_unit, &pcnt_cbs, &my_timer_ctx));
ESP_ERROR_CHECK(pcnt_unit_clear_count(pcnt_unit));
ESP_ERROR_CHECK(pcnt_unit_start(pcnt_unit));
printf("init PID control block\r\n");
pid_ctrl_block_handle_t pid_ctrl;
@ -193,13 +233,12 @@ void app_main(void)
xTaskCreate(bdc_ctrl_task, "bdc_ctrl_task", 4096, &my_ctrl_task_ctx, 5, NULL);
printf("start motor control timer\r\n");
static motor_control_timer_context_t my_timer_ctx = {};
my_timer_ctx.pid_feedback_queue = pid_fb_queue;
my_timer_ctx.encoder = speed_encoder;
gptimer_event_callbacks_t cbs = {
my_timer_ctx.hall_pcnt_encoder = pcnt_unit;
gptimer_event_callbacks_t gptimer_cbs = {
.on_alarm = motor_ctrl_timer_cb,
};
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &cbs, &my_timer_ctx));
ESP_ERROR_CHECK(gptimer_register_event_callbacks(gptimer, &gptimer_cbs, &my_timer_ctx));
gptimer_alarm_config_t alarm_config = {
.reload_count = 0,
.alarm_count = BDC_PID_CALCULATION_PERIOD_US,

View File

@ -1,5 +1,5 @@
| Supported Targets | ESP32 | ESP32-S2 |
| ----------------- | ----- | -------- |
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- |
# Rotary Encoder Example
@ -49,6 +49,8 @@ Connection :
+--------+ +---------------------------------+
```
The GPIO used by the example can be changed according to your board by `EXAMPLE_EC11_GPIO_A` and `EXAMPLE_EC11_GPIO_B` in [source file](main/rotary_encoder_example_main.c);
### Build and Flash
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
@ -60,18 +62,42 @@ See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/l
## Example Output
```
I (181323) example: Encoder value: 0
I (182323) example: Encoder value: 0
I (183323) example: Encoder value: -12
I (184323) example: Encoder value: -18
I (185323) example: Encoder value: -24
I (188323) example: Encoder value: 4
I (189323) example: Encoder value: 8
I (190323) example: Encoder value: 8
I (191323) example: Encoder value: 8
I (0) cpu_start: Starting scheduler on APP CPU.
I (325) example: install pcnt unit
I (335) example: set glitch filter
I (345) example: install pcnt channels
I (395) example: set edge and level actions for pcnt channels
I (405) example: add watch points and register callbacks
I (405) example: clear pcnt unit
I (415) example: start pcnt unit
I (1415) example: Pulse count: 0
I (2415) example: Pulse count: 8
I (3415) example: Pulse count: 27
I (4415) example: Pulse count: 40
I (4705) example: Watch point event, count: 50
I (5705) example: Pulse count: 72
I (6705) example: Pulse count: 96
I (6785) example: Watch point event, count: 100
I (6785) example: Watch point event, count: 0
I (7785) example: Pulse count: 8
I (8785) example: Pulse count: 8
I (9225) example: Watch point event, count: 0
I (10225) example: Pulse count: -20
I (11225) example: Pulse count: -28
I (12225) example: Pulse count: -48
I (12995) example: Watch point event, count: -50
I (13995) example: Pulse count: -68
I (14995) example: Pulse count: -82
I (15995) example: Pulse count: -92
I (16875) example: Watch point event, count: -100
I (16875) example: Watch point event, count: 0
I (17875) example: Pulse count: -12
I (18875) example: Pulse count: -12
I (19875) example: Pulse count: -12
```
This example enables the 4X mode to parse the rotary signals, which means, each complete rotary step will result PCNT counter to increase/decrease by 4, depending on the direction of rotation.
This example enables the 4X mode to parse the rotary signals, which means, each complete rotary step will result in PCNT counter increasing or decreasing by 4, depending on the direction of rotation.
The example adds five watch points, events will be triggered when counter reaches to any watch point.
## Troubleshooting

View File

@ -1,7 +0,0 @@
set(component_srcs "src/rotary_encoder_pcnt_ec11.c")
idf_component_register(SRCS "${component_srcs}"
INCLUDE_DIRS "include"
PRIV_INCLUDE_DIRS ""
PRIV_REQUIRES "driver"
REQUIRES "")

View File

@ -1,127 +0,0 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
/**
* @brief Type of Rotary underlying device handle
*
*/
typedef void *rotary_encoder_dev_t;
/**
* @brief Type of rotary encoder configuration
*
*/
typedef struct {
rotary_encoder_dev_t dev; /*!< Underlying device handle */
int phase_a_gpio_num; /*!< Phase A GPIO number */
int phase_b_gpio_num; /*!< Phase B GPIO number */
int flags; /*!< Extra flags */
} rotary_encoder_config_t;
/**
* @brief Default rotary encoder configuration
*
*/
#define ROTARY_ENCODER_DEFAULT_CONFIG(dev_hdl, gpio_a, gpio_b) \
{ \
.dev = dev_hdl, \
.phase_a_gpio_num = gpio_a, \
.phase_b_gpio_num = gpio_b, \
.flags = 0, \
}
/**
* @brief Type of rotary encoder handle
*
*/
typedef struct rotary_encoder_t rotary_encoder_t;
/**
* @brief Rotary encoder interface
*
*/
struct rotary_encoder_t {
/**
* @brief Filter out glitch from input signals
*
* @param encoder Rotary encoder handle
* @param max_glitch_us Maximum glitch duration, in us
* @return
* - ESP_OK: Set glitch filter successfully
* - ESP_FAIL: Set glitch filter failed because of other error
*/
esp_err_t (*set_glitch_filter)(rotary_encoder_t *encoder, uint32_t max_glitch_us);
/**
* @brief Start rotary encoder
*
* @param encoder Rotary encoder handle
* @return
* - ESP_OK: Start rotary encoder successfully
* - ESP_FAIL: Start rotary encoder failed because of other error
*/
esp_err_t (*start)(rotary_encoder_t *encoder);
/**
* @brief Stop rotary encoder
*
* @param encoder Rotary encoder handle
* @return
* - ESP_OK: Stop rotary encoder successfully
* - ESP_FAIL: Stop rotary encoder failed because of other error
*/
esp_err_t (*stop)(rotary_encoder_t *encoder);
/**
* @brief Recycle rotary encoder memory
*
* @param encoder Rotary encoder handle
* @return
* - ESP_OK: Recycle rotary encoder memory successfully
* - ESP_FAIL: rotary encoder memory failed because of other error
*/
esp_err_t (*del)(rotary_encoder_t *encoder);
/**
* @brief Get rotary encoder counter value
*
* @param encoder Rotary encoder handle
* @return Current counter value (the sign indicates the direction of rotation)
*/
int (*get_counter_value)(rotary_encoder_t *encoder);
};
/**
* @brief Create rotary encoder instance for EC11
*
* @param config Rotary encoder configuration
* @param ret_encoder Returned rotary encoder handle
* @return
* - ESP_OK: Create rotary encoder instance successfully
* - ESP_ERR_INVALID_ARG: Create rotary encoder instance failed because of some invalid argument
* - ESP_ERR_NO_MEM: Create rotary encoder instance failed because there's no enough capable memory
* - ESP_FAIL: Create rotary encoder instance failed because of other error
*/
esp_err_t rotary_encoder_new_ec11(const rotary_encoder_config_t *config, rotary_encoder_t **ret_encoder);
#ifdef __cplusplus
}
#endif

View File

@ -1,180 +0,0 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdlib.h>
#include <string.h>
#include <sys/cdefs.h>
#include "esp_compiler.h"
#include "esp_log.h"
#include "driver/pcnt.h"
#include "sys/lock.h"
#include "hal/pcnt_hal.h"
#include "rotary_encoder.h"
static const char *TAG = "rotary_encoder";
#define ROTARY_CHECK(a, msg, tag, ret, ...) \
do { \
if (unlikely(!(a))) { \
ESP_LOGE(TAG, "%s(%d): " msg, __FUNCTION__, __LINE__, ##__VA_ARGS__); \
ret_code = ret; \
goto tag; \
} \
} while (0)
#define EC11_PCNT_DEFAULT_HIGH_LIMIT (100)
#define EC11_PCNT_DEFAULT_LOW_LIMIT (-100)
// A flag to identify if pcnt isr service has been installed.
static bool is_pcnt_isr_service_installed = false;
// A lock to avoid pcnt isr service being installed twice in multiple threads.
static _lock_t isr_service_install_lock;
#define LOCK_ACQUIRE() _lock_acquire(&isr_service_install_lock)
#define LOCK_RELEASE() _lock_release(&isr_service_install_lock)
typedef struct {
int accumu_count;
rotary_encoder_t parent;
pcnt_unit_t pcnt_unit;
} ec11_t;
static esp_err_t ec11_set_glitch_filter(rotary_encoder_t *encoder, uint32_t max_glitch_us)
{
esp_err_t ret_code = ESP_OK;
ec11_t *ec11 = __containerof(encoder, ec11_t, parent);
/* Configure and enable the input filter */
ROTARY_CHECK(pcnt_set_filter_value(ec11->pcnt_unit, max_glitch_us * 80) == ESP_OK, "set glitch filter failed", err, ESP_FAIL);
if (max_glitch_us) {
pcnt_filter_enable(ec11->pcnt_unit);
} else {
pcnt_filter_disable(ec11->pcnt_unit);
}
return ESP_OK;
err:
return ret_code;
}
static esp_err_t ec11_start(rotary_encoder_t *encoder)
{
ec11_t *ec11 = __containerof(encoder, ec11_t, parent);
pcnt_counter_resume(ec11->pcnt_unit);
return ESP_OK;
}
static esp_err_t ec11_stop(rotary_encoder_t *encoder)
{
ec11_t *ec11 = __containerof(encoder, ec11_t, parent);
pcnt_counter_pause(ec11->pcnt_unit);
return ESP_OK;
}
static int ec11_get_counter_value(rotary_encoder_t *encoder)
{
ec11_t *ec11 = __containerof(encoder, ec11_t, parent);
int16_t val = 0;
pcnt_get_counter_value(ec11->pcnt_unit, &val);
return val + ec11->accumu_count;
}
static esp_err_t ec11_del(rotary_encoder_t *encoder)
{
ec11_t *ec11 = __containerof(encoder, ec11_t, parent);
free(ec11);
return ESP_OK;
}
static void ec11_pcnt_overflow_handler(void *arg)
{
ec11_t *ec11 = (ec11_t *)arg;
uint32_t status = 0;
pcnt_get_event_status(ec11->pcnt_unit, &status);
if (status & PCNT_EVT_H_LIM) {
ec11->accumu_count += EC11_PCNT_DEFAULT_HIGH_LIMIT;
} else if (status & PCNT_EVT_L_LIM) {
ec11->accumu_count += EC11_PCNT_DEFAULT_LOW_LIMIT;
}
}
esp_err_t rotary_encoder_new_ec11(const rotary_encoder_config_t *config, rotary_encoder_t **ret_encoder)
{
esp_err_t ret_code = ESP_OK;
ec11_t *ec11 = NULL;
ROTARY_CHECK(config, "configuration can't be null", err, ESP_ERR_INVALID_ARG);
ROTARY_CHECK(ret_encoder, "can't assign context to null", err, ESP_ERR_INVALID_ARG);
ec11 = calloc(1, sizeof(ec11_t));
ROTARY_CHECK(ec11, "allocate context memory failed", err, ESP_ERR_NO_MEM);
ec11->pcnt_unit = (pcnt_unit_t)(config->dev);
// Configure channel 0
pcnt_config_t dev_config = {
.pulse_gpio_num = config->phase_a_gpio_num,
.ctrl_gpio_num = config->phase_b_gpio_num,
.channel = PCNT_CHANNEL_0,
.unit = ec11->pcnt_unit,
.pos_mode = PCNT_COUNT_DEC,
.neg_mode = PCNT_COUNT_INC,
.lctrl_mode = PCNT_MODE_REVERSE,
.hctrl_mode = PCNT_MODE_KEEP,
.counter_h_lim = EC11_PCNT_DEFAULT_HIGH_LIMIT,
.counter_l_lim = EC11_PCNT_DEFAULT_LOW_LIMIT,
};
ROTARY_CHECK(pcnt_unit_config(&dev_config) == ESP_OK, "config pcnt channel 0 failed", err, ESP_FAIL);
// Configure channel 1
dev_config.pulse_gpio_num = config->phase_b_gpio_num;
dev_config.ctrl_gpio_num = config->phase_a_gpio_num;
dev_config.channel = PCNT_CHANNEL_1;
dev_config.pos_mode = PCNT_COUNT_INC;
dev_config.neg_mode = PCNT_COUNT_DEC;
ROTARY_CHECK(pcnt_unit_config(&dev_config) == ESP_OK, "config pcnt channel 1 failed", err, ESP_FAIL);
// PCNT pause and reset value
pcnt_counter_pause(ec11->pcnt_unit);
pcnt_counter_clear(ec11->pcnt_unit);
// register interrupt handler in a thread-safe way
LOCK_ACQUIRE();
if (!is_pcnt_isr_service_installed) {
ROTARY_CHECK(pcnt_isr_service_install(0) == ESP_OK, "install isr service failed", err, ESP_FAIL);
// make sure pcnt isr service won't be installed more than one time
is_pcnt_isr_service_installed = true;
}
LOCK_RELEASE();
pcnt_isr_handler_add(ec11->pcnt_unit, ec11_pcnt_overflow_handler, ec11);
pcnt_event_enable(ec11->pcnt_unit, PCNT_EVT_H_LIM);
pcnt_event_enable(ec11->pcnt_unit, PCNT_EVT_L_LIM);
ec11->parent.del = ec11_del;
ec11->parent.start = ec11_start;
ec11->parent.stop = ec11_stop;
ec11->parent.set_glitch_filter = ec11_set_glitch_filter;
ec11->parent.get_counter_value = ec11_get_counter_value;
*ret_encoder = &(ec11->parent);
return ESP_OK;
err:
if (ec11) {
free(ec11);
}
return ret_code;
}

View File

@ -1,37 +1,93 @@
/* PCNT example -- Rotary Encoder
/*
* SPDX-FileCopyrightText: 2010-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: CC0-1.0
*/
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "rotary_encoder.h"
#include "driver/pulse_cnt.h"
static const char *TAG = "example";
#define EXAMPLE_PCNT_HIGH_LIMIT 100
#define EXAMPLE_PCNT_LOW_LIMIT -100
#define EXAMPLE_EC11_GPIO_A 0
#define EXAMPLE_EC11_GPIO_B 2
static bool example_pcnt_on_reach(pcnt_unit_handle_t unit, pcnt_watch_event_data_t *edata, void *user_ctx)
{
BaseType_t high_task_wakeup;
QueueHandle_t queue = (QueueHandle_t)user_ctx;
// send event data to queue, from this interrupt callback
xQueueSendFromISR(queue, &(edata->watch_point_value), &high_task_wakeup);
return (high_task_wakeup == pdTRUE);
}
void app_main(void)
{
// Rotary encoder underlying device is represented by a PCNT unit in this example
uint32_t pcnt_unit = 0;
ESP_LOGI(TAG, "install pcnt unit");
pcnt_unit_config_t unit_config = {
.high_limit = EXAMPLE_PCNT_HIGH_LIMIT,
.low_limit = EXAMPLE_PCNT_LOW_LIMIT,
};
pcnt_unit_handle_t pcnt_unit = NULL;
ESP_ERROR_CHECK(pcnt_new_unit(&unit_config, &pcnt_unit));
// Create rotary encoder instance
rotary_encoder_config_t config = ROTARY_ENCODER_DEFAULT_CONFIG((rotary_encoder_dev_t)pcnt_unit, 14, 15);
rotary_encoder_t *encoder = NULL;
ESP_ERROR_CHECK(rotary_encoder_new_ec11(&config, &encoder));
ESP_LOGI(TAG, "set glitch filter");
pcnt_glitch_filter_config_t filter_config = {
.max_glitch_ns = 1000,
};
ESP_ERROR_CHECK(pcnt_unit_set_glitch_filter(pcnt_unit, &filter_config));
// Filter out glitch (1us)
ESP_ERROR_CHECK(encoder->set_glitch_filter(encoder, 1));
ESP_LOGI(TAG, "install pcnt channels");
pcnt_chan_config_t chan_a_config = {
.edge_gpio_num = EXAMPLE_EC11_GPIO_A,
.level_gpio_num = EXAMPLE_EC11_GPIO_B,
};
pcnt_channel_handle_t pcnt_chan_a = NULL;
ESP_ERROR_CHECK(pcnt_new_channel(pcnt_unit, &chan_a_config, &pcnt_chan_a));
pcnt_chan_config_t chan_b_config = {
.edge_gpio_num = EXAMPLE_EC11_GPIO_B,
.level_gpio_num = EXAMPLE_EC11_GPIO_A,
};
pcnt_channel_handle_t pcnt_chan_b = NULL;
ESP_ERROR_CHECK(pcnt_new_channel(pcnt_unit, &chan_b_config, &pcnt_chan_b));
// Start encoder
ESP_ERROR_CHECK(encoder->start(encoder));
ESP_LOGI(TAG, "set edge and level actions for pcnt channels");
ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_a, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE));
ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_a, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));
ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_b, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_DECREASE));
ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_b, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));
ESP_LOGI(TAG, "add watch points and register callbacks");
int watch_points[] = {EXAMPLE_PCNT_LOW_LIMIT, -50, 0, 50, EXAMPLE_PCNT_HIGH_LIMIT};
for (size_t i = 0; i < sizeof(watch_points) / sizeof(watch_points[0]); i++) {
ESP_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit, watch_points[i]));
}
pcnt_event_callbacks_t cbs = {
.on_reach = example_pcnt_on_reach,
};
QueueHandle_t queue = xQueueCreate(10, sizeof(int));
ESP_ERROR_CHECK(pcnt_unit_register_event_callbacks(pcnt_unit, &cbs, queue));
ESP_LOGI(TAG, "clear pcnt unit");
ESP_ERROR_CHECK(pcnt_unit_clear_count(pcnt_unit));
ESP_LOGI(TAG, "start pcnt unit");
ESP_ERROR_CHECK(pcnt_unit_start(pcnt_unit));
// Report counter value
int pulse_count = 0;
int event_count = 0;
while (1) {
ESP_LOGI(TAG, "Encoder value: %d", encoder->get_counter_value(encoder));
vTaskDelay(pdMS_TO_TICKS(1000));
if (xQueueReceive(queue, &event_count, pdMS_TO_TICKS(1000))) {
ESP_LOGI(TAG, "Watch point event, count: %d", event_count);
} else {
ESP_ERROR_CHECK(pcnt_unit_get_count(pcnt_unit, &pulse_count));
ESP_LOGI(TAG, "Pulse count: %d", pulse_count);
}
}
}

View File

@ -0,0 +1,22 @@
# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import pytest
from pytest_embedded.dut import Dut
@pytest.mark.esp32
@pytest.mark.esp32s2
@pytest.mark.esp32s3
@pytest.mark.generic
def test_gptimer_example(dut: Dut) -> None:
dut.expect_exact('install pcnt unit')
dut.expect_exact('set glitch filter')
dut.expect_exact('install pcnt channels')
dut.expect_exact('set edge and level actions for pcnt channels')
dut.expect_exact('add watch points and register callbacks')
dut.expect_exact('clear pcnt unit')
dut.expect_exact('start pcnt unit')
res = dut.expect(r'Pulse count: (\d+)')
count_val = res.group(1).decode('utf8')
assert -100 <= int(count_val) <= 100

View File

@ -8,29 +8,29 @@ from pytest_embedded import Dut
@pytest.mark.supported_targets
@pytest.mark.generic
def test_gptimer_example(dut: Dut) -> None:
dut.expect(r'Create timer handle', timeout=5)
dut.expect(r'Start timer, stop it at alarm event', timeout=5)
dut.expect_exact('Create timer handle', timeout=5)
dut.expect_exact('Start timer, stop it at alarm event', timeout=5)
res = dut.expect(r'Timer stopped, count=(\d+)', timeout=30)
stopped_count = res.group(1).decode('utf8')
assert (1000000 - 10) < int(stopped_count) < (1000000 + 10)
dut.expect(r'Set count value')
dut.expect(r'Get count value')
dut.expect_exact('Set count value')
dut.expect_exact('Get count value')
res = dut.expect(r'Timer count value=(\d+)', timeout=5)
count_val = res.group(1).decode('utf8')
assert int(count_val) == 100
dut.expect(r'Start timer, auto-reload at alarm event', timeout=5)
dut.expect_exact('Start timer, auto-reload at alarm event', timeout=5)
res = dut.expect(r'Timer reloaded, count=(\d+)', timeout=5)
reloaded_count = res.group(1).decode('utf8')
assert 0 <= int(reloaded_count) < 10
dut.expect(r'Stop timer')
dut.expect(r'Update alarm value dynamically')
dut.expect_exact('Stop timer')
dut.expect_exact('Update alarm value dynamically')
for i in range(1,5):
res = dut.expect(r'Timer alarmed, count=(\d+)', timeout=5)
alarm_count = res.group(1).decode('utf8')
assert (i * 1000000 - 10) < int(alarm_count) < (i * 1000000 + 10)
dut.expect(r'Stop timer')
dut.expect(r'Delete timer')
dut.expect_exact('Stop timer')
dut.expect_exact('Delete timer')