How to use "Pulse Counter" module usage under Arduino framework

Zyv_vz
Posts: 1
Joined: Tue Mar 10, 2020 3:49 pm

How to use "Pulse Counter" module usage under Arduino framework

Postby Zyv_vz » Tue Mar 10, 2020 3:58 pm

Hello,

I am using the Arduino framework in PlatformIO and wanted to use the Pulse Counter module.

How do I configure and use this module using the Arduino framework?

I have seen the reference https://docs.espressif.com/projects/esp ... t_config_t but as far as I'm concerned it is used some kind of structure.

technerdchris
Posts: 3
Joined: Thu May 28, 2020 4:11 pm

Re: How to use "Pulse Counter" module usage under Arduino framework

Postby technerdchris » Mon Jun 01, 2020 8:17 pm

Yes, that is a structure for initializing the PCNT hardware. I have made more steps than you in Arduino, however I have not yet been able to make it work. I am normally very good at taking code snippets and getting a solution. For example, to initialize the struct with the declaration, you must provide all members in the proper order for it to compile in Arduino IDE.

Below is some code I believe required. On my computer, I was able to open the pcnt.h file for reading in the following folder:
C: \ Users \ yourusername \ Documents \ ArduinoData \ packages \ esp32 \ hardware \ esp32 \ 1.0.2 \ tools \ sdk \ include \ driver \ driver

At this point, I am beginning to believe esp-idf is required to make PCNT module operational. When I started on ESP32 last year, I was not able to successfully install the tool chain to operate in windows 10, so I use Arduino IDE. At this point, I am disappointed in my project because of failure to make PCNT work, so I will watch the milli()s pass for my test and also the m5stack UI code is not sufficiently ready for me to employ it for my program. It is elaborate, however seemingly impossible to have it continuously display a status on the screen. So now I have to build x,y and program the entire UI also.
  1. #include "driver/pcnt.h"
  2. #define COUNT_PIN       17  // the gpio I output pulses on which I want to count
  3. pcnt_config_t pcnt_config = {
  4.         .pulse_gpio_num = COUNT_PIN,    // set gpio for pulse input gpio
  5.         .ctrl_gpio_num = -1,            // no gpio for control
  6.         .lctrl_mode = PCNT_MODE_KEEP,   // when control signal is low, keep the primary counter mode
  7.         .hctrl_mode = PCNT_MODE_KEEP,   // when control signal is high, keep the primary counter mode
  8.         .pos_mode = PCNT_COUNT_INC,     // increment the counter on positive edge
  9.         .neg_mode = PCNT_COUNT_DIS,     // do nothing on falling edge
  10.         .counter_h_lim = 2500,
  11.         .counter_l_lim = 0,
  12.         .unit = PCNT_UNIT_0,               /*!< PCNT unit number */
  13.         .channel = PCNT_CHANNEL_0
  14.     };
  15. // https://esp32.com/viewtopic.php?t=6737
  16. pcnt_isr_handle_t user_isr_handle = NULL; //user's ISR service handle
  17. unsigned int test_count = 2500;
  18. void setup() {
  19.     // possible problem, esp_idf mentions pin mode IN_OUT but that is not in arduino. So I tried shorting
  20.     // my pulse output pin to adjacent gpio and look at this input. Still didn't work for me.
  21.     pinMode(COUNT_PIN,INPUT);
  22.     pcnt_unit_config(&pcnt_config);        //init unit
  23.     pcnt_set_filter_value(PCNT_UNIT_0, test_count);
  24.     pcnt_filter_enable(PCNT_UNIT_0);
  25.     /* Register ISR handler and enable interrupts for PCNT unit */
  26.     pcnt_isr_register(pcnt_intr_handler, NULL, 0, &user_isr_handle);
  27.     pcnt_intr_enable(PCNT_UNIT_0);
  28.  
  29.     /* Everything is set up, now go to counting */
  30.     pcnt_counter_resume(PCNT_UNIT_0);
  31. }
  32.  
  33. static void pcnt_intr_handler(void *arg) {
  34.     test = false;
  35.     if(user_isr_handle) {
  36.         //Free the ISR service handle.
  37.         esp_intr_free(user_isr_handle);
  38.         user_isr_handle = NULL;
  39.     }
  40. }

User avatar
jgustavoam
Posts: 165
Joined: Thu Feb 01, 2018 2:43 pm
Location: Belo Horizonte , Brazil
Contact:

Re: How to use "Pulse Counter" module usage under Arduino framework

Postby jgustavoam » Wed Jun 17, 2020 2:28 am

Hi,
This code (part of sketch) was tested and running OK. Comments in portuguese.

Code: Select all

#include "driver/pcnt.h"                                  // Biblioteca de pulse count

