Best Frequency Meter ever made with ESP32 - awesome!

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

Best Frequency Meter ever made with ESP32 - awesome!

Postby jgustavoam » Fri Aug 21, 2020 1:23 am

My friend Rui Viana developed the best Frequency Meter ever made with ESP32 ! With my collaboration, the project was awesome!
High accuracy frequency Meter from 1 Hz to 40 MHz! Resolution of one hertz. Up to eight Digits!
There is also included an Oscillator to test Frequency Meter. Connect the GPIO 33 to GPIO 34 to test it.
The tutorial is in portuguese language. I will translate to english as soon as possible.
There is also a version in the IDF. Wait for it.
Enjoy it!
Obs- the dot in portuguese has a comma function in english. I will translate this soon.

ESP32 Frequency Meter (tutorial in portuguese - you may use Google Tanslator):
https://blog.eletrogate.com/esp32-frequ ... -precisao/

LCD freq 12543.jpg
LCD freq 12543.jpg (2.12 MiB) Viewed 59513 times
ESP32 Frequencimetro I2C LCD.JPG
ESP32 Frequencimetro I2C LCD.JPG (126.07 KiB) Viewed 59513 times


Code: Select all

// BLOG Eletrogate
// ESP32 Frequencimetro
// ESP32 DevKit 38 pinos + LCD 
// https://blog.eletrogate.com/esp32-frequencimetro-de-precisao
// Rui Viana e Gustavo Murta agosto/2020

#include "stdio.h"                                                        // Biblioteca STDIO
#include "driver/ledc.h"                                                  // Biblioteca ESP32 LEDC
#include "driver/pcnt.h"                                                  // Biblioteca ESP32 PCNT
#include "soc/pcnt_struct.h"

#define LCD_OFF                                                           // Defina LCD_ON, para usar LCD, se não, defina LCD_OFF
#define LCD_I2C_OFF                                                       // Defina LCD_I2C_ON, para usar LCD I2C, se não, defina LCD_I2C_OFF

#ifdef LCD_I2C_ON                                                         // Se habilitar LCD I2C
#define I2C_SDA 21                                                        // LCD I2C SDA - GPIO_21
#define I2C_SCL 22                                                        // LCD I2C SCL - GPIO_22
#include <Wire.h>                                                         // Biblioteca para I2C
#include <LiquidCrystal_PCF8574.h>                                        // Biblioteca para LCD com PCF8574
LiquidCrystal_PCF8574 lcd(0x3F);                                          // Instancia LCD I2C com endereço x3F
#endif                                                                    // LCD I2C

#ifdef LCD_ON                                                             // Se habilitar LCD com interface 4 bits
#include <LiquidCrystal.h>                                                // Biblioteca para LCD
LiquidCrystal lcd(4, 16, 17, 5, 18, 19);                                  // Instancia e define os ports
#endif                                                                    // LCD

#define PCNT_COUNT_UNIT       PCNT_UNIT_0                                 // Unidade 0 do Contador de pulso PCNT do ESP32
#define PCNT_COUNT_CHANNEL    PCNT_CHANNEL_0                              // Canal 0 do Contador de pulso PCNT do ESP32

#define PCNT_INPUT_SIG_IO     GPIO_NUM_34                                 // Entrada do Frequencimetro -  GPIO 34
#define LEDC_HS_CH0_GPIO      GPIO_NUM_33                                 // Saida do LEDC - gerador de pulsos - GPIO_33
#define PCNT_INPUT_CTRL_IO    GPIO_NUM_35                                 // Pino de controle do PCNT - HIGH = count up, LOW = count down 
#define OUTPUT_CONTROL_GPIO   GPIO_NUM_32                                 // Saida do timer - Controla a contagem - GPIO_32
#define PCNT_H_LIM_VAL        overflow                                    // Limite superior de contagem

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

