ホーム >> 無線ブログ集 >> ESP32DevKitCでの周波数カウンタその1

無線ブログ集

  メイン  |  簡易ヘッドライン  

link jl7gmnのblog jl7gmnのblog (2024/7/3 7:06:06)

feed ESP32DevKitCでの周波数カウンタその1 (2023/1/11 3:06:55)
あるWebページからのきっかけで、ESP32DevkitC対応のカウンタをためしてみたくなり、ESP32DevKitCでの周波数カウンタのサンプルをWebで探したところ、説明がポルトガル語ですが、ありました。ESPを楽しむ上ではとても良いサンプルだと思います。割り込みを使った安定動作するカウンタです。入力波形が矩形波である程度きれいな場合は1Hz桁の変動だけありますが精度よく動作してくれるようです。
早速、サンプル作製と詳細な動作説明がありましたのでWebアプリを使いポルトガル語を和訳してみました。和訳はフリーのWebアプリを使いました。和訳説明は最後に添付します。
因みにこの周波数カウンタでは校正確認用のOSCも内蔵されています。内蔵OSCを1Hzから最大40MHzまで変えてみて実際に40MHzまでカウントできました。

ブログは次のurlです。

ブログサンプルの回路図通りに液晶を準備し、接続します。
周波数入力はGPIO34
OSC出力はGPIO33
端子接続GPIO32とGPIO35
LCD GNDはGND
LCD VCCは+5V
LCD SDAはGPIO21
LCD SCLはGPIO22
内蔵OSC発振周波数を使っての確認時は周波数入力GPIO34とOSC出力GPIO33を繋ぎます。

【主な変更箇所】
サンプルで使用してる液晶LCDは16x2のI2C接続ですが、ブログのLCDのスケッチでは手持ちのLCDと違う為表示は動作しませんので、手持ち用の実績のある1602AのLCDのスケッチに書き直してます。アドレスは手持ちで確認してある0x27です。自分のLCDに合わせてアドレスは事前確認必要です。

オリジナルスケッチ
#include <LiqudCrystal_PCF8574.h>
LiquidCrystal_PCF8574  lcd(0x3F);

手持ちのLCDのスケッチへ変更
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);

ほか,修正箇所として、LCDの種類切り替えは削除してます。
動作の保証はあくまでも私の手持ち部品での動作確認を行なった内容なのでありませんし、しません。

下記に全スケッチ(和訳コメントあり)を示します。
***********************************************

// file:FREQ-COUNTER-2023JAN10-001.ino
// Modefy by JL7GMN
// ESP32DivKitC-FREQ-Counter
// ----ORIGINAL-------------------------------------------------
// 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       //stdioライブラリ
#include "driver/ledc.h"                 // Biblioteca ESP32 LEDC //ESP32 LEDCライブラリ
#include "driver/pcnt.h"                 // Biblioteca ESP32 PCNT //ESP32 PCNTライブラリ
#include "soc/pcnt_struct.h"


#define LCD_ON      //LCDを使用する場合はLCD_ONを、使用しない場合はLCD_OFFを設定する。
#define LCD_I2C_OFF //I2C LCDを使用する場合はLCD_I2C_ONを、使用しない場合はLCD_I2C_OFFを設定する。           
//-------------------------------------------------------------
#define I2C_SDA 21  //液晶ディスプレイ i2c SDA - Gpio_21
#define I2C_SCL 22  //液晶ディスプレイ i2c SDA - Gpio_22

#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2);
                   