#define PCNT_FREQ_UNIT      PCNT_UNIT_0                   // Unidade de Pulse Count 0 
#define PCNT_H_LIM_VAL      10000                        // Limite superior de contagem 32767 
#define PCNT_INPUT_SIG_IO   4                             // Pulse Input GPIO 4 

int16_t contador = 0;                                     // Contador de pulsos - valor max 65536
int contadorOverflow;                                     // Contador de overflow do Contador de Pulsos
float frequencia = 0;                                     // Frequencia medida
String unidade;                                           // Unidade de medida da escala
unsigned long tempo;                                      // base de tempo da medida dos pulsos
int prescaler;                                            // Divisor de frequencia do Timer
bool contadorOK = false;

pcnt_isr_handle_t user_isr_handle = NULL;                 // handler da interrupção - não usado
hw_timer_t * timer = NULL;                                // Instancia do timer

//------------------------------------------------------------------------------------
void IRAM_ATTR overflowContador(void *arg)                // Rotina de interrupção de overflow do Contador
{
  contadorOverflow = contadorOverflow + 1;                // soma contador de overflow
  PCNT.int_clr.val = BIT(PCNT_FREQ_UNIT);                 // Limpa flag de overflow
  pcnt_counter_clear(PCNT_FREQ_UNIT);                     // Zera e reseta o Contador de Pulsos
}

//------------------------------------------------------------
void iniciaContadorPulsos ()
{
  pcnt_config_t pcntFreqConfig = { };                        // Instancia do Contador de Pulsos
  pcntFreqConfig.pulse_gpio_num = PCNT_INPUT_SIG_IO;         // Port de entrada dos pulsos  = GPIO 4
  pcntFreqConfig.pos_mode = PCNT_COUNT_INC;                  // Conta na subida do pulso
  pcntFreqConfig.counter_h_lim = PCNT_H_LIM_VAL;             // Limite maximo de contagem
  pcntFreqConfig.unit = PCNT_FREQ_UNIT;                      // Unidade 0 do Contador de Pulsos
  pcntFreqConfig.channel = PCNT_CHANNEL_0;                   // Canal 0 da Unidade 0 Contador de Pulsos
  pcnt_unit_config(&pcntFreqConfig);                         // configura os registradores do Contador de Pulsos

  pcnt_counter_pause(PCNT_FREQ_UNIT);                        // pausa o Contador de Pulsos
  pcnt_counter_clear(PCNT_FREQ_UNIT);                        // Zera e reseta o Contador de Pulsos

  pcnt_event_enable(PCNT_FREQ_UNIT, PCNT_EVT_H_LIM);         // Ativa evento - interrupção no limite maximo de contagem
  pcnt_isr_register(overflowContador, NULL, 0, &user_isr_handle);  // configura registrador da interrupção
  pcnt_intr_enable(PCNT_FREQ_UNIT);                          // habilita as interrupções do Contador de Pulsos

  pcnt_counter_resume(PCNT_FREQ_UNIT);                       // reinicia o Contador de Pulsos
}

//------------------------------------------------------------
void baseTempo()                                            // Rotina de leitura do contador de pulsos (Base de tempo)
{
  pcnt_get_counter_value(PCNT_FREQ_UNIT, &contador);        // obtem o valor do contador de pulsos - valor max 16 bits
  contadorOverflow = 0;                                     // zera o contador de overflow
  pcnt_counter_clear(PCNT_FREQ_UNIT);                       // Zera e reseta o Contador de Pulsos
  contadorOK = true;
}
Retired IBM Brasil
Electronic hobbyist since 1976.

jorail
Posts: 1
Joined: Mon May 10, 2021 10:16 pm

Re: How to use "Pulse Counter" module usage under Arduino framework

Postby jorail » Mon May 10, 2021 10:40 pm

I successfully used the code of jgustavoam! Thanks a lot. This was the only clear source on Arduino IDE and ESP32 pulse counter, which I found on the net. I was happy to understand your Portuguese with my Spanish, so that your comments plus the explanations from
https://docs.espressif.com/projects/esp ... ing-pulses added up to a lot of sense.

:D

#include "driver/pcnt.h" worked without any additional step in Arduino IDE after having installed the
arduino-esp32-master
i.e. in my case the file was already there at the following path C:\Users\User\Documents\Arduino\hardware\arduino-esp32-master\tools\sdk\include\driver\driver\pcnt.h
when in the Arduino IDE properties the sketchbook storage location had been set to C:\Users\User\Documents\Arduino

My remaining problem for the correct pulse count are glitches. Significant overcounting improved somewhat, but not completely, after defining the pulse counter filter: https://docs.espressif.com/projects/esp ... ing-pulses

Increasing the filter value to 1000, i.e. close to maximum, did not yet solve the problem. Any further idea would be helpful.

