Page 1 of 1

LEDC and PCNT on same GPIO

Posted: Sat Nov 14, 2020 9:45 am
by micha5104
Hi,

I'm using the LEDC module to sent signals to a stepper control board (A4988). To figure out how many steps the motor has received I'm using the PCNT module on a different GPIO. Currently these two pins are wired together externally.

What I would like to do now is to use only one pin instead of two. I was thinking this could be achieved using an internal signal and routing the LEDC output to the signal, and routing the signal back into the PCNT input.

So far none of my attempts succeeded.

I have seen and unsuccessfully tried this one here: esp32.com/viewtopic.php?f=13&t=4253

Has anyone succeeded to do this and would be willing to share the solution?

Best regards,
Micha

Re: LEDC and PCNT on same GPIO

Posted: Wed Nov 18, 2020 8:28 am
by ESP_morris
yes you can route different peripheral signals to a same GPIO by GPIO matrix. We have many example in our unit test cases. For example: https://github.com/espressif/esp-idf/bl ... _rmt.c#L67

Re: LEDC and PCNT on same GPIO

Posted: Sat Dec 26, 2020 5:18 pm
by micha5104
Hi, thanks for the hint!
I finally got it working. The trick is to set the GPIO, as you suggested, to GPIO_MODE_INPUT_OUTPUT.
But I also had to setup the GPIO matrix again for the LEDC functionality which was probably lost when LEDC was first configured on the GPIO, but then PCNT was configured on the same GPIO.

So the following works for me:
  • configure LEDC on a pin of your choice
  • configure PCNT on the same pin as you configured the LEDC on
  • set GPIO to GPIO_MODE_INPUT_OUTPUT:

    Code: Select all

    gpio_set_direction(myPin, GPIO_MODE_INPUT_OUTPUT);
  • configure GPIO matrix again for LEDC:

    Code: Select all

    gpio_matrix_out(myPin, LEDC_HS_SIG_OUT0_IDX + myLedcChannel, 0, 0);

Re: LEDC and PCNT on same GPIO

Posted: Sat Apr 03, 2021 5:54 am
by frankgigeur
micha5104 wrote:
Sat Dec 26, 2020 5:18 pm
Hi, thanks for the hint!
I finally got it working. The trick is to set the GPIO, as you suggested, to GPIO_MODE_INPUT_OUTPUT.
But I also had to setup the GPIO matrix again for the LEDC functionality which was probably lost when LEDC was first configured on the GPIO, but then PCNT was configured on the same GPIO.

So the following works for me:
  • configure LEDC on a pin of your choice
  • configure PCNT on the same pin as you configured the LEDC on
  • set GPIO to GPIO_MODE_INPUT_OUTPUT:

    Code: Select all

    gpio_set_direction(myPin, GPIO_MODE_INPUT_OUTPUT);
  • configure GPIO matrix again for LEDC:

    Code: Select all

    gpio_matrix_out(myPin, LEDC_HS_SIG_OUT0_IDX + myLedcChannel, 0, 0);
Hi micha5104,

I'm trying to accomplish the same thing , but i think i miss some practice with pcnt. Can you share your code to help me to understand what i should to do? It will be very appreciated.

Code: Select all

ledc_timer_config_t pwm_test = {
    .speed_mode = LEDC_HIGH_SPEED_MODE,
    .duty_resolution = LEDC_TIMER_1_BIT,
    .timer_num = LEDC_TIMER_3,
    .freq_hz = 1000,
    .clk_cfg = LEDC_AUTO_CLK};

ledc_channel_config_t ch_pwm_test = {
    .gpio_num = GPIO_NUM_25,
    .speed_mode = LEDC_HIGH_SPEED_MODE,
    .channel = LEDC_CHANNEL_5,
    .intr_type = LEDC_INTR_DISABLE,
    .timer_sel = LEDC_TIMER_3,
    .duty = 1,
    .hpoint = 0 // La valeur à laquel le high s'enclanche ex : hpoint = 10 , l'état changera lorsque le compteur sera à 10. De 10 à duty
};

pcnt_config_t pcnt_test = {
        .pulse_gpio_num = GPIO_NUM_25,             /*!< Pulse input GPIO number, if you want to use GPIO16, enter pulse_gpio_num = 16, a negative value will be ignored */
        .ctrl_gpio_num = PCNT_PIN_NOT_USED,              /*!< Control signal input GPIO number, a negative value will be ignored */
        .lctrl_mode = PCNT_MODE_REVERSE, // Reverse counting direction if low
        .hctrl_mode = PCNT_MODE_KEEP,    /*!< PCNT high control mode */
        .pos_mode = PCNT_COUNT_INC,   // Count up on the positive edge
        .neg_mode = PCNT_COUNT_DIS,    /*!< PCNT negative edge count mode */
        .counter_h_lim = 255,
        .counter_l_lim = 0,         /*!< Minimum counter value */
        .unit = PCNT_UNIT_0, 
        .channel = PCNT_CHANNEL_0
};

