Best Frequency Meter ever made with ESP32 - awesome!

rin67630
Posts: 139
Joined: Sun Mar 11, 2018 5:13 pm

Re: Best Frequency Meter ever made with ESP32 - awesome!

Postby rin67630 » Mon Jul 08, 2024 6:28 am

boarchuz wrote:
Sun Jul 07, 2024 4:30 pm
The hardware design guidelines recommend <=10ppm for the 40MHz XTAL, so you can be confident that any espressif module and any reputable 3rd party product will have much less error than "several minutes a day".
In sleep modes...
No sleep mode on my devices and my real experience with ESP8266 / ESP32, not theory.

A frequency meter that deserves the name "Best Frequency Meter ever made with ESP32" should at least be two orders of magnitude preciser than average Xtals, else it is a frequency "display".

jhsrennie
Posts: 10
Joined: Sat Jul 13, 2024 6:16 pm

Re: Best Frequency Meter ever made with ESP32 - awesome!

Postby jhsrennie » Mon Jul 15, 2024 3:32 pm

Solved. I needed to #include "esp32/rom/gpio.h". Apparently this changed going from v2 to v3.

-------------------------------------------------------------------------------------------------------------------

Thanks for posting this :-)

When I try to compile it I get errors:

Code: Select all

D:\rhs\ESP32\Frequency\Frequency.ino:151:3: error: 'gpio_pad_select_gpio' was not declared in this scope; did you mean 'esp_rom_gpio_pad_select_gpio'?
  151 |   gpio_pad_select_gpio(OUTPUT_CONTROL_GPIO);                              // Set GPIO pad
      |   ^~~~~~~~~~~~~~~~~~~~
      |   esp_rom_gpio_pad_select_gpio
D:\rhs\ESP32\Frequency\Frequency.ino:159:3: error: 'gpio_matrix_in' was not declared in this scope
  159 |   gpio_matrix_in(PCNT_INPUT_SIG_IO, SIG_IN_FUNC226_IDX, false);           // Set GPIO matrin IN - Freq Meter input
      |   ^~~~~~~~~~~~~~
D:\rhs\ESP32\Frequency\Frequency.ino:160:3: error: 'gpio_matrix_out' was not declared in this scope; did you mean 'gpio_iomux_out'?
  160 |   gpio_matrix_out(IN_BOARD_LED, SIG_IN_FUNC226_IDX, false, false);        // Set GPIO matrix OUT - to inboard LED
      |   ^~~~~~~~~~~~~~~
The first error is easily fixed as gpio_pad_select_gpio has indeed been renamed to esp_rom_gpio_pad_select_gpio in the latest version of the Arduino IDE. However I don't know how to fix the gpio_matrix_in and gpio_matrix_out errors. Have they been renamed as well, or do I need an additional include file?

Araqel
Posts: 4
Joined: Wed Apr 29, 2020 4:39 pm

Re: Best Frequency Meter ever made with ESP32 - awesome!

Postby Araqel » Wed Jul 17, 2024 12:09 pm

jgustavoam wrote:
Tue Jun 04, 2024 8:55 pm
Hi Araquel,
The code created by the topic is for Arduino. Please inform which IDF code you are using.
It's this one? Arduino Release v3.0.0 based on ESP-IDF v5.1.4 (latest)?
Please report the error message.

If you want to test compiling with the ESP32 IDF, try this:
https://github.com/Gustavomurta/ESP32_f ... 2freqMeter
Hello Sir.
Truly said I lost hope of getting an answer from you and I am very glad that you still manage the given topic.
Please, review the compilation log beneath (ESP32 core support version for now is the v3.0.2)

Code: Select all

In file included from C:\Users\woodl\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-bd2b9390ef\esp32/include/xtensa/include/xt_utils.h:16,
                 from C:\Users\woodl\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-bd2b9390ef\esp32/include/esp_hw_support/include/esp_cpu.h:16,
                 from C:\Users\woodl\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-bd2b9390ef\esp32/include/esp_hw_support/include/spinlock.h:11,
                 from C:\Users\woodl\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-bd2b9390ef\esp32/include/freertos/FreeRTOS-Kernel/portable/xtensa/include/freertos/portmacro.h:74,
                 from C:\Users\woodl\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-bd2b9390ef\esp32/include/freertos/FreeRTOS-Kernel/include/freertos/portable.h:59,
                 from C:\Users\woodl\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-bd2b9390ef\esp32/include/freertos/FreeRTOS-Kernel/include/freertos/FreeRTOS.h:71,
                 from C:\Users\woodl\AppData\Local\Arduino15\packages\esp32\hardware\esp32\3.0.2\cores\esp32/Arduino.h:33,
                 from C:\Users\woodl\AppData\Local\Temp\arduino\sketches\6EF41278B30E93579C9865EAA3D2B7AE\sketch\brazil_freq.ino.cpp:1:
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino: In function 'void pcnt_intr_handler(void*)':
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:28:31: error: 'PCNT_UNIT_0' was not declared in this scope
   28 | #define PCNT_COUNT_UNIT       PCNT_UNIT_0                                 // Set Pulse Counter Unit - 0
      |                               ^~~~~~~~~~~
C:\Users\woodl\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-bd2b9390ef\esp32/include/esp_common/include/esp_bit_defs.h:79:42: note: in definition of macro 'BIT'
   79 | #define BIT(nr)                 (1UL << (nr))
      |                                          ^~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:107:26: note: in expansion of macro 'PCNT_COUNT_UNIT'
  107 |   PCNT.int_clr.val = BIT(PCNT_COUNT_UNIT);                                // Clear Pulse Counter interrupt bit
      |                          ^~~~~~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino: In function 'void init_PCNT()':
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:114:3: error: 'pcnt_config_t' was not declared in this scope; did you mean 'uart_config_t'?
  114 |   pcnt_config_t pcnt_config = { };                                        // PCNT unit instance
      |   ^~~~~~~~~~~~~
      |   uart_config_t
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:116:3: error: 'pcnt_config' was not declared in this scope
  116 |   pcnt_config.pulse_gpio_num = PCNT_INPUT_SIG_IO;                         // Pulse input GPIO 34 - Freq Meter Input
      |   ^~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:28:31: error: 'PCNT_UNIT_0' was not declared in this scope
   28 | #define PCNT_COUNT_UNIT       PCNT_UNIT_0                                 // Set Pulse Counter Unit - 0
      |                               ^~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:118:22: note: in expansion of macro 'PCNT_COUNT_UNIT'
  118 |   pcnt_config.unit = PCNT_COUNT_UNIT;                                     // Unidade de contagem PCNT - 0
      |                      ^~~~~~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:29:31: error: 'PCNT_CHANNEL_0' was not declared in this scope; did you mean 'LEDC_CHANNEL_0'?
   29 | #define PCNT_COUNT_CHANNEL    PCNT_CHANNEL_0                              // Set Pulse Counter channel - 0
      |                               ^~~~~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:119:25: note: in expansion of macro 'PCNT_COUNT_CHANNEL'
  119 |   pcnt_config.channel = PCNT_COUNT_CHANNEL;                               // PCNT unit number - 0
      |                         ^~~~~~~~~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:121:26: error: 'PCNT_COUNT_INC' was not declared in this scope; did you mean 'PCNT_COUNT_UNIT'?
  121 |   pcnt_config.pos_mode = PCNT_COUNT_INC;                                  // PCNT positive edge count mode - inc
      |                          ^~~~~~~~~~~~~~
      |                          PCNT_COUNT_UNIT
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:123:28: error: 'PCNT_MODE_DISABLE' was not declared in this scope; did you mean 'GPIO_MODE_DISABLE'?
  123 |   pcnt_config.lctrl_mode = PCNT_MODE_DISABLE;                             // PCNT low control mode - disable
      |                            ^~~~~~~~~~~~~~~~~
      |                            GPIO_MODE_DISABLE
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:124:28: error: 'PCNT_MODE_KEEP' was not declared in this scope
  124 |   pcnt_config.hctrl_mode = PCNT_MODE_KEEP;                                // PCNT high control mode - won't change counter mode
      |                            ^~~~~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:125:3: error: 'pcnt_unit_config' was not declared in this scope; did you mean 'pcnt_unit_config_t'?
  125 |   pcnt_unit_config(&pcnt_config);                                         // Initialize PCNT unit
      |   ^~~~~~~~~~~~~~~~
      |   pcnt_unit_config_t
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:127:3: error: 'pcnt_counter_pause' was not declared in this scope
  127 |   pcnt_counter_pause(PCNT_COUNT_UNIT);                                    // Pause PCNT unit
      |   ^~~~~~~~~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:128:3: error: 'pcnt_counter_clear' was not declared in this scope
  128 |   pcnt_counter_clear(PCNT_COUNT_UNIT);                                    // Clear PCNT unit
      |   ^~~~~~~~~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:130:38: error: 'PCNT_EVT_H_LIM' was not declared in this scope
  130 |   pcnt_event_enable(PCNT_COUNT_UNIT, PCNT_EVT_H_LIM);                     // Enable event to watch - max count
      |                                      ^~~~~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:130:3: error: 'pcnt_event_enable' was not declared in this scope; did you mean 'pcnt_unit_enable'?
  130 |   pcnt_event_enable(PCNT_COUNT_UNIT, PCNT_EVT_H_LIM);                     // Enable event to watch - max count
      |   ^~~~~~~~~~~~~~~~~
      |   pcnt_unit_enable
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:131:3: error: 'pcnt_isr_register' was not declared in this scope; did you mean 'ledc_isr_register'?
  131 |   pcnt_isr_register(pcnt_intr_handler, NULL, 0, NULL);                    // Setup Register ISR handler
      |   ^~~~~~~~~~~~~~~~~
      |   ledc_isr_register
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:132:3: error: 'pcnt_intr_enable' was not declared in this scope; did you mean 'pcnt_unit_enable'?
  132 |   pcnt_intr_enable(PCNT_COUNT_UNIT);                                      // Enable interrupts for PCNT unit
      |   ^~~~~~~~~~~~~~~~
      |   pcnt_unit_enable
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:134:3: error: 'pcnt_counter_resume' was not declared in this scope
  134 |   pcnt_counter_resume(PCNT_COUNT_UNIT);                                   // Resume PCNT unit - starts count
      |   ^~~~~~~~~~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino: In function 'void read_PCNT(void*)':
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:28:31: error: 'PCNT_UNIT_0' was not declared in this scope
   28 | #define PCNT_COUNT_UNIT       PCNT_UNIT_0                                 // Set Pulse Counter Unit - 0
      |                               ^~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:141:26: note: in expansion of macro 'PCNT_COUNT_UNIT'
  141 |   pcnt_get_counter_value(PCNT_COUNT_UNIT, &pulses);                       // Read Pulse Counter value
      |                          ^~~~~~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:141:3: error: 'pcnt_get_counter_value' was not declared in this scope
  141 |   pcnt_get_counter_value(PCNT_COUNT_UNIT, &pulses);                       // Read Pulse Counter value
      |   ^~~~~~~~~~~~~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino: In function 'void init_frequencyMeter()':
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:151:3: error: 'gpio_pad_select_gpio' was not declared in this scope; did you mean 'esp_rom_gpio_pad_select_gpio'?
  151 |   gpio_pad_select_gpio(OUTPUT_CONTROL_GPIO);                              // Set GPIO pad
      |   ^~~~~~~~~~~~~~~~~~~~
      |   esp_rom_gpio_pad_select_gpio
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:159:3: error: 'gpio_matrix_in' was not declared in this scope
  159 |   gpio_matrix_in(PCNT_INPUT_SIG_IO, SIG_IN_FUNC226_IDX, false);           // Set GPIO matrin IN - Freq Meter input
      |   ^~~~~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:160:3: error: 'gpio_matrix_out' was not declared in this scope; did you mean 'gpio_iomux_out'?
  160 |   gpio_matrix_out(IN_BOARD_LED, SIG_IN_FUNC226_IDX, false, false);        // Set GPIO matrix OUT - to inboard LED
      |   ^~~~~~~~~~~~~~~
      |   gpio_iomux_out
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino: In function 'void loop()':
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:28:31: error: 'PCNT_UNIT_0' was not declared in this scope
   28 | #define PCNT_COUNT_UNIT       PCNT_UNIT_0                                 // Set Pulse Counter Unit - 0
      |                               ^~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:213:24: note: in expansion of macro 'PCNT_COUNT_UNIT'
  213 |     pcnt_counter_clear(PCNT_COUNT_UNIT);                                // Clear Pulse Counter
      |                        ^~~~~~~~~~~~~~~