:idea: :?:

For the Forum find here my conversion of the code of jgustavoam to English:

Code: Select all

  
  #include "driver/pcnt.h"  // ESP32 library for pulse count
  // e.g. stored in following path C:\Users\User\Documents\Arduino\hardware\arduino-esp32-master\tools\sdk\include\driver\driver\pcnt.h
  // when in the Arduino IDE properties the sketchbook storage location is set to C:\Users\User\Documents\Arduino
  
  #define PCNT_FREQ_UNIT      PCNT_UNIT_0                      // select ESP32 pulse counter unit 0 (out of 0 to 7 indipendent counting units)
                                                               // https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/pcnt.html

  int SPEED_IR_INPUT_PIN = 15;                                 // Input D15 = signal from IR-diode for pulse counter
  bool _flag = 0;  // only for testing ####################################
  
  int16_t PulseCounter =     0;                                // pulse counter, max. value is 65536
  int OverflowCounter =      0;                                // pulse counter overflow counter
  int PCNT_H_LIM_VAL =       10000;                            // upper limit of counting  max. 32767, write +1 to overflow counter, when reached 
  uint16_t PCNT_FILTER_VAL=  1000;                             // filter (damping, inertia) value for avoiding glitches in the count, max. 1023

  // not in use, copy from example code ########################################
  //float frequencia = 0;                                      // Frequencia medida
  //String unidade;                                            // Unidade de medida da escala
  //unsigned long tempo;                                       // base de tempo da medida dos pulsos
  //int prescaler;                                             // frequency devider of timer
  //bool conterOK = false;
  
  pcnt_isr_handle_t user_isr_handle = NULL;                    // interrupt handler - not used
  hw_timer_t * timer = NULL;                                   // Instancia do timer

  void IRAM_ATTR CounterOverflow(void *arg) {                  // Interrupt for overflow of pulse counter
    OverflowCounter = OverflowCounter + 1;                     // increase overflow counter
    PCNT.int_clr.val = BIT(PCNT_FREQ_UNIT);                    // clean overflow flag
    pcnt_counter_clear(PCNT_FREQ_UNIT);                        // zero and reset of pulse counter unit
  }
  
  void initPulseCounter (){                                    // initialise pulse counter
    pcnt_config_t pcntFreqConfig = { };                        // Instance of pulse counter
    pcntFreqConfig.pulse_gpio_num = SPEED_IR_INPUT_PIN;        // pin assignment for pulse counter = GPIO 15
    pcntFreqConfig.pos_mode = PCNT_COUNT_INC;                  // count rising edges (=change from low to high logical level) as pulses
    pcntFreqConfig.counter_h_lim = PCNT_H_LIM_VAL;             // set upper limit of counting 
    pcntFreqConfig.unit = PCNT_FREQ_UNIT;                      // select ESP32 pulse counter unit 0
    pcntFreqConfig.channel = PCNT_CHANNEL_0;                   // select channel 0 of pulse counter unit 0
    pcnt_unit_config(&pcntFreqConfig);                         // configur rigisters of the pulse counter
  
    pcnt_counter_pause(PCNT_FREQ_UNIT);                        // pause puls counter unit
    pcnt_counter_clear(PCNT_FREQ_UNIT);                        // zero and reset of pulse counter unit
  
    pcnt_event_enable(PCNT_FREQ_UNIT, PCNT_EVT_H_LIM);         // enable event for interrupt on reaching upper limit of counting
    pcnt_isr_register(CounterOverflow, NULL, 0, &user_isr_handle);  // configure register overflow interrupt handler
    pcnt_intr_enable(PCNT_FREQ_UNIT);                          // enable overflow interrupt

    pcnt_set_filter_value(PCNT_FREQ_UNIT, PCNT_FILTER_VAL);    // set damping, inertia 
    pcnt_filter_enable(PCNT_FREQ_UNIT);                        // enable counter glitch filter (damping)
  
    pcnt_counter_resume(PCNT_FREQ_UNIT);                       // resume counting on pulse counter unit
  }
  
  void Read_Reset_PCNT() {                                     // function for reading pulse counter (for timer)
    pcnt_get_counter_value(PCNT_FREQ_UNIT, &PulseCounter);     // get pulse counter value - maximum value is 16 bits

    // resetting counter as if example, delet for application in PiedPiperS
    OverflowCounter = 0;                                       // set overflow counter to zero
    pcnt_counter_clear(PCNT_FREQ_UNIT);                        // zero and reset of pulse counter unit
    //conterOK = true;                                         // not in use, copy from example code ########################################
  }

  void Read_PCNT() {                                           // function for reading pulse counter (for timer)
    pcnt_get_counter_value(PCNT_FREQ_UNIT, &PulseCounter);     // get pulse counter value - maximum value is 16 bits
  }

