Pulse Counter (PCNT)

Introduction

The PCNT (Pulse Counter) module is designed to count the number of rising and/or falling edges of an input signal. Each pulse counter unit has a 16-bit signed counter register and two channels that can be configured to either increment or decrement the counter. Each channel has a signal input that accepts signal edges to be detected, as well as a control input that can be used to enable or disable the signal input. The inputs have optional filters that can be used to discard unwanted glitches in the signal.

Functionality Overview

Description of functionality of this API has been broken down into four sections:

  • Configuration - describes counter’s configuration parameters and how to setup the counter.

  • Operating the Counter - provides information on control functions to pause, measure and clear the counter.

  • Filtering Pulses - describes options to filtering pulses and the counter control signals.

  • Using Interrupts - presents how to trigger interrupts on specific states of the counter.

Configuration

The PCNT module has 4 independent counting “units” numbered from 0 to 3. In the API they are referred to using pcnt_unit_t. Each unit has two independent channels numbered as 0 and 1 and specified with pcnt_channel_t.

The configuration is provided separately per unit’s channel using pcnt_config_t and covers:

  • The unit and the channel number this configuration refers to.

  • GPIO numbers of the pulse input and the pulse gate input.

  • Two pairs of parameters: pcnt_ctrl_mode_t and pcnt_count_mode_t to define how the counter reacts depending on the the status of control signal and how counting is done positive / negative edge of the pulses.

  • Two limit values (minimum / maximum) that are used to establish watchpoints and trigger interrupts when the pulse count is meeting particular limit.

Setting up of particular channel is then done by calling a function pcnt_unit_config() with above pcnt_config_t as the input parameter.

To disable the pulse or the control input pin in configuration, provide PCNT_PIN_NOT_USED instead of the GPIO number.

Operating the Counter

After doing setup with pcnt_unit_config(), the counter immediately starts to operate. The accumulated pulse count can be checked by calling pcnt_get_counter_value().

There are couple of functions that allow to control the counter’s operation: pcnt_counter_pause(), pcnt_counter_resume() and pcnt_counter_clear()

It is also possible to dynamically change the previously set up counter modes with pcnt_unit_config() by calling pcnt_set_mode().

If desired, the pulse input pin and the control input pin may be changed “on the fly” using pcnt_set_pin(). To disable particular input provide as a function parameter PCNT_PIN_NOT_USED instead of the GPIO number.

Note

For the counter not to miss any pulses, the pulse duration should be longer than one APB_CLK cycle (12.5 ns). The pulses are sampled on the edges of the APB_CLK clock and may be missed, if fall between the edges. This applies to counter operation with or without a filer.

Filtering Pulses

The PCNT unit features filters on each of the pulse and control inputs, adding the option to ignore short glitches in the signals.

The length of ignored pulses is provided in APB_CLK clock cycles by calling pcnt_set_filter_value(). The current filter setting may be checked with pcnt_get_filter_value(). The APB_CLK clock is running at 80 MHz.

The filter is put into operation / suspended by calling pcnt_filter_enable() / pcnt_filter_disable().

Using Interrupts

There are five counter state watch events, defined in pcnt_evt_type_t, that are able to trigger an interrupt. The event happens on the pulse counter reaching specific values:

To register, enable or disable an interrupt to service the above events, call pcnt_isr_register(), pcnt_intr_enable(). and pcnt_intr_disable(). To enable or disable events on reaching threshold values, you will also need to call functions pcnt_event_enable() and pcnt_event_disable().

In order to check what are the threshold values currently set, use function pcnt_get_event_value().

Application Example

API Reference

Functions

esp_err_t pcnt_unit_config(const pcnt_config_t *pcnt_config)

Configure Pulse Counter unit.

Note

This function will disable three events: PCNT_EVT_L_LIM, PCNT_EVT_H_LIM, PCNT_EVT_ZERO.

Parameters

pcnt_config – Pointer of Pulse Counter unit configure parameter

Returns

  • ESP_OK Success

  • ESP_ERR_INVALID_STATE pcnt driver already initialized

  • ESP_ERR_INVALID_ARG Parameter error

esp_err_t pcnt_get_counter_value(pcnt_unit_t pcnt_unit, int16_t *count)

Get pulse counter value.

Parameters
  • pcnt_unit – Pulse Counter unit number

  • count – Pointer to accept counter value

Returns

  • 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)

Pause PCNT counter of PCNT unit.

Parameters

pcnt_unit – PCNT unit number

Returns

  • 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)

Resume counting for PCNT counter.

Parameters

pcnt_unit – PCNT unit number, select from pcnt_unit_t

Returns

  • 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)