bool            flag          = true;                                     // Indicador de fim de contagem - libera impressão
uint32_t        overflow      = 20000;                                    // Valor maximo para overflow do contador PCNT
int16_t         pulses        = 0;                                        // Quantidade de pulsos contados
uint32_t        multPulses    = 0;                                        // Quantidade de overflows do contador PCNT
uint32_t        janela        = 1000000;                                  // Tempo de amostragem  de 1 segundo para a contagem de pulsos 999990
uint32_t        oscilador     = 12543;                                    // Frequencia inicial do oscilador - 12543 Hz
uint32_t        mDuty         = 0;                                        // Valor calculado do ciclo de carga
uint32_t        resolucao     = 0;                                        // Valor calculado da resolucao
float           frequencia    = 0;                                        // Variavel para calculo de frequencia
char            buf[32];                                                  // Buffer para guardar a pontuacao

esp_timer_create_args_t create_args;                                      // Argumentos do ESP-Timer
esp_timer_handle_t timer_handle;                                          // Instancia de ESP-Timer

portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;                     // variavel tipo portMUX_TYPE para sincronismo

//----------------------------------------------------------------------------------------
void setup()
{
  Serial.begin(115200);                                                   // Inicializa a serial 115200 Bps
  Serial.println(" Digite uma frequencia - 1 a 40 MHz");                  // Print na console

#ifdef LCD_I2C_ON                                                         // Se estiver usando LCD I2C 
  Wire.begin(I2C_SDA, I2C_SCL);                                           // Inicializa Interface I2C
  lcd.setBacklight(255);                                                  // Ativa leds do backlight do LCD
#endif

#if defined LCD_ON || defined LCD_I2C_ON                                  // Se estiver usando LCD ou LCD I2C      
  lcd.begin(16, 2);                                                       // Inicializa LCD 16 colunas 2 linhas
  lcd.print("  Frequencia:");                                             // Print no LCD
#endif

  inicializa_frequencimetro();                                            // Inicializa o frequencimetro
}

//----------------------------------------------------------------------------
void inicializa_oscilador ()                                              // Inicializa gerador de pulsos
{
  resolucao = (log (80000000 / oscilador)  / log(2)) / 2 ;                // Calculo da resolucao para o oscilador
  if (resolucao < 1) resolucao = 1;                                       // Resoluçao mínima 
  // Serial.println(resolucao);                                           // Print
  mDuty = (pow(2, resolucao)) / 2;                                        // Calculo do ciclo de carga 50% do pulso
  // Serial.println(mDuty);                                               // Print

  ledc_timer_config_t ledc_timer = {};                                    // Instancia a configuracao do timer do LEDC

  ledc_timer.duty_resolution =  ledc_timer_bit_t(resolucao);              // Configura resolucao
  ledc_timer.freq_hz    = oscilador;                                      // Configura a frequencia do oscilador
  ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;                           // Modo de operacao em alta velocidade
  ledc_timer.timer_num = LEDC_TIMER_0;                                    // Usar timer0 do LEDC
  ledc_timer_config(&ledc_timer);                                         // Configura o timer do LEDC

  ledc_channel_config_t ledc_channel = {};                                // Instancia a configuracao canal do LEDC

  ledc_channel.channel    = LEDC_CHANNEL_0;                               // Configura canal 0 
  ledc_channel.duty       = mDuty;                                        // Configura o ciclo de carga
  ledc_channel.gpio_num   = LEDC_HS_CH0_GPIO;                             // Configura GPIO da saida do LEDC - oscilador
  ledc_channel.intr_type  = LEDC_INTR_DISABLE;                            // Desabilita interrupção do LEDC
  ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE;                         // Modo de operacao do canal em alta velocidade
  ledc_channel.timer_sel  = LEDC_TIMER_0;                                 // Seleciona timer 0 do LEDC
  ledc_channel_config(&ledc_channel);                                     // Configura o canal do LEDC
}

