Merge branch 'feature/docs_update_pcnt_api' into 'master'

Extended description of the Pulse Counter API and removed redundant comments from the example

See merge request !1312
This commit is contained in:
Jeroen Domburg 2017-10-10 14:06:03 +08:00
commit 1835c3a213
4 changed files with 269 additions and 154 deletions

View File

@ -18,15 +18,23 @@
extern "C" {
#endif
#define PCNT_PIN_NOT_USED (-1) /*!< Pin are not used */
#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_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 */
@ -34,6 +42,9 @@ typedef enum {
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 */
@ -46,12 +57,18 @@ typedef enum {
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_L_LIM = 0, /*!< PCNT watch point event: Minimum counter value */
PCNT_EVT_H_LIM = 1, /*!< PCNT watch point event: Maximum counter value */
@ -62,11 +79,11 @@ typedef enum {
} pcnt_evt_type_t;
/**
* @brief Pulse Counter configure struct
* @brief Pulse Counter configuration for a single channel
*/
typedef struct {
int pulse_gpio_num; /*!< Pulse input gpio_num, if you want to use gpio16, pulse_gpio_num = 16, a negative value will be ignored */
int ctrl_gpio_num; /*!< Contol signal input gpio_num, a negative value will be ignored*/
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 */
@ -150,7 +167,7 @@ esp_err_t pcnt_counter_clear(pcnt_unit_t pcnt_unit);
esp_err_t pcnt_intr_enable(pcnt_unit_t pcnt_unit);
/**
* @brief Disable PCNT interrupt for PCNT uint
* @brief Disable PCNT interrupt for PCNT unit
*
* @param pcnt_unit PCNT unit number
*
@ -236,11 +253,9 @@ esp_err_t pcnt_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags,
* @param unit PCNT unit number
* @param channel PCNT channel number
* @param pulse_io Pulse signal input GPIO
* @note
* Set to PCNT_PIN_NOT_USED if unused.
* @param ctrl_io Control signal input GPIO
* @note
* Set to PCNT_PIN_NOT_USED if unused.
*
* @note Set the signal input to PCNT_PIN_NOT_USED if unused.
*
* @return
* - ESP_OK Success

View File

@ -1,16 +1,94 @@
Pulse Counter
=============
Overview
--------
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:
* :ref:`pcnt-api-configuration` - describes counter's configuration parameters and how to setup the counter.
* :ref:`pcnt-api-operating-the-counter` - provides information on control functions to pause, measure and clear the counter.
* :ref:`pcnt-api-filtering-pulses` - describes options to filtering pulses and the counter control signals.
* :ref:`pcnt-api-using-interrupts` - presents how to trigger interrupts on specific states of the counter.
.. _pcnt-api-configuration:
Configuration
-------------
The PCNT module has eight independent counting "units" numbered from 0 to 7. In the API they are referred to using :cpp:type:`pcnt_unit_t`. Each unit has two independent channels numbered as 0 and 1 and specified with :cpp:type:`pcnt_channel_t`.
The configuration is provided separately per unit's channel using :cpp:type:`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: :cpp:type:`pcnt_ctrl_mode_t` and :cpp:type:`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 :cpp:func:`pcnt_unit_config` with above :cpp:type:`pcnt_config_t` as the input parameter.
To disable the pulse or the control input pin in configuration, provide :cpp:type:`PCNT_PIN_NOT_USED` instead of the GPIO number.
.. _pcnt-api-operating-the-counter:
Operating the Counter
---------------------
After doing setup with :cpp:func:`pcnt_unit_config`, the counter immediately starts to operate. The accumulated pulse count can be checked by calling :cpp:func:`pcnt_get_counter_value`.
There are couple of functions that allow to control the counter's operation: :cpp:func:`pcnt_counter_pause`, :cpp:func:`pcnt_counter_resume` and :cpp:func:`pcnt_counter_clear`
It is also possible to dynamically change the previously set up counter modes with :cpp:func:`pcnt_unit_config` by calling :cpp:func:`pcnt_set_mode`.
If desired, the pulse input pin and the control input pin may be changed "on the fly" using :cpp:func:`pcnt_set_pin`. To disable particular input provide as a function parameter :cpp:type:`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 :ref:`filer <pcnt-api-filtering-pulses>`.
.. _pcnt-api-filtering-pulses:
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 :cpp:func:`pcnt_set_filter_value`. The current filter setting may be checked with :cpp:func:`pcnt_get_filter_value`. The APB_CLK clock is running at 80 MHz.
The filter is put into operation / suspended by calling :cpp:func:`pcnt_filter_enable` / :cpp:func:`pcnt_filter_disable`.
.. _pcnt-api-using-interrupts:
Using Interrupts
----------------
There are five counter state watch events, defined in :cpp:type:`pcnt_evt_type_t`, that are able to trigger an interrupt. The event happens on the pulse counter reaching specific values:
* Minimum or maximum count values: :cpp:member:`counter_l_lim` or :cpp:member:`counter_h_lim` provided in :cpp:type:`pcnt_config_t` as discussed in :ref:`pcnt-api-configuration`
* Threshold 0 or Threshold 1 values set using function :cpp:func:`pcnt_set_event_value`.
* Pulse count = 0
To register, enable or disable an interrupt to service the above events, call :cpp:func:`pcnt_isr_register`, :cpp:func:`pcnt_intr_enable`. and :cpp:func:`pcnt_intr_disable`. To enable or disable events on reaching threshold values, you will also need to call functions :cpp:func:`pcnt_event_enable` and :cpp:func:`pcnt_event_disable`.
In order to check what are the threshold values currently set, use function :cpp:func:`pcnt_get_event_value`.
Application Example
-------------------
Pulse counter with control signal and event interrupt example: :example:`peripherals/pcnt`.
API Reference
-------------

View File

@ -1,14 +1,51 @@
# Example: pcnt
This example uses the pulse counter module (PCNT) to count the rising edges of pulses generated by the LED Controller module (LEDC).
This example uses the pulse counter module (PCNT) to count the rising edges of the PWM pulses generated by the LED Controller module (LEDC).
By default GPIO18 is used as output pin, GPIO4 is used as pulse input pin and GPIO5 is used as control input pin. This configuration (pin numbers, etc.) can be modified in top of the main/pcnt_test.c file.
The examples is setting up and then using the following GPIO pins:
* GPIO18 - output pin of a sample 1 Hz pulse generator,
* GPIO4 - pulse input pin,
* GPIO5 - control input pin.
* Open serial port to view the message printed on your screen
* To do this test, you should connect GPIO18 with GPIO4
* GPIO5 is the control signal, you can leave it floating with internal pulled up, or connect it to ground. HIGH = Count increases, LOW = count decreases.
* An interrupt is configured to trigger when the count reaches threshold values.
* The counter will reset when it reaches the limit values.
The configuration parameters of this example (pin numbers, etc.) may be modified at the top of the `main/pcnt_test.c` file.
## Run the Test
1, Compile and load the example.
2. Open a serial port monitor to view the message printed out on your screen.
3. Connect GPIO18 with GPIO4.
4. GPIO5 is the control signal, you can leave it floating with internal pull up, or connect it to ground. If left floating, the count value will be increasing. If you connect GPIO5 to GND, the count will be decreasing.
## Check Functionality
The example will print out the current counter values and events.
An interrupt will be triggered when the counter value:
* reaches 'thresh1' or 'thresh0' value,
* reaches 'l_lim' value or 'h_lim' value,
* will be reset to zero.
A sample output on the serial monitor:
```
Current counter value :-1
Current counter value :-2
Current counter value :-3
Current counter value :-4
Event PCNT unit[0]; cnt: -5
THRES0 EVT
Current counter value :-5
Current counter value :-6
Current counter value :-7
Current counter value :-8
Current counter value :-9
Event PCNT unit[0]; cnt: 0
L_LIM EVT
ZERO EVT
Current counter value :0
Current counter value :-1
...
```
See the README.md file in the upper level 'examples' directory for more information about examples.

View File

@ -24,35 +24,49 @@
/**
* TEST CODE BRIEF
*
* Use PCNT module to count rising edges generated by LEDC module.
* GPIO18 is used as output pin, GPIO4 is used as pulse input pin and GPIO5 is used as control input pin
*
* Open serial port to view the message printed on your screen
* Functionality of GPIOs used in this example:
* - GPIO18 - output pin of a sample 1 Hz pulse generator,
* - GPIO4 - pulse input pin,
* - GPIO5 - control input pin.
*
* To do this test, you should connect GPIO18 with GPIO4
* GPIO5 is the control signal, you can leave it floating with internal pulled up, or connect it to ground.
* If you connect gpio5 to GND ,you will found the count value decreasing.
* Load example, open a serial port to view the message printed on your screen.
*
* When counter value reaches thresh1 or thresh0 value, it will trigger interrupt.
* When counter value reaches l_lim value or h_lim value, counter value will be reset to zero and trigger interrupt.
* To do this test, you should connect GPIO18 with GPIO4.
* GPIO5 is the control signal, you can leave it floating with internal pull up,
* or connect it to ground. If left floating, the count value will be increasing.
* If you connect GPIO5 to GND, the count value will be decreasing.
*
* An interrupt will be triggered when the counter value:
* - reaches 'thresh1' or 'thresh0' value,
* - 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
#define PCNT_THRESH0_VAL -5
#define PCNT_INPUT_SIG_IO 4 /* Pulse Input GPIO */
#define PCNT_INPUT_CTRL_IO 5 /* Control GPIO HIGH=count up, LOW=count down */
#define LEDC_OUTPUT_IO 18 /* Output GPIO */
xQueueHandle pcnt_evt_queue; /*A queue to handle pulse counter event*/
#define PCNT_INPUT_SIG_IO 4 // Pulse Input GPIO
#define PCNT_INPUT_CTRL_IO 5 // Control GPIO HIGH=count up, LOW=count down
#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
/* A sample structure to pass events from the PCNT
* interrupt handler to the main program.
*/
typedef struct {
int unit; /*pulse counter unit*/
uint32_t status; /*pulse counter internal status*/
int unit; // the PCNT unit that originated an interrupt
uint32_t status; // information on the event type that caused the interrupt
} pcnt_evt_t;
/* Decode what PCNT's unit originated an interrupt
* and pass this information together with the event type
* the main program using a queue.
*/
static void IRAM_ATTR pcnt_example_intr_handler(void *arg)
{
uint32_t intr_status = PCNT.int_st.val;
@ -63,28 +77,10 @@ static void IRAM_ATTR pcnt_example_intr_handler(void* arg)
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);
/*H LIM EVT*/
if(PCNT.status_unit[i].h_lim_lat) {
//do something
}
/*L LIM EVT*/
if(PCNT.status_unit[i].l_lim_lat) {
//do something
}
/*THRES0 EVT*/
if(PCNT.status_unit[i].thres0_lat) {
//do something
}
/*THRES1 EVT*/
if(PCNT.status_unit[i].thres1_lat) {
//do something
}
/*ZERO EVT*/
if(PCNT.status_unit[i].zero_lat) {
//do something
}
xQueueSendFromISR(pcnt_evt_queue, &evt, &HPTaskAwoken);
if (HPTaskAwoken == pdTRUE) {
portYIELD_FROM_ISR();
@ -93,109 +89,99 @@ static void IRAM_ATTR pcnt_example_intr_handler(void* arg)
}
}
/* Configure LED PWM Controller
* to output sample pulses at 1 Hz with duty of about 10%
*/
static void ledc_init(void)
{
ledc_channel_config_t ledc_channel;
/*use LEDC_OUTPUT_IO as output pin*/
ledc_channel.gpio_num = LEDC_OUTPUT_IO;
/*LEDC high speed mode */
ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE;
/*use LEDC channel 1*/
ledc_channel.channel = LEDC_CHANNEL_1;
/*Disable LEDC interrupt*/
ledc_channel.intr_type = LEDC_INTR_DISABLE;
/*Select LEDC timer 1 */
ledc_channel.timer_sel = LEDC_TIMER_1;
/*Set duty 100 */
ledc_channel.duty = 100;
ledc_channel_config(&ledc_channel); //ledc config
// Prepare and then apply the LEDC PWM timer configuration
ledc_timer_config_t ledc_timer;
/*LEDC timer high speed mode*/
ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;
/*10 bit PWM*/
ledc_timer.bit_num = LEDC_TIMER_10_BIT;
/*Select timer 1*/
ledc_timer.timer_num = LEDC_TIMER_1;
/*Set frequency 1 Hz */
ledc_timer.freq_hz = 1;
ledc_timer.bit_num = LEDC_TIMER_10_BIT;
ledc_timer.freq_hz = 1; // set output frequency at 1 Hz
ledc_timer_config(&ledc_timer);
// Prepare and then apply the LEDC PWM channel configuration
ledc_channel_config_t ledc_channel;
ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE;
ledc_channel.channel = LEDC_CHANNEL_1;
ledc_channel.timer_sel = LEDC_TIMER_1;
ledc_channel.intr_type = LEDC_INTR_DISABLE;
ledc_channel.gpio_num = LEDC_OUTPUT_IO;
ledc_channel.duty = 100; // set duty at about 10%
ledc_channel_config(&ledc_channel);
}
/* Initialize PCNT functions:
* - configure and initialize PCNT
* - set up the input filter
* - set up the counter events to watch
*/
static void pcnt_example_init(void)
{
/* Prepare configuration for the PCNT unit */
pcnt_config_t pcnt_config = {
/*Set PCNT_INPUT_SIG_IO as pulse input gpio */
// Set PCNT input signal and control GPIOs
.pulse_gpio_num = PCNT_INPUT_SIG_IO,
/*set PCNT_INPUT_CTRL_IO as control gpio */
.ctrl_gpio_num = PCNT_INPUT_CTRL_IO,
/*Choose channel 0 */
.channel = PCNT_CHANNEL_0,
/*Choose unit 0 */
.unit = PCNT_TEST_UNIT,
/*Set counter and control mode*/
/*Counter increase for positive edge on pulse input GPIO*/
.pos_mode = PCNT_COUNT_INC,
/*Counter decrease for negative edge on pulse input GPIO*/
.neg_mode = PCNT_COUNT_DIS, //keep the counter value
/*Counter mode reverse when control input is low level*/
.lctrl_mode = PCNT_MODE_REVERSE,
/*Counter mode does not change when control input is high level*/
.hctrl_mode = PCNT_MODE_KEEP, //when control signal is high,keep the primary counter mode
/*Set maximum value for increasing counter*/
// 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
// What to do when control input is low or high?
.lctrl_mode = PCNT_MODE_REVERSE, // Reverse counting direction if low
.hctrl_mode = PCNT_MODE_KEEP, // Keep the primary counter mode if high
// Set the maximum and minimum limit values to watch
.counter_h_lim = PCNT_H_LIM_VAL,
/*Set minimum value for decreasing counter*/
.counter_l_lim = PCNT_L_LIM_VAL,
};
/* Initialize PCNT unit */
pcnt_unit_config(&pcnt_config);
/*Configure input filter value*/
/* Configure and enable the input filter */
pcnt_set_filter_value(PCNT_TEST_UNIT, 100);
/*Enable input filter*/
pcnt_filter_enable(PCNT_TEST_UNIT);
/*Set value for watch point thresh1*/
/* 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);
/*Enable watch point event of thresh1*/
pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_THRES_1);
/*Set value for watch point thresh0*/
pcnt_set_event_value(PCNT_TEST_UNIT, PCNT_EVT_THRES_0, PCNT_THRESH0_VAL);
/*Enable watch point event of thresh0*/
pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_THRES_0);
/*Enable watch point event of h_lim*/
pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_H_LIM);
/*Enable watch point event of l_lim*/
pcnt_event_enable(PCNT_TEST_UNIT, PCNT_EVT_L_LIM);
/*Enable watch point event of zero*/
/* 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);
/*Pause counter*/
/* Initialize PCNT's counter */
pcnt_counter_pause(PCNT_TEST_UNIT);
/*Reset counter value*/
pcnt_counter_clear(PCNT_TEST_UNIT);
/*Register ISR handler*/
/* Register ISR handler and enable interrupts for PCNT unit */
pcnt_isr_register(pcnt_example_intr_handler, NULL, 0, NULL);
/*Enable interrupt for PCNT unit*/
pcnt_intr_enable(PCNT_TEST_UNIT);
/*Resume counting*/
/* Everything is set up, now go to counting */
pcnt_counter_resume(PCNT_TEST_UNIT);
}
void app_main()
{
/*Init LEDC for pulse input signal */
/* Initialize LEDC to generate sample pulse signal */
ledc_init();
/*Init PCNT event queue */
/* Initialize PCNT event queue and PCNT functions */
pcnt_evt_queue = xQueueCreate(10, sizeof(pcnt_evt_t));
/*Init PCNT functions*/
pcnt_example_init();
int16_t count = 0;
pcnt_evt_t evt;
portBASE_TYPE res;
while(1)
{
while (1) {
/* Wait for the event information passed from PCNT's interrupt handler.
* Once received, decode the event type and print it on the serial monitor.
*/
res = xQueueReceive(pcnt_evt_queue, &evt, 1000 / portTICK_PERIOD_MS);
if (res == pdTRUE) {
pcnt_get_counter_value(PCNT_TEST_UNIT, &count);
@ -221,4 +207,3 @@ void app_main()
}
}
}