//--------------------------------------------------------------
#define PCNT_COUNT_UNIT       PCNT_UNIT_0    //ESP32 パルスカウンター PCNT ユニット 0
#define PCNT_COUNT_CHANNEL    PCNT_CHANNEL_0 //ESP32 パルスカウンター PCNT チャンネル 0
#define PCNT_INPUT_SIG_IO     GPIO_NUM_34    //周波数計入力 - GPIO 34
#define LEDC_HS_CH0_GPIO      GPIO_NUM_33    //LEDC出力 - パルスジェネレータ - GPIO_33  
#define PCNT_INPUT_CTRL_IO    GPIO_NUM_35    //PCNT制御端子 - HIGH=カウントアップ、LOW=カウントダウン
#define OUTPUT_CONTROL_GPIO   GPIO_NUM_32    //タイマ出力 - カウントを制御する - GPIO_32
#define PCNT_H_LIM_VAL        overflow       //カウント上限値
#define IN_BOARD_LED          GPIO_NUM_2     //ESP32 ネイティブLED - GPIO 2     

bool flag = true;           //カウント終了インジケーター - 印刷を再開する
uint32_t overflow = 20000;  //PCNTカウンタオーバーフローの最大値
int16_t pulses = 0;         //カウントされたパルス数       
uint32_t multPulses = 0;    //カウンタオーバーフロー回数 PCNT
uint32_t janela = 1000000;  //パルスカウントのための1秒間のサンプリング時間 999990

//uint32_t oscilador =  12543; //発振器の初期周波数 - 12543 Hz -> 1MHz
uint32_t oscilador =  1000000;//resol:3   1,000,008Hz (1MHz)

uint32_t mDuty = 0;     //ロードサイクルの計算値   
uint32_t resolucao = 0; //解像度の計算値
float frequencia = 0;   //周波数計算用変数  
char buf[32]; //スコアを記録するためのバッファー          

esp_timer_create_args_t create_args; //ESP-Timerの引数
esp_timer_handle_t timer_handle;     //ESP-Timerインスタンス
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; //同期用変数型portMUX_TYPE
//----------------------------------------------------------------------------------------

void setup()
{
  Serial.begin(115200);  //シリアル115200Bpsの初期化
  //Serial.println(" Digite uma frequencia - 1 a 40 MHz"); // Print na console 
   
  lcd.init();
  lcd.backlight();
  lcd.clear();

 #if defined LCD_ON                          // LCDまたはI2C LCDを使用している場合         
  lcd.begin(16, 2);                         // LCD初期化 16列2行
  //lcd.print("  Frequencia:");             //LCDに周波数("FREQUENCY:")文字を表示
  lcd.print("  FREQUENCY:"); //Change Portugal to English
#endif

Serial.println(" Input Frequency 1 to 40 MHz"); // Print message for input freq

  inicializa_frequencimetro();  // 周波数メーターの初期化
}

//----------------------------------------------------------------------------

void inicializa_oscilador ()  //パルスジェネレータの初期化
{
  resolucao = (log (80000000 / oscilador)  / log(2)) / 2 ; //発振器の解を計算する
  if (resolucao < 1) resolucao = 1;  // 最小分解能
  Serial.print("resolucao :");
  Serial.println(resolucao);         // Print //SERIAL MONITOR PRINT
    
  mDuty = (pow(2, resolucao)) / 2;   //ロードサイクル計算 パルスの50%   
  // Serial.println(mDuty);          //SERIAL MONITOR PRINT mDuty

  ledc_timer_config_t ledc_timer = {};  //LEDC タイマー設定のインストール
  ledc_timer.duty_resolution =  ledc_timer_bit_t(resolucao); //解像度を設定する
  ledc_timer.freq_hz    = oscilador;              //発振周波数を設定
  ledc_timer.speed_mode = LEDC_HIGH_SPEED_MODE;   //高速動作モード
  ledc_timer.timer_num = LEDC_TIMER_0;            //LEDC タイマー0を使用
  ledc_timer_config(&ledc_timer);                 //LEDCタイマーを設定
  ledc_channel_config_t ledc_channel = {};        //LEDCのチャンネル構成をインスタンス化します。
  ledc_channel.channel    = LEDC_CHANNEL_0;       //チャンネル0を設定する
  ledc_channel.duty       = mDuty;                //充電サイクルを設定する
  ledc_channel.gpio_num   = LEDC_HS_CH0_GPIO;     //LEDC出力のGPIO設定 - 発振器
  ledc_channel.intr_type  = LEDC_INTR_DISABLE;    //LEDC割り込み禁止
  ledc_channel.speed_mode = LEDC_HIGH_SPEED_MODE; //高速チャンネル動作モード
  ledc_channel.timer_sel  = LEDC_TIMER_0;         //LEDC タイマー0を選択
  ledc_channel_config(&ledc_channel);             //LEDCチャンネルを設定
}

