Projetos

ESP32 – Frequencímetro de Precisão

Eletrogate 13 de agosto de 2020

Definição de um Frequencímetro:

Para quem não sabe o que é um frequencímetro, é um instrumento eletrônico utilizado para medição da frequência de um sinal elétrico periódico. A unidade de medida de frequência é o Hertz (Hz) ou pulsos por segundo.

Definição de frequencímetro

Mas ainda pode ficar a dúvida! Qual é a utilidade de um frequencímetro? Como a faixa de frequência é bem ampla no caso do nosso frequencímetro, esse instrumento pode ter inúmeras aplicações! E com algumas alterações no projeto pode-se medir RPM, pulsação cardíaca e muitas outras variáveis cíclicas. Além de possibilitar o desenvolvimento de aparelhos de medição de capacitância, indutância, etc. Veja abaixo um moderno (e muito caro) frequencímetro do fabricante Keysight:

Frequencímetro Keysight 53220A 

Algumas aplicações do ESP32 frequencímetro:

O ESP32 frequencímetro pode medir frequências entre 1 Hertz e 40.000.000 Hertz (40 MHz). A resolução, isto é, a menor unidade entre duas medições é de 1 Hertz também. Ele possui até 8 dígitos no mostrador do Display, o que é bem incomum nos projetos encontrados na WEB e o torna bem útil! Veja algumas aplicações abaixo:

  • medições de frequências de rádios transmissores (até 40 MHz),
  • medições da frequência da rede elétrica,
  • medições de sinais de áudio,
  • medições de osciladores a cristal,
  • etc.

Eu e meu amigo Rui Viana, temos estudado bastante o ESP32. Admiramos muito esse microcontrolador, pela grande quantidade de recursos que ele possui e pela rapidez de processamento. Para quem ainda não conhece o ESP32 pode dar uma olhada nos meus tutoriais:

Conhecendo o ESP32 – Introdução (1)

Conhecendo o ESP32 – Usando Arduino IDE (2)

O manual técnico de referência desse microcontrolador é o ESP32 Technical reference. Esse manual tem atualmente mais de 680 páginas, devido aos inúmeros recursos do chip. Esse manual é frequentemente atualizado pela ESPRESSIF, fabricante do ESP32. Ás vezes eu brinco com o Rui, e comento com ele, que esse manual deveria ter umas 2000 páginas para abranger todas as informações, com todos os detalhes!

Alguns dispositivos internos do ESP32, são chamados de periféricos, como as interfaces serial, I2C, SPI, I2S e muitos outros. Temporizadores e contadores são também periféricos bem interessantes e úteis. Esses tipos de periféricos nos chamaram a atenção, pois alguns Timers e contadores possuem 32 bits e até 64 bits, isto é, podem ser usados para fazer a contagem de um número muito grande de pulsos. E para brilhar os nossos olhos, esses contadores podem fazer contagem muito rápida de pulsos com a “velocidade” de até 80 MHz. É uma frequência bem mais rápida do que as disponíveis nos microcontroladores atuais e mais usados, como o ATMega328 do Arduino.

O Rui foi o responsável pelo desenvolvimento do frequencímetro e eu fui um colaborador desenvolvendo outros recursos e testando o programa. O interessante desse projeto é que inclui também um gerador de pulsos desenvolvido inicialmente por mim, um oscilador programável que pode ser usado para testar o frequencímetro. E esse oscilador pode gerar pulsos de 1 Hz a 40 MHz, inclusive.

O projeto foi todo desenvolvido com a IDE Arduino  para o ESP32 , mas como o Rui gosta de um desafio, elaborou também uma versão para a Plataforma IDF (plataforma oficial da ESPRESSIF). Não posso deixar de citar o nome de mais um amigo nosso, o Celso Ito, que nos ajudou na programação IDF. Nesse tutorial disponibilizamos somente o código Arduino, pois é o mais comum e mais fácil de ser interpretado e compreendido.

Nosso Frequencímetro utiliza esses seguintes periféricos internos do ESP32 (clique nos links, se quiser saber mais):

Pulse Count Controller

O módulo de Contador de pulsos PCNT foi projetado para contar o número de bordas ascendentes e / ou descendentes de um sinal de entrada. Ele possui oito unidades independentes!  Cada unidade de PCNT tem um contador de 16 bits e dois canais que podem habilitar contagens crescentes ou decrescentes. Cada canal tem uma entrada para os pulsos a serem contados e uma porta de controle que serve para ativar ou desativar a contagem.   Cada unidade PCNT possui também registradores que são usados para a configuração dos contadores. Esses contadores podem ser programados para gerar interrupções de acordo com alguns parâmetros selecionados, como máximo valor do contador, mínimo valor do contador e um determinado valor do contador. No nosso caso, usamos o valor máximo do contador para gerar interrupções no ESP32.