//----------------------------------------------------------------------------------
static void IRAM_ATTR pcnt_intr_handler(void *arg)                        // Contagem do contador de Overflow
{
  portENTER_CRITICAL_ISR(&timerMux);                                      // Bloqueia nova interrupção
  multPulses++;                                                           // Incrementa contador de overflow
  PCNT.int_clr.val = BIT(PCNT_COUNT_UNIT);                                // Limpa indicador de interrupção
  portEXIT_CRITICAL_ISR(&timerMux);                                       // Libera nova interrupção
}

//----------------------------------------------------------------------------------
void inicializa_contador(void)                                            // Inicializacao do contador de pulsos
{
  pcnt_config_t pcnt_config = { };                                        // Instancia PCNT config

  pcnt_config.pulse_gpio_num = PCNT_INPUT_SIG_IO;                         // Configura GPIO para entrada dos pulsos
  pcnt_config.ctrl_gpio_num = PCNT_INPUT_CTRL_IO;                         // Configura GPIO para controle da contagem
  pcnt_config.unit = PCNT_COUNT_UNIT;                                     // Unidade de contagem PCNT - 0
  pcnt_config.channel = PCNT_COUNT_CHANNEL;                               // Canal de contagem PCNT - 0
  pcnt_config.counter_h_lim = PCNT_H_LIM_VAL;                             // Limite maximo de contagem - 20000
  pcnt_config.pos_mode = PCNT_COUNT_INC;                                  // Incrementa contagem na subida do pulso
  pcnt_config.neg_mode = PCNT_COUNT_INC;                                  // Incrementa contagem na descida do pulso
  pcnt_config.lctrl_mode = PCNT_MODE_DISABLE;                             // PCNT - modo lctrl desabilitado
  pcnt_config.hctrl_mode = PCNT_MODE_KEEP;                                // PCNT - modo hctrl - se HIGH conta incrementando
  pcnt_unit_config(&pcnt_config);                                         // Configura o contador PCNT

  pcnt_counter_pause(PCNT_COUNT_UNIT);                                    // Pausa o contador PCNT
  pcnt_counter_clear(PCNT_COUNT_UNIT);                                    // Zera o contador PCNT

  pcnt_event_enable(PCNT_COUNT_UNIT, PCNT_EVT_H_LIM);                     // Configura limite superior de contagem
  pcnt_isr_register(pcnt_intr_handler, NULL, 0, NULL);                    // Conigura rotina de interrupção do PCNT
  pcnt_intr_enable(PCNT_COUNT_UNIT);                                      // Habilita interrupções do PCNT

  pcnt_counter_resume(PCNT_COUNT_UNIT);                                   // Reinicia a contagem no contador PCNT
}

//----------------------------------------------------------------------------------
void tempo_controle(void *p)                                              // Fim de tempo de leitura de pulsos
{
  gpio_set_level(OUTPUT_CONTROL_GPIO, 0);                                 // Controle do PCNT - para o contador
  pcnt_get_counter_value(PCNT_COUNT_UNIT, &pulses);                       // Obtem o valor contado no PCNT
  flag = true;                                                            // Informa que ocorreu interrupção de controle
}

//---------------------------------------------------------------------------------
void inicializa_frequencimetro()
{
  inicializa_oscilador ();                                                // Inicia a geração de pulsos no oscilador
  inicializa_contador();                                                  // Inicializa o contador de pulsos PCNT

  gpio_pad_select_gpio(OUTPUT_CONTROL_GPIO);                              // Define o port decontrole
  gpio_set_direction(OUTPUT_CONTROL_GPIO, GPIO_MODE_OUTPUT);              // Define o port de controle como saida

  create_args.callback = tempo_controle;                                  // Instancia o tempo de controle
  esp_timer_create(&create_args, &timer_handle);                          // Cria parametros do timer

  gpio_set_direction(IN_BOARD_LED, GPIO_MODE_OUTPUT);                     // Port LED como saida

  gpio_matrix_in(PCNT_INPUT_SIG_IO, SIG_IN_FUNC226_IDX, false);           // Direciona a entrada de pulsos
  gpio_matrix_out(IN_BOARD_LED, SIG_IN_FUNC226_IDX, false, false);        // Para o LED do ESP32
}