//----------------------------------------------------------------------------------

static void IRAM_ATTR pcnt_intr_handler(void *arg) //オーバーフローカウンタカウント
{
  portENTER_CRITICAL_ISR(&timerMux);          //それ以上の割り込みをブロック
  multPulses++;                               //オーバーフローカウンタをインクリメント
  PCNT.int_clr.val = BIT(PCNT_COUNT_UNIT);    //インタラプションインジケーターワイパー
  portEXIT_CRITICAL_ISR(&timerMux);           //新しい割り込みをリリース
}
//----------------------------------------------------------------------------------
void inicializa_contador(void)  //パルスカウンタの初期化
{
  pcnt_config_t pcnt_config = { };                //インスタンスPCNT設定
  pcnt_config.pulse_gpio_num = PCNT_INPUT_SIG_IO; //GPIOをパルス入力に設定
  pcnt_config.ctrl_gpio_num = PCNT_INPUT_CTRL_IO; //カウント制御用GPIOの設定
  pcnt_config.unit = PCNT_COUNT_UNIT;             //カウントユニット PCNT - 0
  pcnt_config.channel = PCNT_COUNT_CHANNEL;       //カウントチャネル PCNT - 0      
  pcnt_config.counter_h_lim = PCNT_H_LIM_VAL;     //最大カウント数 20000
  pcnt_config.pos_mode = PCNT_COUNT_INC;          //パルスの立ち上がりでカウントアップ
  pcnt_config.neg_mode = PCNT_COUNT_INC;          //パルスの立ち下がりでカウントを増加させる
  pcnt_config.lctrl_mode = PCNT_MODE_DISABLE;     //PCNT - lctrlモード無効
  pcnt_config.hctrl_mode = PCNT_MODE_KEEP;        //PCNT - hctrlモード - HIGHならカウントアップ
  pcnt_unit_config(&pcnt_config);                 //PCNTカウンターの設定
  pcnt_counter_pause(PCNT_COUNT_UNIT);            //PCNTカウンターを一時停止
  pcnt_counter_clear(PCNT_COUNT_UNIT);            //PCNTカウンターを0にリセット
  pcnt_event_enable(PCNT_COUNT_UNIT, PCNT_EVT_H_LIM); //カウント上限値の設定
  pcnt_isr_register(pcnt_intr_handler, NULL, 0, NULL);//PCNT割り込みルーチンの設定
  pcnt_intr_enable(PCNT_COUNT_UNIT);                  //PCNT割り込みの有効化
  pcnt_counter_resume(PCNT_COUNT_UNIT);               //PCNTカウンターのカウントをリセットする
}

//----------------------------------------------------------------------------------

void tempo_controle(void *p) //パルス読み出し終了時間
{
 
  gpio_set_level(OUTPUT_CONTROL_GPIO, 0);          //PCNTコントロール - メーター用
  pcnt_get_counter_value(PCNT_COUNT_UNIT, &pulses);//PCNTにカウントされた値を取得
  flag = true;                                     //制御の中断が発生したことを通知
}