Timer

Existem quatro temporizadores de uso geral incorporados no ESP32. Todos eles são temporizadores genéricos de 64 bits baseados em prescalers (divisores de frequência) de 16 bits e contadores de 64 bits.
O ESP32 contém dois módulos de timer, cada um contendo dois timers. Os Timers apresentam essas características:
• Um prescaler de 16 bits, de 2 a 65536,
• Um contador de base de tempo de 64 bits,
• Contador de base de tempo configurável para cima / para baixo: aumentando ou diminuindo a contagem,
• Parada e resumo do contador da base de tempo,
• Recarga automática em alarme,
• Recarga instantânea controlada por software,
• Geração de interrupção por nível e  por borda de pulso.

Cada timer usa o clock APB (APB_CLK, normalmente 80 MHz) como o relógio básico. Este relógio é então dividido por um prescaler de 16 bits que gera o pulso de base de tempo. Esse pulso pode ser usado para incrementar ou diminuir o contador da base de tempo, dependendo da configuração.

Temporizador de alta resolução (High Resolution Timer)

A ESPRESSIF criou um conjunto de APIs (Interface de Programação de Aplicativos) para os temporizadores do ESP32, para permitir um controle mais preciso e mais rápido dos Timers de hardware. O conjunto de APIs esp_timer fornece temporizadores únicos e periódicos, resolução de microssegundos e intervalo de 64 bits. Internamente, o esp_timer usa um timer de hardware de 64 bits. No nosso projeto, o esp_timer é usado para temporizar a contagem de pulsos do PCNT. Como a unidade de medida de frequência é o Hertz, que traduz se por pulsos por segundos, a cada um segundo é realizada a contagem dos pulsos que entram no contador PCNT.

LEDC

O controlador LEDC ou LED_PWM foi projetado principalmente para controlar a intensidade dos LEDs, embora possa ser usado para gerar sinais PWM para outros fins também. Possui 16 canais que podem gerar pulsos independentes para acionar dispositivos LED RGB coloridos. O controlador LED_PWM possui oito canais de alta velocidade e oito de baixa velocidade PWM. Esses canais  podem usar o clock APB_CLK de 80 Mhz, e com a aplicação de divisores de frequência, de contadores e comparadores, permitem a geração de pulsos com ajuste da frequência e do ciclo de trabalho (Duty Cycle). Toda a configuração desse controlador LED PWM é feita através de vários registradores internos. 


Teoria de Operação

O frequencímetro é dividido em 5 partes

  1. Contador de pulsos;
  2.  Controle de tempo de contagem;
  3.  Gerador de sinais programável (1 Hz a 40 Mhz);
  4.  Espaço para outras funções;
  5.  Impressão do resultado.

1. O contador de pulso usa o módulo de Contador PCNT. Na configuração do PCNT são usados os seguintes parâmetros:

  • porta de entrada;
  • canal de entrada;
  • porta de controle;
  • contagem na subida do pulso;
  • contagem na descida do pulso;
  • contagem só com o controle em nível elevado;
  •  limite máximo de contagem.

2. O Controle de tempo de contagem usa o esp-timer. O esp-timer tem o seguinte parâmetro:

  • controle do tempo;

3. Gerador de frequências para testes do frequencímetro usa o ledc. O ledc tem os seguintes parâmetros:

  • porta de saída;
  • canal de lcd;
  • frequência;
  • resolução do ledc;
  • ciclo de trabalho (duty cycle).

Funcionamento do Frequencímetro:

O frequencímetro baseia-se na contagem de pulsos durante o tempo de um segundo.

Primeiramente o oscilador LEDC e o contador PCNT são configurados e inicializados.  Depois são configurados o esp-timer, a porta de controle do contador como saída e a porta de entrada do contador é conectada ao Led da placa ESP32.

O oscilador LEDC começa a gerar a frequência de 12,543 KHz, definida na variável oscilador.  Escolhi essa frequência, para que vocês percebam quão preciso é o frequencímetro. Nessa função de inicialização do oscilador, a resolução e o ciclo de trabalho (Duty Cycle) já são calculados automaticamente, dependendo do valor da frequência.  Ciclo de trabalho é a proporção entre a parte HIGH e a parte LOW do pulso gerado. Por exemplo, no nosso programa esse Duty é de 50%, isto é, a largura da parte HIGH é a metade do ciclo total do pulso.

Para a contagem dos pulsos, foi usado o contador PCNT. Como ele tem o valor limite para 32.768 pulsos, foi definido um valor de 20.000 como máximo nesse programa. Se a contagem for maior que 20.000 pulsos durante o tempo de contagem, ocorre um registro de overflow (transborda).  Para cada overflow que ocorre, ele é contabilizado na variável multPulses.

