RuiViana

ESP32_FreqMeter_0.33.0.c

Jul 16th, 2020
1,194
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // Atual ESP32_FreqMeter_0.33.0.ino    Antigo NovoTeste08.ino  
  2.  
  3. // V01 Teste com pcnt
  4. // V02 teste com timer
  5. // V03 Teste de interrupt
  6. // V04 Definicao com escala baseada no prescaler
  7. // V05 Calculos com interrupt
  8. // V06 Versão inicial do frequeciimetro com ESP32 nao esta preciso , Seleciona a escala.
  9. // V07 Modificacoes no loop pra melhorar a escala  funcionou bem
  10. // V07_A Fixando o valor de prescaler em 80   Criada 5a escala
  11. // V07_B teste de erro na rotina pcnt
  12. // V08 Sem escala, fixando o prescaler e o timer e contando os overflows
  13. // V09 Limpando a V08
  14. // V10 Testes com rotina de contagem de overflow
  15. // V11 Teste de overflow de pcnt e timer  funciona  teste com frequenci DE 10 khZ  Copia do frequencimetro 2 modificado
  16. // V12 Resumir e limpar V11  enroscou a partir de 300 KHz
  17. // V13 Consegui bom resultado deste aqui até 20MHz Usando RMT com referencia de tempo
  18. // V14 Interrupt de pcnt e contar tempo de RMT
  19. // V15 Novo teste com RMT
  20. // V16 Qtde de pulsos entre ocorrencias de interrupt
  21. // V17 Outro teste de ocorrencias de interrupt
  22. // V18 Teste com rotina de interrupt com poucas funcoes
  23. // V19 Teste com esp-timer  e interrupt no fim do pulso do esp-timer
  24. // V20 Teste = V19
  25. // V20a Preparada para IDF
  26. // V20aGustavo Sem interrupt de esp-timer
  27. // V21 Inclusao de LCD com diretivas de compilacao
  28. // 0.0.21 = V21
  29. // 0.0.22  Pontuação do numero
  30. // V23 Teste com 2 rotinas de pontuacao
  31. // V24 Parametros de ledc calculados   Diretivas de compilacao para ARDUINO e IDF
  32. // V25 Nova rotina de pontuação
  33. // V26 Usando printf na rotina de pontuacao
  34. // V27 Teste com IDF  Compilou com a IDF do CEI
  35. // V28 Teste com nova rotina de pontuacao e printf
  36. // V30 Rotina de teste depois de erro no IDF e limpeza de variaveis desnecessarias e organização
  37. // V31 Nova Diretiva de compilação  ARDUINO   Inclusão de LCD  ok
  38. // V32 Novamente com rotinas de LCD Mudanca do formato dos port GPIO
  39.  
  40. // V33 Calculo de Resolucao com base na frequencia e duty de 50%, inclusao de LCD I2C com diretiva e teste gpio matrix incluido
  41.  
  42.  
  43. /*
  44.  
  45.   Desenvolvedores: Rui Viana e Gustavo Murta   08/jul/2020
  46.  
  47.   Para desenvolver este projeto, foram aproveitadas partes de códigos dos desenvolvedores
  48.   abaixo referenciados.
  49.  
  50.   O Projeto:
  51.   Um frequencímetro usando ESP32, sem necessidade de escalas e mostrando até 8 dígitos,
  52.   medindo com precisão até 20MHz ou mais.
  53.   Este projeto pode ser compilado com a IDE do arduino ou com o IDF
  54.  
  55.   Definições:
  56.  
  57.   #define PCNT_INPUT_SIG_IO     GPIO_NUM_34                                 // Freq Meter Input GPIO 34
  58.   #define LEDC_HS_CH0_GPIO      GPIO_NUM_25
  59.  
  60.   #define PCNT_INPUT_CTRL_IO    GPIO_NUM_35                                 // Count Control GPIO HIGH=count up, LOW=count down GPIO 25
  61.   #define OUTPUT_CONTROL_GPIO   GPIO_NUM_32                                 // Saida do timer GPIO 2
  62.  
  63.   PORT de entrada do frequencímetro PCNT_INPUT_SIG_IO (GPIO 34)
  64.   PORT de entrada de controle PCNT_INPUT_CTRL_IO      (GPIO 35)
  65.   PORT de saída do timer OUTPUT_CONTROL_GPIO          (GPIO 32)
  66.   O PORT de entrada de controle (GPIO 35) deve ser ligado ao PORT de saída do timer (GPIO 32).
  67.   Estes são os ports usados no projeto, mas podem ser modificados para sua melhor conveniência.
  68.  
  69.   O frequencímetro é dividido em 5 partes:
  70.     1. Contador de pulsos;
  71.     2. Controle de tempo de contagem;
  72.     3. Impressão do resultado;
  73.     4. Espaço para outras funções.
  74.     5. Gerador de sinais programado para 2  Hz  (ou 50.000)
  75.  
  76.   1. O contador de pulso usa o pcnt do ESP32.
  77.      O pcnt tem os seguintes parâmetros:
  78.       a. port de entrada;
  79.       b. canal de entrada;
  80.       c. port de controle;
  81.       d. contagem na subida do pulso;
  82.       e. contagem na descida do pulso;
  83.       f. contagem só com o controle em nível elevado;
  84.       g. limite máximo de contagem.
  85.  
  86.   2. O Controle de tempo de contagem usa o esp-timer.
  87.      O esp-timer tem o seguinte parâmetro:
  88.       a. controle do tempo;
  89.  
  90.   5. Gerador de frequncias para testes usa o ledc
  91.      O ledc tem o seguinte parâmetro:
  92.       a. port de saida;
  93.       b. canal de lcd;
  94.       c. frequencia;
  95.       d. resolucao do ledc;
  96.       e. duty cycle em 5;
  97.  
  98.  
  99.   Funcionamento:
  100.     O port de controle de contagem em nível alto, libera o contador para contar os pulsos que chegam no port de entrada de pulsos.
  101.   Os pulsos são contado tanto na subida quanto na descida do pulso, para melhorar a media de contagem.
  102.   O tempo de contagem é definido pelo esp-timer, e esta' definido em 1 segundo, na variável janela.
  103.   Se a contagem for maior que 20000 pulsos durante o tempo de contagem, ocorre overflow e a cada overflow que ocorre
  104.   e' contabilizado na variável multPulses, e o contador de pulso retorna a zero continuando a contar.
  105.     Quando o tempo de leitura termina, uma rotina é chamada e o valor do no contador de pulsos e' lido e salvo,
  106.     um flag e' ligado indicando que terminou a leitura dos pulsos
  107.  
  108.     No loop, ao verificar que o flag indica que terminou a leitura dos pulsos, o valor é calculado multiplicando
  109.   o numero de overflow por 20000 e somando ao numero de pulsos restantes e dividindo por 2, pois contou 2 vezes.
  110.   Como o pulsos são contados na subida e na descida, a contagem e´ o dobro da frequência.
  111.     Na frequência é insserido pontos e impressa no serial monitor.
  112.   Os registradores são resetados e o port de controle de entrada é novamente elevado para nível alto e a contagem de
  113.   pulsos se inicia.
  114.  
  115.   Tem também um gerador de sinais que gera 50.000 Hz, e pode ser usado para testes.
  116.   Este gerador pode ser alterado para gerar frequencias até 40 MHz.
  117.   Usamos o recurso ledc do ESP32 para gerar frequencia que pode ser usada como teste.
  118.     O valor da frequencia base é 2 (ou 50.000) Hz, mas pode ser digitavo outo valor no serial monitor
  119.     O duty foi fixado como 50%
  120.     A resulução é calculada.
  121.   O Port de saida deste gerador é definido na linha #define LEDC_GPIO.
  122.   Atualmente está definido como GPIO 25.
  123.  
  124.   Internamente usando GPIO matrix,o pulso de entrada foi direcionado para o LED nativo do ESP32,
  125.   assim o LED piscara na frequencia de entrada.
  126.  
  127.   O compilador usa as diretivas de compilacaoo para selecionar:
  128.    Compilador Arduino ou IDF    automatico
  129.    Uso de LCD                   LCD_ON ou LCD_OFF
  130.    Uso de LCD I2C               LCD_I2C_ON ou LCD_I2C_OFF
  131.  
  132.   Referências:
  133.   author=krzychb https://github.com/espressif/esp-idf/tree/master/examples/peripherals/pcnt
  134.   resposta by Deouss » Thu May 17, 2018 3:07 pm no tópico https://esp32.com/viewtopic.php?t=5734
  135.   Gerador de sinais Gustavo https://github.com/Gustavomurta/ESP32_frequenceMeter/blob/master/ESP32OscilatorV03.ino
  136.   Formatação de numero https://arduino.stackexchange.com/questions/28603/the-most-effective-way-to-format-numbers-on-arduino
  137.   https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/esp_timer.html
  138.   https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/pcnt.html
  139.   Agradecemos muito também ao CELSO ITO pelas sugestões e pelos incansáveis teste com IDF.
  140. */
  141.  
  142. #define LCD_OFF                                                           // Define LCD_ON, para usar LCD, se não, defina LCD_OFF
  143. #define LCD_I2C_OFF                                                       // Define LCD_I2C_ON, para usar LCD I2C, se não, defina LCD_I2C_OFF
  144.  
  145. #include <stdio.h>
  146. #include "freertos/FreeRTOS.h"
  147. #include "freertos/portmacro.h"
  148. #include "freertos/task.h"
  149. #include "freertos/queue.h"
  150. #include "driver/periph_ctrl.h"
  151. #include "driver/ledc.h"
  152. #include "driver/gpio.h"
  153. #include "driver/pcnt.h"
  154. #include "driver/ledc.h"
  155. #include "esp_attr.h"
  156. #include "esp_log.h"
  157. #include "esp_timer.h"
  158. #include "sdkconfig.h"
  159. #include "math.h"
  160.  
  161. #ifdef LCD_ON                                                             // Se tem LCD
  162.  
  163. #ifdef LCD_I2C_ON                                                         // Se tem LCD I2C
  164. #include <LiquidCrystal_I2C.h>                                            // Inclue a bibliotea do LCD I2C
  165. LiquidCrystal_I2C lcd(0x3F, 16, 2);                                       // Instancia e define I2C addr
  166. #else
  167. #include <LiquidCrystal.h>                                                // Inclue a bibliotea do LCD
  168. LiquidCrystal lcd(5, 18, 19, 21, 22, 23);                                 // Instancia e define port
  169. #endif
  170.  
  171. #endif
  172.  
  173. #define PCNT_COUNT_UNIT       PCNT_UNIT_0                                 // Unidade 0 do pcnt
  174. #define PCNT_COUNT_CHANNEL    PCNT_CHANNEL_0                              // Canal 0 do pcnt
  175.  
  176. //                 Para usar teste jumper GPIO 25 com GPIO 34
  177. //                 Port de entrada do frequencimetro  GPIO 34
  178. #define PCNT_INPUT_SIG_IO     GPIO_NUM_34                                 // Freq Meter Input GPIO 34
  179. #define LEDC_HS_CH0_GPIO      GPIO_NUM_25                                 // Saida do ledc gerador de pulsos
  180.  
  181. //                  Necessario jumper entre GPIO 32 e GPIO 35
  182. #define PCNT_INPUT_CTRL_IO    GPIO_NUM_35                                 // Count Control GPIO HIGH = count up, LOW = count down
  183. #define OUTPUT_CONTROL_GPIO   GPIO_NUM_32                                 // Saida do timer GPIO 32 Controla a contagem
  184.  
  185. #define IN_BOARD_LED          (gpio_num_t)2                                // LED nativo ESP32 GPIO 2
  186.  
  187. #define LEDC_HS_CH0_CHANNEL   LEDC_CHANNEL_0                              // LEDC no canal 0
  188. #define LEDC_HS_MODE          LEDC_HIGH_SPEED_MODE                        // LEDC em high speed
  189. #define LEDC_HS_TIMER         LEDC_TIMER_0                                // Usar timer0 do ledc
  190.  
  191. uint32_t         overflow  =  20000;                                      // Valor maximo para overflow de pcnt
  192. #define PCNT_H_LIM_VAL        overflow                                    // Limite superior de contagem
  193.  
  194. esp_timer_create_args_t create_args;                                      // Argumentos do esp-timer
  195. esp_timer_handle_t timer_handle;                                          // Instancia de esp-timer
  196.  
  197. //  Calculo do ajustes para cada faixa de frequencia
  198. //  Resolucao = log2(Clock(80MHz)/f) + 1   ex: 50.000 HZ = 80.0000/50.000 = 1.600 log2(1600) = 10 + 1 = 11
  199. //  Duty 50%  = (2**Resolucao)/2       ex: 2**11 = 2048   2048/2 = 1024
  200.  
  201. bool            flag          = true;                                     // Indicador de fim de contagem libera impressao
  202. int16_t         pulses        = 0;                                        // Contador de pulsos de entrada
  203. uint32_t        multPulses    = 0;                                        // Contador de overflows de pcnt
  204. uint32_t        janela        = 1000000;                                  // Janela de 1 segundo para a contagem de pulsos
  205. uint32_t        oscilator     = 2;                                        // Frequencia em Hz
  206. uint32_t        mDuty         = 0;                                        // Valor calculado do duty
  207. uint32_t        resolucao         = 0;                                    // Valor calculado da resolucao
  208.  
  209. portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;                     // variavel tipo portMUX_TYPE para sincronismo
  210. //----------------------------------------------------------------------------------------
  211. // Sem comentarios originais  https://arduino.stackexchange.com/questions/28603/the-most-effective-way-to-format-numbers-on-arduino
  212. char *ultos_recursive(unsigned long val, char *s, unsigned radix, int pos)
  213. {
  214.   int c;
  215.   if (val >= radix)
  216.     s = ultos_recursive(val / radix, s, radix, pos + 1);
  217.   c = val % radix;
  218.   c += (c < 10 ? '0' : 'a' - 10);
  219.   *s++ = c;
  220.   if (pos % 3 == 0) *s++ = '.';
  221.   return s;
  222. }
  223. //----------------------------------------------------------------------------------------
  224. // Sem comentarios originais  https://arduino.stackexchange.com/questions/28603/the-most-effective-way-to-format-numbers-on-arduino
  225. char *ltos(long val, char *s, int radix)
  226. {
  227.   if (radix < 2 || radix > 36) {
  228.     s[0] = 0;
  229.   } else {
  230.     char *p = s;
  231.     if (radix == 10 && val < 0) {
  232.       val = -val;
  233.       *p++ = '-';
  234.     }
  235.     p = ultos_recursive(val, p, radix, 0) - 1;
  236.     *p = 0;
  237.   }
  238.   return s;
  239. }
  240. //----------------------------------------------------------------------------
  241. void ledcInit ()
  242. {
  243.   resolucao = log((80000000 / oscilator) + 1);                            // Calculo da resolucao para ledc
  244.   //  Serial.println(resolucao);
  245.   mDuty = (pow(2, resolucao)) / 2;                                        // Calculo do duty para ledc
  246.   //  Serial.println(mDuty);
  247.  
  248.   ledc_timer_config_t ledc_timer = {};                                    // Instancia a configuracao do timer do ledc
  249.  
  250.   ledc_timer.duty_resolution = (ledc_timer_bit_t) + resolucao;            // Configura resolucao
  251.   ledc_timer.freq_hz    = oscilator;                                      // Frequencia de oscilacao
  252.   ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;                           // Mode de operacao em high speed
  253.   ledc_timer.timer_num = LEDC_TIMER_0;                                    // Usar timer0 do ledc
  254.   ledc_timer_config(&ledc_timer);                                         // Configurar o timer do ledc
  255.  
  256.   ledc_channel_config_t ledc_channel = {};                                // Instancia a configuracao canal do ledc
  257.  
  258.   ledc_channel.channel    = LEDC_HS_CH0_CHANNEL;                          // Configura canal0
  259.   ledc_channel.duty       = mDuty;                                        // Valor calculado do duty em %
  260.   ledc_channel.gpio_num   = LEDC_HS_CH0_GPIO;                             // Saida no GPIO defino no inicio
  261.   ledc_channel.intr_type  = LEDC_INTR_DISABLE;                            // Desabilita interrupt de ledc
  262.   ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE;                         // Mode de operacao do canal em high speed
  263.   ledc_channel.timer_sel  = LEDC_TIMER_0;                                 // Usar timer0 do ledc
  264.  
  265.   ledc_channel_config(&ledc_channel);                                     // Configurar o canal do ledc
  266. }
  267. //----------------------------------------------------------------------------------
  268. void tempo_controle(void *p)                                              // Fim de tempo de leitura de pulsos
  269. {
  270.   gpio_set_level(OUTPUT_CONTROL_GPIO, 0);                                 // Controle do PCount - stop count
  271.   pcnt_get_counter_value(PCNT_COUNT_UNIT, &pulses);                       // Obtem o valor contado
  272.   flag = true;                                                            // Informa que ocorreu interrupt de controle
  273. }
  274. //----------------------------------------------------------------------------------
  275. static void IRAM_ATTR pcnt_intr_handler(void *arg)                        // Overflow de contagem de pulsos
  276. {
  277.   portENTER_CRITICAL_ISR(&timerMux);                                      // Desabilita interrupção ?
  278.   multPulses++;                                                           // Incrementa contador de overflow
  279.   PCNT.int_clr.val = BIT(PCNT_COUNT_UNIT);                                // Limpa indicador de interrupt
  280.   portEXIT_CRITICAL_ISR(&timerMux);                                       // Libera novo interrupt
  281. }
  282. //----------------------------------------------------------------------------------
  283. void pcnt_init(void)                                                      // Rotina de inicializacao do pulse count
  284. {
  285.   pcnt_config_t pcnt_config = { };                                        // Instancia pulse config
  286.  
  287.   pcnt_config.pulse_gpio_num = PCNT_INPUT_SIG_IO;                         // Port de entrada dos pulsos
  288.   pcnt_config.ctrl_gpio_num = PCNT_INPUT_CTRL_IO;                         // Controle da contagem
  289.   pcnt_config.unit = PCNT_COUNT_UNIT;                                     // Unidade de contagem
  290.   pcnt_config.channel = PCNT_COUNT_CHANNEL;                               // Canal de contagem
  291.   pcnt_config.counter_h_lim = PCNT_H_LIM_VAL;                             // Limite maximo de contagem
  292.   pcnt_config.pos_mode = PCNT_COUNT_INC;                                  // Conta na subida do pulso
  293.   pcnt_config.neg_mode = PCNT_COUNT_INC;                                  // Conta na descida do pulso
  294.   pcnt_config.lctrl_mode = PCNT_MODE_DISABLE;                             // Nao usado
  295.   pcnt_config.hctrl_mode = PCNT_MODE_KEEP;                                // Se HIGH conta incrementando
  296.   pcnt_unit_config(&pcnt_config);                                         // Inicializa PCNT
  297.  
  298.   pcnt_counter_pause(PCNT_COUNT_UNIT);                                    // Inicializa o contador PCNT
  299.   pcnt_counter_clear(PCNT_COUNT_UNIT);                                    // Zera o contador PCNT
  300.  
  301.   pcnt_event_enable(PCNT_COUNT_UNIT, PCNT_EVT_H_LIM);                     // Limite superior de contagem
  302.   pcnt_isr_register(pcnt_intr_handler, NULL, 0, NULL);                    // Rotina de Interrupt de pcnt
  303.   pcnt_intr_enable(PCNT_COUNT_UNIT);                                      // Habilita interrup de pcnt
  304.  
  305.   pcnt_counter_resume(PCNT_COUNT_UNIT);                                   // inicia a contagem
  306. }
  307. //----------------------------------------------------------------------------------
  308. void myInit()
  309. {
  310. #ifdef LCD_ON                                                             // Se tem LCD
  311.   lcd.begin(16, 2);                                                       // Inicializa lcd
  312.   lcd.print("Frequencimetro");                                            // Print
  313. #endif
  314.  
  315.   ledcInit();                                                             // Inicializa o ledc
  316.   pcnt_init();                                                            // Inicializa o pulse count
  317.   gpio_pad_select_gpio(OUTPUT_CONTROL_GPIO);                              // Define o port decontrole
  318.   gpio_set_direction(OUTPUT_CONTROL_GPIO, GPIO_MODE_OUTPUT);              // Define o port de controle como saida
  319.  
  320.   create_args.callback = tempo_controle;                                  // Instancia o tempo de controle
  321.   esp_timer_create(&create_args, &timer_handle);                          // Cria parametros do timer
  322.  
  323.   gpio_set_direction(IN_BOARD_LED, GPIO_MODE_OUTPUT);                     // Port LED como saida
  324.  
  325.   gpio_matrix_in(PCNT_INPUT_SIG_IO, SIG_IN_FUNC226_IDX, false);           // Direciona a entrada de pulsos
  326.   gpio_matrix_out(IN_BOARD_LED, SIG_IN_FUNC226_IDX, false, false);        // Para o LED do ESP32
  327. }
  328. //---------------------------------------------------------------------------------
  329. void app_main(void)
  330. {
  331. #ifndef ARDUINO                                                           // IDF
  332.   myInit();                                                               // IDF
  333.   while (1)                                                               // IDF
  334.   {
  335. #endif
  336.     if (flag == true)                                                     // Se a contagem tiver terminado
  337.     {
  338.       flag = false;                                                       // Impede nova impresao
  339.       float frequencia = 0;                                               // Variavel para calculo de frequencia
  340.       frequencia = (pulses + (multPulses * overflow)) / 2  ;              // Calcula qtos pulsos ocorreram
  341.       char buf[32];                                                       // Buffer para guardar a pontuacao
  342.       printf("frequencia: %s", (ltos(frequencia, buf, 10)));              // Imprime pontuado
  343.       printf(" Hz \n");                                                   // Sufixo
  344. #ifdef LCD_ON                                                             // LCD
  345.       lcd.setCursor(2, 1);                                                // Posiciona cusros na posicao 3 da linha 2
  346.       lcd.print((ltos(frequencia, buf, 10)));                             // Print
  347.       lcd.print(" Hz              ");                                     // Sufixo
  348. #endif
  349.       multPulses = 0;                                                     // Zera contador de overflow
  350.       // Aqui pode rodar qq funcao                                        // Espaco para qualquer função
  351.       vTaskDelay(1);
  352.       // Aqui pode rodar qq funcao                                        // Espaco para qualquer função
  353.       pcnt_counter_clear(PCNT_COUNT_UNIT);                                // Zera o contador PCNT
  354.       esp_timer_start_once(timer_handle, janela);                         // Inicia contador de tempo de 1 segundo
  355.       gpio_set_level(OUTPUT_CONTROL_GPIO, 1);                             // Porta de controle habilita contagem dos pulsos
  356.     }
  357. #ifndef ARDUINO                                                           // IDF
  358.   }                                                                       // IDF
  359. #endif
  360. }
  361. //---------------------------------------------------------------------------------
  362. #ifdef ARDUINO                                                            // Arduino
  363. void setup()
  364. {
  365.   Serial.begin(115200);                                                   // Inicializa a serial
  366.   myInit();                                                               // Chaama inicializacao
  367. }
  368. //---------------------------------------------------------------------------------
  369. void loop()
  370. {
  371.   app_main();                                                             // Roda rotina principal
  372.   String inputString = "";                                                // Temporaria transformar o valor da frequencia
  373.   oscilator = 0;                                                          // Limpa valor original
  374.   while (Serial.available())                                              // Enquanto tiver dados na serial
  375.   {
  376.     char inChar = (char)Serial.read();                                    // Le um byte:
  377.     inputString += inChar;                                                // Adicione na string:
  378.     if (inChar == '\n')                                                   // Se new line:
  379.     {
  380.       oscilator = inputString.toInt();                                    // Transforma a string em inteiro
  381.       inputString = "";                                                   // Limpa a string
  382.     }
  383.   }
  384.   if (oscilator != 0)                                                     // Se foi digitado algum valor
  385.   {
  386.     ledcInit();                                                           // Reconfigura ledc
  387.   }
  388. }
  389. #endif
RAW Paste Data