//---------------------------------------------------------------------------------
void inicializa_frequencimetro()
{
  inicializa_oscilador (); //発振器でパルスの発生を開始する。
  inicializa_contador();   //PCNTパルスカウンタの初期化
  gpio_pad_select_gpio(OUTPUT_CONTROL_GPIO);                      //制御ポートを設定する
  gpio_set_direction(OUTPUT_CONTROL_GPIO, GPIO_MODE_OUTPUT);      //制御ポートを出力に設定
  create_args.callback = tempo_controle;                          //制御時間のインスタンス化     
  esp_timer_create(&create_args, &timer_handle);                  //タイマーのパラメータを作成する
  gpio_set_direction(IN_BOARD_LED, GPIO_MODE_OUTPUT);             //ポートLEDを出力
  gpio_matrix_in(PCNT_INPUT_SIG_IO, SIG_IN_FUNC226_IDX, false);   //パルス入力を指示する
  gpio_matrix_out(IN_BOARD_LED, SIG_IN_FUNC226_IDX, false, false);//ESP32のLEDの場合
}

//----------------------------------------------------------------------------------------

//ドットを含む32ビット長数値の書式設定
char *ultos_recursive(unsigned long val, char *s, unsigned radix, int pos)
{
  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++ = '.';
  if (pos % 3 == 0) *s++ = ',';
  return s;
}
//----------------------------------------------------------------------------------------
//ドットを含む32ビット長数値の書式設定
char *ltos(long val, char *s, int radix)
{
  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)   //カウントダウンが終了した場合
  {
    flag = false;     //COUNTDOWN NOT FINISH FLAG SETTING
    frequencia = (pulses + (multPulses * overflow)) / 2  ;    //PCNTでカウントされたパルスの合計を計算する
    //printf("Frequencia : %s", (ltos(frequencia, buf, 10))); // Print frequencia com pontos
    printf("FREQUENCY : %s", (ltos(frequencia, buf, 10)));    // Print frequencia com pontos
    printf(" Hz \n");                                         // Print unidade Hz

    //#if defined LCD_ON || defined LCD_I2C_ON  //LCDまたはI2C LCDを使用する場合
    #if defined LCD_ON
      lcd.setCursor(2, 1);                      //カーソルを1行目の2番目に位置させる
      lcd.print((ltos(frequencia, buf, 10)));   //LCDに表示される周波数プリント頻度
      lcd.print(" Hz              ");           //LCDにHz単位を表示
    #endif

    multPulses = 0; //オーバーフローカウンターのクリア
    delay (100);    // Delay 100 ms あらゆる用途に対応できるdelaytimer
    // Espaco para qualquer função
    pcnt_counter_clear(PCNT_COUNT_UNIT);        //PCNTカウンターをゼロにリセット      
    esp_timer_start_once(timer_handle, janela); //1秒タイムカウンター開始
    gpio_set_level(OUTPUT_CONTROL_GPIO, 1);     //制御ポート - パルスカウントを有効にする   
  }
 
  String inputString = "";     //データ入力用クリア文字列 初期化  
  oscilador = 0;               //周波数値をゼロにリセット
 
  while (Serial.available())   //シリアルでデータを持っている間
  {
    char inChar = (char)Serial.read(); //1 バイトの読み出し。
    inputString += inChar;             //文字列を追加
    if (inChar == '\n')                //ENTERを押した場合
    {
      oscilador = inputString.toInt(); //文字列を整数に変換する
      inputString = "";                //文字列を消去する
    }
  }
 
  if (oscilador != 0)         //何らかの値が入力されている場合
  {
    inicializa_oscilador ();  //発振器の周波数をリセットする
  }
}

***********************************************
上記のスケッチではOSC出力は12.543KHzを1MHzに変更して動作確認できています。