A porta de controle de contagem (GPIO 35) quando em nível alto, libera o contador para contar os pulsos que chegam na porta de entrada de pulsos (GPIO 34). Os pulsos são contados tanto na subida quanto na descida do pulso de controle, para melhorar a média de contagem. Essas duas contagens são posteriormente divididas por 2, para o cálculo da frequência.

O tempo de contagem é definido pelo esp-timer, e é configurado para um segundo através variável janela. Como cada pulso do Timer corresponde à um micro segundo, a contagem de um milhão de pulsos completa um segundo para a amostragem da frequência.

Duas funções que trabalham com as interrupções foram definidas no programa. A primeira função IRAM_ATTR pcnt incrementa o contador de overflow, sempre que o contador PCNT estiver cheio. A outra função tempo_controle obtém o valor contido nos registradores do contador PCNT, após o tempo do esp-timer expirar.

Após expirar o tempo de um segundo do esp-timer, os registradores do Contador PCNT são lidos e um Flag é alterado para verdadeiro, indicando o fim da contagem. No loop do programa, ao se constatar que o Flag é verdadeiro, o valor da frequência é calculado.  O número de overflows é multiplicado por 20.000 e somado ao número de pulsos restantes. Essa soma é dividida por dois, já que a contagem é realizada duas vezes (os pulsos são contados na subida e na descida do pulso de controle).
Para facilitar a leitura da frequência no display, pontos são inseridos a cada 3 dígitos, usando as funções ultos e ltos. Essa frequência, além de ser mostrada no display LCD e é enviada também para a console serial da Arduino IDE. Após a amostragem da frequência, os registradores são zerados e as contagens de tempo e dos pulsos são reiniciadas.

Usando também a console serial da Arduino IDE é possível alterar a frequência do Oscilador, para efeito de testes. Digite um valor entre 1 Hz e 40 MHz no campo de entrada e pressione ENTER. Como a saída pulsos do Oscilador foi definida no pino GPIO 33, é necessário que conecte essa saída de pulsos na entrada do frequencímetro GPIO 34, para realizar os testes. Mas para medições de frequências de sinais externos, retire essa conexão. Como o ESP32 trabalha com sinais de tensão de 3,3 Volts, se for medir sinais com outros níveis de tensão, é recomendável que use um Conversor de Nível Lógico 3.3V-5V Bidirecional – 4 Canais na porta de entrada. Internamente usando GPIO matrix, o pulso de entrada do frequencímetro foi direcionado para o LED nativo do ESP32, assim o LED piscará de acordo com a frequência.

Bibliotecas utilizadas:

A versão da IDE Arduino usada nesse projeto foi a V 1.8.12. Atualize a sua versão se estiver desatualizada, para evitar alguma incompatibilidade de códigos.

Arduino Software

Se você ainda não configurou a Arduino IDE para ser usada com o Placa ESP32, siga o procedimento do tutorial no link abaixo. Essa configuração é essencial para que o projeto funcione adequadamente.  Se a sua Placa ESP32 for diferente do Tutorial, altere as configurações na Arduino IDE.

Conhecendo o ESP32 – Usando Arduino IDE (2)

A Versão do firmware do ESP32 para a Arduino IDE usada nesse projeto foi a V 1.0.4. Se estiver desatualizada, atualize-a antes de compilar o seu programa, para evitar algum tipo de problema. Use o Gerenciador de placas para atualizar, se necessário.

Para usar o Display LCD com a interface I2C (PCF8574) é necessário que você instale a Biblioteca LiquidCrystal_PCF8574. Use o Gerenciador de Bibliotecas da Arduino IDE para instalá-la.

E se for usar o display LCD paralelo(com 4 bits), instale a Biblioteca LiquidCrystal também com o Gerenciador de Bibliotecas da Arduino IDE.

Diagramas dos circuitos:

Abaixo estão os dois diagramas dos circuitos do ESP32 Frequencímetro. Você poderá optar pelo Display com interface I2C (PCF8574), muito mais fácil para montar ou pelo Display LCD com a interface paralela de 4 bits. Para facilitar a compreensão, fiz os dois diagramas separados. Sobre a alimentação do projeto, poderá usar o cabo USB conectado ao seu PC. Ou então conecte o cabo USB em um carregador de celular que tem saída USB 5 Volts. E mais uma alternativa, se quiser mobilidade, conecte o cabo USB à um Battery Pack com saída USB 5 V.

Se quiser aprender mais sobre o uso dos Displays LCD, sugiro a leitura desse meu outro tutorial :

Guia completo do Display LCD – Arduino

Diagrama para Display com interface I2C (PCF8574) (Para ampliar a imagem, abra-a em outra aba)

