JH7UBCブログ

アマチュア無線 電子工作 家庭菜園など趣味のブログです

ESP32 ロータリーエンコーダーその2

2021-02-06 20:21:20 | ESP32
 約1年ぶりにESP32を取り出して、実験しました。

 昨年、ESP32でロータリーエンコーダーのテストをしました。この時は、ライブラリを使わず、自前のスケッチでテストを行いました。

 最近、Arduino Pro Miniで、Ben Buxton氏のライブラリ を使い、大変よく動作することを確認しましたので、ESP32でも氏のライブラリーを使ってテストしてみます。

 ライブラリは、GitHubからダウンロードできます。こちらからRotary-master.zipをダウンロードして、Arduino IDEのライブラリに登録します。 

  GPIO12とGPIO13を使ってテストします。回路図です。


 まず、割込みを使わない方法で、GitHubにも掲載されている方法で、テストしてみました。スケッチです。すべてのGPIOで利用可能で、プルアップは、ライブラリで行いますので、プルアップ抵抗は必要ありません。しかし、GPIO34,GPIO35,GPIO36,GPIO39は、入力専用で、プルアップ抵抗が必要です。
 ロータリーエンコーダーを時計回り(右回り)に回転させるとシリアルモニタに「Right」と反時計回り(左回り)に回転させると「Left」と表示されます。
--------------------------------------------------------------------------------------
#include<Rotary.h>
#define ENC_A 12
#define ENC_B 13

Rotary r = Rotary(ENC_A,ENC_B);

void setup() {
Serial.begin(9600);
r.begin();
}

void loop() {
unsigned char result = r.process();
if (result) {
Serial.println(result == DIR_CW ? "Right" : "Left");
}
}
--------------------------------------------------------------------------------------
 問題なく、動作しました。

 次に、ピン変化割込みを使う方法です。スケッチです。ESP32では、全てのGPIOで割込みが使用可能でので、Arduinoよりピン割り当ての自由度が大きくなります。
--------------------------------------------------------------------------------------
#include<Rotary.h>
#define ENC_A 12
#define ENC_B 13

Rotary r = Rotary(ENC_A,ENC_B);

void setup() {
r.begin();
Serial.begin(9600);
attachInterrupt(ENC_A,rotary_encoder,CHANGE);
attachInterrupt(ENC_B,rotary_encoder,CHANGE);
}

void loop() {
}

void rotary_encoder(){
unsigned char result = r.process();
if(result){
if(result == DIR_CW){
Serial.println("Right");
}else{
Serial.println("Left");
}
}
}
--------------------------------------------------------------------------------------
 これも問題なく動作しました。
 目的、ケースによって使い分けします。
 ライブラリを使うと簡単で良いですね。

ESP32 BME280 温湿度・気圧センサーテスト

2020-04-24 20:43:00 | ESP32
 BOSCHの温湿度・気圧センサーBME280の動作テストをします。
 BME280を使ったモジュールは、秋月電子、スイッチサイエンス、Amazonなどで販売されています。
 今回は、秋月電子のAE-BME280モジュール・キットを購入しました。
 このモジュールは、SPI、I2Cどちらでも使えますが、今回はI2C接続で使用します。
 従って、右上部のJ3をハンダでショートします。更にプルアップ抵抗を有効にするためにJ1とJ2もハンダでショートします。

 BME280とESP32は次のように接続します。
 BME280    ESP32
 VDD → 3V3
 GND → GND
 CSB → 未接続
 SDI → SDA(GPIO21)
 SDO → GND(GNDの場合I2Cアドレスは0x76、VDDの場合は0x77)
 SCK → SCL(GPIO22)
