矿石收音机论坛

 找回密码
 加入会员

QQ登录

只需一步,快速开始

搜索
查看: 3230|回复: 16

要聽 BPM, 先做個 40MHz 的頻率計

[复制链接]
发表于 2023-2-9 23:24:12 | 显示全部楼层 |阅读模式
對普通人等或者小學等級的玩具, 通常失敗居多, 收聽 BPM 15MHz 設計的電路還沒成功, 就是上一篇記錄的.
http://www.crystalradio.cn/forum ... ead&tid=2055837

猜想原因有幾個, 會不會接收 2.5/5/10MHz 比較容易? 天線不好? 接受電路共振頻率不對? 發射時間對不上? 地點收訊有問體? 電腦的SDR設定有問題? 所有這些猜想, 都跟震盪頻率有關, 也沒有工具驗證, 所以玩不轉了.

找了一下資訊, 有大神等級的人設計了一個30元左右的頻率計, 電路設計很簡單, 幾根線接好就可以實驗, 還說可以測量到達 40MHz. 另外一個設計還可以到 100MHz, 用料大概也是30元錢就能做出來. 一向都害怕蜘蛛網一樣的電路圖, 多幾根線飛來飛去看起來就怕了, 難度太大不好學也不容易成功, 恐怕沒做成可能熱情就退卻了. 對這種接線簡單, 容易明白的設計比較有興趣, 因為很快就可以實驗得到結論.

抄襲了一下, 果然半小時就可以搭出一個 20MHz 的頻率計和方波訊號源, 因為沒有工具比對它測 40MHz 能力, 只有 20MHz 的示波器比較, 確定了 100Hz ~ 20MHz 以下都是可靠的, 完全是無段任意調整頻率, 同時觀看了一些其他玩家的實際操作過程, 應該沒問題好工具, 所以抄襲遠比自己從零開始要快速達陣.

慢慢寫下搭棚的過程, 其實比打字快很多, 半小時不到.

评分

1

查看全部评分

     
发表于 2023-2-10 05:21:24 | 显示全部楼层
图呢?分享一下。
回复 支持 反对

使用道具 举报

     
发表于 2023-2-10 07:44:00 | 显示全部楼层
一般收音机都能收BPM,经常在10MHz听到,整点人声报,标准时间标准频率发播台。

评分

1

查看全部评分

回复 支持 反对

使用道具 举报

     
发表于 2023-2-10 09:31:54 | 显示全部楼层
这么简单不发个图分享一下
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-2-10 19:59:18 | 显示全部楼层
本帖最后由 xiaolaba 于 2023-2-10 20:02 编辑
L北斗 发表于 2023-2-10 05:21
图呢?分享一下。


閒餘的玩具, 只能每天寫一點, 玩過了, 容後慢慢一點一點筆記
最近搬出來用, 有些小小的疑問, 又問了一下源作者
整理好了筆記一次貼圖就比較容易看明白
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-2-10 20:00:10 | 显示全部楼层
hawkins 发表于 2023-2-10 09:31
这么简单不发个图分享一下