OSCは最大40MHzまでカウント動作しました。各周波数でのカウンタ動作確認時のスケッチと周波数を示します。*の表示桁はバラツキ動作がある桁です。
(一部resol値記載がありますが、個人的に分解能変数を知りたくて調べた時の値です。)
//uint32_t oscilador =  12543; //発振器の初期周波数 - 12543 Hz -> 10MHz
//uint32_t oscilador =  1;  //1Hz
//uint32_t oscilador =  10;  //10Hz
//uint32_t oscilador =  50;  //50Hz
//uint32_t oscilador =  100;  //100Hz
//uint32_t oscilador =  500;  //resol:8    500Hz
//uint32_t oscilador =  1000;  //1,000Hz
//uint32_t oscilador =  5000;  //5,000Hz
//uint32_t oscilador =    10000;//resol:6      10,000Hz
//uint32_t oscilador =    12500;//resol:6      12,500Hz
//uint32_t oscilador =    20000;//resol:5      20,000Hz
//uint32_t oscilador =    50000;//resol:5      50,000Hz
//uint32_t oscilador =    60000;//resol:5      60,000Hz
//uint32_t oscilador =    70000;//resol:5      70,007Hz
//uint32_t oscilador =    80000;//resol:4      80,000Hz
//uint32_t oscilador =    90000;//resol:4      90,002Hz
//uint32_t oscilador =   100000;//resol:4     100,000Hz
//uint32_t oscilador =   200000;//resol:4     200,002Hz
//uint32_t oscilador =   300000;//resol:4     300,049Hz
//uint32_t oscilador =   400000;//resol:3     400,004Hz
//uint32_t oscilador =   500000;//resol:3     500,005Hz
//uint32_t oscilador =   600000;//resol:3     600,099Hz
//uint32_t oscilador =   700000;//resol:3     700,033Hz
//uint32_t oscilador =   800000;//resol:3     800,007Hz
//uint32_t oscilador =   900000;//resol:3     900,149Hz
  uint32_t oscilador =  1000000;//resol:3   1,000,008Hz (1MHz)
//uint32_t oscilador =  5000000;//resol:2   5,000,050Hz  
//uint32_t oscilador = 10000000;//resol:1 10,000,099Hz
//uint32_t oscilador = 15000000;//resol:1 15,014,80*Hz
//uint32_t oscilador = 20000000;//20,000,184Hz
//uint32_t oscilador = 25000000;//25,036,9**Hz
//uint32_t oscilador = 30000000;//30,029,59*Hz
//uint32_t oscilador = 35000000;//35,068,8**Hz
//uint32_t oscilador = 40000000;//40,000,352Hz

上記のカウントした周波数の誤差はOSC出力波形が歪をおこしている事が原因の様でした。オシロスコープでの波形確認は大切です。Hi!
カウンタ入力に波形成形回路やレベルアンプを追加すると精度良く使用出来ると思われます。ちなみにサンプリングは1secです。また直接のカウンタ入力への正弦波入力では精度は取れません。波形成形回路を入れることで精度良くなると思われます。波形成形回路を追加する時には、カウント確認ポートとしてGPIO2のポートが動作している時に反転出力が出ますのでレベルの調整とかの時に使えます。(カウント時の出力ポートです。)
一部周波数の入力のスケッチ部もありますが、確認してません。

*********************************************
【動作説明の和訳】おかしな訳部分は適当に修正理解お願いします。(アプリでの和訳です。)
■PULSE COUNT CONTROLLER
PCNTパルスカウンタモジュールは、入力信号の立ち上がりや立ち下がりのエッジ数をカウントするように設計されています。独立した8つのユニットを搭載しているのです  各PCNTユニットは、16ビットカウンタと、立ち上がりまたは立ち下がりカウントを有効にすることができる2つのチャンネルを備えています。各チャンネルには、カウントするパルスの入力と、カウントの有効/無効を切り替えるコントロールポートがあります。  また、各PCNTユニットには、カウンターの設定に使用するレジスタがあります。これらのカウンタは、最大カウンタ値、最小カウンタ値、あるカウンタ値など、選択されたいくつかのパラメータに従って割り込みを発生させるようにプログラムすることができます。今回のケースでは、ESP32で割り込みを発生させるために、カウンタの最大値を使用します。