接続回路図です。

 ブレッドボードです。


 Arduino IDEを立ち上げて、スケッチ→ライブラリをインクルード→ライブラリマネージャーを開きます。
 検索ウインドウに「BME280 ESP」と入力すると上の図のように1件ヒットしますので、インストールします。

 次に、サンプルプログラムを実行するために、ファイル→スケッチ例→BME280→BME_280_I2C_Testのスケッチを読み込みます。
 ESP32を接続して、このスケッチをコンパイルしESP32に書き込みます。
 シリアルモニタを開くと(通信速度は115200bps)温度、湿度、気圧が1秒ごとに表示されます。


 まずは、ESP32でBME280の動作テストができました。
 測定値は、温度はまずまずですが、湿度と気圧はやや低めに出るようです。
 今後、測定値を安定させる方法などを考え、WiFi接続での測定などをやっていきたいと思います。
 これを使った簡易気象観測システムが目標です。




ESP32 Si5351A 7MHz VFOの試作

2020-02-16 09:53:36 | ESP32

 ESP32の機能について一通りのテストができましたので、ロータリーエンコーダ、TFT表示、Si5351Aのコントロールを組み合わせて、7MHzのVFOを試作します。

 このVFOは、すでにSTM32で試作したスケッチを活用していますが、Si5351Aのコントロール部分は、今回改めてプログラムしました。

全体の回路図です。

 ブレッドボードです。

CLK0から7.01234MHzを出力しています。

 ロータリーエンコーダは、秋月電子で販売している安価なものです。

 スケッチを掲載します。試作ですので、参考までに。

 STEPボタンを押すと周波数STEPが10KHz→1KHz→100Hz→10Hz→10KHzと循環します。

 2.2"TFTの表示用ライブラリは、様々なフォントを使うことができるUcglib.hを利用しました。

-------------------------------------------------------

/*
 * ESP32 Si5351A 7MHz VFO
 * 2020.02.16
 * JH7UBC Keiji Hata
 */

//TFT関係の定義
#include
#include
#define __CS 5
#define __DC 17
#define __RST 16
// SDA(MOSI) 23 
// SCK 18
// VCC,LED 3V3

Ucglib_ILI9341_18x240x320_HWSPI ucg(__DC, __CS, __RST);

//I2C
#include

//Si5351A関係の定義
#define Si5351A_ADDR 0x60
#define MSNA_ADDR 26
#define MSNB_ADDR 34
#define MS0_ADDR 42
#define MS1_ADDR 50
#define MS2_ADDR 58
#define CLK0_CTRL 16
#define CLK1_CTRL 17
#define CLK2_CTRL 18
#define OUTPUT_CTRL 3
#define XTAL_LOAD_C 183
#define PLL_RESET 177
const uint32_t XtalFreq = 25000000;
uint32_t divider;
uint32_t PllFreq;
uint8_t mult;
uint32_t num;
uint32_t denom;
uint32_t l;
float f;
uint32_t P1;
uint32_t P2;
uint32_t P3;


//Rotary endoder関係の定義
#define ENC_A 12
#define ENC_B 13
volatile uint8_t old_value = 0x11;
volatile uint8_t value = 0;
volatile uint8_t D;
volatile int8_t count = 0;

//STEP関係の定義
#define SW_STEP 14
uint16_t STEP = 10000; //STEP 初期値

//VFO関係の定義
const uint32_t LOW_FREQ = 7000000; //下限周波数
const uint32_t HI_FREQ = 7200000; //上限周波数
uint32_t FREQ = 7000000; //VFO周波数初期値
uint32_t FREQ_OLD = FREQ; //周波数の前の値
int16_t df = 130; //周波数補正値(Hz)
String freqt = String(FREQ);
String fH_old = "";
String fM_old = "";
String fL_old = "";

//ROtary encoder 割込みサービスルーチン
void rotary(){
  value = (digitalRead(ENC_B)<<1) | digitalRead(ENC_A);
  if(old_value != value){
    D = ((old_value << 1) ^ value) & 3;
    if(D < 2){
      count += 1;
    }else{
      count -= 1;
    }
  if(count >= 4){
    FREQ += STEP;
    count = 0;
  }else if(count <= -4){
    FREQ -= STEP;
    count = 0;
  }
  FREQ = constrain(FREQ,LOW_FREQ,HI_FREQ); //VFOの下限と上限を超えないように
  old_value = value;
  }
}