C:\Users\woodl\Documents\Arduino\brazil_freq\brazil_freq.ino:213:5: error: 'pcnt_counter_clear' was not declared in this scope
  213 |     pcnt_counter_clear(PCNT_COUNT_UNIT);                                // Clear Pulse Counter
      |     ^~~~~~~~~~~~~~~~~~

exit status 1

Compilation error: 'PCNT_UNIT_0' was not declared in this scope

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

Re: Best Frequency Meter ever made with ESP32 - awesome!

Postby jgustavoam » Mon Jul 22, 2024 2:10 am

ESP32 FREQUENCY METER - NEW VERSION:

For the happiness of those interested in the project, I am posting an update to the code and schematics.
I chose another library for the LCD I2C Display. Simpler and functional with the new ESP32 firmware.
I removed the option to connect directly to the LCD, as it was confusing for some.
I changed the inclusion of some ESP32 libraries, to make them compatible with the new ESP32 firmware.

Connect the oscillator output to the frequency meter input to perform tests.
Use the serial console (Arduino IDE - 115200 bps) to change the oscillator frequency for testing.
Use resistors R1 and R2 to equalize the voltage level on the I2C bus to 3.3V used in ESP32.

If you are in doubt about the I2C address of your I2C LCD module, use the I2C scanner.
ESP32 - I2C Scanner using Arduino IDE
viewtopic.php?f=18&t=4742

Add this library using the Arduino IDE library manager
https://github.com/hasenradball/LCD-I2C
LCD I2C library.JPG
LCD I2C library.JPG (14.99 KiB) Viewed 5036 times

ESP32 Frequency Meter
ESP32 DevKit 38 pins + I2C PCF8574 LCD
Arduino IDE 2.3.2 / ESP32 Arduino V 3.0.2
Gustavo Murta e Rui Viana august/2020 - updated 2024/07/19


Github - project Frequency Meter 2024
https://github.com/Gustavomurta/ESP32_f ... Meter_2024

Code: Select all

// ESP32 Frequency Meter
// ESP32 DevKit 38 pins + I2C PCF8574 LCD
// Arduino IDE 2.3.2   / ESP32 Arduino V 3.0.2
// Gustavo Murta e Rui Viana august/2020 - updated 2024/07/19
// https://blog.eletrogate.com/esp32-frequencimetro-de-precisao
// https://www.esp32.com/viewtopic.php?f=19&t=17018
// LCD I2C SDA - GPIO_21
// LCD I2C SCL - GPIO_22

#include "stdio.h"            // Library STDIO
#include "driver/ledc.h"      // Library ESP32 LEDC
#include "driver/pcnt.h"      // Library ESP32 PCNT
#include "soc/pcnt_struct.h"  // Library ESP32 PCNT
#include "esp32/rom/gpio.h"   // Library ESP32 GPIO

#include <LCD-I2C.h>       // Library LCD with PCF8574
LCD_I2C lcd(0x3F, 16, 2);  // Instance LCD I2C - address x3F

#define PCNT_COUNT_UNIT PCNT_UNIT_0        // Set Pulse Counter Unit - 0
#define PCNT_COUNT_CHANNEL PCNT_CHANNEL_0  // Set Pulse Counter channel - 0

#define PCNT_INPUT_SIG_IO GPIO_NUM_34    // Set Pulse Counter input - Freq Meter Input GPIO 34
#define LEDC_HS_CH0_GPIO GPIO_NUM_33     // LEDC output - pulse generator - GPIO_33
#define PCNT_INPUT_CTRL_IO GPIO_NUM_35   // Set Pulse Counter Control GPIO pin - HIGH = count up, LOW = count down
#define OUTPUT_CONTROL_GPIO GPIO_NUM_32  // Timer output control port - GPIO_32
#define PCNT_H_LIM_VAL overflow          // Overflow of Pulse Counter

#define IN_BOARD_LED GPIO_NUM_2  // ESP32 native LED - GPIO 2

bool flag = true;                // Flag to enable print frequency reading
uint32_t overflow = 20000;       // Max Pulse Counter value 20000
int16_t pulses = 0;              // Pulse Counter value
uint32_t multPulses = 0;         // Number of PCNT counter overflows
uint32_t sample_time = 1000000;  // Sample time of 1 second to count pulses (change the value to calibrate frequency meter)
uint32_t osc_freq = 16000;       // Oscillator frequency - initial 16000 Hz (may be 1 Hz to 40 MHz)
uint32_t mDuty = 0;              // Duty value
uint32_t resolution = 0;         // Resolution value of Oscillator
float frequency = 0;             // frequency value
char buf[32];                    // Buffer

esp_timer_create_args_t create_args;  // Create an esp_timer instance
esp_timer_handle_t timer_handle;      // Create an single timer

portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;  // portMUX_TYPE to do synchronism

//----------------------------------------------------------------------------------------
void setup() {
  Serial.begin(115200);                                  // Serial Console Arduino 115200 Bps
  Serial.println(" Input the Frequency - 1 to 40 MHz");  // Console print

  lcd.begin();
  lcd.display();
  lcd.backlight();
  lcd.print("  Frequency:");  // LCD print
  init_frequencyMeter();      // Initialize Frequency Meter
}

//----------------------------------------------------------------------------
void init_osc_freq()  // Initialize Oscillator to test Freq Meter
{
  resolution = (log(80000000 / osc_freq) / log(2)) / 2;  // Calc of resolution of Oscillator
  if (resolution < 1) resolution = 1;                    // set min resolution
  Serial.println(resolution);                            // Print
  mDuty = (pow(2, resolution)) / 2;                      // Calc of Duty Cycle 50% of the pulse
  Serial.println(mDuty);                                 // Print

  ledc_timer_config_t ledc_timer = {};  // LEDC timer config instance

  ledc_timer.duty_resolution = ledc_timer_bit_t(resolution);  // Set resolution
  ledc_timer.freq_hz = osc_freq;                              // Set Oscillator frequency
  ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;               // Set high speed mode
  ledc_timer.timer_num = LEDC_TIMER_0;                        // Set LEDC timer index - 0
  ledc_timer_config(&ledc_timer);                             // Set LEDC Timer config

  ledc_channel_config_t ledc_channel = {};  // LEDC Channel config instance

  ledc_channel.channel = LEDC_CHANNEL_0;           // Set HS Channel - 0
  ledc_channel.duty = mDuty;                       // Set Duty Cycle 50%
  ledc_channel.gpio_num = LEDC_HS_CH0_GPIO;        // LEDC Oscillator output GPIO 33
  ledc_channel.intr_type = LEDC_INTR_DISABLE;      // LEDC Fade interrupt disable
  ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE;  // Set LEDC high speed mode
  ledc_channel.timer_sel = LEDC_TIMER_0;           // Set timer source of channel - 0
  ledc_channel_config(&ledc_channel);              // Config LEDC channel
}

//----------------------------------------------------------------------------------
static void IRAM_ATTR pcnt_intr_handler(void *arg)  // Counting overflow pulses
{
  portENTER_CRITICAL_ISR(&timerMux);        // disabling the interrupts
  multPulses++;                             // increment Overflow counter
  PCNT.int_clr.val = BIT(PCNT_COUNT_UNIT);  // Clear Pulse Counter interrupt bit
  portEXIT_CRITICAL_ISR(&timerMux);         // enabling the interrupts
}