■TIMER
ESP32には、4つの汎用タイマが内蔵されています。これらはいずれも16ビットのプリスケーラ(分周器)と64ビットのカウンタをベースとした汎用的な64ビットタイマである。
ESP32には2つのタイマモジュールがあり、それぞれ2つのタイマが搭載されています。タイマーの特徴は以下の通りです。
- 2~65536の16ビットプリスケーラです。
- 64ビットのタイムベースカウンターです。
- タイムベースカウンターのアップ/ダウン:カウントの増減を設定可能。
- タイムベースカウンターの停止と再開。
- アラームで自動充電。
- ソフトウェア制御による瞬時充電。
- レベル、パルスエッジ割り込み発生。

各タイマは,APBクロック(APB_CLK,通常80MHz)を基本クロックとして使用します。このクロックを16ビットのプリスケーラで分周し、タイムベースパルスを生成します。このパルスは,構成によってタイムベースカウンタのインクリメントまたはデクリメントに使用することができます。

■高分解能タイマー Temporizador de alta resolução (High Resolution Timer)
ESPRESSIFは、ハードウェアタイマーをより正確かつ高速に制御できるように、ESP32タイマー用のAPI(Application Programming Interface)セットを作成しました。 esp_timerのAPIセットは、シングルおよびピリオディックタイマー、マイクロ秒の分解能と64ビット範囲を提供します。内部的には、esp_timerは64ビットハードウェアタイマーを使用します。このプロジェクトでは、PCNTのパルスカウントを計時するためにesp_timerが使用されています。周波数計測の単位はヘルツで、1秒あたりのパルス数に換算されるため、1秒ごとにPCNTカウンターに入るパルスのカウントが行われる。

■LEDC
LEDCまたはLED_PWMコントローラは、主にLEDの輝度を制御するために設計されていますが、他の目的のためにPWM信号を生成するために使用することも可能です。16個のチャンネルを持ち、独立したパルスを生成してカラーRGB LEDデバイスを駆動することができます。LED_PWMコントローラは、高速PWM8チャンネル、低速PWM8チャンネルを搭載しています。これらのチャンネルは,80MhzのAPB_CLKクロックを使用し,分周器,カウンタ,コンパレータの応用で,周波数とDuty Cycleを調整したパルスを発生させることが可能である。このPWM LEDコントローラのすべての設定は,いくつかの内部レジスタを介して行われます。

■動作原理
周波数計は5つのパーツに分かれています。

  1.  パルスカウンターです。
  2.  カウントタイム制御。
  3.  プログラマブル信号発生器(1Hz~40Mhz)。
  4.  その他の機能用スペース。
  5.  結果のプリントアウト。

1. パルスカウンタはPCNTカウンタモジュールを使用します。PCNTの設定には、以下のパラメータを使用します。

   ・ゲートウェイ
  ・入力チャンネルになります。
   ・制御ポート
   ・パルスの立ち上がりに応じてカウントされます。
   ・カウントは、立下りパルスで行います。
   ・高水準で制御して初めてカウントされる。
   ・最大カウント数制限

2. カウントタイム制御はesp-timerを使用します。esp-timerは以下のパラメータを持ちます。

  ・時間制御を行います。

3. 周波数メーターのテスト用周波数発生器は、ledcを使用しています。ledcは以下のパラメータを持つ。

   ・出力ポートになります。
   ・lcdチャンネル。
   ・周波数を使用します。
   ・ledcの解像度です。
   ・デューティサイクル

■周波数計の仕組み
・周波数計は、1秒の時間の間にパルスを数えることが基本です。
 まず、LEDC発振器とPCNTカウンタの設定と初期化を行います。 そして、esp-timer、カウンターの制御ポートを出力として、カウンターの入力ポートをESP32ボードのLedに接続します。