//レジスタに1バイトデータを書き込む。
void Si5351_write(byte Reg , byte Data){
  Wire.beginTransmission(Si5351A_ADDR);
  Wire.write(Reg);
  Wire.write(Data);
  Wire.endTransmission();
}

//Si5351Aの初期化
void Si5351_init(){
  Si5351_write(OUTPUT_CTRL,0xFF); //Disable Output
  Si5351_write(CLK0_CTRL,0x80); //CLOCK0 Power down
  Si5351_write(CLK1_CTRL,0x80); //CLOCK1 Power down
  Si5351_write(CLK2_CTRL,0x80); //CLOCK2 Power down 
  Si5351_write(XTAL_LOAD_C,0x92); //Crystal Load Capasitance=8pF
  Si5351_write(PLL_RESET,0xA0); //Reset PLLA and PLLB
  Si5351_write(CLK0_CTRL,0x4F); //CLOCK0 Power up
  Si5351_write(OUTPUT_CTRL,0xFE); //Enable CLOCK0
}

void VFO_Set(uint32_t freqency){
  
//PLLのセット
  divider = 900000000 / freqency;
  if (divider % 2) divider--;
  PllFreq = divider * freqency;
  mult = PllFreq / XtalFreq;
  l = PllFreq % XtalFreq;
  f = l;
  f *= 1048575;
  f /= XtalFreq;
  num = f;
  denom = 1048575;
  P1 = (uint32_t)(128 * ((float)num /(float)denom));
  P1 = (uint32_t)(128 * (uint32_t)(mult) + P1 - 512);
  P2 = (uint32_t)(128 * ((float)num / (float)denom));
  P2 = (uint32_t)(128 * num -denom * P2);
  P3=denom;
  Parameter_write(MSNA_ADDR,P1,P2,P3);

//MultiSynth(分周器)のセット
  P1 = 128 * divider - 512;
  P2 = 0;
  P3 = 1;
  Parameter_write(MS0_ADDR,P1,P2,P3); 
}

//レジスタにパラメータP1,P2,P3を書き込む。
void Parameter_write(uint8_t REG_ADDR,uint32_t Pa1,uint32_t Pa2,uint32_t Pa3){
  Si5351_write(REG_ADDR + 0,(Pa3 & 0x0000FF00) >> 8);
  Si5351_write(REG_ADDR + 1,(Pa3 & 0x000000FF));
  Si5351_write(REG_ADDR + 2,(Pa1 & 0x00030000) >> 16);
  Si5351_write(REG_ADDR + 3,(Pa1 & 0x0000FF00) >> 8);
  Si5351_write(REG_ADDR + 4,(Pa1 & 0x000000FF));
  Si5351_write(REG_ADDR + 5,((Pa3 & 0x000F0000) >> 12) | ((Pa2 & 0X000F0000) >> 16));
  Si5351_write(REG_ADDR + 6,(Pa2 & 0x0000FF00) >> 8);
  Si5351_write(REG_ADDR + 7,(Pa2 & 0x000000FF)); 
}