//----------------------------------------------------------------------------------
void init_PCNT(void)  // Initialize and run PCNT unit
{
  pcnt_config_t pcnt_config = {};  // PCNT unit instance

  pcnt_config.pulse_gpio_num = PCNT_INPUT_SIG_IO;  // Pulse input GPIO 34 - Freq Meter Input
  pcnt_config.ctrl_gpio_num = PCNT_INPUT_CTRL_IO;  // Control signal input GPIO 35
  pcnt_config.unit = PCNT_COUNT_UNIT;              // Unidade de contagem PCNT - 0
  pcnt_config.channel = PCNT_COUNT_CHANNEL;        // PCNT unit number - 0
  pcnt_config.counter_h_lim = PCNT_H_LIM_VAL;      // Maximum counter value - 20000
  pcnt_config.pos_mode = PCNT_COUNT_INC;           // PCNT positive edge count mode - inc
  pcnt_config.neg_mode = PCNT_COUNT_INC;           // PCNT negative edge count mode - inc
  pcnt_config.lctrl_mode = PCNT_MODE_DISABLE;      // PCNT low control mode - disable
  pcnt_config.hctrl_mode = PCNT_MODE_KEEP;         // PCNT high control mode - won't change counter mode
  pcnt_unit_config(&pcnt_config);                  // Initialize PCNT unit

  pcnt_counter_pause(PCNT_COUNT_UNIT);  // Pause PCNT unit
  pcnt_counter_clear(PCNT_COUNT_UNIT);  // Clear PCNT unit

  pcnt_event_enable(PCNT_COUNT_UNIT, PCNT_EVT_H_LIM);   // Enable event to watch - max count
  pcnt_isr_register(pcnt_intr_handler, NULL, 0, NULL);  // Setup Register ISR handler
  pcnt_intr_enable(PCNT_COUNT_UNIT);                    // Enable interrupts for PCNT unit

  pcnt_counter_resume(PCNT_COUNT_UNIT);  // Resume PCNT unit - starts count
}

//----------------------------------------------------------------------------------
void read_PCNT(void *p)  // Read Pulse Counter
{
  gpio_set_level(OUTPUT_CONTROL_GPIO, 0);            // Stop counter - output control LOW
  pcnt_get_counter_value(PCNT_COUNT_UNIT, &pulses);  // Read Pulse Counter value
  flag = true;                                       // Change flag to enable print
}

//---------------------------------------------------------------------------------
void init_frequencyMeter() {
  init_osc_freq();  // Initialize Oscillator
  init_PCNT();      // Initialize and run PCNT unit

  gpio_pad_select_gpio(OUTPUT_CONTROL_GPIO);                  // Set GPIO pad
  gpio_set_direction(OUTPUT_CONTROL_GPIO, GPIO_MODE_OUTPUT);  // Set GPIO 32 as output

  create_args.callback = read_PCNT;               // Set esp-timer argument
  esp_timer_create(&create_args, &timer_handle);  // Create esp-timer instance

  gpio_set_direction(IN_BOARD_LED, GPIO_MODE_OUTPUT);  // Set LED inboard as output

  gpio_matrix_in(PCNT_INPUT_SIG_IO, SIG_IN_FUNC226_IDX, false);     // Set GPIO matrin IN - Freq Meter input
  gpio_matrix_out(IN_BOARD_LED, SIG_IN_FUNC226_IDX, false, false);  // Set GPIO matrix OUT - to inboard LED
}

//----------------------------------------------------------------------------------------
char *ultos_recursive(unsigned long val, char *s, unsigned radix, int pos)  // Format an unsigned long (32 bits) into a string
{
  int c;
  if (val >= radix)
    s = ultos_recursive(val / radix, s, radix, pos + 1);
  c = val % radix;
  c += (c < 10 ? '0' : 'a' - 10);
  *s++ = c;
  if (pos % 3 == 0) *s++ = ',';  // decimal separator
  return s;
}
//----------------------------------------------------------------------------------------
char *ltos(long val, char *s, int radix)  // Format an long (32 bits) into a string
{
  if (radix < 2 || radix > 36) {
    s[0] = 0;
  } else {
    char *p = s;
    if (radix == 10 && val < 0) {
      val = -val;
      *p++ = '-';
    }
    p = ultos_recursive(val, p, radix, 0) - 1;
    *p = 0;
  }
  return s;
}

//---------------------------------------------------------------------------------
void loop() {
  if (flag == true)  // If count has ended
  {
    flag = false;                                          // Change flag to disable print
    frequency = (pulses + (multPulses * overflow)) / 2;    // Calculation of frequency
    printf("Frequency : %s", (ltos(frequency, buf, 10)));  // Print frequency with commas
    printf(" Hz \n");                                      // Print unity Hz

    lcd.setCursor(2, 1);                    // Set cursor position - column and row
    lcd.print((ltos(frequency, buf, 10)));  // LCD print frequency
    lcd.print(" Hz              ");         // LCD print unity Hz

    multPulses = 0;  // Clear overflow counter
    // Put your function here, if you want
    delay(100);  // Delay 100 ms
    // Put your function here, if you want

    pcnt_counter_clear(PCNT_COUNT_UNIT);              // Clear Pulse Counter
    esp_timer_start_once(timer_handle, sample_time);  // Initialize High resolution timer (1 sec)
    gpio_set_level(OUTPUT_CONTROL_GPIO, 1);           // Set enable PCNT count
  }

  String inputString = "";  // clear temporary string
  osc_freq = 0;             // Clear oscillator frequency
  while (Serial.available()) {
    char inChar = (char)Serial.read();  // Reads a byte on the console
    inputString += inChar;              // Add char to string
    if (inChar == '\n')                 // If new line (enter)
    {
      osc_freq = inputString.toInt();  // Converts String into integer value
      inputString = "";                // Clear string
    }
  }
  if (osc_freq != 0)  // If some value inputted to oscillator frequency
  {
    init_osc_freq();  // reconfigure ledc function - oscillator
  }
}

Schematic in PDF for download:
ESP32_Frequency_Meter.pdf
(74.84 KiB) Downloaded 139 times
ESP32 Frequency Meter 2024.JPG
ESP32 Frequency Meter 2024.JPG (101.63 KiB) Viewed 5036 times
Open this image in another page in your browser to view at a better size.
Last edited by jgustavoam on Thu Aug 01, 2024 2:52 am, edited 3 times in total.
Retired IBM Brasil
Electronic hobbyist since 1976.

Araqel
Posts: 4
Joined: Wed Apr 29, 2020 4:39 pm

Re: Best Frequency Meter ever made with ESP32 - awesome!

Postby Araqel » Mon Jul 22, 2024 7:26 pm

Greetings. Mr. Gustavo.
Thanks for posting the update. Please note that the compilation gone again with error/warning:

Code: Select all

In file included from C:\Users\woodl\AppData\Local\Temp\.arduinoIDE-unsaved2024622-16084-1nys5hn.k2sy\sketch_jul22a\sketch_jul22a.ino:3:
C:\Users\woodl\AppData\Local\Arduino15\packages\esp32\tools\esp32-arduino-libs\idf-release_v5.1-bd2b9390ef\esp32/include/driver/deprecated/driver/pcnt.h:15:2: warning: #warning "legacy pcnt driver is deprecated, please migrate to use driver/pulse_cnt.h" [-Wcpp]
   15 | #warning "legacy pcnt driver is deprecated, please migrate to use driver/pulse_cnt.h"
      |  ^~~~~~~
To be adequate in environment, please in "File/Preferences/Additional board manager URL's" add these lines:

Code: Select all

http://dl.espressif.com/dl/package_esp32_index.json
https://espressif.github.io/arduino-esp32/package_esp32_dev_index.json
https://espressif.github.io/arduino-esp32/package_esp32_index.json
Waiting for final correction. Thanks in advance.

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

Re: Best Frequency Meter ever made with ESP32 - awesome!

Postby jgustavoam » Mon Jul 22, 2024 9:21 pm

Araqel,

I suggest you use the same version of the Arduino IDE and the new ESP32 firmware as the updated project, to avoid the errors you are encountering. And of course, use the new updated sketch. Here at home, everything has been tested and working!

1) Arduino IDE version 2.3.2
https://www.arduino.cc/en/software

2) ESP32 Arduino V 3.0.2 - Install via Board Manager - This way you won't need to add links to the IDE preference!
it is not necessary = in "File/Preferences/Additional board manager URL's" add these lines.


ESP32 ide arduino.JPG
ESP32 ide arduino.JPG (21.94 KiB) Viewed 4889 times

Arduino IDE installed in my PC:
IDE prefernces.JPG
IDE prefernces.JPG (32.22 KiB) Viewed 4889 times


Look! Error-free compilation!

Code: Select all

Sketch uses 315925 bytes (24%) of program storage space. Maximum is 1310720 bytes.
Global variables use 20768 bytes (6%) of dynamic memory, leaving 306912 bytes for local variables. Maximum is 327680 bytes.
esptool.py v4.6
Serial port COM14
Connecting.....
Chip is ESP32-D0WD-V3 (revision v3.0)
Features: WiFi, BT, Dual Core, 240MHz, VRef calibration in efuse, Coding Scheme None
Crystal is 40MHz
MAC: 3c:e9:0e:85:2f:1c
Uploading stub...
Running stub...
Stub running...
Changing baud rate to 921600
Changed.
Configuring flash size...
Flash will be erased from 0x00001000 to 0x00005fff...
Flash will be erased from 0x00008000 to 0x00008fff...
Flash will be erased from 0x0000e000 to 0x0000ffff...
Flash will be erased from 0x00010000 to 0x0005dfff...
Compressed 19744 bytes to 13604...
Writing at 0x00001000... (100 %)
Wrote 19744 bytes (13604 compressed) at 0x00001000 in 0.4 seconds (effective 370.9 kbit/s)...
Hash of data verified.
Compressed 3072 bytes to 146...
Writing at 0x00008000... (100 %)
Wrote 3072 bytes (146 compressed) at 0x00008000 in 0.1 seconds (effective 420.0 kbit/s)...
Hash of data verified.
Compressed 8192 bytes to 47...
Writing at 0x0000e000... (100 %)
Wrote 8192 bytes (47 compressed) at 0x0000e000 in 0.1 seconds (effective 610.5 kbit/s)...
Hash of data verified.
Compressed 316288 bytes to 179344...
Writing at 0x00010000... (9 %)
Writing at 0x0001c2a7... (18 %)
Writing at 0x0002715f... (27 %)
Writing at 0x0002c712... (36 %)
Writing at 0x0003211c... (45 %)
Writing at 0x000376ca... (54 %)
Writing at 0x0003cda3... (63 %)
Writing at 0x00042251... (72 %)
Writing at 0x0004760e... (81 %)
Writing at 0x0004cffc... (90 %)
Writing at 0x000564d3... (100 %)
Wrote 316288 bytes (179344 compressed) at 0x00010000 in 3.3 seconds (effective 761.7 kbit/s)...
Hash of data verified.

