esp-idf/components/driver/test/test_pcnt.c
morris 16677b0d3c global: make periph enable/disable APIs private
peripheral enable/disable usually should be managed by driver itself,
so make it as espressif private APIs, not recommended for user to use it
in application code.
However, if user want to re-write the driver or ports to other platform,
this is still possible by including the header in this way:
"esp_private/peripheral_ctrl.h"
2021-11-08 10:37:47 +08:00

681 lines
26 KiB
C

/*
* SPDX-FileCopyrightText: 2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* this case is used for test PCNT
* prepare job for test environment UT_T1_PCNT:
* We use internal signals instead of external wiring, but please keep the following IO connections, or connect nothing to prevent the signal from being disturbed.
* 1. prepare one ESP-WROOM-32 board and connect it to PC.
* 2. connect GPIO21 with GPIO4
* 3. GPIO5 connect to 3.3v
* 4. GPIO19 connect to GND
* 5. logic analyzer will help too if possible
*
* the GPIO18 is the pulse producer, the GPIO4 is the input GPIO
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "soc/soc_caps.h"
#if SOC_PCNT_SUPPORTED
#include "driver/gpio.h"
#include "driver/pcnt.h"
#include "driver/ledc.h"
#include "esp_attr.h"
#include "esp_log.h"
#include "soc/gpio_periph.h"
#include "soc/pcnt_struct.h"
#include "unity.h"
#include "esp_rom_gpio.h"
#define PULSE_IO 21
#define PCNT_INPUT_IO 4
#define PCNT_CTRL_VCC_IO 5
#define PCNT_CTRL_GND_IO 2
#define HIGHEST_LIMIT 10
#define LOWEST_LIMIT 0
#define MAX_THRESHOLD 5
#define MIN_THRESHOLD 0
#define PCNT_CTRL_HIGH_LEVEL 1
#define PCNT_CTRL_LOW_LEVEL 0
static xQueueHandle pcnt_evt_queue = NULL;
typedef struct {
int zero_times;
int h_limit;
int l_limit;
int h_threshold;
int l_threshold;
int filter_time;
} event_times;
static void pcnt_test_io_config(int ctrl_level)
{
// Connect internal signals using IO matrix.
gpio_set_direction(PULSE_IO, GPIO_MODE_INPUT_OUTPUT);
esp_rom_gpio_connect_out_signal(PULSE_IO, LEDC_LS_SIG_OUT1_IDX, 0, 0); // LEDC_TIMER_1, LEDC_LOW_SPEED_MODE
esp_rom_gpio_connect_in_signal(PULSE_IO, PCNT_SIG_CH0_IN0_IDX, 0); // PCNT_UNIT_0, PCNT_CHANNEL_0
esp_rom_gpio_connect_in_signal(ctrl_level ? GPIO_MATRIX_CONST_ONE_INPUT : GPIO_MATRIX_CONST_ZERO_INPUT, PCNT_CTRL_CH0_IN0_IDX, 0); // PCNT_UNIT_0, PCNT_CHANNEL_0
}
/* use LEDC to produce pulse for PCNT
* the frequency of LEDC is 1000, so every second will get 1000 count values
* the PCNT count the LEDC pulse
* */
static void produce_pulse(void)
{
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = LEDC_TIMER_1,
.duty_resolution = LEDC_TIMER_10_BIT,
.freq_hz = 1,
.clk_cfg = LEDC_AUTO_CLK,
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
ledc_channel_config_t ledc_channel = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LEDC_CHANNEL_1,
.timer_sel = LEDC_TIMER_1,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = PULSE_IO,
.duty = 100,
.hpoint = 0,
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
}
static void IRAM_ATTR pcnt_intr_handler(void *arg)
{
uint32_t intr_status = PCNT.int_st.val;
int i;
uint32_t status;
BaseType_t port_status = pdFALSE;
for (i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
if (intr_status & (BIT(i))) {
status = PCNT.status_unit[i].val;
PCNT.int_clr.val = BIT(i);
xQueueSendFromISR(pcnt_evt_queue, &status, &port_status);
if (port_status == pdTRUE) {
portYIELD_FROM_ISR();
}
}
}
}
static void event_calculate(event_times *event)
{
int16_t test_counter = 0;
int times = 0;
BaseType_t port_status;
uint32_t status = 0;
while (times < 10) {
port_status = xQueueReceive(pcnt_evt_queue, &status, 1000 / portTICK_PERIOD_MS);
if (port_status == pdTRUE) {
event->filter_time++;
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
printf("Current counter value :%d\n", test_counter);
if (status & PCNT_EVT_THRES_1) {
printf("THRES1 EVT\n");
event->h_threshold++;
}
if (status & PCNT_EVT_THRES_0) {
printf("THRES0 EVT\n");
event->l_threshold++;
}
if (status & PCNT_EVT_L_LIM) {
printf("L_LIM EVT\n");
event->l_limit++;
}
if (status & PCNT_EVT_H_LIM) {
printf("H_LIM EVT\n");
event->h_limit++;
}
if (status & PCNT_EVT_ZERO) {
printf("ZERO EVT\n");
event->zero_times++;
}
} else {
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
printf("Current counter value :%d\n", test_counter);
}
times++;
}
printf("%d, %d, %d, %d, %d, %d\n", event->h_threshold, event->l_threshold,
event->l_limit, event->h_limit, event->zero_times, event->filter_time);
}
/*
* There exist 2 kind of counting methods: positive and negative counting method
* 1. when control IO is high level, PCNT is positive counting mode
* 2. when control IO is low level, PCNT is positive negative mode
* the positive method standard is as below:
* ----------------------------------------------------------------------------------
* POS_ MODE | LCTRL_ MODE | HCTRL_ MODE | sig l→h when ctrl=0 | sig l→h when ctrl=1
* NEG_ MODE | | | |
* ===================================================================================
* 1 (inc) | 0 (-) | 0 (-) | Inc ctr | Inc ctr
* 2 (dec) | 0 (-) | 0 (-) | Dec ctr | Dec ctr
* 0 (-) | x | x | No action | No action
* 1 (inc) | 0 (-) | 1 (inv) | Inc ctr | Dec ctr
* 1 (inc) | 1 (inv) | 0 (-) | Dec ctr | Inc ctr
* 2 (dec) | 0 (-) | 1 (inv) | Dec ctr | Inc ctr
* 1 (inc) | 0 (-) | 2 (dis) | Inc ctr | No action
* 1 (inc) | 2 (dis) | 0 (-) | No action | Inc ctr
* -----------------------------------------------------------------------------------
* */
static void count_mode_test(gpio_num_t ctl_io)
{
int16_t test_counter;
//produce pulse, 100HZ
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.timer_num = LEDC_TIMER_1,
.duty_resolution = LEDC_TIMER_10_BIT,
.freq_hz = 100,
.clk_cfg = LEDC_AUTO_CLK,
};
ledc_timer_config(&ledc_timer);
ledc_channel_config_t ledc_channel = {
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LEDC_CHANNEL_1,
.timer_sel = LEDC_TIMER_1,
.intr_type = LEDC_INTR_DISABLE,
.gpio_num = PULSE_IO,
.duty = 100,
.hpoint = 0,
};
ledc_channel_config(&ledc_channel);
pcnt_config_t pcnt_config = {
.pulse_gpio_num = PCNT_INPUT_IO,
.ctrl_gpio_num = ctl_io,
.channel = PCNT_CHANNEL_0,
.unit = PCNT_UNIT_0,
.pos_mode = PCNT_COUNT_INC,
.neg_mode = PCNT_COUNT_DIS,
.lctrl_mode = PCNT_MODE_REVERSE,
.hctrl_mode = PCNT_MODE_KEEP,
.counter_h_lim = 101,
.counter_l_lim = -101,
};
TEST_ESP_OK(pcnt_unit_config(&pcnt_config));
pcnt_test_io_config((ctl_io == PCNT_CTRL_VCC_IO) ? PCNT_CTRL_HIGH_LEVEL : PCNT_CTRL_LOW_LEVEL);
int16_t result1[8] = {100, -100, 0, -100, 100, 100, 0, 100};
int16_t result2[8] = {100, -100, 0, 100, -100, -100, 100, 0};
int16_t *result;
if (ctl_io == PCNT_CTRL_VCC_IO) {
result = result1;
} else {
result = result2;
}
// 1, 0, 0, 0
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_set_mode(PCNT_UNIT_0, PCNT_CHANNEL_0,
PCNT_COUNT_INC, PCNT_COUNT_DIS,
PCNT_MODE_KEEP, PCNT_MODE_KEEP));
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
vTaskDelay(1000 / portTICK_RATE_MS);
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
printf("value: %d\n", test_counter);
TEST_ASSERT_INT16_WITHIN(1, test_counter, result[0]);
//2, 0, 0, 0
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_set_mode(PCNT_UNIT_0, PCNT_CHANNEL_0,
PCNT_COUNT_DEC, PCNT_COUNT_DIS,
PCNT_MODE_KEEP, PCNT_MODE_KEEP));
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
vTaskDelay(1000 / portTICK_RATE_MS);
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
printf("value: %d\n", test_counter);
TEST_ASSERT_INT16_WITHIN(1, test_counter, result[1]);
//0,0,0,0
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_set_mode(PCNT_UNIT_0, PCNT_CHANNEL_0,
PCNT_COUNT_DIS, PCNT_COUNT_DIS,
PCNT_MODE_KEEP, PCNT_MODE_KEEP));
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
vTaskDelay(1000 / portTICK_RATE_MS);
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
printf("value: %d\n", test_counter);
TEST_ASSERT_INT16_WITHIN(1, test_counter, result[2]);
//1,0,1,0
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_set_mode(PCNT_UNIT_0, PCNT_CHANNEL_0,
PCNT_COUNT_INC, PCNT_COUNT_DIS,
PCNT_MODE_REVERSE, PCNT_MODE_KEEP));
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
vTaskDelay(1000 / portTICK_RATE_MS);
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
printf("value: %d\n", test_counter);
TEST_ASSERT_INT16_WITHIN(1, test_counter, result[3]);
//1,0,0,1
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_set_mode(PCNT_UNIT_0, PCNT_CHANNEL_0,
PCNT_COUNT_INC, PCNT_COUNT_DIS,
PCNT_MODE_KEEP, PCNT_MODE_REVERSE));
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
vTaskDelay(1000 / portTICK_RATE_MS);
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
printf("value: %d\n", test_counter);
TEST_ASSERT_INT16_WITHIN(1, test_counter, result[4]);
//2,0,0,1
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_set_mode(PCNT_UNIT_0, PCNT_CHANNEL_0,
PCNT_COUNT_DEC, PCNT_COUNT_DIS,
PCNT_MODE_REVERSE, PCNT_MODE_KEEP));
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
vTaskDelay(1000 / portTICK_RATE_MS);
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
printf("value: %d\n", test_counter);
TEST_ASSERT_INT16_WITHIN(1, test_counter, result[5]);
//1,0,2,0
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_set_mode(PCNT_UNIT_0, PCNT_CHANNEL_0,
PCNT_COUNT_INC, PCNT_COUNT_DIS,
PCNT_MODE_DISABLE, PCNT_MODE_KEEP));
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
vTaskDelay(1000 / portTICK_RATE_MS);
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
printf("value: %d\n", test_counter);
TEST_ASSERT_INT16_WITHIN(1, test_counter, result[6]);
//1,0,0,2
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_set_mode(PCNT_UNIT_0, PCNT_CHANNEL_0,
PCNT_COUNT_INC, PCNT_COUNT_DIS,
PCNT_MODE_KEEP, PCNT_MODE_DISABLE));
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
vTaskDelay(1000 / portTICK_RATE_MS);
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
printf("value: %d\n", test_counter);
TEST_ASSERT_INT16_WITHIN(1, test_counter, result[7]);
}
// test PCNT basic configuration
TEST_CASE("PCNT test config", "[pcnt]")
{
pcnt_config_t pcnt_config = {
.pulse_gpio_num = PCNT_INPUT_IO,
.ctrl_gpio_num = PCNT_CTRL_VCC_IO,
.channel = PCNT_CHANNEL_0,
.unit = PCNT_UNIT_0,
.pos_mode = PCNT_COUNT_INC,
.neg_mode = PCNT_COUNT_DIS,
.lctrl_mode = PCNT_MODE_REVERSE,
.hctrl_mode = PCNT_MODE_KEEP,
.counter_h_lim = 100,
.counter_l_lim = 0,
};
// basic configuration
pcnt_config_t temp_pcnt_config = pcnt_config;
TEST_ESP_OK(pcnt_unit_config(&pcnt_config));
// test SOC_PCNT_UNITS_PER_GROUP units, from 0-(SOC_PCNT_UNITS_PER_GROUP-1)
pcnt_config = temp_pcnt_config;
pcnt_config.unit = SOC_PCNT_UNITS_PER_GROUP;
TEST_ASSERT_NOT_NULL((void *)pcnt_unit_config(&pcnt_config));
for (int i = 0; i < SOC_PCNT_UNITS_PER_GROUP; i++) {
pcnt_config.unit = i;
TEST_ESP_OK(pcnt_unit_config(&pcnt_config));
}
// test channels
pcnt_config = temp_pcnt_config;
pcnt_config.channel = PCNT_CHANNEL_MAX;
TEST_ASSERT_NOT_NULL((void *)pcnt_unit_config(&pcnt_config));
pcnt_config = temp_pcnt_config;
pcnt_config.pulse_gpio_num = -1;
TEST_ESP_OK(pcnt_unit_config(&pcnt_config));
pcnt_config = temp_pcnt_config;
pcnt_config.pulse_gpio_num = GPIO_NUM_MAX + 1;
TEST_ASSERT_NOT_NULL((void *)pcnt_unit_config(&pcnt_config));
// test pulse_gpio_num and ctrl_gpio_num is the same
pcnt_config = temp_pcnt_config;
pcnt_config.pulse_gpio_num = PCNT_INPUT_IO;
pcnt_config.ctrl_gpio_num = PCNT_INPUT_IO;
TEST_ASSERT_NOT_NULL((void *)pcnt_unit_config(&pcnt_config));
pcnt_config = temp_pcnt_config;
pcnt_config.pos_mode = PCNT_COUNT_MAX;
TEST_ASSERT_NOT_NULL((void *)pcnt_unit_config(&pcnt_config));
pcnt_config = temp_pcnt_config;
pcnt_config.hctrl_mode = PCNT_MODE_MAX;
TEST_ASSERT_NOT_NULL((void *)pcnt_unit_config(&pcnt_config));
pcnt_config = temp_pcnt_config;
pcnt_config.lctrl_mode = PCNT_MODE_MAX;
TEST_ASSERT_NOT_NULL((void *)pcnt_unit_config(&pcnt_config));
}
/* PCNT basic property:
* 1. pause counter
* 2. resume counter
* 3. clear counter
* 4. check the counter value*/
TEST_CASE("PCNT basic function test", "[pcnt]")
{
int16_t test_counter;
int16_t time = 0;
int clear_count = 0;
int resume_count = 0;
int temp_value = 0;
pcnt_config_t pcnt_config = {
.pulse_gpio_num = PCNT_INPUT_IO,
.ctrl_gpio_num = PCNT_CTRL_VCC_IO,
.channel = PCNT_CHANNEL_0,
.unit = PCNT_UNIT_0,
.pos_mode = PCNT_COUNT_INC,
.neg_mode = PCNT_COUNT_DIS,
.lctrl_mode = PCNT_MODE_REVERSE,
.hctrl_mode = PCNT_MODE_KEEP,
.counter_h_lim = 10,
.counter_l_lim = -10,
};
TEST_ESP_OK(pcnt_unit_config(&pcnt_config));
// use LEDC to produce the pulse, then the PCNT to count it
produce_pulse();
pcnt_test_io_config(PCNT_CTRL_HIGH_LEVEL);
// initialize first, the initail value should be 0
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
TEST_ASSERT_EQUAL_INT16(test_counter, 0);
// resume the PCNT
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
TEST_ASSERT_EQUAL_INT16(test_counter, 0);
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
TEST_ASSERT_EQUAL_INT16(test_counter, 0);
//count now
while (time != 10) {
vTaskDelay(1001 / portTICK_RATE_MS); // in case of can't wait to get counter(edge effect)
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
printf("COUNT: %d\n", test_counter);
TEST_ASSERT_NOT_EQUAL(test_counter, temp_value);
temp_value = test_counter;
if (test_counter == 5 || test_counter == -5) {
//test clear
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
TEST_ASSERT_EQUAL_INT16(test_counter, 0);
clear_count++;
}
if (test_counter == 0) {
//test pause
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
vTaskDelay(1000 / portTICK_RATE_MS);
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
printf("PAUSE: %d\n", test_counter);
TEST_ASSERT_EQUAL_INT16(test_counter, 0);
// test resume
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
vTaskDelay(1000 / portTICK_RATE_MS);
TEST_ESP_OK(pcnt_get_counter_value(PCNT_UNIT_0, &test_counter));
printf("RESUME: %d\n", test_counter);
TEST_ASSERT_EQUAL_INT16(test_counter, 1);
resume_count++;
}
time++;
}
TEST_ASSERT_EQUAL_INT16(clear_count, 2);
TEST_ASSERT_EQUAL_INT16(resume_count, 2);
}
/**
* there are 4 situations will be tested:
* 1. set event to interrupt triggered
* 2. disable interrupt to stop triggering interrupt
* 3. enable interrupt to interrupt triggered again
* 4. disable event to stop triggering interrupt
*
* PCNT interrupt type:
* 1. PCNT_EVT_THRES_1
* 2. PCNT_EVT_THRES_0
* 3. PCNT_EVT_ZERO
* 4. PCNT_EVT_H_LIM
* 5. PCNT_EVT_L_LIM
* */
TEST_CASE("PCNT interrupt method test(control IO is high)", "[pcnt][timeout=120]")
{
pcnt_config_t config = {
.pulse_gpio_num = PCNT_INPUT_IO,
.ctrl_gpio_num = PCNT_CTRL_VCC_IO,
.channel = PCNT_CHANNEL_0,
.unit = PCNT_UNIT_0,
.pos_mode = PCNT_COUNT_INC,
.neg_mode = PCNT_COUNT_DIS,
.lctrl_mode = PCNT_MODE_REVERSE,
.hctrl_mode = PCNT_MODE_KEEP,
.counter_h_lim = 5,
.counter_l_lim = 0,
};
TEST_ESP_OK(pcnt_unit_config(&config));
produce_pulse();
pcnt_test_io_config(PCNT_CTRL_HIGH_LEVEL);
event_times event = {
.zero_times = 0,
.h_limit = 0,
.l_limit = 0,
.h_threshold = 0,
.l_threshold = 0,
.filter_time = 0,
};
//interrupt set
TEST_ESP_OK(pcnt_set_filter_value(PCNT_UNIT_0, 2));
TEST_ESP_OK(pcnt_filter_enable(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_set_event_value(PCNT_UNIT_0, PCNT_EVT_THRES_1, 4)); // when arrive to max threshold trigger
TEST_ESP_OK(pcnt_event_enable(PCNT_UNIT_0, PCNT_EVT_THRES_1));
TEST_ESP_OK(pcnt_set_event_value(PCNT_UNIT_0, PCNT_EVT_THRES_0, 1)); // when arrive to minimum threshold trigger
TEST_ESP_OK(pcnt_event_enable(PCNT_UNIT_0, PCNT_EVT_THRES_0));
TEST_ESP_OK(pcnt_event_enable(PCNT_UNIT_0, PCNT_EVT_ZERO));
TEST_ESP_OK(pcnt_event_enable(PCNT_UNIT_0, PCNT_EVT_H_LIM)); // when arrive to max limit trigger
TEST_ESP_OK(pcnt_event_enable(PCNT_UNIT_0, PCNT_EVT_L_LIM)); // when arrive to minimum limit trigger
// initialize first, the initail value should be 0
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
pcnt_evt_queue = xQueueCreate(10, sizeof(uint32_t));
pcnt_isr_handle_t pcnt_isr_service;
TEST_ESP_OK(pcnt_isr_register(pcnt_intr_handler, NULL, 0, &pcnt_isr_service));
TEST_ESP_OK(pcnt_intr_enable(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
// test event
event_calculate(&event);
TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 2);
TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 2);
TEST_ASSERT(event.l_limit == 0);
TEST_ASSERT_INT_WITHIN(2, event.h_limit, 2);
TEST_ASSERT_INT_WITHIN(2, event.zero_times, 2);
TEST_ASSERT_INT_WITHIN(3, event.filter_time, 4);
// test interrupt disable
TEST_ESP_OK(pcnt_intr_disable(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
// for the original control io disable interrupt status
event_calculate(&event);
TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 2);
TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 2);
TEST_ASSERT(event.l_limit == 0);
TEST_ASSERT_INT_WITHIN(2, event.h_limit, 2);
TEST_ASSERT_INT_WITHIN(2, event.zero_times, 2);
TEST_ASSERT_INT_WITHIN(3, event.filter_time, 4);
// enable the intr
TEST_ESP_OK(pcnt_intr_enable(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
event_calculate(&event);
TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 4);
TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 4);
TEST_ASSERT(event.l_limit == 0);
TEST_ASSERT_INT_WITHIN(2, event.h_limit, 4);
TEST_ASSERT_INT_WITHIN(2, event.zero_times, 4);
TEST_ASSERT_INT_WITHIN(3, event.filter_time, 10);
// disable part of events
TEST_ESP_OK(pcnt_event_disable(PCNT_UNIT_0, PCNT_EVT_ZERO));
TEST_ESP_OK(pcnt_event_disable(PCNT_UNIT_0, PCNT_EVT_L_LIM));
TEST_ESP_OK(pcnt_event_disable(PCNT_UNIT_0, PCNT_EVT_THRES_0));
event_calculate(&event);
TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 5);
TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 4);
TEST_ASSERT(event.l_limit == 0);
TEST_ASSERT_INT_WITHIN(3, event.h_limit, 6);
TEST_ASSERT_INT_WITHIN(2, event.zero_times, 4);
TEST_ASSERT_INT_WITHIN(3, event.filter_time, 14);
// Because this test uses its own ISR, we need to release it with `pcnt_isr_unregister` instead of `pcnt_isr_service_uninstall`
TEST_ESP_OK(pcnt_isr_unregister(pcnt_isr_service));
vQueueDelete(pcnt_evt_queue);
}
TEST_CASE("PCNT interrupt method test(control IO is low)", "[pcnt][timeout=120]")
{
pcnt_config_t config = {
.pulse_gpio_num = PCNT_INPUT_IO,
.ctrl_gpio_num = PCNT_CTRL_GND_IO,
.channel = PCNT_CHANNEL_0,
.unit = PCNT_UNIT_0,
.pos_mode = PCNT_COUNT_INC,
.neg_mode = PCNT_COUNT_DIS,
.lctrl_mode = PCNT_MODE_REVERSE,
.hctrl_mode = PCNT_MODE_KEEP,
.counter_h_lim = 0,
.counter_l_lim = -5,
};
TEST_ESP_OK(pcnt_unit_config(&config));
produce_pulse();
pcnt_test_io_config(PCNT_CTRL_LOW_LEVEL);
event_times event = {
.zero_times = 0,
.h_limit = 0,
.l_limit = 0,
.h_threshold = 0,
.l_threshold = 0,
.filter_time = 0,
};
//interrupt set
TEST_ESP_OK(pcnt_set_filter_value(PCNT_UNIT_0, 2));
TEST_ESP_OK(pcnt_filter_enable(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_set_event_value(PCNT_UNIT_0, PCNT_EVT_THRES_1, -4)); // when arrive to max threshold trigger
TEST_ESP_OK(pcnt_event_enable(PCNT_UNIT_0, PCNT_EVT_THRES_1));
TEST_ESP_OK(pcnt_set_event_value(PCNT_UNIT_0, PCNT_EVT_THRES_0, 0)); // when arrive to minimum threshold trigger
TEST_ESP_OK(pcnt_event_enable(PCNT_UNIT_0, PCNT_EVT_THRES_0));
TEST_ESP_OK(pcnt_event_enable(PCNT_UNIT_0, PCNT_EVT_ZERO));
TEST_ESP_OK(pcnt_event_enable(PCNT_UNIT_0, PCNT_EVT_H_LIM)); // when arrive to max limit trigger
TEST_ESP_OK(pcnt_event_enable(PCNT_UNIT_0, PCNT_EVT_L_LIM)); // when arrive to minimum limit trigger
// to initialize for PCNT
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
pcnt_evt_queue = xQueueCreate(10, sizeof(uint32_t));
pcnt_isr_handle_t pcnt_isr_service;
TEST_ESP_OK(pcnt_isr_register(pcnt_intr_handler, NULL, 0, &pcnt_isr_service));
TEST_ESP_OK(pcnt_intr_enable(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
// test event
event_calculate(&event);
TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 1);
TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 1);
TEST_ASSERT_INT_WITHIN(2, event.l_limit, 1);
TEST_ASSERT_INT_WITHIN(2, event.h_limit, 0);
TEST_ASSERT_INT_WITHIN(2, event.zero_times, 1);
TEST_ASSERT_INT_WITHIN(2, event.filter_time, 2);
// test interrupt disable
TEST_ESP_OK(pcnt_intr_disable(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
// for the original control io disable interrupt status
event_calculate(&event);
TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 1);
TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 1);
TEST_ASSERT_INT_WITHIN(2, event.l_limit, 1);
TEST_ASSERT_INT_WITHIN(2, event.h_limit, 0);
TEST_ASSERT_INT_WITHIN(2, event.zero_times, 1);
TEST_ASSERT_INT_WITHIN(2, event.filter_time, 2);
// enable the intr
pcnt_unit_config(&config);
pcnt_test_io_config(PCNT_CTRL_LOW_LEVEL);
TEST_ESP_OK(pcnt_intr_enable(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_pause(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_clear(PCNT_UNIT_0));
TEST_ESP_OK(pcnt_counter_resume(PCNT_UNIT_0));
event_calculate(&event);
TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 2);
TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 3);
TEST_ASSERT_INT_WITHIN(2, event.l_limit, 2);
TEST_ASSERT_INT_WITHIN(2, event.h_limit, 0);
TEST_ASSERT_INT_WITHIN(2, event.zero_times, 2);
TEST_ASSERT_INT_WITHIN(2, event.filter_time, 6);
// disable part of events
TEST_ESP_OK(pcnt_event_disable(PCNT_UNIT_0, PCNT_EVT_ZERO));
TEST_ESP_OK(pcnt_event_disable(PCNT_UNIT_0, PCNT_EVT_L_LIM));
TEST_ESP_OK(pcnt_event_disable(PCNT_UNIT_0, PCNT_EVT_THRES_0));
event_calculate(&event);
TEST_ASSERT_INT_WITHIN(2, event.h_threshold, 4);
TEST_ASSERT_INT_WITHIN(2, event.l_threshold, 3);
TEST_ASSERT_INT_WITHIN(2, event.l_limit, 2);
TEST_ASSERT_INT_WITHIN(2, event.h_limit, 0);
TEST_ASSERT_INT_WITHIN(2, event.zero_times, 2);
TEST_ASSERT_INT_WITHIN(2, event.filter_time, 8);
// Because this test uses its own ISR, we need to release it with `pcnt_isr_unregister` instead of `pcnt_isr_service_uninstall`
TEST_ESP_OK(pcnt_isr_unregister(pcnt_isr_service));
vQueueDelete(pcnt_evt_queue);
}
TEST_CASE("PCNT counting mode test", "[pcnt]")
{
printf("PCNT mode test for positive count\n");
count_mode_test(PCNT_CTRL_VCC_IO);
printf("PCNT mode test for negative count\n");
count_mode_test(PCNT_CTRL_GND_IO);
}
#endif // #if SOC_PCNT_SUPPORTED