//周波数表示
void Freq_Disp(long frequency){
  freqt = String(frequency);
  String fH = freqt.substring(0,1);
  String fM = freqt.substring(1,4);
  String fL = freqt.substring(4,6);
  ucg.setFont(ucg_font_fur35_tr);//Font設定
  
//MHzの表示
  if (fH != fH_old){
    ucg.setColor(0,0,0);
    ucg.drawBox(60,40,30,40);
    ucg.setColor(0,255,255);
    ucg.setPrintPos(60,80);
    ucg.print(fH);
    fH_old = fH;
  }

//KHzの表示
  if (fM != fM_old){
    ucg.setColor(0,0,0);
    ucg.drawBox(100,40,80,40);
    ucg.setColor(0,255,255);
    ucg.setPrintPos(100,80);
    ucg.print(fM);
    fM_old = fM;
  }

//Hzの表示
  if (fL != fL_old){
    ucg.setColor(0,0,0);
    ucg.drawBox(200,40,70,40);
    ucg.setColor(0,255,255);
    ucg.setPrintPos(200,80);
    ucg.print(fL);
    fL_old = fL;
  }
}

//STEP表示
void Step_Disp(){
  ucg.setFont(ucg_font_fur17_tr);
  ucg.setColor(0,0,0);
  ucg.drawBox(60,120,70,35);
  ucg.setColor(255,255,255);
  ucg.setPrintPos(60,140);
  if (STEP == 10)ucg.print("10Hz");
  if (STEP == 100)ucg.print("100Hz");
  if (STEP == 1000)ucg.print("1KHz");
  if (STEP == 10000)ucg.print("10KHz");
}

//STEP切り替え
void Set_Step(){
  if (STEP == 10){
    STEP = 10000;
  }else{
    STEP /= 10;
  }
  delay(10);
  Step_Disp();
  while(digitalRead(SW_STEP) == LOW){
    delay(10);
  }
}
void setup(){
  //ロータリーエンコーダとSTEP使用ピンの設定とプルアップ
  pinMode(ENC_A, INPUT_PULLUP);
  pinMode(ENC_B, INPUT_PULLUP);
  pinMode(SW_STEP, INPUT_PULLUP);
  
  //ロータリーエンコーダ割込み設定
  attachInterrupt(ENC_A, rotary, CHANGE);
  attachInterrupt(ENC_B, rotary, CHANGE);

  Wire.begin();  //I2C初期化
  Si5351_init(); //Si5351Aの初期化

  //TFT初期化
  ucg.begin(UCG_FONT_MODE_TRANSPARENT);//透過モード
  ucg.clearScreen(); //画面クリア
  ucg.setRotate270(); //画面を270°回転

  //Titleの表示
  ucg.setFont(ucg_font_ncenB12_tr);
  ucg.setColor(0,0,255);
  ucg.setPrintPos(20,20);
  ucg.print("ESP32 Si5351A 7MHz VFO test");

  //周波数初期値表示
  Freq_Disp(FREQ);
  ucg.setPrintPos(85,80);
  ucg.print(".");
  ucg.setPrintPos(185,80);
  ucg.print(".");

//周波数STEPの表示
  ucg.setFont(ucg_font_fur17_tr);
  ucg.setColor(255,255,255);
  ucg.setPrintPos(60,115);
  ucg.print("STEP");
  Step_Disp();
  
  VFO_Set(FREQ + df);
  Freq_Disp(FREQ);
}

//Main program
void loop(){
  if(digitalRead(SW_STEP) == LOW)Set_Step();
  if(FREQ != FREQ_OLD){ //周波数FREQが変わったら、Si5351Aの周波数を変更
    VFO_Set(FREQ + df);
    Freq_Disp(FREQ);
    FREQ_OLD = FREQ; //変更された周波数を保存
  }
  delay(10); 
}

-------------------------------------------------------


ESP32 Si5351A テスト

2020-02-11 17:33:52 | ESP32

 ESP32で、3チャンネルクロックジェネレータSi5351Aをコントロールするテストです。

 Si5351Aのコントロールの仕方は、JH7UBCホームページのArduinoのページのこちらで解説しています。

 Arduino用のスケッチをESP32に書き込んでみると何の問題もなく動作しました。

 そこで、「Si5351Aの実験その3(マルチチャンネル化)」のスケッチを書き込んでみました。

 CLK0に1MHzをCLK1に10MHzをCLK2に5MHzを出力します。

 ESP32とSi5351A(秋月電子のモジュール)との接続は、次のようにします。

 (I2Cのプルアップ抵抗10kΩは、つけなくとも動作しました。)