k7ilo1
Posts: 5
Joined: Tue May 10, 2022 4:01 am

Re: How to use "Pulse Counter" module usage under Arduino framework

Postby k7ilo1 » Tue May 10, 2022 4:07 am

Hello all.

I am trying to use either of the codes mentioned here but continue to get a "PCNT was not declared in this scope" for the line
PCNT.int_clr.val = BIT(PCNT_FREQ_UNIT); // clean overflow flag.

Anything specific I need to look for to rectify this issue?

Thanks

Kilo

k7ilo1
Posts: 5
Joined: Tue May 10, 2022 4:01 am

Re: How to use "Pulse Counter" module usage under Arduino framework

Postby k7ilo1 » Wed May 11, 2022 10:05 pm

Never-mind everyone, if there is anyone even monitoring this.
I found the answer after days on someone else's website about this counter.
For those that might run across this in days or years to come with the same issue, adding #include "soc/pcnt_struct.h" did the trick for me so now it will at least compile without errors.
Now to see if this serves the purpose for my project.

Thanks for the help anyway.

Kilo

k7ilo1
Posts: 5
Joined: Tue May 10, 2022 4:01 am

Re: How to use "Pulse Counter" module usage under Arduino framework

Postby k7ilo1 » Thu May 19, 2022 8:58 pm

Now that I have found the answer to my original issue and determined that this is the closes to what I need for my project, I need to ask a question based on specifically my projects needs.

My project was originally developed using the Arduino Nano which has a 16bit timer used as a counter. The timer, as a counter is trigger every 1 second with an external interrupt with my gps pps pulse and the counter counts how many pulses happen within that 1 second period for 40 seconds, equally 100Mhz or 1 million or so pulses, then some other math is done with those results of the to pulses and the overruns since the counter has a limit of 65536 events. Its actually used to gps calibrate my VFO built using W3PM's VFO project. I really want to transition this code over to the esp32 because of its power, memory and speed due to my modifications of the LCD being used which i have everything else working. I just need this function to work in order to restore the gps calibration.
PCNT seems to be the closest of what I need because it seems the esp32 does not like the direct 2.5Mhz input as an external interrupt because it continues to reboot the esp32.

My main question is, is there a way to have PCNT to continuously count the pulses and I have my external interrupt from the PPS input take a sample every 1 second for 40 seconds and reset for the next calibration period?

The original interrupt that is triggered every 1 second by the gps is below. I think you can figure out most of it and for those who have used the timers/counters on a Nano can see that the variables are the timers variables for the pulse counts and the overruns

An explanation of how the timer is also copied/pasted from the original author of how the math is done in the interrupt.

Thanks all for any help....

void PPSinterrupt()
{
s_GpsOneSecTick = true; // New second by GPS.

tcount++;
if (tcount == 4) // Start counting the 2.5 MHz signal from Si5351A CLK0
{
TCCR1B = 7; //Clock on rising edge of pin 5
}
else if (tcount == 44) //The 40 second gate time elapsed - stop counting
{
TCCR1B = 0; //Turn off counter
XtalFreq = mult * 0x10000 + TCNT1; //Calculate correction factor
TCNT1 = 0; //Reset count to zero
mult = 0;
tcount = 0; //Reset the seconds counter
}
}

Explanation of Counter...

Timer1 is configured as a counter with an input of 2.5MHz in the setup() function. It can only count a maximum of 65536 events. Every time it hits the maximum count it triggers an overflow interrupt 'ISR(TIMER1_OVF_vect)' which counts the number of overflows.

The counter is turned on and off every 40 seconds by 'tcount' in the PPSinterrupt(). Therefore 40 x 2.5MHz = 100 MHz. Remember timer1 can only count to 65536 therefore 100 MHz / 65536 = 1525.87891. Timer1 overflowed 1525 times as stored in the 'mult' variable. The remainder (0.87891 x 65536 = 57600) is stored as TCNT1.

Thus:

XtalFreq = mult * 0x10000 + TCNT1;
XtalFreq = 1525 x 65536 + 57600
XtalFreq = 100,000,000

The 100,000,000is divided by 4 elsewhere in the sketch to give the crystal frequency of 25,000,000.

rekza_
Posts: 1
Joined: Tue Jun 07, 2022 1:56 pm

Re: How to use "Pulse Counter" module usage under Arduino framework

Postby rekza_ » Tue Jun 07, 2022 1:59 pm

i've tried input the program like yours but mine is keep gettin error. it says: too few arguments to function 'void overflowContador(void*)' . how to fix it? i'm a newbie on esp32 controller

Who is online

Users browsing this forum: No registered users and 105 guests