espressif / arduino-esp32

Arduino core for the ESP32
GNU Lesser General Public License v2.1
13.52k stars 7.39k forks source link

ESP32 PCNT _pcnt_isr_service_install fail with driver error #8244

Closed anvgfr closed 1 year ago

anvgfr commented 1 year ago

Board

ESP32 WRoom 32

Device Description

Lilygo T8 v1.7.1

Hardware Configuration

GPIO 25,26,27,12,14,35 for PCNT usage GPIO 23,18,4,5 for SPI display

Version

v2.0.9

IDE Name

Arduino IDE 2.1.0

Operating System

Windows 10

Flash frequency

80 Mhz

PSRAM enabled

yes

Upload speed

912500

Description

When trying to compile with lastest ESP32 library, pcnt_isr_install_service call failed with a driver error. Building & Running same project with ESP32 Arduino library 1.6.0 run successfully...

Sketch

#include <Wire.h>
#include "driver/pcnt.h"  // ESP32 library for pulse count
#include "soc/pcnt_struct.h" 

setup()
{
  pcnt_isr_service_install(ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_IRAM);
}

void loop()
{
}

Debug Message

Counter 0E (599) pcnt: _pcnt_isr_service_install(316): PCNT driver error
PCNT ISR Installed
E (600) pcnt: _pcnt_isr_handler_add(264): ISR service is not installed, call pcnt_install_isr_service() first
PCNT #0 Started
 OK

Other Steps to Reproduce

No response

I have checked existing issues, online documentation and the Troubleshooting Guide

anvgfr commented 1 year ago

Made an error : read library v1.0.6 instead of 1.6.0 I have tried down to 2.0.0, still the same issue, seems that something is broken starting with 2.0.0 CPU is ESP32-D0WDQ6-V3

SuGlider commented 1 year ago

Arduino Core 2.0.x is based on IDF 4.4. Arduino Core 1.0.6 is based on IDF 3.3. I suggest using Arduino Core 2.0.9 and PCNT IDF API from IDF 4.4.4. It is possible and valid to use IDF 4.4.4 PCNT calls inside an Arduino Sketch.

Documentation: https://docs.espressif.com/projects/esp-idf/en/v4.4.4/esp32/api-reference/peripherals/pcnt.html

Examples can be found at: https://github.com/espressif/esp-idf/tree/v4.4.4/examples/peripherals/pcnt/pulse_count_event https://github.com/espressif/esp-idf/tree/v4.4.4/examples/peripherals/pcnt/rotary_encoder

anvgfr commented 1 year ago

If i look at the pulse_count_event sample, the call for pcnt_isr_service_install seems to take a 0 for parameter (isr_flags), but when i search for it in the api reference, there is no change, It doesn't seems to work, if i use 0 i got the same error (driver not initialized).

To make it work, i had to move the pcnt_isr_service_install call after the first call to pcnt_unit_config... Strange...

SuGlider commented 1 year ago

About the parameter:

It says that intr_alloc_flags is none (0) or more flags found in esp_intr_alloc.h (ESP_INTR_FLAG_* using OR |) https://docs.espressif.com/projects/esp-idf/en/v4.4.4/esp32/api-reference/peripherals/pcnt.html#_CPPv424pcnt_isr_service_installi

https://github.com/espressif/arduino-esp32/blob/00214d5c2a1c2b1904f2caf6f0d5ddfe952331ff/tools/sdk/esp32/include/esp_hw_support/include/esp_intr_alloc.h#L30-L49

SuGlider commented 1 year ago

Your code must do the configuration as desribed in the example:

https://github.com/espressif/esp-idf/blob/v4.4.4/examples/peripherals/pcnt/pulse_count_event/main/pcnt_event_example_main.c#L108-L154


#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

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;  // 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)
{
    int pcnt_unit = (int)arg;
    pcnt_evt_t evt;
    evt.unit = pcnt_unit;
    /* Save the PCNT event type that caused an interrupt
       to pass it to the main program */
    pcnt_get_event_status(pcnt_unit, &evt.status);
    xQueueSendFromISR(pcnt_evt_queue, &evt, NULL);
}

/* Initialize PCNT functions:
 *  - configure and initialize PCNT
 *  - set up the input filter
 *  - set up the counter events to watch
 */
static void pcnt_example_init(int unit)
{
    /* Prepare configuration for the PCNT unit */
    pcnt_config_t pcnt_config = {
        // Set PCNT input signal and control GPIOs
        .pulse_gpio_num = PCNT_INPUT_SIG_IO,
        .ctrl_gpio_num = PCNT_INPUT_CTRL_IO,
        .channel = PCNT_CHANNEL_0,
        .unit = unit,
        // What to do on the positive / negative edge of pulse input?
        .pos_mode = PCNT_COUNT_INC,   // Count up on the positive edge
        .neg_mode = PCNT_COUNT_DIS,   // Keep the counter value on the negative edge
        // 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,
        .counter_l_lim = PCNT_L_LIM_VAL,
    };
    /* Initialize PCNT unit */
    pcnt_unit_config(&pcnt_config);

    /* Configure and enable the input filter */
    pcnt_set_filter_value(unit, 100);
    pcnt_filter_enable(unit);

    /* Set threshold 0 and 1 values and enable events to watch */
    pcnt_set_event_value(unit, PCNT_EVT_THRES_1, PCNT_THRESH1_VAL);
    pcnt_event_enable(unit, PCNT_EVT_THRES_1);
    pcnt_set_event_value(unit, PCNT_EVT_THRES_0, PCNT_THRESH0_VAL);
    pcnt_event_enable(unit, PCNT_EVT_THRES_0);
    /* Enable events on zero, maximum and minimum limit values */
    pcnt_event_enable(unit, PCNT_EVT_ZERO);
    pcnt_event_enable(unit, PCNT_EVT_H_LIM);
    pcnt_event_enable(unit, PCNT_EVT_L_LIM);

    /* Initialize PCNT's counter */
    pcnt_counter_pause(unit);
    pcnt_counter_clear(unit);

    /* Install interrupt service and add isr callback handler */
    pcnt_isr_service_install(0);
    pcnt_isr_handler_add(unit, pcnt_example_intr_handler, (void *)unit);

    /* Everything is set up, now go to counting */
    pcnt_counter_resume(unit);
}
SuGlider commented 1 year ago