Code: Select all

    pcnt_unit_config(&pcnt_test);

    ledc_timer_config(&pwm_test);
    ledc_channel_config(&ch_pwm_test);
    gpio_set_direction(GPIO_NUM_25, GPIO_MODE_INPUT_OUTPUT);
    gpio_matrix_out(GPIO_NUM_25, LEDC_HS_SIG_OUT0_IDX + LEDC_CHANNEL_5, 0, 0);

Code: Select all

    int16_t compteur;
    for (;;)
    {
        vTaskDelay(1000 / portTICK_PERIOD_MS);
        pcnt_get_counter_value(PCNT_UNIT_0,&compteur);
        printf("compteur = %d", compteur);
        ...}

Re: LEDC and PCNT on same GPIO

Posted: Sat Apr 03, 2021 5:15 pm
by micha5104
Hi frankgigeur,

this is the code I am using. Note: I am also using the ISR handler on the PCNT, but it should also work without.
On first glance I don't see a lot of difference between your and my code...
Please let me know if you figured out, what it was.

Initialization:

Code: Select all

    ledc_timer_config_t ledc_timer;
    ledc_timer.timer_num = LEDC_TIMER_0;                // timer index
    ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE; // timer mode
    ledc_timer.freq_hz = 4000;           // frequency of PWM signal
    ledc_timer.duty_resolution = LEDC_TIMER_8_BIT; // resolution of PWM duty
    ledc_timer.clk_cfg = LEDC_AUTO_CLK;           // Auto select the source clock
    // Set configuration of timer for high speed channels
    esp_err_t err = ledc_timer_config(&ledc_timer);
    if (err != ESP_OK)
    {
        mLogger->log(Logger::ERROR) << "Error configuring ledc_timer_config: " << esp_err_to_name(err);
    }
    
    ledc_channel_config_t ledc_conf;
    ledc_conf.channel = LEDC_CHANNEL_0;
    ledc_conf.gpio_num = GPIO_NUM_23;
    ledc_conf.duty = 0; // don't run off right away :-)
    ledc_conf.hpoint = 0; // where in the clock interval the level will be set to high; like a phase shift
    ledc_conf.intr_type = LEDC_INTR_DISABLE;
    ledc_conf.speed_mode = LEDC_HIGH_SPEED_MODE;
    ledc_conf.timer_sel = LEDC_TIMER_0;
    err = ledc_channel_config(&ledc_conf);
    if (err != ESP_OK)
    {
        mLogger->log(Logger::ERROR) << "Error configuring ledc_channel_config: " << esp_err_to_name(err);
    }    
    
    pcnt_config_t pcnt_config =
    {
        // Set PCNT input signal and control GPIOs
        .pulse_gpio_num = GPIO_NUM_23,
        .ctrl_gpio_num = -1,
        .lctrl_mode = PCNT_MODE_KEEP,    // Keep the primary counter mode if high
        .hctrl_mode = PCNT_MODE_KEEP,    // Keep the primary counter mode if high
        .pos_mode = PCNT_COUNT_INC,   // Count up on the positive edge
        .neg_mode = PCNT_COUNT_DIS,   // Ignore
        // Set the maximum and minimum limit values to watch
        .counter_h_lim = 32760,
        .counter_l_lim = -1,
        .unit = PCNT_UNIT_0,
        .channel = PCNT_CHANNEL_0,
    };
    /* Initialize PCNT unit */
    pcnt_unit_config(&pcnt_config);

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

    pcnt_event_enable(PCNT_UNIT_0, PCNT_EVT_H_LIM);
    pcnt_event_enable(PCNT_UNIT_0, PCNT_EVT_THRES_0);

    pcnt_counter_pause(PCNT_UNIT_0);
    pcnt_counter_clear(PCNT_UNIT_0);

    /* Register ISR handler and enable interrupts for PCNT unit */
    pcnt_isr_register(pcnt_intr_handler, &PCNT_UNIT_0, 0, &user_isr_handle);
    pcnt_intr_enable(PCNT_UNIT_0);

    pcnt_counter_resume(PCNT_UNIT_0);

    gpio_set_direction(GPIO_NUM_23, GPIO_MODE_INPUT_OUTPUT);
    // NOTE: the following line assumes that we use LEDC_HIGH_SPEED_MODE
    gpio_matrix_out(GPIO_NUM_23, LEDC_HS_SIG_OUT0_IDX + LEDC_CHANNEL_0, 0, 0);    
Start the signal:

Code: Select all

    ledc_set_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0, (1 << LEDC_TIMER_8_BIT) / 2);
    ledc_update_duty(LEDC_HIGH_SPEED_MODE, LEDC_CHANNEL_0);
Query the counter value:

Code: Select all

int16_t count;
esp_err_t err = pcnt_get_counter_value(PCNT_UNIT_0, &count);
if (err != ESP_OK)
{
    mLogger->log(Logger::ERROR) << "Error calling pcnt_get_counter_value";
}
Good luck!

Re: LEDC and PCNT on same GPIO

Posted: Sun Jan 09, 2022 11:45 am
by zhhq8008
Hi frankgigeur, have your problem figured out ?
i have a same problem like you,
i can generat pulse from a defined pin, but can't read counter from this pin