Merge branch 'refactor/hal_pcnt_driver' into 'master'

refactor(pcnt): add hal pcnt driver

See merge request espressif/esp-idf!5600
This commit is contained in:
Wang Jia Lin 2019-11-19 17:22:49 +08:00
commit 7494877b1c
11 changed files with 1418 additions and 316 deletions

View File

@ -1,5 +1,18 @@
#ifndef __PCNT_H__
#define __PCNT_H__
// Copyright 2015-2019 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
#include <esp_types.h>
#include "esp_intr_alloc.h"
@ -8,92 +21,15 @@
#include "freertos/semphr.h"
#include "freertos/xtensa_api.h"
#include "soc/soc.h"
#include "soc/pcnt_periph.h"
#include "soc/pcnt_caps.h"
#include "driver/gpio.h"
#include "hal/pcnt_types.h"
#include "hal/pcnt_hal.h"
#ifdef __cplusplus
extern "C" {
#endif
#define PCNT_PIN_NOT_USED (-1) /*!< When selected for a pin, this pin will not be used */
/**
* @brief Selection of available modes that determine the counter's action depending on the state of the control signal's input GPIO
* @note Configuration covers two actions, one for high, and one for low level on the control input
*/
typedef enum {
PCNT_MODE_KEEP = 0, /*!< Control mode: won't change counter mode*/
PCNT_MODE_REVERSE = 1, /*!< Control mode: invert counter mode(increase -> decrease, decrease -> increase) */
PCNT_MODE_DISABLE = 2, /*!< Control mode: Inhibit counter(counter value will not change in this condition) */
PCNT_MODE_MAX
} pcnt_ctrl_mode_t;
/**
* @brief Selection of available modes that determine the counter's action on the edge of the pulse signal's input GPIO
* @note Configuration covers two actions, one for positive, and one for negative edge on the pulse input
*/
typedef enum {
PCNT_COUNT_DIS = 0, /*!< Counter mode: Inhibit counter(counter value will not change in this condition) */
PCNT_COUNT_INC = 1, /*!< Counter mode: Increase counter value */
PCNT_COUNT_DEC = 2, /*!< Counter mode: Decrease counter value */
PCNT_COUNT_MAX
} pcnt_count_mode_t;
/**
* @brief Selection of all available PCNT units
*/
typedef enum {
PCNT_UNIT_0 = 0, /*!< PCNT unit 0 */
PCNT_UNIT_1 = 1, /*!< PCNT unit 1 */
PCNT_UNIT_2 = 2, /*!< PCNT unit 2 */
PCNT_UNIT_3 = 3, /*!< PCNT unit 3 */
//ESP32-S2 only have 4 unit
#ifdef CONFIG_IDF_TARGET_ESP32
PCNT_UNIT_4 = 4, /*!< PCNT unit 4 */
PCNT_UNIT_5 = 5, /*!< PCNT unit 5 */
PCNT_UNIT_6 = 6, /*!< PCNT unit 6 */
PCNT_UNIT_7 = 7, /*!< PCNT unit 7 */
#endif
PCNT_UNIT_MAX,
} pcnt_unit_t;
/**
* @brief Selection of channels available for a single PCNT unit
*/
typedef enum {
PCNT_CHANNEL_0 = 0x00, /*!< PCNT channel 0 */
PCNT_CHANNEL_1 = 0x01, /*!< PCNT channel 1 */
PCNT_CHANNEL_MAX,
} pcnt_channel_t;
/**
* @brief Selection of counter's events the may trigger an interrupt
*/
typedef enum {
PCNT_EVT_THRES_1 = BIT(2), /*!< PCNT watch point event: threshold1 value event */
PCNT_EVT_THRES_0 = BIT(3), /*!< PCNT watch point event: threshold0 value event */
PCNT_EVT_L_LIM = BIT(4), /*!< PCNT watch point event: Minimum counter value */
PCNT_EVT_H_LIM = BIT(5), /*!< PCNT watch point event: Maximum counter value */
PCNT_EVT_ZERO = BIT(6), /*!< PCNT watch point event: counter value zero event */
PCNT_EVT_MAX
} pcnt_evt_type_t;
/**
* @brief Pulse Counter configuration for a single channel
*/
typedef struct {
int pulse_gpio_num; /*!< Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored */
int ctrl_gpio_num; /*!< Control signal input GPIO number, a negative value will be ignored */
pcnt_ctrl_mode_t lctrl_mode; /*!< PCNT low control mode */
pcnt_ctrl_mode_t hctrl_mode; /*!< PCNT high control mode */
pcnt_count_mode_t pos_mode; /*!< PCNT positive edge count mode */
pcnt_count_mode_t neg_mode; /*!< PCNT negative edge count mode */
int16_t counter_h_lim; /*!< Maximum counter value */
int16_t counter_l_lim; /*!< Minimum counter value */
pcnt_unit_t unit; /*!< PCNT unit number */
pcnt_channel_t channel; /*!< the PCNT channel */
} pcnt_config_t;
typedef intr_handle_t pcnt_isr_handle_t;
/**
@ -105,6 +41,7 @@ typedef intr_handle_t pcnt_isr_handle_t;
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver already initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_unit_config(const pcnt_config_t *pcnt_config);
@ -115,11 +52,12 @@ esp_err_t pcnt_unit_config(const pcnt_config_t *pcnt_config);
* @param pcnt_unit Pulse Counter unit number
* @param count Pointer to accept counter value
*
* @return
* @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_counter_value(pcnt_unit_t pcnt_unit, int16_t* count);
esp_err_t pcnt_get_counter_value(pcnt_unit_t pcnt_unit, int16_t *count);
/**
* @brief Pause PCNT counter of PCNT unit
@ -128,6 +66,7 @@ esp_err_t pcnt_get_counter_value(pcnt_unit_t pcnt_unit, int16_t* count);
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_counter_pause(pcnt_unit_t pcnt_unit);
@ -139,6 +78,7 @@ esp_err_t pcnt_counter_pause(pcnt_unit_t pcnt_unit);
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_counter_resume(pcnt_unit_t pcnt_unit);
@ -150,6 +90,7 @@ esp_err_t pcnt_counter_resume(pcnt_unit_t pcnt_unit);
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_counter_clear(pcnt_unit_t pcnt_unit);
@ -164,6 +105,7 @@ esp_err_t pcnt_counter_clear(pcnt_unit_t pcnt_unit);
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_intr_enable(pcnt_unit_t pcnt_unit);
@ -175,6 +117,7 @@ esp_err_t pcnt_intr_enable(pcnt_unit_t pcnt_unit);
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_intr_disable(pcnt_unit_t pcnt_unit);
@ -187,6 +130,7 @@ esp_err_t pcnt_intr_disable(pcnt_unit_t pcnt_unit);
* All enabled events share the same interrupt (one interrupt per pulse counter unit).
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_event_enable(pcnt_unit_t unit, pcnt_evt_type_t evt_type);
@ -199,6 +143,7 @@ esp_err_t pcnt_event_enable(pcnt_unit_t unit, pcnt_evt_type_t evt_type);
* All enabled events share the same interrupt (one interrupt per pulse counter unit).
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_event_disable(pcnt_unit_t unit, pcnt_evt_type_t evt_type);
@ -214,6 +159,7 @@ esp_err_t pcnt_event_disable(pcnt_unit_t unit, pcnt_evt_type_t evt_type);
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_set_event_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16_t value);
@ -228,6 +174,7 @@ esp_err_t pcnt_set_event_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16
*
* @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_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16_t *value);
@ -250,7 +197,7 @@ esp_err_t pcnt_get_event_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16
* - ESP_ERR_NOT_FOUND Can not find the interrupt that matches the flags.
* - ESP_ERR_INVALID_ARG Function pointer error.
*/
esp_err_t pcnt_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, pcnt_isr_handle_t *handle);
esp_err_t pcnt_isr_register(void (*fn)(void *), void *arg, int intr_alloc_flags, pcnt_isr_handle_t *handle);
/**
* @brief Configure PCNT pulse signal input pin and control input pin
@ -264,6 +211,7 @@ esp_err_t pcnt_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags,
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_set_pin(pcnt_unit_t unit, pcnt_channel_t channel, int pulse_io, int ctrl_io);
@ -275,6 +223,7 @@ esp_err_t pcnt_set_pin(pcnt_unit_t unit, pcnt_channel_t channel, int pulse_io, i
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_filter_enable(pcnt_unit_t unit);
@ -286,6 +235,7 @@ esp_err_t pcnt_filter_enable(pcnt_unit_t unit);
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_filter_disable(pcnt_unit_t unit);
@ -301,6 +251,7 @@ esp_err_t pcnt_filter_disable(pcnt_unit_t unit);
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_set_filter_value(pcnt_unit_t unit, uint16_t filter_val);
@ -313,6 +264,7 @@ esp_err_t pcnt_set_filter_value(pcnt_unit_t unit, uint16_t filter_val);
*
* @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_filter_value(pcnt_unit_t unit, uint16_t *filter_val);
@ -329,6 +281,7 @@ esp_err_t pcnt_get_filter_value(pcnt_unit_t unit, uint16_t *filter_val);
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_set_mode(pcnt_unit_t unit, pcnt_channel_t channel,
@ -356,6 +309,7 @@ esp_err_t pcnt_set_mode(pcnt_unit_t unit, pcnt_channel_t channel,
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_isr_handler_add(pcnt_unit_t unit, void(*isr_handler)(void *), void *args);
@ -364,13 +318,14 @@ esp_err_t pcnt_isr_handler_add(pcnt_unit_t unit, void(*isr_handler)(void *), voi
* @brief Install PCNT ISR service.
* @note We can manage different interrupt service for each unit.
* This function will use the default ISR handle service, Calling pcnt_isr_service_uninstall to
* uninstall the default service if needed. Please do not use pcnt_isr_register if this function was called.
* uninstall the default service if needed. Please do not use pcnt_isr_register if this function was called.
*
* @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred)
* ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_NO_MEM No memory to install this service
* - ESP_ERR_INVALID_STATE ISR service already installed
*/
@ -388,6 +343,7 @@ void pcnt_isr_service_uninstall(void);
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_STATE pcnt driver has not been initialized
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t pcnt_isr_handler_remove(pcnt_unit_t unit);
@ -428,9 +384,6 @@ esp_err_t pcnt_isr_handler_remove(pcnt_unit_t unit);
* @}
*/
#ifdef __cplusplus
#ifdef __cplusplus
}
#endif
#endif