The pulse_count_event sample converted to Arduino Sketch looks like this: I have tested it. It seems to work fine. It is necessary to connect GPIO18 to GPIO5 and GPIO4 to GND or 3.3V. GPIO 18 generates a signal of 1KHz with 10% duty. GPIO5 counts Pulses GPIO4 tells the sketch to count UP (when connected to GND) or DOWN (when connected to 3.3V)

#include "driver/pcnt.h"

#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 of a sample 1 Hz pulse generator
#define LEDC_CHANNEL_1      1

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;  // 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 pcnt_unit = (uint32_t)arg;
  pcnt_evt_t evt;
  evt.unit = (pcnt_unit_t)pcnt_unit;
  /* Save the PCNT event type that caused an interrupt
     to pass it to the main program */
  pcnt_get_event_status((pcnt_unit_t) pcnt_unit, &evt.status);
  xQueueSendFromISR(pcnt_evt_queue, &evt, NULL);
}

/* Initialize PCNT functions:
    - configure and initialize PCNT
    - set up the input filter
    - set up the counter events to watch
*/
static void pcnt_example_init(pcnt_unit_t unit)
{
  /* Prepare configuration for the PCNT unit */
  pcnt_config_t pcnt_config = {
    // Set PCNT input signal and control GPIOs
    .pulse_gpio_num = PCNT_INPUT_SIG_IO,
    .ctrl_gpio_num = PCNT_INPUT_CTRL_IO,
    // 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
    // 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
    // Set the maximum and minimum limit values to watch
    .counter_h_lim = PCNT_H_LIM_VAL,
    .counter_l_lim = PCNT_L_LIM_VAL,
    .unit = unit,
    .channel = PCNT_CHANNEL_0,
  };
  /* Initialize PCNT unit */
  pcnt_unit_config(&pcnt_config);

  /* Configure and enable the input filter */
  pcnt_set_filter_value(unit, 100);
  pcnt_filter_enable(unit);

  /* Set threshold 0 and 1 values and enable events to watch */
  pcnt_set_event_value(unit, PCNT_EVT_THRES_1, PCNT_THRESH1_VAL);
  pcnt_event_enable(unit, PCNT_EVT_THRES_1);
  pcnt_set_event_value(unit, PCNT_EVT_THRES_0, PCNT_THRESH0_VAL);
  pcnt_event_enable(unit, PCNT_EVT_THRES_0);
  /* Enable events on zero, maximum and minimum limit values */
  pcnt_event_enable(unit, PCNT_EVT_ZERO);
  pcnt_event_enable(unit, PCNT_EVT_H_LIM);
  pcnt_event_enable(unit, PCNT_EVT_L_LIM);

  /* Initialize PCNT's counter */
  pcnt_counter_pause(unit);
  pcnt_counter_clear(unit);

  /* Install interrupt service and add isr callback handler */
  pcnt_isr_service_install(0);
  pcnt_isr_handler_add(unit, pcnt_example_intr_handler, (void *)unit);

  /* Everything is set up, now go to counting */
  pcnt_counter_resume(unit);
}

pcnt_unit_t pcnt_unit = PCNT_UNIT_0;

void pcnt_task(void *param) {
  int16_t count = 0;
  pcnt_evt_t evt;
  portBASE_TYPE res;
  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_unit, &count);
      log_i("Event PCNT unit[%d]; cnt: %d", evt.unit, count);
      if (evt.status & PCNT_EVT_THRES_1) {
        log_i("THRES1 EVT");
      }
      if (evt.status & PCNT_EVT_THRES_0) {
        log_i("THRES0 EVT");
      }
      if (evt.status & PCNT_EVT_L_LIM) {
        log_i("L_LIM EVT");
      }
      if (evt.status & PCNT_EVT_H_LIM) {
        log_i("H_LIM EVT");
      }
      if (evt.status & PCNT_EVT_ZERO) {
        log_i("ZERO EVT");
      }
    } else {
      pcnt_get_counter_value(pcnt_unit, &count);
      log_i("Current counter value :%d", count);
    }
  }
}

void setup() {

  ledcSetup(LEDC_CHANNEL_1, 1000, 10);  // 1KHz with 10 bit resoution
  ledcAttachPin(LEDC_OUTPUT_IO, LEDC_CHANNEL_1);
  ledcWrite(LEDC_CHANNEL_1, 102);      // about 10% Duty (1024 / 10)

  /* Initialize PCNT event queue and PCNT functions */
  pcnt_evt_queue = xQueueCreate(10, sizeof(pcnt_evt_t));
  xTaskCreateUniversal(pcnt_task, "pcnt_task", 4096, NULL, 1, NULL, ARDUINO_RUNNING_CORE);
  pcnt_example_init(pcnt_unit);
}

void loop() {
}
VojtechBartoska commented 1 year ago

Closing as expired due to no asnwer.