Leaving...
Hard resetting via RTS pin...
Arduino IDE - Serial Console (115200 bps) :

Code: Select all

ets Jul 29 2019 12:21:46

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:1448
load:0x40078000,len:14844
ho 0 tail 12 room 4
load:0x40080400,len:4
load:0x40080404,len:3356
entry 0x4008059c
 Input the Frequency - 1 to 40 MHz
6
32
Frequency : 0 Hz 
Frequency : 16,000 Hz 
Frequency : 16,000 Hz 
Frequency : 16,000 Hz 
Frequency : 16,000 Hz 
Frequency : 16,000 Hz
Retired IBM Brasil
Electronic hobbyist since 1976.

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

Re: Best Frequency Meter ever made with ESP32 - awesome!

Postby jgustavoam » Tue Jul 30, 2024 2:36 am

Users, good news ahead!

In response to requests from some users (Akmin_PP, MotoDan, CP2011) , I am reworking the code to measure two frequencies at the same time (frequency meter with 2 channels). Almost finished!

And responding to a request from Xiaolab, I implemented a variable for calibrating measurements (offset)

Wow, this is getting really good.

Code: Select all

uint calibrator = 1;             // calibrator of frequency reading (may be + or - integer numbers)
frequency_1 = (pulses_1 + (multPulses_1 * (overflow + calibrator))) / 2;  // Calculation of frequency
Example:
Frequency CH0: 38,001 Hz
Frequency CH1: 10,000 Hz
Retired IBM Brasil
Electronic hobbyist since 1976.

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

Re: Best Frequency Meter ever made with ESP32 - awesome!

Postby jgustavoam » Thu Aug 01, 2024 2:23 am

ESP32 FREQUENCY METER - 2 CHANNELS:

I am posting now an upgraded version with 2 channels - under development!!!

There is still a problem with the program - the interrupts of Pulse counter 0 is interfering with the interrupts of Pulse counter 1.
This causes the program to crash, if frequencies are greater than 16,000 Hz. Test both channels of the frequency meter with frequencies below 15999 Hz, only for now.

Maybe the solution is to configure the interrupt priorities of the two counters, but this is only allowed in the new drivers for PC Unit.
I think that to use this new driver version, I will have to make several changes. I am researching a solution.

I implemented a variable for calibrating measurements (offset)- calibrator => calibrator of frequency reading (may be + or - integer numbers).

I chose another library for the LCD I2C Display. Simpler and functional with the new ESP32 firmware.
I removed the option to connect directly to the LCD, as it was confusing for some.
I changed the inclusion of some ESP32 libraries, to make them compatible with the new ESP32 firmware.

Connect the oscillator outputs (0 or 1) to the frequency meter inputs (CH0 or CH1) to perform tests.
Use the serial console (Arduino IDE - 115200 bps) to change the oscillators frequency for testing (same frequency in the both channels for while).
Use resistors R1 and R2 to equalize the voltage level on the I2C bus to 3.3V used in ESP32.

If you are in doubt about the I2C address of your I2C LCD module, use the I2C scanner.
ESP32 - I2C Scanner using Arduino IDE
viewtopic.php?f=18&t=4742

Add this library LCD-I2C using the Arduino IDE library manager
https://github.com/hasenradball/LCD-I2C

Github Project:
https://github.com/Gustavomurta/ESP32_f ... _Meter_2CH

ESP32 API references used in this project:
https://docs.espressif.com/projects/esp ... /ledc.html
https://docs.espressif.com/projects/esp ... timer.html
https://docs.espressif.com/projects/esp ... /pcnt.html

Arduino Code: ESP32 Frequency Meter / 2 Channel - Version 2
ESP32 DevKit + I2C PCF8574 LCD
Arduino IDE version 2.3.2 / ESP32 Arduino Version 3.02

Code: Select all

// ESP32 Frequency Meter / 2 Channel - Version 2
// ESP32 DevKit + I2C PCF8574 LCD
// Arduino IDE 2.3.2   / ESP32 Arduino V 3.02
// Gustavo Murta e Rui Viana august/2020 - 2024/07/29
// https://blog.eletrogate.com/esp32-frequencimetro-de-precisao
// https://www.esp32.com/viewtopic.php?f=19&t=17018
// References:
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/esp_timer.html
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/pcnt.html
// LCD I2C SDA - GPIO_21
// LCD I2C SCL - GPIO_22

#include "stdio.h"            // Library STDIO
#include "driver/ledc.h"      // Library ESP32 LEDC
#include "driver/pcnt.h"      // Library ESP32 PCNT
#include "soc/pcnt_struct.h"  // Library ESP32 PCNT
#include "esp32/rom/gpio.h"   // Library ESP32 GPIO

#include <LCD-I2C.h>       // Library LCD with PCF8574
LCD_I2C lcd(0x3F, 16, 2);  // Instance LCD I2C - address 0x3F

#define PCNT_COUNT_UNIT_0 PCNT_UNIT_0        // Set Pulse Counter Unit - 0
#define PCNT_COUNT_CHANNEL_0 PCNT_CHANNEL_0  // Set Pulse Counter channel - 0
#define PCNT_INPUT_SIG_IO_0 GPIO_NUM_34      // Set Pulse Counter input - Freq Meter Input GPIO 34
#define PCNT_INPUT_CTRL_IO_0 GPIO_NUM_35     // Set Pulse Counter Control GPIO pin - HIGH = count up, LOW = count down
#define OUTPUT_CONTROL_GPIO_0 GPIO_NUM_32    // Timer output control port - GPIO_32

#define PCNT_COUNT_UNIT_1 PCNT_UNIT_1        // Set Pulse Counter Unit - 1
#define PCNT_COUNT_CHANNEL_1 PCNT_CHANNEL_1  // Set Pulse Counter channel - 0
#define PCNT_INPUT_SIG_IO_1 GPIO_NUM_25      // Set Pulse Counter input - Freq Meter Input GPIO 25
#define PCNT_INPUT_CTRL_IO_1 GPIO_NUM_26     // Set Pulse Counter Control GPIO pin - HIGH = count up, LOW = count down
#define OUTPUT_CONTROL_GPIO_1 GPIO_NUM_27    // Timer output control port - GPIO_27

#define LEDC_HS_CH0_GPIO GPIO_NUM_33  // LEDC output - pulse generator 0 - GPIO_33
#define LEDC_HS_CH1_GPIO GPIO_NUM_14  // LEDC output - pulse generator 1 - GPIO_14
#define IN_BOARD_LED GPIO_NUM_2       // ESP32 native LED - GPIO 2

#define PCNT_H_LIM_VAL overflow  // Overflow of Pulse Counter

uint32_t overflow = 32000;       // Max Pulse Counter value 32000 - limit 32767
uint32_t sample_time = 1000000;  // Sample time of 1 second to count pulses (change the value to calibrate frequency meter)
uint calibrator = 1;             // calibrator of frequency reading (may be + or - integer numbers)

bool flag_0 = true;         // Flag to enable print frequency reading
int16_t pulses_0 = 0;       // Pulse Counter value
uint32_t multPulses_0 = 0;  // Number of PCNT counter overflows
float frequency_0 = 0;      // frequency value

bool flag_1 = true;         // Flag to enable print frequency reading
int16_t pulses_1 = 0;       // Pulse Counter value
uint32_t multPulses_1 = 0;  // Number of PCNT counter overflows
float frequency_1 = 0;      // frequency value

uint32_t osc_freq_0 = 38000;  // Oscillator frequency - initial 32000 Hz (may be 1 Hz to 40 MHz)
uint32_t mDuty_0 = 0;         // Duty cycle value
uint32_t resolution_0 = 0;    // Resolution value of Oscillator

uint32_t osc_freq_1 = 10000;  // Oscillator frequency - initial 16000 Hz (may be 1 Hz to 40 MHz)
uint32_t mDuty_1 = 0;         // Duty cycle value
uint32_t resolution_1 = 0;    // Resolution value of Oscillator

char buf_0[32];  // Buffer 0
char buf_1[32];  // Buffer 1

esp_timer_create_args_t timer_args_0;  // Create an esp_timer instance
esp_timer_handle_t timer_handle_0;     // Create an single timer

esp_timer_create_args_t timer_args_1;  // Create an esp_timer instance
esp_timer_handle_t timer_handle_1;     // Create an single timer

portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;  // portMUX_TYPE to do synchronism

//----------------------------------------------------------------------------------------
void setup() {
  Serial.begin(115200);                                            // Serial Console Arduino 115200 Bps
  Serial.println();                                                // Print line
  Serial.println(" Input the Frequency - 1 to 40 MHz");            // Console print
  Serial.println(" Oscillators have some accuracy limitations ");  // Console print
  Serial.println();                                                // Print line

  lcd.begin();
  lcd.display();
  lcd.backlight();
  lcd.setCursor(0, 0);    // Set cursor position - column and row
  lcd.print("F0:");       // LCD print
  lcd.setCursor(0, 1);    // Set cursor position - column and row
  lcd.print("F1:");       // LCD print
  init_frequencyMeter();  // Initialize Frequency Meter
}

//----------------------------------------------------------------------------
void init_osc_freq_0()  // Initialize Oscillator to test Freq Meter
{
  resolution_0 = (log(80000000 / osc_freq_0) / log(2)) / 2;  // Calc of resolution of Oscillator
  if (resolution_0 < 1) resolution_0 = 1;                    // set min resolution
  // Serial.println(resolution);                            // Print
  mDuty_0 = (pow(2, resolution_0)) / 2;  // Calc of Duty Cycle 50% of the pulse
  // Serial.println(mDuty);                                 // Print

  ledc_timer_config_t ledc_timer = {};  // LEDC timer config instance

  ledc_timer.duty_resolution = ledc_timer_bit_t(resolution_0);  // Set resolution
  ledc_timer.freq_hz = osc_freq_0;                              // Set Oscillator frequency
  ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;                 // Set high speed mode
  ledc_timer.timer_num = LEDC_TIMER_0;                          // Set LEDC timer index - 0
  ledc_timer_config(&ledc_timer);                               // Set LEDC Timer config

  ledc_channel_config_t ledc_channel = {};  // LEDC Channel config instance

  ledc_channel.channel = LEDC_CHANNEL_0;           // Set HS Channel - 0
  ledc_channel.duty = mDuty_0;                     // Set Duty Cycle 50%
  ledc_channel.gpio_num = LEDC_HS_CH0_GPIO;        // LEDC Oscillator output GPIO 33
  ledc_channel.intr_type = LEDC_INTR_DISABLE;      // LEDC Fade interrupt disable
  ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE;  // Set LEDC high speed mode
  ledc_channel.timer_sel = LEDC_TIMER_0;           // Set timer source of channel - 0
  ledc_channel_config(&ledc_channel);              // Config LEDC channel
}