スケッチです。

---------------------------------------

/*
 * ESP32 Si5351A test2
 * 2020.02.11
 * JH7UBC Keiji Hata 
 * マルチチャンネル出力の実験
 * CLK0=1MHz
 * CLK1=10MHz
 * CLK2=5MHz
 */
 
#include

#define Si5351A_ADDR 0x60
#define MSNA_ADDR 26
#define MSNB_ADDR 34
#define MS0_ADDR 42
#define MS1_ADDR 50
#define MS2_ADDR 58
#define CLK0_CTRL 16
#define CLK1_CTRL 17
#define CLK2_CTRL 18
#define OUTPUT_CTRL 3
#define XTAL_LOAD_C 183
#define PLL_RESET 177
uint32_t frequency;
const uint32_t XtalFreq = 25000000;

uint32_t P1;
uint32_t P2;
uint32_t P3;
uint32_t PllFreq;
uint32_t l;
float f;
uint8_t mult;
uint32_t num;
uint32_t denom;
uint32_t divider;
char PLL;
uint8_t PLL_ADDR;
uint8_t MS_ADDR;


void setup(){
Wire.begin();
Si5351_init(); //Si5351Aの初期化

//PLLA=900MHz,CLK0=1MHz
frequency = 1000000;
divider = 900000000 / frequency;
if (divider % 2) divider--;
PLL_Set('A',frequency,divider);
Si5351_write(CLK0_CTRL,0x4C); //CLK0 Sorce PLLA
MS_Set(0,divider);

//PLLB=900MHz
frequency = 10000000;
divider = 900000000 / frequency;
if (divider % 2) divider--;
PLL_Set('B',frequency,divider);

//CLK1=10MHz
Si5351_write(CLK1_CTRL,0x6C); //CLK1 Sorce PLLB
MS_Set(1,divider);

//CLK2=5MHz
Si5351_write(CLK2_CTRL,0x6C); //CLK2 Sorce PLLB
divider=divider*2;
MS_Set(2,divider);
}

void loop(){

}

//Si5351のレジスタに1バイトデータを書き込む。
void Si5351_write(byte Reg , byte Data)
{
Wire.beginTransmission(Si5351A_ADDR);
Wire.write(Reg);
Wire.write(Data);
Wire.endTransmission();
}

//Si5351Aの初期化
void Si5351_init(){
  Si5351_write(OUTPUT_CTRL,0xFF); //Disable Output
  Si5351_write(CLK0_CTRL,0x80); //CLOCK0 Power down
  Si5351_write(CLK1_CTRL,0x80); //CLOCK1 Power down
  Si5351_write(CLK2_CTRL,0x80); //CLOCK2 Power down 
  Si5351_write(XTAL_LOAD_C,0x92); //Crystal Load Capasitance=8pF
  Si5351_write(PLL_RESET,0xA0); //Reset PLLA and PLLB
  Si5351_write(CLK0_CTRL,0x4F); //CLOCK0 Power up
  Si5351_write(CLK1_CTRL,0x4F); //CLOCK0 Power up
  Si5351_write(CLK2_CTRL,0x4F); //CLOCK0 Power up 
  Si5351_write(OUTPUT_CTRL,0xF8); //Enable CLOCK0,CLOCK1,CLOCK2
}