・LEDC発振器は発振器変数で設定された12.543KHzの周波数の発生を開始します。
 この周波数を選んだのは、周波数メーターの精度が分かるようにするためです。この発振器初期化機能では、周波数値に応じて分解能とデューティサイクルが既に自動計算されています。 デューティサイクルとは、生成されたパルスのHIGH部分とLOW部分の比率のことです。例えば、今回のプログラムでは、このDutyは50%、つまりHIGH部分の幅はパルスの全周期の半分となる。

 パルスのカウントには、PCNTカウンターを使用した。32,768パルスの制限値があるため、このプログラムでは20,000の値を最大値として設定した。カウント時間中に20,000パルスを超えた場合、オーバーフローレジスタが発生します。 オーバーフローが発生するたびに、変数multPulsesにカウントされます。

カウント制御ポート(GPIO 35)がハイレベルになると、パルス入力ポート(GPIO 34)から入力されるパルスをカウントするようにカウンタを解放します。制御パルスの立ち上がりと立ち下がりの両方でパルスをカウントし、平均的なカウントを向上させます。この2つのカウントを2で割ると頻度が算出される。

・カウント時間は、esp-timerで設定します。
可変窓から1秒に設定されています。タイマーの1パルスが1マイクロ秒に相当するので、100万パルスをカウントすると1秒の周波数サンプリングが完了します。

割り込みに対応した2つの機能がプログラム上で定義されています。最初の関数IRAM_ATTR pcntは、PCNTカウンタが満杯になるたびに、オーバーフローカウンタをインクリメントします。もう一つの関数time_controlは、esp-timerの時間切れ後、PCNTカウンターのレジスタに含まれる値を取得します。

1秒経過後、PCNTカウンタレジスタが読み出され、カウント終了を示すFlagがtrueに変更されます。プログラムループでは、Flagがtrueであることがわかると、周波数値が計算される。 オーバーフロー回数に20,000を乗じ、残りパルス数に加算します。この和を2で割ると、カウントが2回行われるため(制御パルスの立ち上がりと立ち下がりでパルスをカウント)、2回目のカウントが行われます。
ディスプレイの周波数を読みやすくするために、ultosとltosという機能を使って、3桁ごとにドットを挿入しています。この周波数は、LCDディスプレイに表示される以外に、Arduino IDEのシリアルコンソールにも送信されます。周波数サンプリング後、レジスタをリセットし、時刻とパルスカウントを再開します。

また、Arduino IDEのシリアルコンソールを使用して、テスト用に発振器の周波数を変更することも可能です。入力フィールドに1Hz~40MHzの値を入力し、ENTERを押します。

発振器のパルス出力はGPIO33ピンに設定されていますので、このパルス出力をGPIO34の周波数メータ入力に接続して、テストを行う必要があります。ただし、外部信号の周波数測定では、この接続を外してください。ESP32は3.3Vの電圧信号で動作するため、それ以外の電圧レベルの信号を測定したい場合は、入力ポートにロジックレベルコンバータ 3.3V-5V Bidirectional - 4 Channelを使用することを推奨します。内部ではGPIOマトリックスを使って、周波数計からの入力パルスをESP32のネイティブLEDに導き、周波数に応じてLEDを点滅させるようにしています。

■■使用したライブラリ■■
このプロジェクトで使用したArduino IDEのバージョンはV 1.8.12ですが、コードの非互換性を避けるため、バージョンが古い場合は更新してください。
2023年1月10日(火曜日)現在【【Arduino IDE 2.0.3】】

ESP32 Boardで使用するためのArduino IDEの設定がまだの場合は、以下のリンク先のチュートリアルの手順に従ってください。この設定は、プロジェクトが正常に動作するために必要不可欠です。 ESP32 BoardがTutorialと異なる場合は、Arduino IDEで設定を変更してください。