//----------------------------------------------------------------------------
void init_osc_freq_1()  // Initialize Oscillator to test Freq Meter
{
  resolution_1 = (log(80000000 / osc_freq_1) / log(2)) / 2;  // Calc of resolution of Oscillator
  if (resolution_1 < 1) resolution_1 = 1;                    // set min resolution
  // Serial.println(resolution);                            // Print
  mDuty_1 = (pow(2, resolution_1)) / 2;  // Calc of Duty Cycle 50% of the pulse
  // Serial.println(mDuty);                                 // Print

  ledc_timer_config_t ledc_timer = {};  // LEDC timer config instance

  ledc_timer.duty_resolution = ledc_timer_bit_t(resolution_1);  // Set resolution
  ledc_timer.freq_hz = osc_freq_1;                              // Set Oscillator frequency
  ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;                 // Set high speed mode
  ledc_timer.timer_num = LEDC_TIMER_1;                          // Set LEDC timer index - 0
  ledc_timer_config(&ledc_timer);                               // Set LEDC Timer config

  ledc_channel_config_t ledc_channel = {};  // LEDC Channel config instance

  ledc_channel.channel = LEDC_CHANNEL_1;           // Set HS Channel - 0
  ledc_channel.duty = mDuty_1;                     // Set Duty Cycle 50%
  ledc_channel.gpio_num = LEDC_HS_CH1_GPIO;        // LEDC Oscillator output GPIO 14
  ledc_channel.intr_type = LEDC_INTR_DISABLE;      // LEDC Fade interrupt disable
  ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE;  // Set LEDC high speed mode
  ledc_channel.timer_sel = LEDC_TIMER_1;           // Set timer source of channel - 0
  ledc_channel_config(&ledc_channel);              // Config LEDC channel
}

//----------------------------------------------------------------------------------
static void IRAM_ATTR pcnt_intr_handler_0(void *arg)  // Counting overflow pulses
{
  multPulses_0++;                             // increment Overflow counter
  PCNT.int_clr.val = BIT(PCNT_COUNT_UNIT_0);  // Clear Pulse Counter interrupt bit
  pcnt_counter_resume(PCNT_COUNT_UNIT_0);     // Resume PCNT unit - starts count
}

//----------------------------------------------------------------------------------
static void IRAM_ATTR pcnt_intr_handler_1(void *arg)  // Counting overflow pulses
{
  multPulses_1++;                             // increment Overflow counter
  PCNT.int_clr.val = BIT(PCNT_COUNT_UNIT_1);  // Clear Pulse Counter interrupt bit
  pcnt_counter_resume(PCNT_COUNT_UNIT_1);     // Resume PCNT unit - starts count
}

//----------------------------------------------------------------------------------
void init_PCNT_0(void)  // Initialize and run PCNT 0 unit
{
  gpio_pad_select_gpio(OUTPUT_CONTROL_GPIO_0);                  // Set GPIO pad
  gpio_set_direction(OUTPUT_CONTROL_GPIO_0, GPIO_MODE_OUTPUT);  // Set GPIO 32 as Timer output
  timer_args_0.callback = read_PCNT_0;                          // Function to call when the timer expires - stop counter
  esp_timer_create(&timer_args_0, &timer_handle_0);             // Create esp-timer instance / single timer

  pcnt_config_t pcnt_config_0 = {};                    // PCNT unit instance
  pcnt_config_0.pulse_gpio_num = PCNT_INPUT_SIG_IO_0;  // Pulse input GPIO 34 - Freq Meter Input
  pcnt_config_0.ctrl_gpio_num = PCNT_INPUT_CTRL_IO_0;  // Pulse Count Control signal - input GPIO 35
  pcnt_config_0.unit = PCNT_COUNT_UNIT_0;              // Pulse Count Unit PCNT - 0
  pcnt_config_0.channel = PCNT_COUNT_CHANNEL_0;        // Pulse Count Unit Channel - 0
  pcnt_config_0.counter_h_lim = PCNT_H_LIM_VAL;        // Maximum counter value - 32000 pulses
  pcnt_config_0.pos_mode = PCNT_COUNT_INC;             // PCNT positive edge count mode - inc
  pcnt_config_0.neg_mode = PCNT_COUNT_INC;             // PCNT negative edge count mode - inc
  pcnt_config_0.lctrl_mode = PCNT_MODE_DISABLE;        // PCNT low control mode - disable
  pcnt_config_0.hctrl_mode = PCNT_MODE_KEEP;           // PCNT high control mode - won't change counter mode  

  pcnt_unit_config(&pcnt_config_0);                       // Initialize PCNT unit
  pcnt_event_enable(PCNT_COUNT_UNIT_0, PCNT_EVT_H_LIM);   // Enable event to watch - max count
  pcnt_isr_register(pcnt_intr_handler_0, NULL, 0, NULL);  // Setup Register ISR handler
  pcnt_intr_enable(PCNT_COUNT_UNIT_0);                    // Enable interrupts for PCNT unit
}

//----------------------------------------------------------------------------------
void init_PCNT_1(void)  // Initialize and run PCNT 1 unit
{
  gpio_pad_select_gpio(OUTPUT_CONTROL_GPIO_1);                  // Set GPIO pad
  gpio_set_direction(OUTPUT_CONTROL_GPIO_1, GPIO_MODE_OUTPUT);  // Set GPIO 27 as Timer output
  timer_args_1.callback = read_PCNT_1;                          // Function to call when the timer expires - stop counter
  esp_timer_create(&timer_args_1, &timer_handle_1);             // Create esp-timer instance / single timer

  pcnt_config_t pcnt_config_1 = {};                    // PCNT unit instance
  pcnt_config_1.pulse_gpio_num = PCNT_INPUT_SIG_IO_1;  // Pulse input GPIO 25 - Freq Meter Input
  pcnt_config_1.ctrl_gpio_num = PCNT_INPUT_CTRL_IO_1;  // Pulse Count Control signal - input GPIO 26
  pcnt_config_1.unit = PCNT_COUNT_UNIT_1;              // Pulse Count Unit PCNT - 1
  pcnt_config_1.channel = PCNT_COUNT_CHANNEL_1;        // Pulse Count Unit Channel - 1
  pcnt_config_1.counter_h_lim = PCNT_H_LIM_VAL;        // Maximum counter value - 32000 pulses
  pcnt_config_1.pos_mode = PCNT_COUNT_INC;             // PCNT positive edge count mode - inc
  pcnt_config_1.neg_mode = PCNT_COUNT_INC;             // PCNT negative edge count mode - inc
  pcnt_config_1.lctrl_mode = PCNT_MODE_DISABLE;        // PCNT low control mode - disable
  pcnt_config_1.hctrl_mode = PCNT_MODE_KEEP;           // PCNT high control mode - won't change counter mode 

  pcnt_unit_config(&pcnt_config_1);                       // Initialize PCNT unit
  pcnt_event_enable(PCNT_COUNT_UNIT_1, PCNT_EVT_H_LIM);   // Enable event to watch - max count
  pcnt_isr_register(pcnt_intr_handler_1, NULL, 1, NULL);  // Setup Register ISR handler
  pcnt_intr_enable(PCNT_COUNT_UNIT_1);                    // Enable interrupts for PCNT unit
}

//----------------------------------------------------------------------------------
void read_PCNT_0(void *p)  // Read Pulse Counter 0 - timeout callback
{
  pcnt_get_counter_value(PCNT_COUNT_UNIT_0, &pulses_0);  // Read Pulse Counter value
  gpio_set_level(OUTPUT_CONTROL_GPIO_0, 0);              // Stop counter - GPIO 32 - output control LOW
  flag_0 = true;                                         // Change flag 0 to enable print
}

//----------------------------------------------------------------------------------
void read_PCNT_1(void *p)  // Read Pulse Counter 1 - timeout callback
{
  pcnt_get_counter_value(PCNT_COUNT_UNIT_1, &pulses_1);  // Read Pulse Counter value
  gpio_set_level(OUTPUT_CONTROL_GPIO_1, 0);              // Stop counter - GPIO 27 - output control LOW
  flag_1 = true;                                         // Change flag 1 to enable print
}

//---------------------------------------------------------------------------------
void init_frequencyMeter() {
  init_osc_freq_0();  // Config and Initialize Oscillator 0
  init_osc_freq_1();  // Config and Initialize Oscillator 1

  init_PCNT_0();  // Config and Initialize PCNT 0 unit
  init_PCNT_1();  // Config and Initialize PCNT 1 unit

  gpio_set_direction(IN_BOARD_LED, GPIO_MODE_OUTPUT);               // Set LED inboard as output
  gpio_matrix_in(PCNT_INPUT_SIG_IO_0, SIG_IN_FUNC226_IDX, false);   // Set GPIO matrin IN - Freq Meter CH0 input
  gpio_matrix_out(IN_BOARD_LED, SIG_IN_FUNC226_IDX, false, false);  // Set GPIO matrix OUT - to inboard LED
}

//----------------------------------------------------------------------------------------
char *ultos_recursive(unsigned long val, char *s, unsigned radix, int pos)  // Format an unsigned long (32 bits) into a string
{
  int c;
  if (val >= radix)
    s = ultos_recursive(val / radix, s, radix, pos + 1);
  c = val % radix;
  c += (c < 10 ? '0' : 'a' - 10);
  *s++ = c;
  if (pos % 3 == 0) *s++ = ',';  // decimal separator
  return s;
}
//----------------------------------------------------------------------------------------
char *ltos(long val, char *s, int radix)  // Format an long (32 bits) into a string
{
  if (radix < 2 || radix > 36) {
    s[0] = 0;
  } else {
    char *p = s;
    if (radix == 10 && val < 0) {
      val = -val;
      *p++ = '-';
    }
    p = ultos_recursive(val, p, radix, 0) - 1;
    *p = 0;
  }
  return s;
}