//----------------------------------------------------------------------------------------
char *ultos_recursive(unsigned long val, char *s, unsigned radix, int pos) // Formata um número longo de 32 bits com pontos
{
  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++ = '.';
  return s;
}
//----------------------------------------------------------------------------------------
char *ltos(long val, char *s, int radix)    // Formata um número longo de 32 bits com pontos
{
  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)                                                       // Se a contagem tiver terminado
  {
    flag = false;                                                         // Impede nova impressao
    frequencia = (pulses + (multPulses * overflow)) / 2  ;                // Calcula a soma dos pulsos contados no PCNT
    printf("Frequencia : %s", (ltos(frequencia, buf, 10)));               // Print frequencia com pontos
    printf(" Hz \n");                                                     // Print unidade Hz

#if defined LCD_ON || defined LCD_I2C_ON                                  // Se estiver usando LCD ou LCD I2C  
    lcd.setCursor(2, 1);                                                  // Posiciona cursor na posicao 2 da linha 1
    lcd.print((ltos(frequencia, buf, 10)));                               // Print frequencia no LCD
    lcd.print(" Hz              ");                                       // Print unidade Hz no LCD
#endif

    multPulses = 0;                                                       // Zera contador de overflow
    // Espaco para qualquer função
    delay (100);                                                          // Delay 100 ms
    // Espaco para qualquer função

    pcnt_counter_clear(PCNT_COUNT_UNIT);                                  // Zera o contador PCNT
    esp_timer_start_once(timer_handle, janela);                           // Inicia contador de tempo de 1 segundo
    gpio_set_level(OUTPUT_CONTROL_GPIO, 1);                               // Porta de controle - habilita contagem dos pulsos
  }

  String inputString = "";                                                // Limpa string para entrada de dados
  oscilador = 0;                                                          // Zera o valor da frequencia
  while (Serial.available())                                              // Enquanto tiver dados na serial
  {
    char inChar = (char)Serial.read();                                    // Le um byte:
    inputString += inChar;                                                // Adicione na string:
    if (inChar == '\n')                                                   // Se pressionar ENTER:
    {
      oscilador = inputString.toInt();                                    // Transforma a string em inteiro
      inputString = "";                                                   // Limpa a string
    }
  }
  if (oscilador != 0)                                                     // Se foi digitado algum valor
  {
    inicializa_oscilador ();                                              // Reconfigura a frequencia do oscilador
  }
}
Last edited by jgustavoam on Sun Dec 18, 2022 1:24 pm, edited 3 times in total.
Retired IBM Brasil
Electronic hobbyist since 1976.

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

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

Postby jgustavoam » Sat Aug 22, 2020 1:27 am

As promised, this version has all been translated into English:

The project:

A high accuracy frequency meter using ESP32, without scales and showing up to 8 digits,
measuring up to 40 MHz. Very stable. Optionally, you can test the frequency meter with internal oscillator.
Caution = input signal to Freq Meter - only 3.3 Volts! If you want 5 Volts, use level converter.

Definitions:

GPIO_34 = Freq Meter Input
GPIO_33 = Oscillator output - to test Frequency Meter
To test freq Meter with internal oscillator, make connection between GIPO_34 and GPIO_33 (optional).
Change frequency, inputting value at Arduino console (1 Hz to 40 MHz)

The compiler uses the compilation directives to select:
Using LCD = LCD_ON or LCD_OFF
Using LCD I2C = LCD_I2C_ON or LCD_I2C_OFF