View File

@ -3,7 +3,7 @@
// 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
@ -11,6 +11,7 @@
// 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 "esp_log.h"
#include "driver/pcnt.h"
#include "driver/periph_ctrl.h"
@ -24,28 +25,313 @@
#define PCNT_CTRL_MODE_ERR_STR "PCNT CTRL MODE ERROR"
#define PCNT_EVT_TYPE_ERR_STR "PCNT value type error"
#define PCNT_LIMT_VAL_ERR_STR "PCNT limit value error"
#define PCNT_NUM_ERR_STR "PCNT num error"
#define PCNT_DRIVER_ERR_STR "PCNT driver error"
#define PCNT_ENTER_CRITICAL(mux) portENTER_CRITICAL(mux)
#define PCNT_EXIT_CRITICAL(mux) portEXIT_CRITICAL(mux)
static const char *TAG = "pcnt";
#define PCNT_CHECK(a, str, ret_val) \
if (!(a)) { \
ESP_LOGE(PCNT_TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
ESP_LOGE(TAG,"%s(%d): %s", __FUNCTION__, __LINE__, str); \
return (ret_val); \
}
typedef struct{
typedef struct {
pcnt_hal_context_t hal; /*!< PCNT hal context*/
} pcnt_obj_t;
static pcnt_obj_t *p_pcnt_obj[PCNT_PORT_MAX] = {0};
#define PCNT_OBJ_CHECK(pcnt_port) { \
PCNT_CHECK((pcnt_port < PCNT_PORT_MAX), PCNT_NUM_ERR_STR, ESP_ERR_INVALID_ARG); \
PCNT_CHECK((p_pcnt_obj[pcnt_port]), PCNT_DRIVER_ERR_STR, ESP_ERR_INVALID_STATE); \
}
typedef struct {
void(*fn)(void *args); /*!< isr function */
void* args; /*!< isr function args */
void *args; /*!< isr function args */
} pcnt_isr_func_t;
static pcnt_isr_func_t *pcnt_isr_func = NULL;
static pcnt_isr_handle_t pcnt_isr_service = NULL;
static portMUX_TYPE pcnt_spinlock = portMUX_INITIALIZER_UNLOCKED;
static const char* PCNT_TAG = "pcnt";
esp_err_t pcnt_unit_config(const pcnt_config_t *pcnt_config)
static inline esp_err_t _pcnt_set_mode(pcnt_port_t pcnt_port, 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)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(channel < PCNT_CHANNEL_MAX, PCNT_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK((pos_mode < PCNT_COUNT_MAX) && (neg_mode < PCNT_COUNT_MAX), PCNT_COUNT_MODE_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK((hctrl_mode < PCNT_MODE_MAX) && (lctrl_mode < PCNT_MODE_MAX), PCNT_CTRL_MODE_ERR_STR, ESP_ERR_INVALID_ARG);
pcnt_hal_set_mode(&(p_pcnt_obj[pcnt_port]->hal), unit, channel, pos_mode, neg_mode, hctrl_mode, lctrl_mode);
return ESP_OK;
}
static inline esp_err_t _pcnt_set_pin(pcnt_port_t pcnt_port, pcnt_unit_t unit, pcnt_channel_t channel, int pulse_io, int ctrl_io)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(channel < PCNT_CHANNEL_MAX, PCNT_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG);
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);
gpio_matrix_in(pulse_io, input_sig_index, 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);
gpio_matrix_in(ctrl_io, ctrl_sig_index, 0);
}
return ESP_OK;
}
static inline esp_err_t _pcnt_get_counter_value(pcnt_port_t pcnt_port, pcnt_unit_t pcnt_unit, int16_t *count)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(pcnt_unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(count != NULL, PCNT_ADDRESS_ERR_STR, ESP_ERR_INVALID_ARG);
pcnt_hal_get_counter_value(&(p_pcnt_obj[pcnt_port]->hal), pcnt_unit, count);
return ESP_OK;
}
static inline esp_err_t _pcnt_counter_pause(pcnt_port_t pcnt_port, pcnt_unit_t pcnt_unit)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(pcnt_unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
pcnt_hal_counter_pause(&(p_pcnt_obj[pcnt_port]->hal), pcnt_unit);
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
}
static inline esp_err_t _pcnt_counter_resume(pcnt_port_t pcnt_port, pcnt_unit_t pcnt_unit)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(pcnt_unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
pcnt_hal_counter_resume(&(p_pcnt_obj[pcnt_port]->hal), pcnt_unit);
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
}
static inline esp_err_t _pcnt_counter_clear(pcnt_port_t pcnt_port, pcnt_unit_t pcnt_unit)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(pcnt_unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
pcnt_hal_counter_clear(&(p_pcnt_obj[pcnt_port]->hal), pcnt_unit);
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
}
static inline esp_err_t _pcnt_intr_enable(pcnt_port_t pcnt_port, pcnt_unit_t pcnt_unit)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(pcnt_unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
pcnt_hal_intr_enable(&(p_pcnt_obj[pcnt_port]->hal), pcnt_unit);
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
}
static inline esp_err_t _pcnt_intr_disable(pcnt_port_t pcnt_port, pcnt_unit_t pcnt_unit)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(pcnt_unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
pcnt_hal_intr_disable(&(p_pcnt_obj[pcnt_port]->hal), pcnt_unit);
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
}
static inline esp_err_t _pcnt_event_enable(pcnt_port_t pcnt_port, pcnt_unit_t unit, pcnt_evt_type_t evt_type)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(evt_type < PCNT_EVT_MAX, PCNT_EVT_TYPE_ERR_STR, ESP_ERR_INVALID_ARG);
pcnt_hal_event_enable(&(p_pcnt_obj[pcnt_port]->hal), unit, evt_type);
return ESP_OK;
}
static inline esp_err_t _pcnt_event_disable(pcnt_port_t pcnt_port, pcnt_unit_t unit, pcnt_evt_type_t evt_type)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(evt_type < PCNT_EVT_MAX, PCNT_EVT_TYPE_ERR_STR, ESP_ERR_INVALID_ARG);
pcnt_hal_event_disable(&(p_pcnt_obj[pcnt_port]->hal), unit, evt_type);
return ESP_OK;
}
static inline esp_err_t _pcnt_set_event_value(pcnt_port_t pcnt_port, pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16_t value)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(evt_type < PCNT_EVT_MAX, PCNT_EVT_TYPE_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(!(evt_type == PCNT_EVT_L_LIM && value > 0), PCNT_LIMT_VAL_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(!(evt_type == PCNT_EVT_H_LIM && value < 0), PCNT_LIMT_VAL_ERR_STR, ESP_ERR_INVALID_ARG);
pcnt_hal_set_event_value(&(p_pcnt_obj[pcnt_port]->hal), unit, evt_type, value);
return ESP_OK;
}
static inline esp_err_t _pcnt_get_event_value(pcnt_port_t pcnt_port, pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16_t *value)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(evt_type < PCNT_EVT_MAX, PCNT_EVT_TYPE_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(value != NULL, PCNT_ADDRESS_ERR_STR, ESP_ERR_INVALID_ARG);
pcnt_hal_get_event_value(&(p_pcnt_obj[pcnt_port]->hal), unit, evt_type, value);
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);
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(filter_val < 1024, PCNT_PARAM_ERR_STR, ESP_ERR_INVALID_ARG);
pcnt_hal_set_filter_value(&(p_pcnt_obj[pcnt_port]->hal), unit, filter_val);
return ESP_OK;
}
static inline esp_err_t _pcnt_get_filter_value(pcnt_port_t pcnt_port, pcnt_unit_t unit, uint16_t *filter_val)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(filter_val != NULL, PCNT_ADDRESS_ERR_STR, ESP_ERR_INVALID_ARG);
pcnt_hal_get_filter_value(&(p_pcnt_obj[pcnt_port]->hal), unit, filter_val);
return ESP_OK;
}
static inline esp_err_t _pcnt_filter_enable(pcnt_port_t pcnt_port, pcnt_unit_t unit)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
pcnt_hal_filter_enable(&(p_pcnt_obj[pcnt_port]->hal), unit);
return ESP_OK;
}
static inline esp_err_t _pcnt_filter_disable(pcnt_port_t pcnt_port, pcnt_unit_t unit)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
pcnt_hal_filter_disable(&(p_pcnt_obj[pcnt_port]->hal), unit);
return ESP_OK;
}
static inline esp_err_t _pcnt_isr_handler_add(pcnt_port_t pcnt_port, pcnt_unit_t unit, void(*isr_handler)(void *), void *args)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(pcnt_isr_func != NULL, "ISR service is not installed, call pcnt_install_isr_service() first", ESP_ERR_INVALID_STATE);
PCNT_CHECK(unit < PCNT_UNIT_MAX, "PCNT unit error", ESP_ERR_INVALID_ARG);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
_pcnt_intr_disable(PCNT_PORT_0, unit);
if (pcnt_isr_func) {
pcnt_isr_func[unit].fn = isr_handler;
pcnt_isr_func[unit].args = args;
}
_pcnt_intr_enable(PCNT_PORT_0, unit);
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
}
static inline esp_err_t _pcnt_isr_handler_remove(pcnt_port_t pcnt_port, pcnt_unit_t unit)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(pcnt_isr_func != NULL, "ISR service is not installed", ESP_ERR_INVALID_STATE);
PCNT_CHECK(unit < PCNT_UNIT_MAX, "PCNT unit error", ESP_ERR_INVALID_ARG);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
_pcnt_intr_disable(PCNT_PORT_0, unit);
if (pcnt_isr_func) {
pcnt_isr_func[unit].fn = NULL;
pcnt_isr_func[unit].args = NULL;
}
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
}
// pcnt interrupt service
static void IRAM_ATTR pcnt_intr_service(void *arg)
{
uint32_t status;
pcnt_port_t pcnt_port = (pcnt_port_t)arg;
pcnt_hal_get_intr_status(&(p_pcnt_obj[pcnt_port]->hal), &status);
while (status) {
int unit = __builtin_ffs(status) - 1;
status &= ~(1 << unit);
if (pcnt_isr_func[unit].fn != NULL) {
(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)
{
PCNT_OBJ_CHECK(pcnt_port);
PCNT_CHECK(pcnt_isr_func == NULL, "ISR service already installed", ESP_ERR_INVALID_STATE);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
esp_err_t ret = ESP_FAIL;
pcnt_isr_func = (pcnt_isr_func_t *) calloc(PCNT_UNIT_MAX, sizeof(pcnt_isr_func_t));
if (pcnt_isr_func == NULL) {
ret = ESP_ERR_NO_MEM;
} else {
ret = pcnt_isr_register(pcnt_intr_service, (void *)pcnt_port, intr_alloc_flags, &pcnt_isr_service);
}
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ret;
}
static inline esp_err_t _pcnt_isr_service_uninstall(pcnt_port_t pcnt_port)
{
PCNT_OBJ_CHECK(pcnt_port);
if (pcnt_isr_func == NULL) {
return ESP_FAIL;
}
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
esp_intr_free(pcnt_isr_service);
free(pcnt_isr_func);
pcnt_isr_func = NULL;
pcnt_isr_service = NULL;
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
}
static inline esp_err_t _pcnt_unit_config(pcnt_port_t pcnt_port, const pcnt_config_t *pcnt_config)
{
PCNT_OBJ_CHECK(pcnt_port);
uint8_t unit = pcnt_config->unit;
uint8_t channel = pcnt_config->channel;
int input_io = pcnt_config->pulse_gpio_num;
@ -65,310 +351,163 @@ esp_err_t pcnt_unit_config(const pcnt_config_t *pcnt_config)
}
periph_module_enable(PERIPH_PCNT_MODULE);
/*Set counter range*/
pcnt_set_event_value(unit, PCNT_EVT_H_LIM, pcnt_config->counter_h_lim);
pcnt_set_event_value(unit, PCNT_EVT_L_LIM, pcnt_config->counter_l_lim);
_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);
/*Default value after reboot is positive, we disable these events like others*/
pcnt_event_disable(unit, PCNT_EVT_H_LIM);
pcnt_event_disable(unit, PCNT_EVT_L_LIM);
pcnt_event_disable(unit, PCNT_EVT_ZERO);
pcnt_filter_disable(unit);
_pcnt_event_disable(pcnt_port, unit, PCNT_EVT_H_LIM);
_pcnt_event_disable(pcnt_port, unit, PCNT_EVT_L_LIM);
_pcnt_event_disable(pcnt_port, unit, PCNT_EVT_ZERO);
_pcnt_filter_disable(pcnt_port, unit);
/*set pulse input and control mode*/
pcnt_set_mode(unit, channel, pcnt_config->pos_mode, pcnt_config->neg_mode, pcnt_config->hctrl_mode, pcnt_config->lctrl_mode);
_pcnt_set_mode(pcnt_port, unit, channel, pcnt_config->pos_mode, pcnt_config->neg_mode, pcnt_config->hctrl_mode, pcnt_config->lctrl_mode);
/*Set pulse input and control pins*/
pcnt_set_pin(unit, channel, input_io, ctrl_io);
_pcnt_set_pin(pcnt_port, unit, channel, input_io, ctrl_io);
return ESP_OK;
}
esp_err_t pcnt_deinit(pcnt_port_t pcnt_port)
{
PCNT_OBJ_CHECK(pcnt_port);
heap_caps_free(p_pcnt_obj[pcnt_port]);
p_pcnt_obj[pcnt_port] = NULL;
return ESP_OK;
}
esp_err_t pcnt_init(pcnt_port_t pcnt_port)
{
PCNT_CHECK((pcnt_port < PCNT_PORT_MAX), PCNT_NUM_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK((p_pcnt_obj[pcnt_port]) == NULL, "pcnt driver already initted", ESP_ERR_INVALID_STATE);
p_pcnt_obj[pcnt_port] = (pcnt_obj_t *)heap_caps_calloc(1, sizeof(pcnt_obj_t), MALLOC_CAP_DEFAULT);
if (p_pcnt_obj[pcnt_port] == NULL) {
ESP_LOGE(TAG, "PCNT driver malloc error");
return ESP_FAIL;
}
pcnt_hal_init(&(p_pcnt_obj[pcnt_port]->hal), pcnt_port);
return ESP_OK;
}
// TODO: The following functions are wrappers, for compatibility with current API.
esp_err_t pcnt_unit_config(const pcnt_config_t *pcnt_config)
{
esp_err_t ret;
if ((p_pcnt_obj[PCNT_PORT_0]) == NULL) {
ret = pcnt_init(PCNT_PORT_0);
if (ret != ESP_OK) {
return ret;
}
}
return _pcnt_unit_config(PCNT_PORT_0, pcnt_config);
}
esp_err_t pcnt_set_mode(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)
{
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(channel < PCNT_CHANNEL_MAX, PCNT_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK((pos_mode < PCNT_COUNT_MAX) && (neg_mode < PCNT_COUNT_MAX), PCNT_COUNT_MODE_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK((hctrl_mode < PCNT_MODE_MAX) && (lctrl_mode < PCNT_MODE_MAX), PCNT_CTRL_MODE_ERR_STR, ESP_ERR_INVALID_ARG);
if(channel == 0) {
PCNT.conf_unit[unit].conf0.ch0_pos_mode = pos_mode;
PCNT.conf_unit[unit].conf0.ch0_neg_mode = neg_mode;
PCNT.conf_unit[unit].conf0.ch0_hctrl_mode = hctrl_mode;
PCNT.conf_unit[unit].conf0.ch0_lctrl_mode = lctrl_mode;
} else {
PCNT.conf_unit[unit].conf0.ch1_pos_mode = pos_mode;
PCNT.conf_unit[unit].conf0.ch1_neg_mode = neg_mode;
PCNT.conf_unit[unit].conf0.ch1_hctrl_mode = hctrl_mode;
PCNT.conf_unit[unit].conf0.ch1_lctrl_mode = lctrl_mode;
}
return ESP_OK;
return _pcnt_set_mode(PCNT_PORT_0, unit, channel, pos_mode, neg_mode, hctrl_mode, lctrl_mode);
}
esp_err_t pcnt_set_pin(pcnt_unit_t unit, pcnt_channel_t channel, int pulse_io, int ctrl_io)
{
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(channel < PCNT_CHANNEL_MAX, PCNT_CHANNEL_ERR_STR, ESP_ERR_INVALID_ARG);
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);
gpio_matrix_in(pulse_io, input_sig_index, 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);
gpio_matrix_in(ctrl_io, ctrl_sig_index, 0);
}
return ESP_OK;
return _pcnt_set_pin(PCNT_PORT_0, unit, channel, pulse_io, ctrl_io);
}
esp_err_t pcnt_get_counter_value(pcnt_unit_t pcnt_unit, int16_t* count)
esp_err_t pcnt_get_counter_value(pcnt_unit_t pcnt_unit, int16_t *count)
{
PCNT_CHECK(pcnt_unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(count != NULL, PCNT_ADDRESS_ERR_STR, ESP_ERR_INVALID_ARG);
*count = (int16_t) PCNT.cnt_unit[pcnt_unit].cnt_val;
return ESP_OK;
return _pcnt_get_counter_value(PCNT_PORT_0, pcnt_unit, count);
}
esp_err_t pcnt_counter_pause(pcnt_unit_t pcnt_unit)
{
PCNT_CHECK(pcnt_unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
PCNT.ctrl.val |= BIT(PCNT_CNT_PAUSE_U0_S + (pcnt_unit * 2));
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
return _pcnt_counter_pause(PCNT_PORT_0, pcnt_unit);
}
esp_err_t pcnt_counter_resume(pcnt_unit_t pcnt_unit)
{
PCNT_CHECK(pcnt_unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
PCNT.ctrl.val &= (~(BIT(PCNT_CNT_PAUSE_U0_S + (pcnt_unit * 2))));
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
return _pcnt_counter_resume(PCNT_PORT_0, pcnt_unit);
}
esp_err_t pcnt_counter_clear(pcnt_unit_t pcnt_unit)
{
PCNT_CHECK(pcnt_unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
#ifdef CONFIG_IDF_TARGET_ESP32
uint32_t reset_bit = BIT(PCNT_PLUS_CNT_RST_U0_S + (pcnt_unit * 2));
#elif defined CONFIG_IDF_TARGET_ESP32S2BETA
uint32_t reset_bit = BIT(PCNT_PULSE_CNT_RST_U0_S + (pcnt_unit * 2));
#endif
PCNT.ctrl.val |= reset_bit;
PCNT.ctrl.val &= ~reset_bit;
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
return _pcnt_counter_clear(PCNT_PORT_0, pcnt_unit);
}
esp_err_t pcnt_intr_enable(pcnt_unit_t pcnt_unit)
{
PCNT_CHECK(pcnt_unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
PCNT.int_ena.val |= BIT(PCNT_CNT_THR_EVENT_U0_INT_ENA_S + pcnt_unit);
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
return _pcnt_intr_enable(PCNT_PORT_0, pcnt_unit);
}
esp_err_t pcnt_intr_disable(pcnt_unit_t pcnt_unit)
{
PCNT_CHECK(pcnt_unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
PCNT.int_ena.val &= (~(BIT(PCNT_CNT_THR_EVENT_U0_INT_ENA_S + pcnt_unit)));
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
return _pcnt_intr_disable(PCNT_PORT_0, pcnt_unit);
}
esp_err_t pcnt_event_enable(pcnt_unit_t unit, pcnt_evt_type_t evt_type)
{
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(evt_type < PCNT_EVT_MAX, PCNT_EVT_TYPE_ERR_STR, ESP_ERR_INVALID_ARG);
if(evt_type == PCNT_EVT_L_LIM) {
PCNT.conf_unit[unit].conf0.thr_l_lim_en = 1;
} else if(evt_type == PCNT_EVT_H_LIM) {
PCNT.conf_unit[unit].conf0.thr_h_lim_en = 1;
} else if(evt_type == PCNT_EVT_THRES_0) {
PCNT.conf_unit[unit].conf0.thr_thres0_en = 1;
} else if(evt_type == PCNT_EVT_THRES_1) {
PCNT.conf_unit[unit].conf0.thr_thres1_en = 1;
} else if(evt_type == PCNT_EVT_ZERO) {
PCNT.conf_unit[unit].conf0.thr_zero_en = 1;
}
return ESP_OK;
return _pcnt_event_enable(PCNT_PORT_0, unit, evt_type);
}
esp_err_t pcnt_event_disable(pcnt_unit_t unit, pcnt_evt_type_t evt_type)
{
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(evt_type < PCNT_EVT_MAX, PCNT_EVT_TYPE_ERR_STR, ESP_ERR_INVALID_ARG);
if(evt_type == PCNT_EVT_L_LIM) {
PCNT.conf_unit[unit].conf0.thr_l_lim_en = 0;
} else if(evt_type == PCNT_EVT_H_LIM) {
PCNT.conf_unit[unit].conf0.thr_h_lim_en = 0;
} else if(evt_type == PCNT_EVT_THRES_0) {
PCNT.conf_unit[unit].conf0.thr_thres0_en = 0;
} else if(evt_type == PCNT_EVT_THRES_1) {
PCNT.conf_unit[unit].conf0.thr_thres1_en = 0;
} else if(evt_type == PCNT_EVT_ZERO) {
PCNT.conf_unit[unit].conf0.thr_zero_en = 0;
}
return ESP_OK;
return _pcnt_event_disable(PCNT_PORT_0, unit, evt_type);
}
esp_err_t pcnt_set_event_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16_t value)
{
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(evt_type < PCNT_EVT_MAX, PCNT_EVT_TYPE_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(!(evt_type == PCNT_EVT_L_LIM && value > 0), PCNT_LIMT_VAL_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(!(evt_type == PCNT_EVT_H_LIM && value < 0), PCNT_LIMT_VAL_ERR_STR, ESP_ERR_INVALID_ARG);
if(evt_type == PCNT_EVT_L_LIM) {
PCNT.conf_unit[unit].conf2.cnt_l_lim = value;
} else if(evt_type == PCNT_EVT_H_LIM) {
PCNT.conf_unit[unit].conf2.cnt_h_lim = value;
} else if(evt_type == PCNT_EVT_THRES_0) {
PCNT.conf_unit[unit].conf1.cnt_thres0 = value;
} else if(evt_type == PCNT_EVT_THRES_1) {
PCNT.conf_unit[unit].conf1.cnt_thres1 = value;
}
return ESP_OK;
return _pcnt_set_event_value(PCNT_PORT_0, unit, evt_type, value);
}
esp_err_t pcnt_get_event_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16_t *value)
{
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(evt_type < PCNT_EVT_MAX, PCNT_EVT_TYPE_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(value != NULL, PCNT_ADDRESS_ERR_STR, ESP_ERR_INVALID_ARG);
if(evt_type == PCNT_EVT_L_LIM) {
*value = (int16_t) PCNT.conf_unit[unit].conf2.cnt_l_lim;
} else if(evt_type == PCNT_EVT_H_LIM) {
*value = (int16_t) PCNT.conf_unit[unit].conf2.cnt_h_lim;
} else if(evt_type == PCNT_EVT_THRES_0) {
*value = (int16_t) PCNT.conf_unit[unit].conf1.cnt_thres0;
} else if(evt_type == PCNT_EVT_THRES_1) {
*value = (int16_t) PCNT.conf_unit[unit].conf1.cnt_thres1;
} else {
*value = 0;
}
return ESP_OK;
return _pcnt_get_event_value(PCNT_PORT_0, unit, evt_type, value);
}
esp_err_t pcnt_set_filter_value(pcnt_unit_t unit, uint16_t filter_val)
{
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(filter_val < 1024, PCNT_PARAM_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT.conf_unit[unit].conf0.filter_thres = filter_val;
return ESP_OK;
return _pcnt_set_filter_value(PCNT_PORT_0, unit, filter_val);
}
esp_err_t pcnt_get_filter_value(pcnt_unit_t unit, uint16_t *filter_val)
{
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT_CHECK(filter_val != NULL, PCNT_ADDRESS_ERR_STR, ESP_ERR_INVALID_ARG);
*filter_val = PCNT.conf_unit[unit].conf0.filter_thres;
return ESP_OK;
return _pcnt_get_filter_value(PCNT_PORT_0, unit, filter_val);
}
esp_err_t pcnt_filter_enable(pcnt_unit_t unit)
{
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT.conf_unit[unit].conf0.filter_en = 1;
return ESP_OK;
return _pcnt_filter_enable(PCNT_PORT_0, unit);
}
esp_err_t pcnt_filter_disable(pcnt_unit_t unit)
{
PCNT_CHECK(unit < PCNT_UNIT_MAX, PCNT_UNIT_ERR_STR, ESP_ERR_INVALID_ARG);
PCNT.conf_unit[unit].conf0.filter_en = 0;
return ESP_OK;
return _pcnt_filter_disable(PCNT_PORT_0, unit);
}
esp_err_t pcnt_isr_register(void (*fun)(void*), void * arg, int intr_alloc_flags, pcnt_isr_handle_t *handle)
esp_err_t pcnt_isr_register(void (*fun)(void *), void *arg, int intr_alloc_flags, pcnt_isr_handle_t *handle)
{
PCNT_CHECK(fun != NULL, PCNT_ADDRESS_ERR_STR, ESP_ERR_INVALID_ARG);
return esp_intr_alloc(ETS_PCNT_INTR_SOURCE, intr_alloc_flags, fun, arg, handle);
}
// pcnt interrupt service
static void IRAM_ATTR pcnt_intr_service(void* arg)
{
const uint32_t intr_status = PCNT.int_st.val;
uint32_t status = intr_status;
while (status) {
int unit = __builtin_ffs(status) - 1;
status &= ~(1 << unit);
if (pcnt_isr_func[unit].fn != NULL) {
(pcnt_isr_func[unit].fn)(pcnt_isr_func[unit].args);
}
}
PCNT.int_clr.val = intr_status;
}
esp_err_t pcnt_isr_handler_add(pcnt_unit_t unit, void(*isr_handler)(void *), void *args)
{
PCNT_CHECK(pcnt_isr_func != NULL, "ISR service is not installed, call pcnt_install_isr_service() first", ESP_ERR_INVALID_STATE);
PCNT_CHECK(unit < PCNT_UNIT_MAX, "PCNT unit error", ESP_ERR_INVALID_ARG);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
pcnt_intr_disable(unit);
if (pcnt_isr_func) {
pcnt_isr_func[unit].fn = isr_handler;
pcnt_isr_func[unit].args = args;
}
pcnt_intr_enable(unit);
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
return _pcnt_isr_handler_add(PCNT_PORT_0, unit, isr_handler, args);
}
esp_err_t pcnt_isr_handler_remove(pcnt_unit_t unit)
{
PCNT_CHECK(pcnt_isr_func != NULL, "ISR service is not installed", ESP_ERR_INVALID_STATE);
PCNT_CHECK(unit < PCNT_UNIT_MAX, "PCNT unit error", ESP_ERR_INVALID_ARG);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
pcnt_intr_disable(unit);
if (pcnt_isr_func) {
pcnt_isr_func[unit].fn = NULL;
pcnt_isr_func[unit].args = NULL;
}
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ESP_OK;
return _pcnt_isr_handler_remove(PCNT_PORT_0, unit);
}
esp_err_t pcnt_isr_service_install(int intr_alloc_flags)
{
PCNT_CHECK(pcnt_isr_func == NULL, "ISR service already installed", ESP_ERR_INVALID_STATE);
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
esp_err_t ret = ESP_FAIL;
pcnt_isr_func = (pcnt_isr_func_t*) calloc(PCNT_UNIT_MAX, sizeof(pcnt_isr_func_t));
if (pcnt_isr_func == NULL) {
ret = ESP_ERR_NO_MEM;
} else {
ret = pcnt_isr_register(pcnt_intr_service, NULL, intr_alloc_flags, &pcnt_isr_service);
}
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
return ret;
return _pcnt_isr_service_install(PCNT_PORT_0, intr_alloc_flags);
}
void pcnt_isr_service_uninstall(void)
void pcnt_isr_service_uninstall()
{
if (pcnt_isr_func == NULL) {
return;
}
PCNT_ENTER_CRITICAL(&pcnt_spinlock);
esp_intr_free(pcnt_isr_service);
free(pcnt_isr_func);
pcnt_isr_func = NULL;
pcnt_isr_service = NULL;
PCNT_EXIT_CRITICAL(&pcnt_spinlock);
}
_pcnt_isr_service_uninstall(PCNT_PORT_0);
}

View File

@ -17,6 +17,7 @@ list(APPEND srcs
"src/hal/spi_slave_hal.c"
"src/hal/spi_slave_hal_iram.c"
"src/soc_include_legacy_warn.c"
"src/hal/pcnt_hal.c"
)
# TODO: SPI Flash HAL for ESP32S2Beta also

View File

@ -0,0 +1,301 @@
// Copyright 2015-2019 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.
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use in application code.
* See readme.md in soc/include/hal/readme.md
******************************************************************************/
// The LL layer for ESP32 PCNT register operations
#pragma once
#include "soc/pcnt_periph.h"
#include "hal/pcnt_types.h"
#ifdef __cplusplus
extern "C" {
#endif
// Get PCNT hardware instance with giving pcnt num
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
/**
* @brief Set PCNT counter 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
* @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_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;
}
/**
* @brief Get pulse counter value
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit Pulse Counter unit number
* @param count Pointer to accept counter value
*/
static inline void pcnt_ll_get_counter_value(pcnt_dev_t *hw, pcnt_unit_t unit, int16_t *count)
{
*count = (int16_t) hw->cnt_unit[unit].cnt_val;
}
/**
* @brief Pause PCNT counter of PCNT unit
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
*/
static inline void pcnt_ll_counter_pause(pcnt_dev_t *hw, pcnt_unit_t unit)
{
hw->ctrl.val |= BIT(PCNT_CNT_PAUSE_U0_S + (unit * 2));
}
/**
* @brief Resume counting for PCNT counter
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number, select from pcnt_unit_t
*/
static inline void pcnt_ll_counter_resume(pcnt_dev_t *hw, pcnt_unit_t unit)
{
hw->ctrl.val &= (~(BIT(PCNT_CNT_PAUSE_U0_S + (unit * 2))));
}
/**
* @brief Clear and reset PCNT counter value to zero
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number, select from pcnt_unit_t
*/
static inline void pcnt_ll_counter_clear(pcnt_dev_t *hw, pcnt_unit_t unit)
{
uint32_t reset_bit = BIT(PCNT_PLUS_CNT_RST_U0_S + (unit * 2));
hw->ctrl.val |= reset_bit;
hw->ctrl.val &= ~reset_bit;
}
/**
* @brief Enable PCNT interrupt for PCNT unit
* @note
* Each Pulse counter unit has five watch point events that share the same interrupt.
* Configure events with pcnt_event_enable() and pcnt_event_disable()
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
*/
static inline void pcnt_ll_intr_enable(pcnt_dev_t *hw, pcnt_unit_t unit)
{
hw->int_ena.val |= BIT(PCNT_CNT_THR_EVENT_U0_INT_ENA_S + unit);
}
/**
* @brief Disable PCNT interrupt for PCNT unit
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
*/
static inline void pcnt_ll_intr_disable(pcnt_dev_t *hw, pcnt_unit_t unit)
{
hw->int_ena.val &= (~(BIT(PCNT_CNT_THR_EVENT_U0_INT_ENA_S + unit)));
}
/**
* @brief Get PCNT interrupt status
*
* @param hw Peripheral PCNT hardware instance address.
* @param status Pointer to accept value
*/
static inline void pcnt_ll_get_intr_status(pcnt_dev_t *hw, uint32_t *status)
{
*status = hw->int_st.val;
}
/**
* @brief Clear PCNT interrupt status
*
* @param hw Peripheral PCNT hardware instance address.
* @param status value to clear interrupt status
*/
static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
{
hw->int_clr.val = status;
}
/**
* @brief Enable PCNT event of PCNT unit
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @param evt_type Watch point event type.
* All enabled events share the same interrupt (one interrupt per pulse counter unit).
*/
static inline void pcnt_ll_event_enable(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_evt_type_t evt_type)
{
if (evt_type == PCNT_EVT_L_LIM) {
hw->conf_unit[unit].conf0.thr_l_lim_en = 1;
} else if (evt_type == PCNT_EVT_H_LIM) {
hw->conf_unit[unit].conf0.thr_h_lim_en = 1;
} else if (evt_type == PCNT_EVT_THRES_0) {
hw->conf_unit[unit].conf0.thr_thres0_en = 1;
} else if (evt_type == PCNT_EVT_THRES_1) {
hw->conf_unit[unit].conf0.thr_thres1_en = 1;
} else if (evt_type == PCNT_EVT_ZERO) {
hw->conf_unit[unit].conf0.thr_zero_en = 1;
}
}
/**
* @brief Disable PCNT event of PCNT unit
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @param evt_type Watch point event type.
* All enabled events share the same interrupt (one interrupt per pulse counter unit).
*/
static inline void pcnt_ll_event_disable(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_evt_type_t evt_type)
{
if (evt_type == PCNT_EVT_L_LIM) {
hw->conf_unit[unit].conf0.thr_l_lim_en = 0;
} else if (evt_type == PCNT_EVT_H_LIM) {
hw->conf_unit[unit].conf0.thr_h_lim_en = 0;
} else if (evt_type == PCNT_EVT_THRES_0) {
hw->conf_unit[unit].conf0.thr_thres0_en = 0;
} else if (evt_type == PCNT_EVT_THRES_1) {
hw->conf_unit[unit].conf0.thr_thres1_en = 0;
} else if (evt_type == PCNT_EVT_ZERO) {
hw->conf_unit[unit].conf0.thr_zero_en = 0;
}
}
/**
* @brief Set PCNT event value of PCNT unit
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @param evt_type Watch point event type.
* All enabled events share the same interrupt (one interrupt per pulse counter unit).
*
* @param value Counter value for PCNT event
*/
static inline void pcnt_ll_set_event_value(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16_t value)
{
if (evt_type == PCNT_EVT_L_LIM) {
hw->conf_unit[unit].conf2.cnt_l_lim = value;
} else if (evt_type == PCNT_EVT_H_LIM) {
hw->conf_unit[unit].conf2.cnt_h_lim = value;
} else if (evt_type == PCNT_EVT_THRES_0) {
hw->conf_unit[unit].conf1.cnt_thres0 = value;
} else if (evt_type == PCNT_EVT_THRES_1) {
hw->conf_unit[unit].conf1.cnt_thres1 = value;
}
}
/**
* @brief Get PCNT event value of PCNT unit
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @param evt_type Watch point event type.
* All enabled events share the same interrupt (one interrupt per pulse counter unit).
* @param value Pointer to accept counter value for PCNT event
*/
static inline void pcnt_ll_get_event_value(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16_t *value)
{
if (evt_type == PCNT_EVT_L_LIM) {
*value = (int16_t) hw->conf_unit[unit].conf2.cnt_l_lim;
} else if (evt_type == PCNT_EVT_H_LIM) {
*value = (int16_t) hw->conf_unit[unit].conf2.cnt_h_lim;
} else if (evt_type == PCNT_EVT_THRES_0) {
*value = (int16_t) hw->conf_unit[unit].conf1.cnt_thres0;
} else if (evt_type == PCNT_EVT_THRES_1) {
*value = (int16_t) hw->conf_unit[unit].conf1.cnt_thres1;
} else {
*value = 0;
}
}
/**
* @brief Set PCNT filter value
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @param filter_val PCNT signal filter value, counter in APB_CLK cycles.
* Any pulses lasting shorter than this will be ignored when the filter is enabled.
* @note
* filter_val is a 10-bit value, so the maximum filter_val should be limited to 1023.
*/
static inline void pcnt_ll_set_filter_value(pcnt_dev_t *hw, pcnt_unit_t unit, uint16_t filter_val)
{
hw->conf_unit[unit].conf0.filter_thres = filter_val;
}
/**
* @brief Get PCNT filter value
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @param filter_val Pointer to accept PCNT filter value.
*/
static inline void pcnt_ll_get_filter_value(pcnt_dev_t *hw, pcnt_unit_t unit, uint16_t *filter_val)
{
*filter_val = hw->conf_unit[unit].conf0.filter_thres;
}
/**
* @brief Enable PCNT input filter
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
*/
static inline void pcnt_ll_filter_enable(pcnt_dev_t *hw, pcnt_unit_t unit)
{
hw->conf_unit[unit].conf0.filter_en = 1;
}
/**
* @brief Disable PCNT input filter
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
*/
static inline void pcnt_ll_filter_disable(pcnt_dev_t *hw, pcnt_unit_t unit)
{
hw->conf_unit[unit].conf0.filter_en = 0;
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,40 @@
// Copyright 2015-2019 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
// ESP32 have 1 PCNT peripheral
#define PCNT_PORT_0 (0) /*!< PCNT port 0 */
#define PCNT_PORT_MAX (1) /*!< PCNT port max */
#define SOC_PCNT_NUM (PCNT_PORT_MAX)
#define PCNT_PIN_NOT_USED (-1) /*!< When selected for a pin, this pin will not be used */
#define PCNT_UNIT_0 (0) /*!< PCNT unit 0 */
#define PCNT_UNIT_1 (1) /*!< PCNT unit 1 */
#define PCNT_UNIT_2 (2) /*!< PCNT unit 2 */
#define PCNT_UNIT_3 (3) /*!< PCNT unit 3 */
#define PCNT_UNIT_4 (4) /*!< PCNT unit 4 */
#define PCNT_UNIT_5 (5) /*!< PCNT unit 5 */
#define PCNT_UNIT_6 (6) /*!< PCNT unit 6 */
#define PCNT_UNIT_7 (7) /*!< PCNT unit 7 */
#define PCNT_UNIT_MAX (8)
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,301 @@
// Copyright 2015-2019 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.
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use in application code.
* See readme.md in soc/include/hal/readme.md
******************************************************************************/
// The LL layer for ESP32-S2 PCNT register operations
#pragma once
#include "soc/pcnt_periph.h"
#include "hal/pcnt_types.h"
#ifdef __cplusplus
extern "C" {
#endif
// Get PCNT hardware instance with giving pcnt num
#define PCNT_LL_GET_HW(num) (((num) == 0) ? (&PCNT) : NULL)
/**
* @brief Set PCNT counter 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
* @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_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;
}
/**
* @brief Get pulse counter value
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit Pulse Counter unit number
* @param count Pointer to accept counter value
*/
static inline void pcnt_ll_get_counter_value(pcnt_dev_t *hw, pcnt_unit_t unit, int16_t *count)
{
*count = (int16_t) hw->cnt_unit[unit].cnt_val;
}
/**
* @brief Pause PCNT counter of PCNT unit
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
*/
static inline void pcnt_ll_counter_pause(pcnt_dev_t *hw, pcnt_unit_t unit)
{
hw->ctrl.val |= BIT(PCNT_CNT_PAUSE_U0_S + (unit * 2));
}
/**
* @brief Resume counting for PCNT counter
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number, select from pcnt_unit_t
*/
static inline void pcnt_ll_counter_resume(pcnt_dev_t *hw, pcnt_unit_t unit)
{
hw->ctrl.val &= (~(BIT(PCNT_CNT_PAUSE_U0_S + (unit * 2))));
}
/**
* @brief Clear and reset PCNT counter value to zero
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number, select from pcnt_unit_t
*/
static inline void pcnt_ll_counter_clear(pcnt_dev_t *hw, pcnt_unit_t unit)
{
uint32_t reset_bit = BIT(PCNT_PULSE_CNT_RST_U0_S + (unit * 2));
hw->ctrl.val |= reset_bit;
hw->ctrl.val &= ~reset_bit;
}
/**
* @brief Enable PCNT interrupt for PCNT unit
* @note
* Each Pulse counter unit has five watch point events that share the same interrupt.
* Configure events with pcnt_event_enable() and pcnt_event_disable()
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
*/
static inline void pcnt_ll_intr_enable(pcnt_dev_t *hw, pcnt_unit_t unit)
{
hw->int_ena.val |= BIT(PCNT_CNT_THR_EVENT_U0_INT_ENA_S + unit);
}
/**
* @brief Disable PCNT interrupt for PCNT unit
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
*/
static inline void pcnt_ll_intr_disable(pcnt_dev_t *hw, pcnt_unit_t unit)
{
hw->int_ena.val &= (~(BIT(PCNT_CNT_THR_EVENT_U0_INT_ENA_S + unit)));
}
/**
* @brief Get PCNT interrupt status
*
* @param hw Peripheral PCNT hardware instance address.
* @param status Pointer to accept value
*/
static inline void pcnt_ll_get_intr_status(pcnt_dev_t *hw, uint32_t *status)
{
*status = hw->int_st.val;
}
/**
* @brief Clear PCNT interrupt status
*
* @param hw Peripheral PCNT hardware instance address.
* @param status value to clear interrupt status
*/
static inline void pcnt_ll_clear_intr_status(pcnt_dev_t *hw, uint32_t status)
{
hw->int_clr.val = status;
}
/**
* @brief Enable PCNT event of PCNT unit
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @param evt_type Watch point event type.
* All enabled events share the same interrupt (one interrupt per pulse counter unit).
*/
static inline void pcnt_ll_event_enable(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_evt_type_t evt_type)
{
if (evt_type == PCNT_EVT_L_LIM) {
hw->conf_unit[unit].conf0.thr_l_lim_en = 1;
} else if (evt_type == PCNT_EVT_H_LIM) {
hw->conf_unit[unit].conf0.thr_h_lim_en = 1;
} else if (evt_type == PCNT_EVT_THRES_0) {
hw->conf_unit[unit].conf0.thr_thres0_en = 1;
} else if (evt_type == PCNT_EVT_THRES_1) {
hw->conf_unit[unit].conf0.thr_thres1_en = 1;
} else if (evt_type == PCNT_EVT_ZERO) {
hw->conf_unit[unit].conf0.thr_zero_en = 1;
}
}
/**
* @brief Disable PCNT event of PCNT unit
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @param evt_type Watch point event type.
* All enabled events share the same interrupt (one interrupt per pulse counter unit).
*/
static inline void pcnt_ll_event_disable(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_evt_type_t evt_type)
{
if (evt_type == PCNT_EVT_L_LIM) {
hw->conf_unit[unit].conf0.thr_l_lim_en = 0;
} else if (evt_type == PCNT_EVT_H_LIM) {
hw->conf_unit[unit].conf0.thr_h_lim_en = 0;
} else if (evt_type == PCNT_EVT_THRES_0) {
hw->conf_unit[unit].conf0.thr_thres0_en = 0;
} else if (evt_type == PCNT_EVT_THRES_1) {
hw->conf_unit[unit].conf0.thr_thres1_en = 0;
} else if (evt_type == PCNT_EVT_ZERO) {
hw->conf_unit[unit].conf0.thr_zero_en = 0;
}
}
/**
* @brief Set PCNT event value of PCNT unit
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @param evt_type Watch point event type.
* All enabled events share the same interrupt (one interrupt per pulse counter unit).
*
* @param value Counter value for PCNT event
*/
static inline void pcnt_ll_set_event_value(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16_t value)
{
if (evt_type == PCNT_EVT_L_LIM) {
hw->conf_unit[unit].conf2.cnt_l_lim = value;
} else if (evt_type == PCNT_EVT_H_LIM) {
hw->conf_unit[unit].conf2.cnt_h_lim = value;
} else if (evt_type == PCNT_EVT_THRES_0) {
hw->conf_unit[unit].conf1.cnt_thres0 = value;
} else if (evt_type == PCNT_EVT_THRES_1) {
hw->conf_unit[unit].conf1.cnt_thres1 = value;
}
}
/**
* @brief Get PCNT event value of PCNT unit
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @param evt_type Watch point event type.
* All enabled events share the same interrupt (one interrupt per pulse counter unit).
* @param value Pointer to accept counter value for PCNT event
*/
static inline void pcnt_ll_get_event_value(pcnt_dev_t *hw, pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16_t *value)
{
if (evt_type == PCNT_EVT_L_LIM) {
*value = (int16_t) hw->conf_unit[unit].conf2.cnt_l_lim;
} else if (evt_type == PCNT_EVT_H_LIM) {
*value = (int16_t) hw->conf_unit[unit].conf2.cnt_h_lim;
} else if (evt_type == PCNT_EVT_THRES_0) {
*value = (int16_t) hw->conf_unit[unit].conf1.cnt_thres0;
} else if (evt_type == PCNT_EVT_THRES_1) {
*value = (int16_t) hw->conf_unit[unit].conf1.cnt_thres1;
} else {
*value = 0;
}
}
/**
* @brief Set PCNT filter value
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @param filter_val PCNT signal filter value, counter in APB_CLK cycles.
* Any pulses lasting shorter than this will be ignored when the filter is enabled.
* @note
* filter_val is a 10-bit value, so the maximum filter_val should be limited to 1023.
*/
static inline void pcnt_ll_set_filter_value(pcnt_dev_t *hw, pcnt_unit_t unit, uint16_t filter_val)
{
hw->conf_unit[unit].conf0.filter_thres = filter_val;
}
/**
* @brief Get PCNT filter value
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
* @param filter_val Pointer to accept PCNT filter value.
*/
static inline void pcnt_ll_get_filter_value(pcnt_dev_t *hw, pcnt_unit_t unit, uint16_t *filter_val)
{
*filter_val = hw->conf_unit[unit].conf0.filter_thres;
}
/**
* @brief Enable PCNT input filter
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
*/
static inline void pcnt_ll_filter_enable(pcnt_dev_t *hw, pcnt_unit_t unit)
{
hw->conf_unit[unit].conf0.filter_en = 1;
}
/**
* @brief Disable PCNT input filter
*
* @param hw Peripheral PCNT hardware instance address.
* @param unit PCNT unit number
*/
static inline void pcnt_ll_filter_disable(pcnt_dev_t *hw, pcnt_unit_t unit)
{
hw->conf_unit[unit].conf0.filter_en = 0;
}
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,37 @@
// Copyright 2015-2019 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
// ESP32-S2 have 1 PCNT peripheral
#define PCNT_PORT_0 (0) /*!< PCNT port 0 */
#define PCNT_PORT_MAX (1) /*!< PCNT port max */
#define SOC_PCNT_NUM (PCNT_PORT_MAX)
#define PCNT_PIN_NOT_USED (-1) /*!< When selected for a pin, this pin will not be used */
// ESP32-S2 only have 4 unit
#define PCNT_UNIT_0 (0) /*!< PCNT unit 0 */
#define PCNT_UNIT_1 (1) /*!< PCNT unit 1 */
#define PCNT_UNIT_2 (2) /*!< PCNT unit 2 */
#define PCNT_UNIT_3 (3) /*!< PCNT unit 3 */
#define PCNT_UNIT_MAX (4)
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,214 @@
// Copyright 2015-2019 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.
/*******************************************************************************
* NOTICE
* The hal is not public api, don't use in application code.
* See readme.md in soc/include/hal/readme.md
******************************************************************************/
// The HAL layer for PCNT.
// There is no parameter check in the hal layer, so the caller must ensure the correctness of the parameters.
#pragma once
#include <stdio.h>
#include "soc/pcnt_periph.h"
#include "hal/pcnt_types.h"
#include "hal/pcnt_ll.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* Context that should be maintained by both the driver and the HAL
*/
typedef struct {
pcnt_dev_t *dev;
} pcnt_hal_context_t;
/**
* @brief Set PCNT counter mode
*
* @param hal Context of the HAL layer
* @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
* @param hctrl_mode Counter mode when control signal is high level
* @param lctrl_mode Counter mode when control signal is low level
*/
#define pcnt_hal_set_mode(hal, unit, channel, pos_mode, neg_mode, hctrl_mode, lctrl_mode) pcnt_ll_set_mode((hal)->dev, unit, channel, pos_mode, neg_mode, hctrl_mode, lctrl_mode)
/**
* @brief Get pulse counter value
*
* @param hal Context of the HAL layer
* @param unit Pulse Counter unit number
* @param count Pointer to accept counter value
*/
#define pcnt_hal_get_counter_value(hal, unit, count) pcnt_ll_get_counter_value((hal)->dev, unit, count)
/**
* @brief Pause PCNT counter of PCNT unit
*
* @param hal Context of the HAL layer
* @param unit PCNT unit number
*/
#define pcnt_hal_counter_pause(hal, unit) pcnt_ll_counter_pause((hal)->dev, unit)
/**
* @brief Resume counting for PCNT counter
*
* @param hal Context of the HAL layer
* @param unit PCNT unit number, select from unit_t
*/
#define pcnt_hal_counter_resume(hal, unit) pcnt_ll_counter_resume((hal)->dev, unit)
/**
* @brief Clear and reset PCNT counter value to zero
*
* @param hal Context of the HAL layer
* @param unit PCNT unit number, select from unit_t
*/
#define pcnt_hal_counter_clear(hal, unit) pcnt_ll_counter_clear((hal)->dev, unit)
/**
* @brief Enable PCNT interrupt for PCNT unit
* @note
* Each Pulse counter unit has five watch point events that share the same interrupt.
* Configure events with pcnt_event_enable() and pcnt_event_disable()
*
* @param hal Context of the HAL layer
* @param unit PCNT unit number
*/
#define pcnt_hal_intr_enable(hal, unit) pcnt_ll_intr_enable((hal)->dev, unit)
/**
* @brief Disable PCNT interrupt for PCNT unit
*
* @param hal Context of the HAL layer
* @param unit PCNT unit number
*/
#define pcnt_hal_intr_disable(hal, unit) pcnt_ll_intr_disable((hal)->dev, unit)
/**
* @brief Get PCNT interrupt status
*
* @param hal Context of the HAL layer
* @param mask The interrupt status mask to be cleared. Pointer to accept value interrupt status mask.
*/
#define pcnt_hal_get_intr_status(hal, mask) pcnt_ll_get_intr_status((hal)->dev, mask)
/**
* @brief Clear PCNT interrupt status
*
* @param hal Context of the HAL layer
* @param mask The interrupt status mask to be cleared.
*/
#define pcnt_hal_clear_intr_status(hal, mask) pcnt_ll_clear_intr_status((hal)->dev, mask)
/**
* @brief Enable PCNT event of PCNT unit
*
* @param hal Context of the HAL layer
* @param unit PCNT unit number
* @param evt_type Watch point event type.
* All enabled events share the same interrupt (one interrupt per pulse counter unit).
*/
#define pcnt_hal_event_enable(hal, unit, evt_type) pcnt_ll_event_enable((hal)->dev, unit, evt_type)
/**
* @brief Disable PCNT event of PCNT unit
*
* @param hal Context of the HAL layer
* @param unit PCNT unit number
* @param evt_type Watch point event type.
* All enabled events share the same interrupt (one interrupt per pulse counter unit).
*/
#define pcnt_hal_event_disable(hal, unit, evt_type) pcnt_ll_event_disable((hal)->dev, unit, evt_type)
/**
* @brief Set PCNT event value of PCNT unit
*
* @param hal Context of the HAL layer
* @param unit PCNT unit number
* @param evt_type Watch point event type.
* All enabled events share the same interrupt (one interrupt per pulse counter unit).
*
* @param value Counter value for PCNT event
*/
#define pcnt_hal_set_event_value(hal, unit, evt_type, value) pcnt_ll_set_event_value((hal)->dev, unit, evt_type, value)
/**
* @brief Get PCNT event value of PCNT unit
*
* @param hal Context of the HAL layer
* @param unit PCNT unit number
* @param evt_type Watch point event type.
* All enabled events share the same interrupt (one interrupt per pulse counter unit).
* @param value Pointer to accept counter value for PCNT event
*/
#define pcnt_hal_get_event_value(hal, unit, evt_type, value) pcnt_ll_get_event_value((hal)->dev, unit, evt_type, value)
/**
* @brief Set PCNT filter value
*
* @param hal Context of the HAL layer
* @param unit PCNT unit number
* @param filter_val PCNT signal filter value, counter in APB_CLK cycles.
* Any pulses lasting shorter than this will be ignored when the filter is enabled.
* @note
* filter_val is a 10-bit value, so the maximum filter_val should be limited to 1023.
*/
#define pcnt_hal_set_filter_value(hal, unit, filter_val) pcnt_ll_set_filter_value((hal)->dev, unit, filter_val)
/**
* @brief Get PCNT filter value
*
* @param hal Context of the HAL layer
* @param unit PCNT unit number
* @param filter_val Pointer to accept PCNT filter value.
*/
#define pcnt_hal_get_filter_value(hal, unit, filter_val) pcnt_ll_get_filter_value((hal)->dev, unit, filter_val)
/**
* @brief Enable PCNT input filter
*
* @param hal Context of the HAL layer
* @param unit PCNT unit number
*/
#define pcnt_hal_filter_enable(hal, unit) pcnt_ll_filter_enable((hal)->dev, unit)
/**
* @brief Disable PCNT input filter
*
* @param hal Context of the HAL layer
* @param unit PCNT unit number
*/
#define pcnt_hal_filter_disable(hal, unit) pcnt_ll_filter_disable((hal)->dev, unit)
/**
* @brief Init the PCNT hal and set the PCNT to the default configuration. This function should be called first before other hal layer function is called
*
* @param hal Context of the HAL layer
* @param pcnt_num The uart port number, the max port number is (PCNT_NUM_MAX -1)
*/
void pcnt_hal_init(pcnt_hal_context_t *hal, int pcnt_num);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,92 @@
// Copyright 2015-2019 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
/**
* @brief PCNT port number, the max port number is (PCNT_PORT_MAX - 1).
*/
typedef int pcnt_port_t;
/**
* @brief Selection of all available PCNT units
*/
typedef int pcnt_unit_t;
/**
* @brief Selection of available modes that determine the counter's action depending on the state of the control signal's input GPIO
* @note Configuration covers two actions, one for high, and one for low level on the control input
*/
typedef enum {
PCNT_MODE_KEEP = 0, /*!< Control mode: won't change counter mode*/
PCNT_MODE_REVERSE = 1, /*!< Control mode: invert counter mode(increase -> decrease, decrease -> increase) */
PCNT_MODE_DISABLE = 2, /*!< Control mode: Inhibit counter(counter value will not change in this condition) */
PCNT_MODE_MAX
} pcnt_ctrl_mode_t;
/**
* @brief Selection of available modes that determine the counter's action on the edge of the pulse signal's input GPIO
* @note Configuration covers two actions, one for positive, and one for negative edge on the pulse input
*/
typedef enum {
PCNT_COUNT_DIS = 0, /*!< Counter mode: Inhibit counter(counter value will not change in this condition) */
PCNT_COUNT_INC = 1, /*!< Counter mode: Increase counter value */
PCNT_COUNT_DEC = 2, /*!< Counter mode: Decrease counter value */
PCNT_COUNT_MAX
} pcnt_count_mode_t;
/**
* @brief Selection of channels available for a single PCNT unit
*/
typedef enum {
PCNT_CHANNEL_0 = 0x00, /*!< PCNT channel 0 */
PCNT_CHANNEL_1 = 0x01, /*!< PCNT channel 1 */
PCNT_CHANNEL_MAX,
} pcnt_channel_t;
/**
* @brief Selection of counter's events the may trigger an interrupt
*/
typedef enum {
PCNT_EVT_THRES_1 = BIT(2), /*!< PCNT watch point event: threshold1 value event */
PCNT_EVT_THRES_0 = BIT(3), /*!< PCNT watch point event: threshold0 value event */
PCNT_EVT_L_LIM = BIT(4), /*!< PCNT watch point event: Minimum counter value */
PCNT_EVT_H_LIM = BIT(5), /*!< PCNT watch point event: Maximum counter value */
PCNT_EVT_ZERO = BIT(6), /*!< PCNT watch point event: counter value zero event */
PCNT_EVT_MAX
} pcnt_evt_type_t;
/**
* @brief Pulse Counter configuration for a single channel
*/
typedef struct {
int pulse_gpio_num; /*!< Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored */
int ctrl_gpio_num; /*!< Control signal input GPIO number, a negative value will be ignored */
pcnt_ctrl_mode_t lctrl_mode; /*!< PCNT low control mode */
pcnt_ctrl_mode_t hctrl_mode; /*!< PCNT high control mode */
pcnt_count_mode_t pos_mode; /*!< PCNT positive edge count mode */
pcnt_count_mode_t neg_mode; /*!< PCNT negative edge count mode */
int16_t counter_h_lim; /*!< Maximum counter value */
int16_t counter_l_lim; /*!< Minimum counter value */
pcnt_unit_t unit; /*!< PCNT unit number */
pcnt_channel_t channel; /*!< the PCNT channel */
} pcnt_config_t;
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,23 @@
// Copyright 2015-2019 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.
// The HAL layer for PCNT (common part)
#include "hal/pcnt_hal.h"
void pcnt_hal_init(pcnt_hal_context_t *hal, int pcnt_num)
{
//Get hardware instance.
hal->dev = PCNT_LL_GET_HW(pcnt_num);
}

View File

@ -104,6 +104,7 @@ INPUT = \
../../components/driver/include/driver/uart.h \
../../components/esp_adc_cal/include/esp_adc_cal.h \
../../components/soc/include/hal/spi_types.h \
../../components/soc/include/hal/pcnt_types.h \
../../components/soc/esp32/include/soc/adc_channel.h \
../../components/soc/esp32/include/soc/dac_channel.h \
../../components/soc/esp32/include/soc/touch_channel.h \