//PLLの設定
void PLL_Set(char Pll,uint32_t Freq,uint32_t Div){
  PllFreq = Div * Freq;
  mult = PllFreq / XtalFreq;
  l = PllFreq % XtalFreq;
  f = l;
  f *= 1048575;
  f /= XtalFreq;
  num = f;
  denom = 1048575;

  P1 = (uint32_t)(128 * ((float)num /(float)denom));
  P1 = (uint32_t)(128 * (uint32_t)(mult) + P1 - 512);
  P2 = (uint32_t)(128 * ((float)num / (float)denom));
  P2 = (uint32_t)(128 * num -denom * P2);
  P3=denom;

  if (Pll == 'A')
    {
    PLL_ADDR = MSNA_ADDR;
    }else
    {
    PLL_ADDR = MSNB_ADDR;
  }
  Parameter_write(PLL_ADDR,P1,P2,P3);
}

//MultiSynth(分周器)のセット
void MS_Set(uint8_t MS_No,uint32_t Div){
  P1 = 128 * Div - 512;
  P2 = 0;
  P3 = 1;
  switch(MS_No){
    case 0:
    MS_ADDR = MS0_ADDR;
    break;
    case 1:
    MS_ADDR = MS1_ADDR;
    break;
    case 2:
    MS_ADDR = MS2_ADDR;
    break;
    default:
    MS_ADDR = MS0_ADDR;
  }
  Parameter_write(MS_ADDR,P1,P2,P3); 
}

//レジスタにパラメータP1,P2,P3を書き込む。
void Parameter_write(uint8_t REG_ADDR,uint32_t Pa1,uint32_t Pa2,uint32_t Pa3)
{
  Si5351_write(REG_ADDR + 0,(Pa3 & 0x0000FF00) >> 8);
  Si5351_write(REG_ADDR + 1,(Pa3 & 0x000000FF));
  Si5351_write(REG_ADDR + 2,(Pa1 & 0x00030000) >> 16);
  Si5351_write(REG_ADDR + 3,(Pa1 & 0x0000FF00) >> 8);
  Si5351_write(REG_ADDR + 4,(Pa1 & 0x000000FF));
  Si5351_write(REG_ADDR + 5,((Pa3 & 0x000F0000) >> 12) | ((Pa2 & 0X000F0000) >> 16));
  Si5351_write(REG_ADDR + 6,(Pa2 & 0x0000FF00) >> 8);
  Si5351_write(REG_ADDR + 7,(Pa2 & 0x000000FF)); 
}

---------------------------------------

水晶の負荷容量は、8pFにしていますが、設定周波数より若干低い周波数を発生します。

CLK0 1MHzです。

CLK1 10MHzです。

CLK2 5MHzです。


ESP32 HC-SR04による距離の測定

2020-02-10 11:39:33 | ESP32

 ESP32で超音波センサーHC-SR04を使って距離を測定するテストです。

 原理はmicro:bitで行った実験と同じです。簡単な説明の図です。

スケッチです。測定した距離はシリアルモニタにcm単位で表示します。

HC-SR04に送るトリガーパルスの幅は10usです。

---------------------------------------

/*
 * ESP32 HC-SR04による距離の測定
 * 2020.2.10
 * JH7UBC Keiji Hata
 */

#define Trigger_Pin 17
#define Echo_Pin 16
int V = 340;//音速

void setup() {
  Serial.begin(115200);
  pinMode(Trigger_Pin, OUTPUT);
  pinMode(Echo_Pin, INPUT);
  digitalWrite(Trigger_Pin, LOW);
}

//Send Trigger pulse
void sendTrigger(){
  digitalWrite(Trigger_Pin,HIGH);
  delayMicroseconds(10);
  digitalWrite(Trigger_Pin, LOW);
}

void loop() {
  sendTrigger();

  while(!digitalRead(Echo_Pin)){ 
  }
  unsigned long t1 = micros();

  while(digitalRead(Echo_Pin)){
  }
  unsigned long t2 = micros();
  unsigned long t = t2 - t1;
  Serial.print(V * t /20000);
  Serial.println("cm");
  delay(1000);
}

---------------------------------------

測定の様子です。

 micro:bitの時と同様に1mくらいまでは、けっこう正確に距離を測定できますが。1mを超えたあたりから測定値のばらつきが大きくなります。