Não se esqueça de ajustar o brilho do display, no potenciômetro na placa de Interface I2C (PCF8574) – conectada atrás do display. Mantenha o jumper conectado.

Diagrama para Display com interface paralela (Para ampliar a imagem, abra-a em outra aba).

O potenciômetro de 10 K ohms é essencial nesse circuito, para o ajuste do brilho do Led de backlight.  A tensão no pino central do POT deverá ser ajustada para aproximadamente 1 V em relação ao terra (GND).

Programa ESP32 Frequencímetro:

Diretivas para seleção do Display

O compilador da Arduino IDE pode usar as diretivas de compilação do programa para habilitar e selecionar o tipo de display LCD:

  • Uso de LCD paralelo – altere para LCD_ON ou LCD_OFF:

           #define LCD_OFF     ou     #define LCD_ON       // ON se for usar esse display 

  • Uso de LCD I2C – altere para LCD_I2C_ON ou LCD_I2C_OFF.

           #define LCD_I2C_ON     ou      #define LCD_I2C_OFF  // ON se for usar esse display

OBs: se não souber o endereço da interface I2C do seu LCD, use o ESP32 I2C Scanner para descobrir: (o endereço da minha placa é 0x3F):

ESP32 – I2C scanner

Mas se não quiser usar nenhum display LCD, poderá visualizar as medições de frequências na Console Serial da Arduino IDE. E através dessa mesma console, poderá digitar a frequência de teste desejada entre um valor de 1 Hz a 40 MHz. Muito legal isso! Dessa forma poderá confirmar se fez a montagem correta do projeto e testar o frequencímetro.

Calibração do Frequencimetro:

Mais uma vantagem desse projeto, é que ele permite uma calibração na medição da frequência. Os valores de medição, normalmente apresentados com o ESP32 são muito satisfatórios. Com o uso de um frequencímetro de precisão, usado como referência, pode-se ajustar o valor da variável janela no programa. Alterando esse valor, poderá conseguir uma precisão ainda maior do que a obtida. Nos meus testes do frequencímetro, usando a função de medição de frequência do meu osciloscópio, alterei o valor da janela e consegui obter os mesmos valores do osciloscópio!

ESP32_Frequencimetro.ino

// 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 
uint32_t        oscilador     = 12543;                                    // Frequencia inicial do oscilador - 12543 Hz
uint32_t        mDuty         = 0;                                        // Valor calculado do ciclo de trabalho
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 trabalho 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 trabalho
  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
  }
}
Se você optou em não montar nenhum dos dois tipos de display LCD, poderá verificar as medições de frequência na

Console Serial da Arduino IDE

. A velocidade da console deverá ser configurada para

115200 Bps

. Para alterar a frequência de teste do frequencímetro, digite um valor de 1 Hz a 40 MHz no campo de entrada e pressione enter. Não é preciso entrar com a unidade de Hertz (Hz). Mas para fazer esse teste, lembre-se que a

Entrada do Frequencímetro (GPIO34)

 precisa estar conectada na saída do

Oscilador (GPIO33)


Sobre os Autores


Rui Viana

Apaixonado por eletrônica desde o início do “ginásio”. Técnico em eletrônica, formado pelo curso Monitor de rádio e televisão em 1966. Possui curso de engenharia Incompleto.
Trabalhou como técnico em eletrônica, na RCA Victor, fábrica de válvulas e em uma fábrica de embalagens. Trabalhou por muitos anos na IBM, na área de manutenção de computadores de grande porte.
Empresário por 20 anos na área de projetos de fibra óptica para grandes Data Centers e plataformas de Petróleo. Aposentou-se, mas continua enfrentando os desafios da tecnologia, principalmente na eletrônica digital e na programação. Aprendendo e ensinando às novas gerações, transferindo conhecimento adquirido e aprendido. Sonhador nas horas vagas


José Gustavo Abreu Murta

Consultor e Projetista de Sistemas Embarcados. Técnico em eletrônica, formado em Curso superior de TPD, pós-graduado em Marketing. Trabalhou por muitos anos na IBM na área de manutenção de computadores de grande porte. Aposentou-se, podendo curtir o que mais gosta : estudar e ensinar Tecnologia. Hobista em eletrônica desde 1976. Gosta muito de Fotografia e Observação de aves.


Eletrogate

13 de agosto de 2020

A Eletrogate é uma loja virtual de componentes eletrônicos do Brasil e possui diversos produtos relacionados à Arduino, Automação, Robótica e Eletrônica em geral.

Tenha a Metodologia Eletrogate dentro da sua Escola! Conheça nosso Programa de Robótica nas Escolas!

Eletrogate Robô

Cadastre-se e fique por
dentro de novidades!