Clear and reset PCNT counter value to zero.

Parameters

pcnt_unit – PCNT unit number, select from pcnt_unit_t

Returns

  • 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)

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()

Parameters

pcnt_unit – PCNT unit number

Returns

  • 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)

Disable PCNT interrupt for PCNT unit.

Parameters

pcnt_unit – PCNT unit number

Returns

  • 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)

Enable PCNT event of PCNT unit.

Parameters
  • unit – PCNT unit number

  • evt_type – Watch point event type. All enabled events share the same interrupt (one interrupt per pulse counter unit).

Returns

  • 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)

Disable PCNT event of PCNT unit.

Parameters
  • unit – PCNT unit number

  • evt_type – Watch point event type. All enabled events share the same interrupt (one interrupt per pulse counter unit).

Returns

  • 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)

Set PCNT event value of PCNT unit.

Parameters
  • unit – PCNT unit number

  • evt_type – Watch point event type. All enabled events share the same interrupt (one interrupt per pulse counter unit).

  • value – Counter value for PCNT event

Returns

  • 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)

Get PCNT event value of PCNT unit.

Parameters
  • unit – PCNT unit number

  • evt_type – Watch point event type. All enabled events share the same interrupt (one interrupt per pulse counter unit).

  • value – Pointer to accept counter value for PCNT event

Returns

  • 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)

Get PCNT event status of PCNT unit.

Parameters
  • unit – PCNT unit number

  • status – Pointer to accept event status word

Returns

  • ESP_OK Success

  • ESP_ERR_INVALID_STATE pcnt driver has not been initialized

  • ESP_ERR_INVALID_ARG Parameter error

esp_err_t pcnt_isr_unregister(pcnt_isr_handle_t handle)

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. If the interrupt service is registered by pcnt_isr_service_install, please call pcnt_isr_service_uninstall instead.

Parameters

handle – handle to unregister the ISR service.

Returns

  • ESP_OK Success

  • 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)

Register PCNT interrupt handler, the handler is an ISR. The handler will be attached to the same CPU core that this function is running on. Please do not use pcnt_isr_service_install if this function was called.

Parameters
  • fn – Interrupt handler function.

  • arg – Parameter for handler function

  • 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.

  • handle – Pointer to return handle. If non-NULL, a handle for the interrupt will be returned here. Calling pcnt_isr_unregister to unregister this ISR service if needed, but only if the handle is not NULL.

Returns

  • ESP_OK Success

  • ESP_ERR_NOT_FOUND Can not find the interrupt that matches the flags.

  • ESP_ERR_INVALID_ARG Function pointer error.

esp_err_t pcnt_set_pin(pcnt_unit_t unit, pcnt_channel_t channel, int pulse_io, int ctrl_io)

Configure PCNT pulse signal input pin and control input pin.

Note

Set the signal input to PCNT_PIN_NOT_USED if unused.

Parameters
  • unit – PCNT unit number

  • channel – PCNT channel number

  • pulse_io – Pulse signal input GPIO

  • ctrl_io – Control signal input GPIO

Returns

  • 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)

Enable PCNT input filter.

Parameters

unit – PCNT unit number

Returns

  • 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)

Disable PCNT input filter.

Parameters

unit – PCNT unit number

Returns

  • 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)

Set PCNT filter value.

Note

filter_val is a 10-bit value, so the maximum filter_val should be limited to 1023.

Parameters
  • unit – PCNT unit number

  • 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.

Returns

  • 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)

Get PCNT filter value.

Parameters
  • unit – PCNT unit number

  • filter_val – Pointer to accept PCNT filter value.

Returns

  • 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, pcnt_count_mode_t pos_mode, pcnt_count_mode_t neg_mode, pcnt_ctrl_mode_t hctrl_mode, pcnt_ctrl_mode_t lctrl_mode)

Set PCNT counter mode.

Parameters
  • unit – PCNT unit number

  • channel – PCNT channel number

  • pos_mode – Counter mode when detecting positive edge

  • neg_mode – Counter mode when detecting negative edge

  • hctrl_mode – Counter mode when control signal is high level

  • lctrl_mode – Counter mode when control signal is low level

Returns

  • 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)

Add ISR handler for specified unit.

Call this function after using pcnt_isr_service_install() to install the PCNT driver’s ISR handler service.

The ISR handlers do not need to be declared with IRAM_ATTR, unless you pass the ESP_INTR_FLAG_IRAM flag when allocating the ISR in pcnt_isr_service_install().

This ISR handler will be called from an ISR. So there is a stack size limit (configurable as “ISR stack size” in menuconfig). This limit is smaller compared to a global PCNT interrupt handler due to the additional level of indirection.

