mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-01 03:34:32 +02:00
Merge branch 'feature/rotary_encoder' into 'master'
pcnt: add rotary encoder example Closes IDF-1308 and IDFGH-4058 See merge request espressif/esp-idf!10280
This commit is contained in:
@@ -175,6 +175,19 @@ esp_err_t pcnt_set_event_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16
|
||||
*/
|
||||
esp_err_t pcnt_get_event_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16_t *value);
|
||||
|
||||
/**
|
||||
* @brief Get PCNT event status of PCNT unit
|
||||
*
|
||||
* @param unit PCNT unit number
|
||||
* @param status Pointer to accept event status word
|
||||
* @return
|
||||
*
|
||||
* - ESP_OK Success
|
||||
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
|
||||
* - ESP_ERR_INVALID_ARG Parameter error
|
||||
*/
|
||||
esp_err_t pcnt_get_event_status(pcnt_unit_t unit, uint32_t *status);
|
||||
|
||||
/**
|
||||
* @brief Unregister PCNT interrupt handler (registered by pcnt_isr_register), the handler is an ISR.
|
||||
* The handler will be attached to the same CPU core that this function is running on.
|
||||
|
@@ -1,4 +1,4 @@
|
||||
// Copyright 2015-2019 Espressif Systems (Shanghai) PTE LTD
|
||||
// Copyright 2015-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.
|
||||
@@ -12,13 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "soc/soc_caps.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/xtensa_api.h"
|
||||
#include "esp_log.h"
|
||||
#include "driver/pcnt.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_PCNT_SUPPORTED
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "driver/pcnt.h"
|
||||
#include "hal/pcnt_hal.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
|
||||
@@ -85,28 +85,18 @@ static inline esp_err_t _pcnt_set_pin(pcnt_port_t pcnt_port, pcnt_unit_t unit, p
|
||||
PCNT_CHECK(GPIO_IS_VALID_GPIO(pulse_io) || pulse_io < 0, PCNT_GPIO_ERR_STR, ESP_ERR_INVALID_ARG);
|
||||
PCNT_CHECK(GPIO_IS_VALID_GPIO(ctrl_io) || ctrl_io < 0, PCNT_GPIO_ERR_STR, ESP_ERR_INVALID_ARG);
|
||||
|
||||
int sig_base = (channel == 0) ? PCNT_SIG_CH0_IN0_IDX : PCNT_SIG_CH1_IN0_IDX;
|
||||
int ctrl_base = (channel == 0) ? PCNT_CTRL_CH0_IN0_IDX : PCNT_CTRL_CH1_IN0_IDX;
|
||||
if (unit > 4) {
|
||||
sig_base += 12; // GPIO matrix assignments have a gap between units 4 & 5
|
||||
ctrl_base += 12;
|
||||
}
|
||||
|
||||
int input_sig_index = sig_base + (4 * unit);
|
||||
int ctrl_sig_index = ctrl_base + (4 * unit);
|
||||
|
||||
if (pulse_io >= 0) {
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[pulse_io], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(pulse_io, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(pulse_io, GPIO_PULLUP_ONLY);
|
||||
esp_rom_gpio_connect_in_signal(pulse_io, input_sig_index, 0);
|
||||
esp_rom_gpio_connect_in_signal(pulse_io, pcnt_periph_signals.units[unit].channels[channel].pulse_sig, 0);
|
||||
}
|
||||
|
||||
if (ctrl_io >= 0) {
|
||||
PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[ctrl_io], PIN_FUNC_GPIO);
|
||||
gpio_set_direction(ctrl_io, GPIO_MODE_INPUT);
|
||||
gpio_set_pull_mode(ctrl_io, GPIO_PULLUP_ONLY);
|
||||
esp_rom_gpio_connect_in_signal(ctrl_io, ctrl_sig_index, 0);
|
||||
esp_rom_gpio_connect_in_signal(ctrl_io, pcnt_periph_signals.units[unit].channels[channel].control_sig, 0);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
@@ -211,6 +201,16 @@ static inline esp_err_t _pcnt_get_event_value(pcnt_port_t pcnt_port, pcnt_unit_t
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static inline esp_err_t _pcnt_get_event_status(pcnt_port_t pcnt_port, pcnt_unit_t unit, uint32_t *status)
|
||||
{
|
||||
PCNT_OBJ_CHECK(pcnt_port);
|
||||
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
|
||||
PCNT_CHECK(status != NULL, PCNT_ADDRESS_ERR_STR, ESP_ERR_INVALID_ARG);
|
||||
|
||||
*status = pcnt_hal_get_event_status(&(p_pcnt_obj[pcnt_port]->hal), unit);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static inline esp_err_t _pcnt_set_filter_value(pcnt_port_t pcnt_port, pcnt_unit_t unit, uint16_t filter_val)
|
||||
{
|
||||
PCNT_OBJ_CHECK(pcnt_port);
|
||||
@@ -284,9 +284,10 @@ static inline esp_err_t _pcnt_isr_handler_remove(pcnt_port_t pcnt_port, pcnt_uni
|
||||
// pcnt interrupt service
|
||||
static void IRAM_ATTR pcnt_intr_service(void *arg)
|
||||
{
|
||||
uint32_t status;
|
||||
uint32_t status = 0;
|
||||
pcnt_port_t pcnt_port = (pcnt_port_t)arg;
|
||||
pcnt_hal_get_intr_status(&(p_pcnt_obj[pcnt_port]->hal), &status);
|
||||
pcnt_hal_clear_intr_status(&(p_pcnt_obj[pcnt_port]->hal), status);
|
||||
|
||||
while (status) {
|
||||
int unit = __builtin_ffs(status) - 1;
|
||||
@@ -296,7 +297,6 @@ static void IRAM_ATTR pcnt_intr_service(void *arg)
|
||||
(pcnt_isr_func[unit].fn)(pcnt_isr_func[unit].args);
|
||||
}
|
||||
}
|
||||
pcnt_hal_clear_intr_status(&(p_pcnt_obj[pcnt_port]->hal), status);
|
||||
}
|
||||
|
||||
static inline esp_err_t _pcnt_isr_service_install(pcnt_port_t pcnt_port, int intr_alloc_flags)
|
||||
@@ -350,10 +350,10 @@ static inline esp_err_t _pcnt_unit_config(pcnt_port_t pcnt_port, const pcnt_conf
|
||||
/*Enalbe hardware module*/
|
||||
static bool pcnt_enable = false;
|
||||
if (pcnt_enable == false) {
|
||||
periph_module_reset(PERIPH_PCNT_MODULE);
|
||||
periph_module_reset(pcnt_periph_signals.module);
|
||||
pcnt_enable = true;
|
||||
}
|
||||
periph_module_enable(PERIPH_PCNT_MODULE);
|
||||
periph_module_enable(pcnt_periph_signals.module);
|
||||
/*Set counter range*/
|
||||
_pcnt_set_event_value(pcnt_port, unit, PCNT_EVT_H_LIM, pcnt_config->counter_h_lim);
|
||||
_pcnt_set_event_value(pcnt_port, unit, PCNT_EVT_L_LIM, pcnt_config->counter_l_lim);
|
||||
@@ -469,6 +469,11 @@ esp_err_t pcnt_get_event_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16
|
||||
return _pcnt_get_event_value(PCNT_PORT_0, unit, evt_type, value);
|
||||
}
|
||||
|
||||
esp_err_t pcnt_get_event_status(pcnt_unit_t unit, uint32_t *status)
|
||||
{
|
||||
return _pcnt_get_event_status(PCNT_PORT_0, unit, status);
|
||||
}
|
||||
|
||||
esp_err_t pcnt_set_filter_value(pcnt_unit_t unit, uint16_t filter_val)
|
||||
{
|
||||
return _pcnt_set_filter_value(PCNT_PORT_0, unit, filter_val);
|
||||
@@ -503,7 +508,7 @@ esp_err_t pcnt_isr_register(void (*fun)(void *), void *arg, int intr_alloc_flags
|
||||
esp_err_t ret = ESP_FAIL;
|
||||
PCNT_CHECK(fun != NULL, PCNT_ADDRESS_ERR_STR, ESP_ERR_INVALID_ARG);
|
||||
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
|
||||
ret = esp_intr_alloc(ETS_PCNT_INTR_SOURCE, intr_alloc_flags, fun, arg, handle);
|
||||
ret = esp_intr_alloc(pcnt_periph_signals.irq, intr_alloc_flags, fun, arg, handle);
|
||||
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
|
||||
return ret;
|
||||
}
|
||||
@@ -527,4 +532,6 @@ esp_err_t pcnt_isr_service_install(int intr_alloc_flags)
|
||||
void pcnt_isr_service_uninstall()
|
||||
{
|
||||
_pcnt_isr_service_uninstall(PCNT_PORT_0);
|
||||
}
|
||||
}
|
||||
|
||||
#endif // #if SOC_PCNT_SUPPORTED
|
||||
|
@@ -14,6 +14,8 @@
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#if SOC_PCNT_SUPPORTED
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/pcnt.h"
|
||||
@@ -21,12 +23,9 @@
|
||||
#include "esp_attr.h"
|
||||
#include "esp_log.h"
|
||||
#include "soc/gpio_periph.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "unity.h"
|
||||
#include "esp_rom_gpio.h"
|
||||
|
||||
#if !TEMPORARY_DISABLED_FOR_TARGETS(ESP32S3)
|
||||
|
||||
#define PULSE_IO 21
|
||||
#define PCNT_INPUT_IO 4
|
||||
#define PCNT_CTRL_VCC_IO 5
|
||||
@@ -673,4 +672,4 @@ TEST_CASE("PCNT counting mode test", "[pcnt]")
|
||||
count_mode_test(PCNT_CTRL_GND_IO);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // #if SOC_PCNT_SUPPORTED
|
||||
|
@@ -32,6 +32,50 @@ extern "C" {
|
||||
// Get PCNT hardware instance with giving pcnt num
|
||||
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
|
||||
|
||||
/**
|
||||
* @brief Set PCNT channel edge mode
|
||||
*
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param unit PCNT unit number
|
||||
* @param channel PCNT channel number
|
||||
* @param pos_mode Counter mode when detecting positive edge
|
||||
* @param neg_mode Counter mode when detecting negative edge
|
||||
*/
|
||||
static inline void pcnt_ll_set_edge_mode(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_channel_t channel, pcnt_count_mode_t pos_mode, pcnt_count_mode_t neg_mode)
|
||||
{
|
||||
typeof(hw->conf_unit[unit].conf0) conf0_reg = hw->conf_unit[unit].conf0;
|
||||
if (channel == 0) {
|
||||
conf0_reg.ch0_pos_mode = pos_mode;
|
||||
conf0_reg.ch0_neg_mode = neg_mode;
|
||||
} else {
|
||||
conf0_reg.ch1_pos_mode = pos_mode;
|
||||
conf0_reg.ch1_neg_mode = neg_mode;
|
||||
}
|
||||
hw->conf_unit[unit].conf0 = conf0_reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set PCNT channel level mode
|
||||
*
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param unit PCNT unit number
|
||||
* @param channel PCNT channel number
|
||||
* @param hctrl_mode Counter mode when control signal is high level
|
||||
* @param lctrl_mode Counter mode when control signal is low level
|
||||
*/
|
||||
static inline void pcnt_ll_set_level_mode(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_channel_t channel, pcnt_ctrl_mode_t hctrl_mode, pcnt_ctrl_mode_t lctrl_mode)
|
||||
{
|
||||
typeof(hw->conf_unit[unit].conf0) conf0_reg = hw->conf_unit[unit].conf0;
|
||||
if (channel == 0) {
|
||||
conf0_reg.ch0_hctrl_mode = hctrl_mode;
|
||||
conf0_reg.ch0_lctrl_mode = lctrl_mode;
|
||||
} else {
|
||||
conf0_reg.ch1_hctrl_mode = hctrl_mode;
|
||||
conf0_reg.ch1_lctrl_mode = lctrl_mode;
|
||||
}
|
||||
hw->conf_unit[unit].conf0 = conf0_reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set PCNT counter mode
|
||||
*
|
||||
@@ -45,19 +89,8 @@ extern "C" {
|
||||
*/
|
||||
static inline void pcnt_ll_set_mode(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_channel_t channel, pcnt_count_mode_t pos_mode, pcnt_count_mode_t neg_mode, pcnt_ctrl_mode_t hctrl_mode, pcnt_ctrl_mode_t lctrl_mode)
|
||||
{
|
||||
typeof(hw->conf_unit[unit].conf0) conf0_reg = hw->conf_unit[unit].conf0;
|
||||
if (channel == 0) {
|
||||
conf0_reg.ch0_pos_mode = pos_mode;
|
||||
conf0_reg.ch0_neg_mode = neg_mode;
|
||||
conf0_reg.ch0_hctrl_mode = hctrl_mode;
|
||||
conf0_reg.ch0_lctrl_mode = lctrl_mode;
|
||||
} else {
|
||||
conf0_reg.ch1_pos_mode = pos_mode;
|
||||
conf0_reg.ch1_neg_mode = neg_mode;
|
||||
conf0_reg.ch1_hctrl_mode = hctrl_mode;
|
||||
conf0_reg.ch1_lctrl_mode = lctrl_mode;
|
||||
}
|
||||
hw->conf_unit[unit].conf0 = conf0_reg;
|
||||
pcnt_ll_set_edge_mode(hw, unit, channel, pos_mode, neg_mode);
|
||||
pcnt_ll_set_level_mode(hw, unit, channel, hctrl_mode, lctrl_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,6 +280,18 @@ static inline void pcnt_ll_get_event_value(pcnt_dev_t *hw, pcnt_unit_t unit, pcn
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get PCNT event status
|
||||
*
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param unit PCNT unit number
|
||||
* @return event status word
|
||||
*/
|
||||
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, pcnt_unit_t unit)
|
||||
{
|
||||
return hw->status_unit[unit].val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set PCNT filter value
|
||||
*
|
||||
|
@@ -32,6 +32,50 @@ extern "C" {
|
||||
// Get PCNT hardware instance with giving pcnt num
|
||||
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
|
||||
|
||||
/**
|
||||
* @brief Set PCNT channel edge mode
|
||||
*
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param unit PCNT unit number
|
||||
* @param channel PCNT channel number
|
||||
* @param pos_mode Counter mode when detecting positive edge
|
||||
* @param neg_mode Counter mode when detecting negative edge
|
||||
*/
|
||||
static inline void pcnt_ll_set_edge_mode(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_channel_t channel, pcnt_count_mode_t pos_mode, pcnt_count_mode_t neg_mode)
|
||||
{
|
||||
typeof(hw->conf_unit[unit].conf0) conf0_reg = hw->conf_unit[unit].conf0;
|
||||
if (channel == 0) {
|
||||
conf0_reg.ch0_pos_mode = pos_mode;
|
||||
conf0_reg.ch0_neg_mode = neg_mode;
|
||||
} else {
|
||||
conf0_reg.ch1_pos_mode = pos_mode;
|
||||
conf0_reg.ch1_neg_mode = neg_mode;
|
||||
}
|
||||
hw->conf_unit[unit].conf0 = conf0_reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set PCNT channel level mode
|
||||
*
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param unit PCNT unit number
|
||||
* @param channel PCNT channel number
|
||||
* @param hctrl_mode Counter mode when control signal is high level
|
||||
* @param lctrl_mode Counter mode when control signal is low level
|
||||
*/
|
||||
static inline void pcnt_ll_set_level_mode(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_channel_t channel, pcnt_ctrl_mode_t hctrl_mode, pcnt_ctrl_mode_t lctrl_mode)
|
||||
{
|
||||
typeof(hw->conf_unit[unit].conf0) conf0_reg = hw->conf_unit[unit].conf0;
|
||||
if (channel == 0) {
|
||||
conf0_reg.ch0_hctrl_mode = hctrl_mode;
|
||||
conf0_reg.ch0_lctrl_mode = lctrl_mode;
|
||||
} else {
|
||||
conf0_reg.ch1_hctrl_mode = hctrl_mode;
|
||||
conf0_reg.ch1_lctrl_mode = lctrl_mode;
|
||||
}
|
||||
hw->conf_unit[unit].conf0 = conf0_reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set PCNT counter mode
|
||||
*
|
||||
@@ -45,19 +89,8 @@ extern "C" {
|
||||
*/
|
||||
static inline void pcnt_ll_set_mode(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_channel_t channel, pcnt_count_mode_t pos_mode, pcnt_count_mode_t neg_mode, pcnt_ctrl_mode_t hctrl_mode, pcnt_ctrl_mode_t lctrl_mode)
|
||||
{
|
||||
typeof(hw->conf_unit[unit].conf0) conf0_reg = hw->conf_unit[unit].conf0;
|
||||
if (channel == 0) {
|
||||
conf0_reg.ch0_pos_mode = pos_mode;
|
||||
conf0_reg.ch0_neg_mode = neg_mode;
|
||||
conf0_reg.ch0_hctrl_mode = hctrl_mode;
|
||||
conf0_reg.ch0_lctrl_mode = lctrl_mode;
|
||||
} else {
|
||||
conf0_reg.ch1_pos_mode = pos_mode;
|
||||
conf0_reg.ch1_neg_mode = neg_mode;
|
||||
conf0_reg.ch1_hctrl_mode = hctrl_mode;
|
||||
conf0_reg.ch1_lctrl_mode = lctrl_mode;
|
||||
}
|
||||
hw->conf_unit[unit].conf0 = conf0_reg;
|
||||
pcnt_ll_set_edge_mode(hw, unit, channel, pos_mode, neg_mode);
|
||||
pcnt_ll_set_level_mode(hw, unit, channel, hctrl_mode, lctrl_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,6 +280,18 @@ static inline void pcnt_ll_get_event_value(pcnt_dev_t *hw, pcnt_unit_t unit, pcn
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get PCNT event status
|
||||
*
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param unit PCNT unit number
|
||||
* @return event status word
|
||||
*/
|
||||
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, pcnt_unit_t unit)
|
||||
{
|
||||
return hw->status_unit[unit].val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set PCNT filter value
|
||||
*
|
||||
|
@@ -32,6 +32,50 @@ extern "C" {
|
||||
// Get PCNT hardware instance with giving pcnt num
|
||||
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
|
||||
|
||||
/**
|
||||
* @brief Set PCNT channel edge mode
|
||||
*
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param unit PCNT unit number
|
||||
* @param channel PCNT channel number
|
||||
* @param pos_mode Counter mode when detecting positive edge
|
||||
* @param neg_mode Counter mode when detecting negative edge
|
||||
*/
|
||||
static inline void pcnt_ll_set_edge_mode(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_channel_t channel, pcnt_count_mode_t pos_mode, pcnt_count_mode_t neg_mode)
|
||||
{
|
||||
typeof(hw->conf_unit[unit].conf0) conf0_reg = hw->conf_unit[unit].conf0;
|
||||
if (channel == 0) {
|
||||
conf0_reg.ch0_pos_mode = pos_mode;
|
||||
conf0_reg.ch0_neg_mode = neg_mode;
|
||||
} else {
|
||||
conf0_reg.ch1_pos_mode = pos_mode;
|
||||
conf0_reg.ch1_neg_mode = neg_mode;
|
||||
}
|
||||
hw->conf_unit[unit].conf0 = conf0_reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set PCNT channel level mode
|
||||
*
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param unit PCNT unit number
|
||||
* @param channel PCNT channel number
|
||||
* @param hctrl_mode Counter mode when control signal is high level
|
||||
* @param lctrl_mode Counter mode when control signal is low level
|
||||
*/
|
||||
static inline void pcnt_ll_set_level_mode(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_channel_t channel, pcnt_ctrl_mode_t hctrl_mode, pcnt_ctrl_mode_t lctrl_mode)
|
||||
{
|
||||
typeof(hw->conf_unit[unit].conf0) conf0_reg = hw->conf_unit[unit].conf0;
|
||||
if (channel == 0) {
|
||||
conf0_reg.ch0_hctrl_mode = hctrl_mode;
|
||||
conf0_reg.ch0_lctrl_mode = lctrl_mode;
|
||||
} else {
|
||||
conf0_reg.ch1_hctrl_mode = hctrl_mode;
|
||||
conf0_reg.ch1_lctrl_mode = lctrl_mode;
|
||||
}
|
||||
hw->conf_unit[unit].conf0 = conf0_reg;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set PCNT counter mode
|
||||
*
|
||||
@@ -45,19 +89,8 @@ extern "C" {
|
||||
*/
|
||||
static inline void pcnt_ll_set_mode(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_channel_t channel, pcnt_count_mode_t pos_mode, pcnt_count_mode_t neg_mode, pcnt_ctrl_mode_t hctrl_mode, pcnt_ctrl_mode_t lctrl_mode)
|
||||
{
|
||||
typeof(hw->conf_unit[unit].conf0) conf0_reg = hw->conf_unit[unit].conf0;
|
||||
if (channel == 0) {
|
||||
conf0_reg.ch0_pos_mode = pos_mode;
|
||||
conf0_reg.ch0_neg_mode = neg_mode;
|
||||
conf0_reg.ch0_hctrl_mode = hctrl_mode;
|
||||
conf0_reg.ch0_lctrl_mode = lctrl_mode;
|
||||
} else {
|
||||
conf0_reg.ch1_pos_mode = pos_mode;
|
||||
conf0_reg.ch1_neg_mode = neg_mode;
|
||||
conf0_reg.ch1_hctrl_mode = hctrl_mode;
|
||||
conf0_reg.ch1_lctrl_mode = lctrl_mode;
|
||||
}
|
||||
hw->conf_unit[unit].conf0 = conf0_reg;
|
||||
pcnt_ll_set_edge_mode(hw, unit, channel, pos_mode, neg_mode);
|
||||
pcnt_ll_set_level_mode(hw, unit, channel, hctrl_mode, lctrl_mode);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -247,6 +280,18 @@ static inline void pcnt_ll_get_event_value(pcnt_dev_t *hw, pcnt_unit_t unit, pcn
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get PCNT event status
|
||||
*
|
||||
* @param hw Peripheral PCNT hardware instance address.
|
||||
* @param unit PCNT unit number
|
||||
* @return event status word
|
||||
*/
|
||||
static inline uint32_t pcnt_ll_get_event_status(pcnt_dev_t *hw, pcnt_unit_t unit)
|
||||
{
|
||||
return hw->status_unit[unit].val;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set PCNT filter value
|
||||
*
|
||||
|
@@ -164,6 +164,15 @@ typedef struct {
|
||||
*/
|
||||
#define pcnt_hal_get_event_value(hal, unit, evt_type, value) pcnt_ll_get_event_value((hal)->dev, unit, evt_type, value)
|
||||
|
||||
/**
|
||||
* @brief Get PCNT event status
|
||||
*
|
||||
* @param hal Context of the HAL layer
|
||||
* @param unit PCNT unit number
|
||||
* @return event status word
|
||||
*/
|
||||
#define pcnt_hal_get_event_status(hal, unit) pcnt_ll_get_event_status((hal)->dev, unit)
|
||||
|
||||
/**
|
||||
* @brief Set PCNT filter value
|
||||
*
|
||||
|
@@ -2,6 +2,7 @@ add_library(soc_esp32 STATIC
|
||||
"adc_periph.c"
|
||||
"dac_periph.c"
|
||||
"gpio_periph.c"
|
||||
"pcnt_periph.c"
|
||||
"rtc_io_periph.c"
|
||||
"rtc_periph.c"
|
||||
"sdio_slave_periph.c"
|
||||
|
@@ -63,6 +63,7 @@
|
||||
#define SOC_MCPWM_SUPPORTED 1
|
||||
#define SOC_SDMMC_HOST_SUPPORTED 1
|
||||
#define SOC_BT_SUPPORTED 1
|
||||
#define SOC_PCNT_SUPPORTED 1
|
||||
#define SOC_SDIO_SLAVE_SUPPORTED 1
|
||||
#define SOC_TWAI_SUPPORTED 1
|
||||
#define SOC_EMAC_SUPPORTED 1
|
||||
@@ -155,6 +156,7 @@
|
||||
// ESP32 have 1 PCNT peripheral
|
||||
#define SOC_PCNT_PORT_NUM (1)
|
||||
#define SOC_PCNT_UNIT_NUM (8)
|
||||
#define SOC_PCNT_UNIT_CHANNEL_NUM (2)
|
||||
|
||||
/*-------------------------- RMT CAPS ----------------------------------------*/
|
||||
#define SOC_RMT_CHANNEL_MEM_WORDS (64) /*!< Each channel owns 64 words memory */
|
||||
|
119
components/soc/soc/esp32/pcnt_periph.c
Normal file
119
components/soc/soc/esp32/pcnt_periph.c
Normal file
@@ -0,0 +1,119 @@
|
||||
// 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 "soc/pcnt_periph.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
|
||||
const pcnt_signal_conn_t pcnt_periph_signals = {
|
||||
.module = PERIPH_PCNT_MODULE,
|
||||
.irq = ETS_PCNT_INTR_SOURCE,
|
||||
.units = {
|
||||
[0] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN0_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN0_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN0_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN0_IDX
|
||||
}
|
||||
}
|
||||
},
|
||||
[1] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN1_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN1_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN1_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN1_IDX
|
||||
}
|
||||
}
|
||||
},
|
||||
[2] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN2_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN2_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN2_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN2_IDX
|
||||
}
|
||||
}
|
||||
},
|
||||
[3] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN3_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN3_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN3_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN3_IDX
|
||||
}
|
||||
}
|
||||
},
|
||||
[4] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN4_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN4_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN4_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN4_IDX
|
||||
}
|
||||
}
|
||||
},
|
||||
[5] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN5_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN5_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN5_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN5_IDX
|
||||
}
|
||||
}
|
||||
},
|
||||
[6] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN6_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN6_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN6_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN6_IDX
|
||||
}
|
||||
}
|
||||
},
|
||||
[7] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN7_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN7_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN7_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN7_IDX
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
@@ -2,6 +2,7 @@ add_library(soc_esp32s2 STATIC
|
||||
"adc_periph.c"
|
||||
"dac_periph.c"
|
||||
"gpio_periph.c"
|
||||
"pcnt_periph.c"
|
||||
"rtc_io_periph.c"
|
||||
"rtc_periph.c"
|
||||
"interrupts.c"
|
||||
|
@@ -43,6 +43,7 @@
|
||||
#define SOC_SUPPORTS_SECURE_DL_MODE 1
|
||||
#define SOC_RISCV_COPROC_SUPPORTED 1
|
||||
#define SOC_USB_SUPPORTED 1
|
||||
#define SOC_PCNT_SUPPORTED 1
|
||||
|
||||
#define SOC_CACHE_SUPPORT_WRAP 1
|
||||
|
||||
@@ -132,6 +133,7 @@
|
||||
// ESP32-S2 have 1 PCNT peripheral
|
||||
#define SOC_PCNT_PORT_NUM (1)
|
||||
#define SOC_PCNT_UNIT_NUM (4) // ESP32-S2 only have 4 unit
|
||||
#define SOC_PCNT_UNIT_CHANNEL_NUM (2)
|
||||
|
||||
/*-------------------------- RMT CAPS ----------------------------------------*/
|
||||
#define SOC_RMT_CHANNEL_MEM_WORDS (64) /*!< Each channel owns 64 words memory (1 word = 4 Bytes) */
|
||||
|
71
components/soc/soc/esp32s2/pcnt_periph.c
Normal file
71
components/soc/soc/esp32s2/pcnt_periph.c
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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 "soc/pcnt_periph.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
|
||||
const pcnt_signal_conn_t pcnt_periph_signals = {
|
||||
.module = PERIPH_PCNT_MODULE,
|
||||
.irq = ETS_PCNT_INTR_SOURCE,
|
||||
.units = {
|
||||
[0] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN0_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN0_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN0_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN0_IDX
|
||||
}
|
||||
}
|
||||
},
|
||||
[1] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN1_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN1_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN1_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN1_IDX
|
||||
}
|
||||
}
|
||||
},
|
||||
[2] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN2_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN2_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN2_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN2_IDX
|
||||
}
|
||||
}
|
||||
},
|
||||
[3] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN3_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN3_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN3_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN3_IDX
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
@@ -6,6 +6,7 @@ add_library(soc_esp32s3 STATIC
|
||||
"i2s_periph.c"
|
||||
"interrupts.c"
|
||||
"ledc_periph.c"
|
||||
"pcnt_periph.c"
|
||||
"rtc_io_periph.c"
|
||||
"rtc_periph.c"
|
||||
"sdio_slave_periph.c"
|
||||
|
@@ -1,26 +0,0 @@
|
||||
// Copyright 2015-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
|
||||
|
||||
#define SOC_PCNT_PORT_NUM (1)
|
||||
#define SOC_PCNT_UNIT_NUM (4)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@@ -6,6 +6,7 @@
|
||||
#pragma once
|
||||
|
||||
/*-------------------------- COMMON CAPS ---------------------------------------*/
|
||||
#define SOC_PCNT_SUPPORTED 1
|
||||
#define SOC_TWAI_SUPPORTED 1
|
||||
#define SOC_GDMA_SUPPORTED 1
|
||||
#define SOC_CPU_CORES_NUM 2
|
||||
@@ -42,7 +43,9 @@
|
||||
#include "mpu_caps.h"
|
||||
|
||||
/*-------------------------- PCNT CAPS ---------------------------------------*/
|
||||
#include "pcnt_caps.h"
|
||||
#define SOC_PCNT_PORT_NUM (1)
|
||||
#define SOC_PCNT_UNIT_NUM (4)
|
||||
#define SOC_PCNT_UNIT_CHANNEL_NUM (2)
|
||||
|
||||
/*-------------------------- RMT CAPS ----------------------------------------*/
|
||||
#include "rmt_caps.h"
|
||||
|
71
components/soc/soc/esp32s3/pcnt_periph.c
Normal file
71
components/soc/soc/esp32s3/pcnt_periph.c
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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 "soc/pcnt_periph.h"
|
||||
#include "soc/gpio_sig_map.h"
|
||||
|
||||
const pcnt_signal_conn_t pcnt_periph_signals = {
|
||||
.module = PERIPH_PCNT_MODULE,
|
||||
.irq = ETS_PCNT_INTR_SOURCE,
|
||||
.units = {
|
||||
[0] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN0_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN0_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN0_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN0_IDX
|
||||
}
|
||||
}
|
||||
},
|
||||
[1] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN1_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN1_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN1_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN1_IDX
|
||||
}
|
||||
}
|
||||
},
|
||||
[2] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN2_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN2_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN2_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN2_IDX
|
||||
}
|
||||
}
|
||||
},
|
||||
[3] = {
|
||||
.channels = {
|
||||
[0] = {
|
||||
.control_sig = PCNT_CTRL_CH0_IN3_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH0_IN3_IDX
|
||||
},
|
||||
[1] = {
|
||||
.control_sig = PCNT_CTRL_CH1_IN3_IDX,
|
||||
.pulse_sig = PCNT_SIG_CH1_IN3_IDX
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
@@ -13,5 +13,30 @@
|
||||
// limitations under the License.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "soc/soc_caps.h"
|
||||
#include "soc/periph_defs.h"
|
||||
#include "soc/pcnt_reg.h"
|
||||
#include "soc/pcnt_struct.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
struct {
|
||||
struct {
|
||||
const uint32_t pulse_sig;
|
||||
const uint32_t control_sig;
|
||||
} channels[SOC_PCNT_UNIT_CHANNEL_NUM];
|
||||
} units[SOC_PCNT_UNIT_NUM];
|
||||
const uint32_t irq;
|
||||
const periph_module_t module;
|
||||
} pcnt_signal_conn_t;
|
||||
|
||||
extern const pcnt_signal_conn_t pcnt_periph_signals;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
@@ -91,7 +91,8 @@ In order to check what are the threshold values currently set, use function :cpp
|
||||
Application Example
|
||||
-------------------
|
||||
|
||||
Pulse counter with control signal and event interrupt example: :example:`peripherals/pcnt`.
|
||||
* Pulse counter with control signal and event interrupt example: :example:`peripherals/pcnt/pulse_count_event`.
|
||||
* Parse the signal generated from rotary encoder: :example:`peripherals/pcnt/rotary_encoder`.
|
||||
|
||||
|
||||
API Reference
|
||||
|
@@ -1,2 +0,0 @@
|
||||
idf_component_register(SRCS "pcnt_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
@@ -3,4 +3,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(pcnt)
|
||||
project(pcnt_event)
|
@@ -3,7 +3,7 @@
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := pcnt
|
||||
PROJECT_NAME := pcnt_event
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
@@ -1,7 +1,4 @@
|
||||
| Supported Targets | ESP32 |
|
||||
| ----------------- | ----- |
|
||||
|
||||
# PCNT Example
|
||||
# Pulse Count Event Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
@@ -18,7 +15,7 @@ Pin connection:
|
||||
|
||||
* GPIO4 is the default output GPIO of the 1 Hz pulse generator.
|
||||
* GPIO18 is the default pulse input GPIO. We need to short GPIO4 and GPIO18.
|
||||
* GPIO5 is the default control signal, which can be left floating with internal pull up, or connected to Ground (If GPIO5 is left floating, the value of counter increases with the rising edges of the PWM pulses. If GPIO15 is connected to Ground, the value decreases).
|
||||
* GPIO5 is the default control signal, which can be left floating with internal pull up, or connected to Ground (If GPIO5 is left floating, the value of counter increases with the rising edges of the PWM pulses. If GPIO5 is connected to Ground, the value decreases).
|
||||
|
||||
### Configure the project
|
||||
|
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "pcnt_event_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
@@ -9,18 +9,16 @@
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/portmacro.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "driver/periph_ctrl.h"
|
||||
#include "driver/ledc.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "driver/pcnt.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
/**
|
||||
* TEST CODE BRIEF
|
||||
*
|
||||
@@ -43,7 +41,6 @@
|
||||
* - reaches 'l_lim' value or 'h_lim' value,
|
||||
* - will be reset to zero.
|
||||
*/
|
||||
#define PCNT_TEST_UNIT PCNT_UNIT_0
|
||||
#define PCNT_H_LIM_VAL 10
|
||||
#define PCNT_L_LIM_VAL -10
|
||||
#define PCNT_THRESH1_VAL 5
|
||||
@@ -53,7 +50,6 @@
|
||||
#define LEDC_OUTPUT_IO 18 // Output GPIO of a sample 1 Hz pulse generator
|
||||
|
||||
xQueueHandle pcnt_evt_queue; // A queue to handle pulse counter events
|
||||
pcnt_isr_handle_t user_isr_handle = NULL; //user's ISR service handle
|
||||
|
||||
/* A sample structure to pass events from the PCNT
|
||||
* interrupt handler to the main program.
|
||||
@@ -69,24 +65,13 @@ typedef struct {
|
||||
*/
|
||||
static void IRAM_ATTR pcnt_example_intr_handler(void *arg)
|
||||
{
|
||||
uint32_t intr_status = PCNT.int_st.val;
|
||||
int i;
|
||||
int pcnt_unit = (int)arg;
|
||||
pcnt_evt_t evt;
|
||||
portBASE_TYPE HPTaskAwoken = pdFALSE;
|
||||
|
||||
for (i = 0; i < PCNT_UNIT_MAX; i++) {
|
||||
if (intr_status & (BIT(i))) {
|
||||
evt.unit = i;
|
||||
/* Save the PCNT event type that caused an interrupt
|
||||
to pass it to the main program */
|
||||
evt.status = PCNT.status_unit[i].val;
|
||||
PCNT.int_clr.val = BIT(i);
|
||||
xQueueSendFromISR(pcnt_evt_queue, &evt, &HPTaskAwoken);
|
||||
if (HPTaskAwoken == pdTRUE) {
|
||||
portYIELD_FROM_ISR();
|
||||
}
|
||||
}
|
||||
}
|
||||
evt.unit = pcnt_unit;
|
||||
/* Save the PCNT event type that caused an interrupt
|
||||
to pass it to the main program */
|
||||
pcnt_get_event_status(pcnt_unit, &evt.status);
|
||||
xQueueSendFromISR(pcnt_evt_queue, &evt, NULL);
|
||||
}
|
||||
|
||||
/* Configure LED PWM Controller
|
||||
@@ -120,7 +105,7 @@ static void ledc_init(void)
|
||||
* - set up the input filter
|
||||
* - set up the counter events to watch
|
||||
*/
|
||||
static void pcnt_example_init(void)
|
||||
static void pcnt_example_init(int unit)
|
||||
{
|
||||
/* Prepare configuration for the PCNT unit */
|
||||
pcnt_config_t pcnt_config = {
|
||||
@@ -128,7 +113,7 @@ static void pcnt_example_init(void)
|
||||
.pulse_gpio_num = PCNT_INPUT_SIG_IO,
|
||||
.ctrl_gpio_num = PCNT_INPUT_CTRL_IO,
|
||||
.channel = PCNT_CHANNEL_0,
|
||||
.unit = PCNT_TEST_UNIT,
|
||||
.unit = unit,
|
||||
// What to do on the positive / negative edge of pulse input?
|
||||
.pos_mode = PCNT_COUNT_INC, // Count up on the positive edge
|
||||
.neg_mode = PCNT_COUNT_DIS, // Keep the counter value on the negative edge
|
||||
@@ -143,39 +128,40 @@ static void pcnt_example_init(void)
|
||||
pcnt_unit_config(&pcnt_config);
|
||||
|
||||
/* Configure and enable the input filter */
|
||||
pcnt_set_filter_value(PCNT_TEST_UNIT, 100);
|
||||
pcnt_filter_enable(PCNT_TEST_UNIT);
|
||||
pcnt_set_filter_value(unit, 100);
|
||||
pcnt_filter_enable(unit);
|
||||
|
||||
/* Set threshold 0 and 1 values and enable events to watch */
|
||||
pcnt_set_event_value(PCNT_TEST_UNIT, PCNT_EVT_THRES_1, PCNT_THRESH1_VAL);
|
||||
pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_THRES_1);
|
||||
pcnt_set_event_value(PCNT_TEST_UNIT, PCNT_EVT_THRES_0, PCNT_THRESH0_VAL);
|
||||
pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_THRES_0);
|
||||
pcnt_set_event_value(unit, PCNT_EVT_THRES_1, PCNT_THRESH1_VAL);
|
||||
pcnt_event_enable(unit, PCNT_EVT_THRES_1);
|
||||
pcnt_set_event_value(unit, PCNT_EVT_THRES_0, PCNT_THRESH0_VAL);
|
||||
pcnt_event_enable(unit, PCNT_EVT_THRES_0);
|
||||
/* Enable events on zero, maximum and minimum limit values */
|
||||
pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_ZERO);
|
||||
pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_H_LIM);
|
||||
pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_L_LIM);
|
||||
pcnt_event_enable(unit, PCNT_EVT_ZERO);
|
||||
pcnt_event_enable(unit, PCNT_EVT_H_LIM);
|
||||
pcnt_event_enable(unit, PCNT_EVT_L_LIM);
|
||||
|
||||
/* Initialize PCNT's counter */
|
||||
pcnt_counter_pause(PCNT_TEST_UNIT);
|
||||
pcnt_counter_clear(PCNT_TEST_UNIT);
|
||||
pcnt_counter_pause(unit);
|
||||
pcnt_counter_clear(unit);
|
||||
|
||||
/* Register ISR handler and enable interrupts for PCNT unit */
|
||||
pcnt_isr_register(pcnt_example_intr_handler, NULL, 0, &user_isr_handle);
|
||||
pcnt_intr_enable(PCNT_TEST_UNIT);
|
||||
/* Install interrupt service and add isr callback handler */
|
||||
pcnt_isr_service_install(0);
|
||||
pcnt_isr_handler_add(unit, pcnt_example_intr_handler, (void *)unit);
|
||||
|
||||
/* Everything is set up, now go to counting */
|
||||
pcnt_counter_resume(PCNT_TEST_UNIT);
|
||||
pcnt_counter_resume(unit);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
int pcnt_unit = PCNT_UNIT_0;
|
||||
/* Initialize LEDC to generate sample pulse signal */
|
||||
ledc_init();
|
||||
|
||||
/* Initialize PCNT event queue and PCNT functions */
|
||||
pcnt_evt_queue = xQueueCreate(10, sizeof(pcnt_evt_t));
|
||||
pcnt_example_init();
|
||||
pcnt_example_init(pcnt_unit);
|
||||
|
||||
int16_t count = 0;
|
||||
pcnt_evt_t evt;
|
||||
@@ -186,31 +172,26 @@ void app_main(void)
|
||||
*/
|
||||
res = xQueueReceive(pcnt_evt_queue, &evt, 1000 / portTICK_PERIOD_MS);
|
||||
if (res == pdTRUE) {
|
||||
pcnt_get_counter_value(PCNT_TEST_UNIT, &count);
|
||||
printf("Event PCNT unit[%d]; cnt: %d\n", evt.unit, count);
|
||||
pcnt_get_counter_value(pcnt_unit, &count);
|
||||
ESP_LOGI(TAG, "Event PCNT unit[%d]; cnt: %d", evt.unit, count);
|
||||
if (evt.status & PCNT_EVT_THRES_1) {
|
||||
printf("THRES1 EVT\n");
|
||||
ESP_LOGI(TAG, "THRES1 EVT");
|
||||
}
|
||||
if (evt.status & PCNT_EVT_THRES_0) {
|
||||
printf("THRES0 EVT\n");
|
||||
ESP_LOGI(TAG, "THRES0 EVT");
|
||||
}
|
||||
if (evt.status & PCNT_EVT_L_LIM) {
|
||||
printf("L_LIM EVT\n");
|
||||
ESP_LOGI(TAG, "L_LIM EVT");
|
||||
}
|
||||
if (evt.status & PCNT_EVT_H_LIM) {
|
||||
printf("H_LIM EVT\n");
|
||||
ESP_LOGI(TAG, "H_LIM EVT");
|
||||
}
|
||||
if (evt.status & PCNT_EVT_ZERO) {
|
||||
printf("ZERO EVT\n");
|
||||
ESP_LOGI(TAG, "ZERO EVT");
|
||||
}
|
||||
} else {
|
||||
pcnt_get_counter_value(PCNT_TEST_UNIT, &count);
|
||||
printf("Current counter value :%d\n", count);
|
||||
pcnt_get_counter_value(pcnt_unit, &count);
|
||||
ESP_LOGI(TAG, "Current counter value :%d", count);
|
||||
}
|
||||
}
|
||||
if(user_isr_handle) {
|
||||
//Free the ISR service handle.
|
||||
esp_intr_free(user_isr_handle);
|
||||
user_isr_handle = NULL;
|
||||
}
|
||||
}
|
6
examples/peripherals/pcnt/rotary_encoder/CMakeLists.txt
Normal file
6
examples/peripherals/pcnt/rotary_encoder/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(rotary_encoder)
|
9
examples/peripherals/pcnt/rotary_encoder/Makefile
Normal file
9
examples/peripherals/pcnt/rotary_encoder/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
#
|
||||
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := rotary_encoder
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
||||
|
75
examples/peripherals/pcnt/rotary_encoder/README.md
Normal file
75
examples/peripherals/pcnt/rotary_encoder/README.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Rotary Encoder Example
|
||||
|
||||
(See the README.md file in the upper level 'examples' directory for more information about examples.)
|
||||
|
||||
The PCNT peripheral is designed to count the number of rising and/or falling edges of an input signal. Each PCNT unit has two channels, which makes it possible to extract more information from two input signals than only one signal.
|
||||
This example shows how to make use of the HW features to decode the differential signals generated from a common rotary encoder -- [EC11](https://tech.alpsalpine.com/prod/e/html/encoder/incremental/ec11/ec11_list.html).
|
||||
|
||||
The signals a rotary encoder produces (and what can be handled by this example) are based on a 2-bit gray code available on 2 digital data signal lines. The typical encoders use 3 output pins: 2 for the signals and one for the common signal usually GND.
|
||||
|
||||
Typical signals:
|
||||
|
||||
```
|
||||
A +-----+ +-----+ +-----+
|
||||
| | | |
|
||||
| | | |
|
||||
+-----+ +-----+
|
||||
B +-----+ +-----+ +-----+
|
||||
| | | |
|
||||
| | | |
|
||||
+-----+ +-----+
|
||||
|
||||
+--------------------------------------->
|
||||
CW direction
|
||||
```
|
||||
|
||||
## How to Use Example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
* An ESP development board
|
||||
* EC11 rotary encoder (or other encoders which can produce quadrature waveforms)
|
||||
|
||||
Connection :
|
||||
|
||||
```
|
||||
+--------+ +---------------------------------+
|
||||
| | | |
|
||||
| A +--------------+ GPIO_A (internal pull up) |
|
||||
| | | |
|
||||
+-------+ | | |
|
||||
| | | GND +--------------+ GND |
|
||||
+-------+ | | |
|
||||
| | | |
|
||||
| B +--------------+ GPIO_B (internal pull up) |
|
||||
| | | |
|
||||
+--------+ +---------------------------------+
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
For any technical queries, please open an [issue] (https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon.
|
@@ -0,0 +1,8 @@
|
||||
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 "")
|
||||
|
@@ -0,0 +1,3 @@
|
||||
COMPONENT_ADD_INCLUDEDIRS := include
|
||||
|
||||
COMPONENT_SRCDIRS := src
|
@@ -0,0 +1,127 @@
|
||||
// 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
|
@@ -0,0 +1,164 @@
|
||||
// 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 "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)
|
||||
|
||||
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
|
||||
ROTARY_CHECK(pcnt_isr_service_install(0) == ESP_OK, "install isr service failed", err, ESP_FAIL);
|
||||
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;
|
||||
}
|
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "rotary_encoder_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
@@ -0,0 +1,37 @@
|
||||
/* PCNT example -- Rotary Encoder
|
||||
|
||||
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 "esp_log.h"
|
||||
#include "rotary_encoder.h"
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// Rotary encoder underlying device is represented by a PCNT unit in this example
|
||||
uint32_t pcnt_unit = 0;
|
||||
|
||||
// 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));
|
||||
|
||||
// Filter out glitch (1us)
|
||||
ESP_ERROR_CHECK(encoder->set_glitch_filter(encoder, 1));
|
||||
|
||||
// Start encoder
|
||||
ESP_ERROR_CHECK(encoder->start(encoder));
|
||||
|
||||
// Report counter value
|
||||
while (1) {
|
||||
ESP_LOGI(TAG, "Encoder value: %d", encoder->get_counter_value(encoder));
|
||||
vTaskDelay(pdMS_TO_TICKS(1000));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user