寫作筆記中, 過幾天就寫好.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-2-14 22:18:34 | 显示全部楼层
上個圖,測試時拍的
offset.JPG
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-2-15 22:58:07 | 显示全部楼层
接線圖
xiaolaba_esp32_40MHz_fre_counter_circuit_diagram - small.jpg
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-2-15 23:12:44 | 显示全部楼层
抄來小改過的源碼
  1. // BLOG Eletrogate
  2. // ESP32 Frequencimetro
  3. // ESP32 DevKit 38 pinos + LCD
  4. // https://blog.eletrogate.com/esp32-frequencimetro-de-precisao
  5. // Rui Viana e Gustavo Murta agosto/2020

  6. #include "stdio.h"                  // STDIO
  7. #include "driver/ledc.h"          //ESP32 LEDC
  8. #include "driver/pcnt.h"          //ESP32 PCNT
  9. #include "soc/pcnt_struct.h"

  10. // select one of below only, xiaolaba
  11. #define USE_LCD_44780 // 1602 LCD, parallel 4 bit mode, HD44780 compatible
  12. //#define USE_LCD_I2C   // 1602 LCD, I2C mode, HD44780 compatible

  13. #ifdef USE_LCD_I2C                  // LCD I2C
  14. #define I2C_SDA 21                  // LCD I2C SDA - GPIO_21
  15. #define I2C_SCL 22                   // LCD I2C SCL - GPIO_22
  16. #include <Wire.h>                  // Biblioteca para I2C
  17. #include <LiquidCrystal_PCF8574.h>  // Biblioteca para LCD com PCF8574
  18. LiquidCrystal_PCF8574 lcd(0x3F);       // Instancia LCD I2C com endere&#231;o x3F
  19. #endif                                      

  20. #ifdef USE_LCD_44780              //LCD 44780 interface 4 bits
  21. #include <LiquidCrystal.h>                 //library for LCD
  22. LiquidCrystal lcd(4, 16, 17, 5, 18, 19);      // port pin# of GPIO#
  23. #endif                                                

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

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

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

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

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

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

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

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

  54. #if defined USE_LCD_44780   || defined USE_LCD_I2C                                 // Se estiver usando LCD ou LCD I2C      
  55.   lcd.begin(16, 2);                                                       // Inicializa LCD 16 colunas 2 linhas
  56.   lcd.print("  Frequencia:");                                             // Print no LCD
  57. #endif

  58.   inicializa_frequencimetro();                                            // Inicializa o frequencimetro
  59. }

  60. //----------------------------------------------------------------------------
  61. void inicializa_oscilador ()                                              // Inicializa gerador de pulsos
  62. {
  63.   resolucao = (log (80000000 / oscilador)  / log(2)) / 2 ;                // Calculo da resolucao para o oscilador
  64.   if (resolucao < 1) resolucao = 1;                                       // Resolu&#231;ao mínima
  65.   // Serial.println(resolucao);                                           // Print
  66.   mDuty = (pow(2, resolucao)) / 2;                                        // Calculo do ciclo de carga 50% do pulso
  67.   // Serial.println(mDuty);                                               // Print

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

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

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

  75.   ledc_channel.channel    = LEDC_CHANNEL_0;                               // Configura canal 0
  76.   ledc_channel.duty       = mDuty;                                        // Configura o ciclo de carga
  77.   ledc_channel.gpio_num   = LEDC_HS_CH0_GPIO;                             // Configura GPIO da saida do LEDC - oscilador
  78.   ledc_channel.intr_type  = LEDC_INTR_DISABLE;                            // Desabilita interrup&#231;&#227;o do LEDC
  79.   ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE;                         // Modo de operacao do canal em alta velocidade
  80.   ledc_channel.timer_sel  = LEDC_TIMER_0;                                 // Seleciona timer 0 do LEDC
  81.   ledc_channel_config(&ledc_channel);                                     // Configura o canal do LEDC
  82. }

  83. //----------------------------------------------------------------------------------
  84. static void IRAM_ATTR pcnt_intr_handler(void *arg)                        // Contagem do contador de Overflow
  85. {
  86.   portENTER_CRITICAL_ISR(&timerMux);                                      // Bloqueia nova interrup&#231;&#227;o
  87.   multPulses++;                                                           // Incrementa contador de overflow
  88.   PCNT.int_clr.val = BIT(PCNT_COUNT_UNIT);                                // Limpa indicador de interrup&#231;&#227;o
  89.   portEXIT_CRITICAL_ISR(&timerMux);                                       // Libera nova interrup&#231;&#227;o
  90. }

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

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

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

  107.   pcnt_event_enable(PCNT_COUNT_UNIT, PCNT_EVT_H_LIM);                     // Configura limite superior de contagem
  108.   pcnt_isr_register(pcnt_intr_handler, NULL, 0, NULL);                    // Conigura rotina de interrup&#231;&#227;o do PCNT
  109.   pcnt_intr_enable(PCNT_COUNT_UNIT);                                      // Habilita interrup&#231;&#245;es do PCNT

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

  112. //----------------------------------------------------------------------------------
  113. void tempo_controle(void *p)                                              // Fim de tempo de leitura de pulsos
  114. {
  115.   gpio_set_level(OUTPUT_CONTROL_GPIO, 0);                                 // Controle do PCNT - para o contador
  116.   pcnt_get_counter_value(PCNT_COUNT_UNIT, &pulses);                       // Obtem o valor contado no PCNT
  117.   flag = true;                                                            // Informa que ocorreu interrup&#231;&#227;o de controle
  118. }

  119. //---------------------------------------------------------------------------------
  120. void inicializa_frequencimetro()
  121. {
  122.   inicializa_oscilador ();                                                // Inicia a gera&#231;&#227;o de pulsos no oscilador
  123.   inicializa_contador();                                                  // Inicializa o contador de pulsos PCNT

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

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

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

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

  132. //----------------------------------------------------------------------------------------
  133. char *ultos_recursive(unsigned long val, char *s, unsigned radix, int pos) // Formata um número longo de 32 bits com pontos
  134. {
  135.   int c;
  136.   if (val >= radix)
  137.     s = ultos_recursive(val / radix, s, radix, pos + 1);
  138.   c = val % radix;
  139.   c += (c < 10 ? '0' : 'a' - 10);
  140.   *s++ = c;
  141.   if (pos % 3 == 0) *s++ = '.';
  142.   return s;
  143. }
  144. //----------------------------------------------------------------------------------------
  145. char *ltos(long val, char *s, int radix)    // Formata um número longo de 32 bits com pontos
  146. {
  147.   if (radix < 2 || radix > 36) {
  148.     s[0] = 0;
  149.   } else {
  150.     char *p = s;
  151.     if (radix == 10 && val < 0) {
  152.       val = -val;
  153.       *p++ = '-';
  154.     }
  155.     p = ultos_recursive(val, p, radix, 0) - 1;
  156.     *p = 0;
  157.   }
  158.   return s;
  159. }

  160. //---------------------------------------------------------------------------------
  161. void loop()
  162. {
  163.   if (flag == true)         // Se a contagem tiver terminado
  164.   {
  165.     flag = false;         // Impede nova impressao
  166.     frequencia = (pulses + (multPulses * overflow)) / 2  ;                // Calcula a soma dos pulsos contados no PCNT
  167.     printf("Frequencia : %s", (ltos(frequencia, buf, 10)));               // Print frequencia com pontos
  168.     printf(" Hz \n");     // Print unidade Hz

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

  174.     multPulses = 0;                                                       // Zera contador de overflow
  175.     // Espaco para qualquer fun&#231;&#227;o
  176.     delay (100);                                                          // Delay 100 ms
  177.     // Espaco para qualquer fun&#231;&#227;o

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

  182.   String inputString = "";        // Limpa string para entrada de dados
  183.   oscilador = 0;                     // Zera o valor da frequencia
  184.   while (Serial.available())       // Enquanto tiver dados na serial
  185.   {
  186.     char inChar = (char)Serial.read();   // Le um byte:
  187.     inputString += inChar;                // Adicione na string:
  188.     if (inChar == '\n')                       // Se pressionar ENTER:
  189.     {
  190.       oscilador = inputString.toInt();    // Transforma a string em inteiro
  191.       inputString = "";                         // Limpa a string
  192.     }
  193.   }
  194.   if (oscilador != 0)       // Se foi digitado algum valor
  195.   {
  196.     inicializa_oscilador ();      // Reconfigura a frequencia do oscilador
  197.   }
  198. }
复制代码


回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-2-15 23:14:37 | 显示全部楼层
原文在esp的論壇, 只有葡語英語版
http_s://esp32.com/viewtopic.php?f=19&t=17018&sid=cd2508c0fff14ac11ef03914797210b7
https://esp32.com/viewtopic.php? ... ac11ef03914797210b7
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-2-15 23:20:13 | 显示全部楼层
另外有個印尼版本, 同樣硬件設計, 增加LM311震盪器的LC測量的功能
同樣的計頻器軟件設計, 都是現買現賣就可以用的便宜方案
看看淘寶貨甚麼時候大抄大賣, 就像AVR晶體管測試儀那樣
htt_p://tentaratartar.blogspot.com/2022/05/esp32-arduino-frequency-counter.html
http://tentaratartar.blogspot.co ... quency-counter.html
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-2-15 23:27:00 | 显示全部楼层
沒有專業的計頻器比對校正, 就不校正了, 直接用對 10MHz / 15MHz 共振應該不會差太多.
原作者給的校正方案不可靠, 因為在100KHz 只用單點的話偏差超過 0.03%, 10MHz 就3.2KHz
原因不明, 也沒時間研究原因, 直接用就算了.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-2-15 23:30:24 | 显示全部楼层
完整的電路圖.
schematic.JPG
回复 支持 反对

使用道具 举报

     
发表于 2023-2-16 12:54:32 | 显示全部楼层
很有意义的制作. 也想玩玩, 请问楼主用什么编译下载软件.
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-2-18 11:02:57 | 显示全部楼层
wangqibiao 发表于 2023-2-16 12:54
很有意义的制作. 也想玩玩, 请问楼主用什么编译下载软件.

esp 原廠的工具 或者 arduino exp32 core 都可以編譯燒錄
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 加入会员

本版积分规则

小黑屋|手机版|矿石收音机 ( 蒙ICP备05000029号-1 )

蒙公网安备 15040402000005号

GMT+8, 2025-4-27 13:29

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表