//---------------------------------------------------------------------------------
void loop() {
  if (flag_0 == true) {  // If count 0 has ended
    flag_0 = false;      // Change flag to enable count

    frequency_0 = (pulses_0 + (multPulses_0 * (overflow + calibrator))) / 2;  // Calculation of frequency
    printf("Frequency CH0: %s", (ltos(frequency_0, buf_0, 10)));              // Print frequency with commas
    printf(" Hz \n");                                                         // Print unity Hz

    lcd.setCursor(3, 0);         // Set cursor position - column and row
    lcd.print("             ");  // Clear Field
    lcd.setCursor(3, 0);
    lcd.print((ltos(frequency_0, buf_0, 10)));  // LCD print frequency
    lcd.print(" Hz");                           // LCD print unity Hz

    multPulses_0 = 0;                                   // Clear overflow counter
    pcnt_counter_clear(PCNT_COUNT_UNIT_0);           // Clear Pulse Counter 0   pcnt_unit_clear_count
    esp_timer_start_once(timer_handle_0, sample_time);  // Start High resolution timer (1 sec)
    gpio_set_level(OUTPUT_CONTROL_GPIO_0, 1);           // Set enable PCNT count 0 - GPIO 32
  }

  if (flag_1 == true) {  // If count 1 has ended
    flag_1 = false;      // Change flag to enable count

    frequency_1 = (pulses_1 + (multPulses_1 * (overflow + calibrator))) / 2;  // Calculation of frequency
    printf("Frequency CH1: %s", (ltos(frequency_1, buf_1, 10)));              // Print frequency with commas
    printf(" Hz \n");                                                         // Print unity Hz
    Serial.println();                                                         // Print line

    lcd.setCursor(3, 1);         // Set cursor position - column and row
    lcd.print("             ");  // Clear field
    lcd.setCursor(3, 1);
    lcd.print((ltos(frequency_1, buf_1, 10)));  // LCD print frequency
    lcd.print(" Hz");                           // LCD print unity Hz

    multPulses_1 = 0;                                   // Clear overflow counter
    pcnt_counter_clear(PCNT_COUNT_UNIT_1);              // Clear Pulse Counter 1
    esp_timer_start_once(timer_handle_1, sample_time);  // Start High resolution timer (1 sec)
    gpio_set_level(OUTPUT_CONTROL_GPIO_1, 1);           // Set enable PCNT count 1 - GPIO 27
  }

  // Input of frequency value
  String inputString = "";  // clear temporary string
  osc_freq_0 = 0;           // Clear oscillator 0 frequency
  osc_freq_1 = 0;           // Clear oscillator 1 frequency
  while (Serial.available()) {
    char inChar = (char)Serial.read();  // Reads a byte on the console
    inputString += inChar;              // Add char to string
    if (inChar == '\n')                 // If new line (enter)
    {
      osc_freq_0 = inputString.toInt();  // Converts String into integer value
      osc_freq_1 = osc_freq_0;
      inputString = "";  // Clear string
    }
  }
  if (osc_freq_0 != 0)  // If some value inputted to oscillator frequency
  {
    init_osc_freq_0();  // reconfigure ledc function - oscillator 0
    init_osc_freq_1();  // reconfigure ledc function - oscillator 1
  }
}

Arduino Console - COM14 - 115200 baud

Code: Select all

ets Jul 29 2019 12:21:46

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:1448
load:0x40078000,len:14844
ho 0 tail 12 room 4
load:0x40080400,len:4
load:0x40080404,len:3356
entry 0x4008059c
E (156) esp_corf�Eյ�}���͡� Core dump data check failed:   **<<<<<<<  Please help me to solve this bug**
Calculated checksum='cbb3a019'
Image che
 Input the Frequency - 1 to 40 MHz
 Oscillators have some accuracy limitations 

Frequency CH0: 0 Hz 
Frequency CH1: 0 Hz 

Frequency CH0: 38,002 Hz 
Frequency CH1: 10,000 Hz 

Frequency CH0: 38,001 Hz 
Frequency CH1: 10,000 Hz 

Frequency CH0: 38,001 Hz 
Frequency CH1: 10,000 Hz 

Frequency CH0: 38,001 Hz 
Frequency CH1: 10,000 Hz 

Frequency CH0: 38,001 Hz 
Frequency CH1: 10,000 Hz


Schematic to download:
ESP32_Frequency_Meter_2CH.pdf
(77.66 KiB) Downloaded 117 times

ESP32_Frequency_Meter_2CH.JPG
ESP32_Frequency_Meter_2CH.JPG (102.66 KiB) Viewed 4134 times
Open these images in another page in your browser to view at a better size.


NOTE: I used a pin extender underneath the board to be able to fit it into the Protoboard.
ESP32 Freq Meter 2CH board.JPG
ESP32 Freq Meter 2CH board.JPG (167.34 KiB) Viewed 4108 times
Last edited by jgustavoam on Thu Aug 01, 2024 4:14 am, edited 2 times in total.
Retired IBM Brasil
Electronic hobbyist since 1976.

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

Re: Best Frequency Meter ever made with ESP32 - awesome!

Postby jgustavoam » Thu Aug 01, 2024 3:58 am

ESP32 FREQUENCY METER - 2 CHANNELS - Checking out

The accuracy of the measurements is impressive!
See measuring the two oscillators with 15.999 Hz signals on both channels of the ESP32 Frequency Meter.

Arduino Console:
Frequency CH0: 15,999 Hz
Frequency CH1: 15,999 Hz

Checking measurements with an oscilloscope.

F0014TEK.JPG
F0014TEK.JPG (163.3 KiB) Viewed 4093 times
Retired IBM Brasil
Electronic hobbyist since 1976.

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

Re: Best Frequency Meter ever made with ESP32 - awesome!

Postby jgustavoam » Sun Aug 04, 2024 1:34 am

ESP32 FREQUENCY METER - 2 CHANNELS - WORKING !!

As promised, I managed to fix the interference problem between the interrupts of the two Pulse Counters 0 and 1.
The solution was to disable the Pulse Counter interrupt right after the complete count using the "pcnt_isr_unregister" function.
After printing the value of frequency 0, I enabled the interrupt of Pulse Counter 1 (function "pcnt_isr_register").
And after printing the value of frequency 1, I enabled the interrupt of Pulse Counter 0. It worked!
Every second, a counter counts the pulses and calculates the frequency. Everything is controlled with the 1-second One-shot Timers.

To test the frequency counter, you can use the signals from both oscillators (initial values ​​for Osc 0 = 10,000 Hz and Osc 1 = 20,000 Hz).
Connect the oscillator output to the frequency counter channel input.
To change the oscillator frequencies, enter one value at a time (without commas) in the Arduino IDE serial console (115200 bps).
The first value will be used in Oscillator 0 and the second value will be used in Oscillator 1. Very simple.

There is only one more challenge left - when the CH0 input channel is disconnected, the frequency value varies different from 0 Hz.
But the CH1 channel, when disconnected presents a frequency of 0 Hz (which is the expected value).
Connect the CH0 input channel to an oscillator circuit and the frequency will be measured accurately!

I implemented a variable for calibrating measurements (offset)- calibrator => calibrator of frequency reading (may be + or - integer numbers).
Use resistors R1 and R2 to equalize the voltage level on the I2C bus to 3.3V used in ESP32.

I didn't understand how to use the "pcnt_isr_unregister" function. And with the help of ChatGPT Artificial Intelligence, everything became clear.
ChatGPT ESP32 unregister ISR.JPG
ChatGPT ESP32 unregister ISR.JPG (49.6 KiB) Viewed 3827 times

If you are in doubt about the I2C address of your I2C LCD module, use the I2C scanner.
ESP32 - I2C Scanner using Arduino IDE
viewtopic.php?f=18&t=4742

Add this library LCD-I2C using the Arduino IDE library manager
https://github.com/hasenradball/LCD-I2C

Github Project: (use "Arduino Code/ESP32_FrequencyMeter_2CH_V3.ino")
https://github.com/Gustavomurta/ESP32_f ... _Meter_2CH

ESP32 API references used in this project:
https://docs.espressif.com/projects/esp ... /ledc.html
https://docs.espressif.com/projects/esp ... timer.html
https://docs.espressif.com/projects/esp ... /pcnt.html

ESP32 Frequency Meter / 2 Channel - Version 3
ESP32 DevKit + I2C PCF8574 LCD
Arduino IDE 2.3.2 / ESP32 Arduino V 3.02
Gustavo Murta e Rui Viana august/2020 - 2024/07/30

Code: Select all

// ESP32 Frequency Meter / 2 Channel - Version 3
// ESP32 DevKit + I2C PCF8574 LCD
// Arduino IDE 2.3.2   / ESP32 Arduino V 3.02
// Gustavo Murta e Rui Viana august/2020 - 2024/07/30
// https://blog.eletrogate.com/esp32-frequencimetro-de-precisao
// https://www.esp32.com/viewtopic.php?f=19&t=17018
// References:
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/esp_timer.html
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/pcnt.html
// LCD I2C SDA - GPIO_21
// LCD I2C SCL - GPIO_22

#include "stdio.h"            // Library STDIO
#include "driver/ledc.h"      // Library ESP32 LEDC
#include "driver/pcnt.h"      // Library ESP32 PCNT
#include "soc/pcnt_struct.h"  // Library ESP32 PCNT
#include "esp32/rom/gpio.h"   // Library ESP32 GPIO

#include <LCD-I2C.h>       // Library LCD with PCF8574
LCD_I2C lcd(0x3F, 16, 2);  // Instance LCD I2C - address 0x3F