Parameters
  • unit – PCNT unit number

  • isr_handler – Interrupt handler function.

  • args – Parameter for handler function

Returns

  • ESP_OK Success

  • ESP_ERR_INVALID_STATE pcnt driver has not been initialized

  • ESP_ERR_INVALID_ARG Parameter error

esp_err_t pcnt_isr_service_install(int intr_alloc_flags)

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.

Parameters

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.

Returns

  • 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

void pcnt_isr_service_uninstall(void)

Uninstall PCNT ISR service, freeing related resources.

esp_err_t pcnt_isr_handler_remove(pcnt_unit_t unit)

Delete ISR handler for specified unit.

Parameters

unit – PCNT unit number

Returns

  • ESP_OK Success

  • ESP_ERR_INVALID_STATE pcnt driver has not been initialized

  • ESP_ERR_INVALID_ARG Parameter error

Structures

struct pcnt_config_t

Pulse Counter configuration for a single channel.

Public Members

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

Macros

PCNT_PIN_NOT_USED

When selected for a pin, this pin will not be used

PCNT_MODE_KEEP

Control mode: won’t change counter mode

PCNT_MODE_REVERSE

Control mode: invert counter mode(increase -> decrease, decrease -> increase)

PCNT_MODE_DISABLE

Control mode: Inhibit counter(counter value will not change in this condition)

PCNT_MODE_MAX
PCNT_COUNT_DIS

Counter mode: Inhibit counter(counter value will not change in this condition)

PCNT_COUNT_INC

Counter mode: Increase counter value

PCNT_COUNT_DEC

Counter mode: Decrease counter value

PCNT_COUNT_MAX

Type Definitions

typedef intr_handle_t pcnt_isr_handle_t
typedef pcnt_channel_level_action_t pcnt_ctrl_mode_t

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 pcnt_channel_edge_action_t pcnt_count_mode_t

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

Enumerations

enum pcnt_port_t

PCNT port number, the max port number is (PCNT_PORT_MAX - 1).

Values:

enumerator PCNT_PORT_0

PCNT port 0

enumerator PCNT_PORT_MAX

PCNT port max

enum pcnt_unit_t

Selection of all available PCNT units.

Values:

enumerator PCNT_UNIT_0

PCNT unit 0

enumerator PCNT_UNIT_1

PCNT unit 1

enumerator PCNT_UNIT_2

PCNT unit 2

enumerator PCNT_UNIT_3

PCNT unit 3

enumerator PCNT_UNIT_MAX
enum pcnt_channel_t

Selection of channels available for a single PCNT unit.

Values:

enumerator PCNT_CHANNEL_0

PCNT channel 0

enumerator PCNT_CHANNEL_1

PCNT channel 1

enumerator PCNT_CHANNEL_MAX
enum pcnt_evt_type_t

Selection of counter’s events the may trigger an interrupt.

Values:

enumerator PCNT_EVT_THRES_1

PCNT watch point event: threshold1 value event

enumerator PCNT_EVT_THRES_0

PCNT watch point event: threshold0 value event

enumerator PCNT_EVT_L_LIM

PCNT watch point event: Minimum counter value

enumerator PCNT_EVT_H_LIM

PCNT watch point event: Maximum counter value

enumerator PCNT_EVT_ZERO

PCNT watch point event: counter value zero event

enumerator PCNT_EVT_MAX

Enumerations

enum pcnt_channel_level_action_t

PCNT channel action on control level.

Values:

enumerator PCNT_CHANNEL_LEVEL_ACTION_KEEP

Keep current count mode

enumerator PCNT_CHANNEL_LEVEL_ACTION_INVERSE

Invert current count mode (increase -> decrease, decrease -> increase)

enumerator PCNT_CHANNEL_LEVEL_ACTION_HOLD

Hold current count value

enum pcnt_channel_edge_action_t

PCNT channel action on signal edge.

Values:

enumerator PCNT_CHANNEL_EDGE_ACTION_HOLD

Hold current count value

enumerator PCNT_CHANNEL_EDGE_ACTION_INCREASE

Increase count value

enumerator PCNT_CHANNEL_EDGE_ACTION_DECREASE

Decrease count value

enum pcnt_unit_count_sign_t

PCNT unit counter value’s sign.

Values:

enumerator PCNT_UNIT_COUNT_SIGN_ZERO_POS

positive value to zero

enumerator PCNT_UNIT_COUNT_SIGN_ZERO_NEG

negative value to zero

enumerator PCNT_UNIT_COUNT_SIGN_NEG

counter value negative

enumerator PCNT_UNIT_COUNT_SIGN_POS

counter value positive