The frequency meter is divided into 5 parts:
1. Pulse counter;
2. Counting time control;
3. Printing the result;
4. Space for other functions.
5. Signal programmed Oscillator

1. The pulse counter uses the ESP32 pcnt.
Pcnt has the following parameters:
a. input port;
b. input channel;
c. control port;
d. count on Pulse raising;
e. count on Pulse falling;
f. counting only with Control - high level;
g. maximum count limit.

2. Counting time control uses the high resolution esp-timer:
The esp-timer has the following parameter:
a. time control;

5. Frequency Oscillator for tests, uses ledc peripheral:
The ledc has the following parameters:
a. output port;
B. lcd channel;
ç. frequency;
d. resolution of ledc;
e. duty cycle at 50%;

Operation:

The high level of control port enable the counter to count the pulses that arrive at the input port.
Pulses are counted both as the pulse rising and falling, to improve the counting average.
The sampling time is defined by the high resolution esp-timer, and it is defined in 1 second, in the sample-time variable.
If the count is greater than 20000 pulses during the counting time, overflow occurs and for with each overflow that occurs
the multPulses variable is incremented. Then pulse counter is cleared and proceed to counting.
Unfortunately the Pulse Counter has only 16 bits that may be used.

When the sampling time ends, a routine is called and the value in the pulse counter is read and saved.
A flag is set on to indicating that the pulse reading has ended.

In the loop, when verifying if the flag is on indicates that the pulse reading has finished, the value is calculated by multiplying
the number of overflow by 20000 and adding to the number of remaining pulses and dividing by 2, because it counted 2 times.

As the pulses are counted on the way up and down, the count is double the frequency.
In the frequency value, commas are inserted and printed on the serial monitor.
The registers are reset and the input control port is set to a high level again and the pulse count starts.

It also has a signal oscillator that generates pulses, and can be used for testing.
This oscillator can be configured to generate frequencies up to 40 MHz.
We use the LEDC peripheral of ESP32 to generate frequency that can be used as a test.
The base frequency value is 12543 Hz, but it can be typed to another value on the serial monitor.
The default duty cycle was set to 50%, and the resolution is properly calculated.
The output port of this generator is currently defined as GPIO 33.

Internally using GPIO matrix, the input pulse was directed to the ESP32 native LED,
so the LED will flash at the input frequency.




LCD 1 MHz.jpg
LCD 1 MHz.jpg (495.75 KiB) Viewed 59619 times

Code: Select all

// BLOG Eletrogate
// ESP32 Frequency Meter
// ESP32 DevKit 38 pins + LCD
// https://blog.eletrogate.com/esp32-frequencimetro-de-precisao
// Rui Viana and Gustavo Murta august/2020

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

#define LCD_OFF                                                            // LCD_ON, if want use LCD 4 bits interface 
#define LCD_I2C_OFF                                                       // LCD_I2C_ON, if want use I2C LCD (PCF8574) 

#ifdef LCD_I2C_ON                                                         // If I2C LCD enabled 
#define I2C_SDA 21                                                        // LCD I2C SDA - GPIO_21
#define I2C_SCL 22                                                        // LCD I2C SCL - GPIO_22
#include <Wire.h>                                                         // I2C Libray 
#include <LiquidCrystal_PCF8574.h>                                        // Library LCD with PCF8574
LiquidCrystal_PCF8574 lcd(0x3F);                                          // Instance LCD I2C - adress x3F
#endif                                                                    // LCD I2C

#ifdef LCD_ON                                                             // LCD with 4 bits interface enabled 
#include <LiquidCrystal.h>                                                // Library LCD
LiquidCrystal lcd(4, 16, 17, 5, 18, 19);                                  // Instance - ports RS,ENA,D4,D5,D6,D7
#endif                                                                    // LCD