今回使用したArduino IDEのESP32ファームウェアのバージョンはV 1.0.4です。もし古い場合は、何らかの不具合を避けるために、プログラムをコンパイルする前にアップデートしてください。必要であれば、Board Managerを使用して更新します。

I2Cインターフェース(PCF8574)の液晶ディスプレイを使用するには、LiquidCrystal_PCF8574ライブラリのインストールが必要です。Arduino IDEのLibrary Managerを使用してインストールします。

また、パラレルLCDディスプレイ(4ビット)を使用する場合は、Arduino IDEのライブラリマネージャでLiquidCrystalライブラリもインストールしてください。

■回路図 Diagramas dos circuitos:

I2Cインターフェース搭載ディスプレイ(PCF8574)の構成図(画像を拡大するには、別のタブで開いてください。)

ディスプレイの裏に接続されているI2C Interfaceボード(PCF8574)のポテンショメーターで、ディスプレイの明るさを調整することを忘れないでください。ジャンパーは接続したままにしてください。

■ESP32 周波数メーターのプログラム。
表示選択指示
Arduino IDEコンパイラは、プログラムコンパイラのディレクティブを使用して、LCDディスプレイの種類を有効にしたり選択したりすることができます。

LCD並列使用 - LCD_ONまたはLCD_OFFに変更してください。
 #define LCD_OFF     ou     #define LCD_ON       // ON se for usar esse display

I2C LCDの使用 - LCD_I2C_ONまたはLCD_I2C_OFFに変更します。
 #define LCD_I2C_ON     ou      #define LCD_I2C_OFF  // ON se for usar esse display

OB:LCDのI2Cインターフェースのアドレスが分からない場合は、ESP32 I2C Scannerを使って調べてください:(私のボードのアドレスは0x3Fです)。

わたしのコメントです。★手持ちのLCDのボードアドレス:0x27

しかし、LCDディスプレイを使いたくない場合は、Arduino IDEのSerial Consoleで周波数の測定値を表示することができます。また、同じコンソールから、1Hzから40MHzの値の間で希望の試験周波数を入力することができます。とてもクールです。この方法で、プロジェクトが正しく組み立てられたことを確認し、周波数メーターをテストすることができます。

■周波数計の校正。
この設計のさらなる利点は、周波数測定の校正を可能にすることです。ESP32で通常表示される測定値は、非常に満足のいくものです。基準として使用される精密な周波数メーターを使用すると、プログラム内の変数ウィンドウの値を調整することができます。この値を変更することで、得られた精度よりもさらに高い精度を得ることができます。オシロスコープの周波数測定機能を使った周波数計のテストでは、ウィンドウの値を変えて、なんとかオシロスコープと同じ値を得ることができたんだ!」。

どちらのタイプの液晶ディスプレイも搭載しないことにした場合、周波数の測定値を確認するために
Arduino IDEシリアルコンソール
. コンソールスピードは、次のように設定します。
115200 Bps

. 周波数計の試験周波数を変更するには、入力フィールドに1Hz~40MHzの値を入力し、Enterキーを押します。ヘルツ(Hz)の単位は入力する必要がありません。しかし、このテストを行うには、次のことを忘れないでください。

周波数メータ入力 (GPIO34)
Oscilador (GPIO33)の出力に接続する必要があります。
*********************************************
参考文献やWeb情報、他は割愛してます。

サンプリング時間変更や、TFT液晶への変更もできますね!
5MHz、6MHzのVFOの周波数などの利用でも100Hz桁も精度良く動作しているので十分使えると思います。
ESP32DevKitCは色々できて、やはりいいですね。

つづく?

execution time : 0.040 sec
サイト内検索

メインメニュー

ログイン
ユーザ名:

パスワード:



パスワード紛失


オンライン状況
121 人のユーザが現在オンラインです。 (60 人のユーザが 無線ブログ集 を参照しています。)

登録ユーザ: 0
ゲスト: 121

もっと...