#define PCNT_COUNT_UNIT_0 PCNT_UNIT_0        // Set Pulse Counter Unit - 0
#define PCNT_COUNT_CHANNEL_0 PCNT_CHANNEL_0  // Set Pulse Counter channel - 0
#define PCNT_INPUT_SIG_IO_0 GPIO_NUM_34      // Set Pulse Counter input - Freq Meter Input GPIO 34
#define PCNT_INPUT_CTRL_IO_0 GPIO_NUM_35     // Set Pulse Counter Control GPIO pin - HIGH = count up, LOW = count down
#define OUTPUT_CONTROL_GPIO_0 GPIO_NUM_32    // Timer output control port - GPIO_32

#define PCNT_COUNT_UNIT_1 PCNT_UNIT_1        // Set Pulse Counter Unit - 1
#define PCNT_COUNT_CHANNEL_1 PCNT_CHANNEL_1  // Set Pulse Counter channel - 0
#define PCNT_INPUT_SIG_IO_1 GPIO_NUM_25      // Set Pulse Counter input - Freq Meter Input GPIO 25
#define PCNT_INPUT_CTRL_IO_1 GPIO_NUM_26     // Set Pulse Counter Control GPIO pin - HIGH = count up, LOW = count down
#define OUTPUT_CONTROL_GPIO_1 GPIO_NUM_27    // Timer output control port - GPIO_27

#define LEDC_HS_CH0_GPIO GPIO_NUM_33  // LEDC output - pulse generator 0 - GPIO_33
#define LEDC_HS_CH1_GPIO GPIO_NUM_14  // LEDC output - pulse generator 1 - GPIO_14

#define PCNT_H_LIM_VAL overflow  // Overflow of Pulse Counter

uint32_t overflow = 32000;       // Max Pulse Counter value 32000 - limit 32767
uint32_t sample_time = 1000000;  // Sample time of 1 second to count pulses 
uint calibrator = 1;             // calibrator of frequency reading (may be + or - integer numbers)

bool flag_0 = false;        // Flag to enable print frequency reading
int16_t pulses_0 = 0;       // Pulse Counter value
uint32_t multPulses_0 = 0;  // Number of PCNT counter overflows
float frequency_0 = 0;      // frequency value
bool pressEnter = false;    //

bool flag_1 = true;         // Flag to enable print frequency reading
int16_t pulses_1 = 0;       // Pulse Counter value
uint32_t multPulses_1 = 0;  // Number of PCNT counter overflows
float frequency_1 = 0;      // frequency value

uint32_t osc_freq_0 = 10000;  // Oscillator frequency - initial 10000 Hz (may be 1 Hz to 40 MHz)
uint32_t mDuty_0 = 0;         // Duty cycle value
uint32_t resolution_0 = 0;    // Resolution value of Oscillator

uint32_t osc_freq_1 = 20000;  // Oscillator frequency - initial 20000 Hz (may be 1 Hz to 40 MHz)
uint32_t mDuty_1 = 0;         // Duty cycle value
uint32_t resolution_1 = 0;    // Resolution value of Oscillator

char buf_0[32];  // Buffer 0
char buf_1[32];  // Buffer 1

esp_timer_create_args_t timer_args_0;  // Create an esp_timer instance
esp_timer_handle_t timer_handle_0;     // Create an single timer

esp_timer_create_args_t timer_args_1;  // Create an esp_timer instance
esp_timer_handle_t timer_handle_1;     // Create an single timer

pcnt_isr_handle_t pcnt_intr_handle_0;
pcnt_isr_handle_t pcnt_intr_handle_1;

//----------------------------------------------------------------------------------------
void setup() {
  Serial.begin(115200);                                                               // Serial Console Arduino 115200 Bps
  Serial.println();                                                                   // Print line
  Serial.println(" Enter the Frequency - 1 to 40 MHz - First Osc 0 / Second Osc 1");  // Console print
  Serial.println(" Oscillators have some accuracy limitations ");                     // Console print
  Serial.println();                                                                   // Print line

  lcd.begin();
  lcd.display();
  lcd.backlight();
  lcd.setCursor(0, 0);    // Set cursor position - column and row
  lcd.print("F0:");       // LCD print
  lcd.setCursor(0, 1);    // Set cursor position - column and row
  lcd.print("F1:");       // LCD print
  init_frequencyMeter();  // Initialize Frequency Meter
}

//----------------------------------------------------------------------------
void init_osc_freq_0()  // Initialize Oscillator to test Freq Meter
{
  resolution_0 = (log(80000000 / osc_freq_0) / log(2)) / 2;  // Calc of resolution of Oscillator
  if (resolution_0 < 1) resolution_0 = 1;                    // set min resolution
  // Serial.println(resolution);                            // Print
  mDuty_0 = (pow(2, resolution_0)) / 2;  // Calc of Duty Cycle 50% of the pulse
  // Serial.println(mDuty);                                 // Print

  ledc_timer_config_t ledc_timer = {};  // LEDC timer config instance

  ledc_timer.duty_resolution = ledc_timer_bit_t(resolution_0);  // Set resolution
  ledc_timer.freq_hz = osc_freq_0;                              // Set Oscillator frequency
  ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;                 // Set high speed mode
  ledc_timer.timer_num = LEDC_TIMER_0;                          // Set LEDC timer index - 0
  ledc_timer_config(&ledc_timer);                               // Set LEDC Timer config

  ledc_channel_config_t ledc_channel = {};  // LEDC Channel config instance

  ledc_channel.channel = LEDC_CHANNEL_0;           // Set HS Channel - 0
  ledc_channel.duty = mDuty_0;                     // Set Duty Cycle 50%
  ledc_channel.gpio_num = LEDC_HS_CH0_GPIO;        // LEDC Oscillator output GPIO 33
  ledc_channel.intr_type = LEDC_INTR_DISABLE;      // LEDC Fade interrupt disable
  ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE;  // Set LEDC high speed mode
  ledc_channel.timer_sel = LEDC_TIMER_0;           // Set timer source of channel - 0
  ledc_channel_config(&ledc_channel);              // Config LEDC channel
}

//----------------------------------------------------------------------------
void init_osc_freq_1()  // Initialize Oscillator to test Freq Meter
{
  resolution_1 = (log(80000000 / osc_freq_1) / log(2)) / 2;  // Calc of resolution of Oscillator
  if (resolution_1 < 1) resolution_1 = 1;                    // set min resolution
  // Serial.println(resolution);                            // Print
  mDuty_1 = (pow(2, resolution_1)) / 2;  // Calc of Duty Cycle 50% of the pulse
  // Serial.println(mDuty);                                 // Print

  ledc_timer_config_t ledc_timer = {};  // LEDC timer config instance

  ledc_timer.duty_resolution = ledc_timer_bit_t(resolution_1);  // Set resolution
  ledc_timer.freq_hz = osc_freq_1;                              // Set Oscillator frequency
  ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;                 // Set high speed mode
  ledc_timer.timer_num = LEDC_TIMER_1;                          // Set LEDC timer index - 0
  ledc_timer_config(&ledc_timer);                               // Set LEDC Timer config

  ledc_channel_config_t ledc_channel = {};  // LEDC Channel config instance

  ledc_channel.channel = LEDC_CHANNEL_1;           // Set HS Channel - 0
  ledc_channel.duty = mDuty_1;                     // Set Duty Cycle 50%
  ledc_channel.gpio_num = LEDC_HS_CH1_GPIO;        // LEDC Oscillator output GPIO 14
  ledc_channel.intr_type = LEDC_INTR_DISABLE;      // LEDC Fade interrupt disable
  ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE;  // Set LEDC high speed mode
  ledc_channel.timer_sel = LEDC_TIMER_1;           // Set timer source of channel - 0
  ledc_channel_config(&ledc_channel);              // Config LEDC channel
}

//----------------------------------------------------------------------------------
static void IRAM_ATTR pcnt_intr_handler_0(void *arg)  // Counting overflow pulses
{
  multPulses_0++;                             // increment Overflow counter
  PCNT.int_clr.val = BIT(PCNT_COUNT_UNIT_0);  // Clear Pulse Counter interrupt bit
  pcnt_counter_resume(PCNT_COUNT_UNIT_0);     // Resume PCNT unit - starts count
}

//----------------------------------------------------------------------------------
static void IRAM_ATTR pcnt_intr_handler_1(void *arg)  // Counting overflow pulses
{
  multPulses_1++;                             // increment Overflow counter
  PCNT.int_clr.val = BIT(PCNT_COUNT_UNIT_1);  // Clear Pulse Counter interrupt bit
  pcnt_counter_resume(PCNT_COUNT_UNIT_1);     // Resume PCNT unit - starts count
}

//----------------------------------------------------------------------------------
void init_PCNT_0(void)  // Initialize and run PCNT 0 unit
{
  gpio_pad_select_gpio(OUTPUT_CONTROL_GPIO_0);                  // Set GPIO pad
  gpio_set_direction(OUTPUT_CONTROL_GPIO_0, GPIO_MODE_OUTPUT);  // Set GPIO 32 as Timer output
  timer_args_0.callback = read_PCNT_0;                          // Function to call when the timer expires - stop counter
  esp_timer_create(&timer_args_0, &timer_handle_0);             // Create esp-timer instance / single timer

  pcnt_config_t pcnt_config_0 = {};                    // PCNT unit instance
  pcnt_config_0.pulse_gpio_num = PCNT_INPUT_SIG_IO_0;  // Pulse input GPIO 34 - Freq Meter Input
  pcnt_config_0.ctrl_gpio_num = PCNT_INPUT_CTRL_IO_0;  // Pulse Count Control signal - input GPIO 35
  pcnt_config_0.unit = PCNT_COUNT_UNIT_0;              // Pulse Count Unit PCNT - 0
  pcnt_config_0.channel = PCNT_COUNT_CHANNEL_0;        // Pulse Count Unit Channel - 0
  pcnt_config_0.counter_h_lim = PCNT_H_LIM_VAL;        // Maximum counter value - 32000 pulses
  pcnt_config_0.pos_mode = PCNT_COUNT_INC;             // PCNT positive edge count mode - inc
  pcnt_config_0.neg_mode = PCNT_COUNT_INC;             // PCNT negative edge count mode - inc
  pcnt_config_0.lctrl_mode = PCNT_MODE_DISABLE;        // PCNT low control mode - disable
  pcnt_config_0.hctrl_mode = PCNT_MODE_KEEP;           // PCNT high control mode - won't change counter mode

  pcnt_unit_config(&pcnt_config_0);                                      // Initialize PCNT unit
  pcnt_event_enable(PCNT_COUNT_UNIT_0, PCNT_EVT_H_LIM);                  // Enable event to watch - max count  
  pcnt_intr_enable(PCNT_COUNT_UNIT_0);                                   // Enable interrupts for PCNT unit
}