#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                                 // Saida do LEDC - gerador de pulsos - 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
int16_t         pulses        = 0;                                        // Pulse Counter value
uint32_t        multPulses    = 0;                                        // Quantidade de overflows do contador PCNT
uint32_t        sample_time   = 1000000;                                  // sample time of 1 second to count pulses
uint32_t        osc_freq      = 12543;                                    // Oscillator frequency - initial 12543 Hz (may be 1 Hz to 40 MHz)
uint32_t        mDuty         = 0;                                        // Duty value
uint32_t        resolution    = 0;                                        // Resolution value
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

#ifdef LCD_I2C_ON                                                         // If usinf I2C LCD
  Wire.begin(I2C_SDA, I2C_SCL);                                           // Begin I2C Interface
  lcd.setBacklight(255);                                                  // Set I2C LCD Backlight ON
#endif

#if defined LCD_ON || defined LCD_I2C_ON                                  // If using LCD or I2C LCD     
  lcd.begin(16, 2);                                                       // LCD init 16 x2
  lcd.print("  Frequency:");                                              // LCD print
#endif

  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++ = ',';
  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

#if defined LCD_ON || defined LCD_I2C_ON                                // If using LCD or I2C LCD  
    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
#endif

    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 
  }
}
ESP32 Frequencimetro LCD.JPG
ESP32 Frequencimetro LCD.JPG (163.37 KiB) Viewed 59605 times
Last edited by jgustavoam on Sun Dec 18, 2022 1:25 pm, edited 2 times in total.
Retired IBM Brasil
Electronic hobbyist since 1976.

bobolink
Posts: 98
Joined: Mon Feb 26, 2018 4:17 pm

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

Postby bobolink » Sat Aug 22, 2020 11:21 am

Thank you for all your work on this.

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

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

Postby jgustavoam » Sat Aug 22, 2020 1:36 pm

Hi Bobolink,
Test it and you will see how accurate the frequency meter is!
And you can calibrate too, changing the variable:
uint32_t sample_time = 1000000;
Retired IBM Brasil
Electronic hobbyist since 1976.

plusorc
Posts: 41
Joined: Sat Nov 09, 2019 6:27 am

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

Postby plusorc » Thu Aug 27, 2020 6:52 pm

Thank you so much
I'll be testing this with mains very soon
Really appreciate putting this effort for the community .

User avatar
jgustavoam
Posts: 165
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 27, 2020 7:20 pm

PlusOrc,
You can test the frequency of mains, but the resolution is one Hertz.
Then you will certanly read 50 Hz in the USA! In my country Brazil, the frequency is 60 Hz.

You can also test the frequency meter with the project's internal oscillator. Make connection between GIPO_34 and GPIO_33.
Retired IBM Brasil
Electronic hobbyist since 1976.

rodmcm
Posts: 65
Joined: Sat Sep 02, 2017 3:31 am

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

Postby rodmcm » Thu Aug 27, 2020 10:35 pm

I'm impressed!

User avatar
jgustavoam
Posts: 165
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 27, 2020 11:38 pm

When I know ESP32, I saw that it had great potential! But due to the complexity of the resources, it takes time for us to learn how to use it!
Retired IBM Brasil
Electronic hobbyist since 1976.

VosperD
Posts: 8
Joined: Tue Aug 11, 2020 7:19 pm

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

Postby VosperD » Wed Sep 02, 2020 2:26 pm

Hi,
Thanks for posting this. I've just uploaded the file to the esp32devkitC with an i2c lcd and tested it with a sig gen and it works fine for me.
Any more interesting projects in the pipeline?
Don m5aky

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

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

Postby jgustavoam » Wed Sep 02, 2020 10:23 pm

Hi don,
Very good! We are happy to know that the project works fine for you.
We are thinking of another method of measuring frequency - counting the duration time of each pulse (for low frequencies).
And then Dual Slope DVM.
Wait for it!
Retired IBM Brasil
Electronic hobbyist since 1976.

Who is online

Users browsing this forum: Google [Bot] and 57 guests