//----------------------------------------------------------------------------------
void init_PCNT_1(void)  // Initialize and run PCNT 1 unit
{
  gpio_pad_select_gpio(OUTPUT_CONTROL_GPIO_1);                  // Set GPIO pad
  gpio_set_direction(OUTPUT_CONTROL_GPIO_1, GPIO_MODE_OUTPUT);  // Set GPIO 27 as Timer output
  timer_args_1.callback = read_PCNT_1;                          // Function to call when the timer expires - stop counter
  esp_timer_create(&timer_args_1, &timer_handle_1);             // Create esp-timer instance / single timer

  pcnt_config_t pcnt_config_1 = {};                    // PCNT unit instance
  pcnt_config_1.pulse_gpio_num = PCNT_INPUT_SIG_IO_1;  // Pulse input GPIO 25 - Freq Meter Input
  pcnt_config_1.ctrl_gpio_num = PCNT_INPUT_CTRL_IO_1;  // Pulse Count Control signal - input GPIO 26
  pcnt_config_1.unit = PCNT_COUNT_UNIT_1;              // Pulse Count Unit PCNT - 1
  pcnt_config_1.channel = PCNT_COUNT_CHANNEL_1;        // Pulse Count Unit Channel - 1
  pcnt_config_1.counter_h_lim = PCNT_H_LIM_VAL;        // Maximum counter value - 32000 pulses
  pcnt_config_1.pos_mode = PCNT_COUNT_INC;             // PCNT positive edge count mode - inc
  pcnt_config_1.neg_mode = PCNT_COUNT_INC;             // PCNT negative edge count mode - inc
  pcnt_config_1.lctrl_mode = PCNT_MODE_DISABLE;        // PCNT low control mode - disable
  pcnt_config_1.hctrl_mode = PCNT_MODE_KEEP;           // PCNT high control mode - won't change counter mode

  pcnt_unit_config(&pcnt_config_1);                                      // Initialize PCNT unit
  pcnt_event_enable(PCNT_COUNT_UNIT_1, PCNT_EVT_H_LIM);                  // Enable event to watch - max count  
  pcnt_intr_enable(PCNT_COUNT_UNIT_1);                                   // Enable interrupts for PCNT unit
}

//----------------------------------------------------------------------------------
void read_PCNT_0(void *p)  // Read Pulse Counter 0 - timeout callback
{
  pcnt_get_counter_value(PCNT_COUNT_UNIT_0, &pulses_0);  // Read Pulse Counter value
  gpio_set_level(OUTPUT_CONTROL_GPIO_0, 0);              // Stop counter - GPIO 32 - output control LOW
  flag_0 = true;                                         // Change flag 0 to enable print
}

//----------------------------------------------------------------------------------
void read_PCNT_1(void *p)  // Read Pulse Counter 1 - timeout callback
{
  pcnt_get_counter_value(PCNT_COUNT_UNIT_1, &pulses_1);  // Read Pulse Counter value
  gpio_set_level(OUTPUT_CONTROL_GPIO_1, 0);              // Stop counter - GPIO 27 - output control LOW
  flag_1 = true;                                         // Change flag 1 to enable print
}

//---------------------------------------------------------------------------------
void init_frequencyMeter() {
  init_osc_freq_0();  // Config and Initialize Oscillator 0
  init_osc_freq_1();  // Config and Initialize Oscillator 1

  init_PCNT_0();  // Config and Initialize PCNT 0 unit
  init_PCNT_1();  // Config and Initialize PCNT 1 unit 
}

//----------------------------------------------------------------------------------------
char *ultos_recursive(unsigned long val, char *s, unsigned radix, int pos)  // Format an unsigned long (32 bits) into a string
{
  int c;
  if (val >= radix)
    s = ultos_recursive(val / radix, s, radix, pos + 1);
  c = val % radix;
  c += (c < 10 ? '0' : 'a' - 10);
  *s++ = c;
  if (pos % 3 == 0) *s++ = ',';  // decimal separator
  return s;
}
//----------------------------------------------------------------------------------------
char *ltos(long val, char *s, int radix)  // Format an long (32 bits) into a string
{
  if (radix < 2 || radix > 36) {
    s[0] = 0;
  } else {
    char *p = s;
    if (radix == 10 && val < 0) {
      val = -val;
      *p++ = '-';
    }
    p = ultos_recursive(val, p, radix, 0) - 1;
    *p = 0;
  }
  return s;
}

//---------------------------------------------------------------------------------
void loop() {
  if (flag_0 == true) {  // If count 0 has ended
    flag_0 = false;      // Change flag to enable count

    pcnt_isr_unregister(pcnt_intr_handle_0);  // Unregister PCNT interrupt handler 0

    frequency_0 = (pulses_0 + (multPulses_0 * (overflow + calibrator))) / 2;  // Calculation of frequency
    printf("Frequency CH0: %s", (ltos(frequency_0, buf_0, 10)));              // Print frequency with commas
    printf(" Hz \n");                                                         // Print unity Hz

    lcd.setCursor(3, 0);         // Set cursor position - column and row
    lcd.print("             ");  // Clear Field
    lcd.setCursor(3, 0);
    lcd.print((ltos(frequency_0, buf_0, 10)));  // LCD print frequency
    lcd.print(" Hz");                           // LCD print unity Hz

    multPulses_1 = 0;                                                      // Clear overflow counter 1
    pcnt_counter_clear(PCNT_COUNT_UNIT_1);                                 // Clear Pulse Counter 1
    esp_timer_start_once(timer_handle_1, sample_time);                     // Start High resolution timer (1 sec)
    gpio_set_level(OUTPUT_CONTROL_GPIO_1, 1);                              // Set enable PCNT count 1 - GPIO 27
    pcnt_isr_register(pcnt_intr_handler_1, NULL, 0, &pcnt_intr_handle_1);  // Setup Register ISR handler
  }

  if (flag_1 == true) {  // If count 1 has ended
    flag_1 = false;      // Change flag to enable count

    pcnt_isr_unregister(pcnt_intr_handle_1);  // Unregister PCNT interrupt handler 1

    frequency_1 = (pulses_1 + (multPulses_1 * (overflow + calibrator))) / 2;  // Calculation of frequency
    printf("Frequency CH1: %s", (ltos(frequency_1, buf_1, 10)));              // Print frequency with commas
    printf(" Hz \n");                                                         // Print unity Hz
    Serial.println();                                                         // Print line

    lcd.setCursor(3, 1);         // Set cursor position - column and row
    lcd.print("             ");  // Clear field
    lcd.setCursor(3, 1);
    lcd.print((ltos(frequency_1, buf_1, 10)));  // LCD print frequency
    lcd.print(" Hz");                           // LCD print unity Hz

    multPulses_0 = 0;                                                      // Clear overflow counter 0
    pcnt_counter_clear(PCNT_COUNT_UNIT_0);                                 // Clear Pulse Counter 0   pcnt_unit_clear_count
    esp_timer_start_once(timer_handle_0, sample_time);                     // Start High resolution timer (1 sec)
    gpio_set_level(OUTPUT_CONTROL_GPIO_0, 1);                              // Set enable PCNT count 0 - GPIO 32
    pcnt_isr_register(pcnt_intr_handler_0, NULL, 0, &pcnt_intr_handle_0);  // Setup Register ISR handler
  }

  // Input of frequency value => First Osc 0 - Second Osc 1
  String inputString = "";  // clear temporary string

  while (Serial.available()) {
    char inChar = (char)Serial.read();  // Reads a byte on the console
    inputString += inChar;              // Add char to string

    if (inChar == '\n')  // If press enter
    {
      if (pressEnter == false) {
        osc_freq_0 = inputString.toInt();  // Converts String into integer value
        Serial.print("Osc. 0 = ");
        Serial.println(osc_freq_0);  // Print Oscillator 0
        Serial.println();            // Print line
        init_osc_freq_0();           // reconfigure ledc function - oscillator 0
        inputString = "";            // Clear string
      } else {
        osc_freq_1 = inputString.toInt();  // Converts String into integer value
        Serial.print("Osc. 1 = ");
        Serial.println(osc_freq_1);  // Print Oscillator 0
        Serial.println();            // Print line
        init_osc_freq_1();           // reconfigure ledc function - oscillator 1
        inputString = "";            // Clear string
      }
      pressEnter = !pressEnter;  // invert pressEnter
    }
  }
}



Arduino Console - COM14 - 115200 baud

Code: Select all

ets Jul 29 2019 12:21:46

rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1
load:0x3fff0030,len:1448
load:0x40078000,len:14844
ho 0 tail 12 room 4
load:0x40080400,len:4
load:0x40080404,len:3356
entry 0x4008059c
E (156) esp_cor�յ�}���͡� Core dump data check failed:
Calculated checksum='cbb3a019'
Image chec
 Enter the Frequency - 1 to 40 MHz - First Osc 0 / Second Osc 1
 Oscillators have some accuracy limitations 

Frequency CH1: 0 Hz 

Frequency CH0: 10,000 Hz 
Frequency CH1: 20,000 Hz 

Frequency CH0: 10,000 Hz 
Frequency CH1: 20,001 Hz 

Frequency CH0: 10,000 Hz 
Frequency CH1: 20,000 Hz 

Frequency CH0: 10,000 Hz 
Frequency CH1: 20,000 Hz

Schematic to download:
ESP32_Frequency_Meter_2CH.pdf
(77.66 KiB) Downloaded 106 times
ESP32_Frequency_Meter_2CH.JPG
ESP32_Frequency_Meter_2CH.JPG (102.66 KiB) Viewed 3827 times
Open these images in another page in your browser to view at a better size.
Last edited by jgustavoam on Sun Aug 04, 2024 3:56 am, edited 9 times in total.
Retired IBM Brasil
Electronic hobbyist since 1976.

Who is online

Users browsing this forum: No registered users and 37 guests