JH7UBCブログ

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

ESP32 ロータリーエンコーダ テスト

2020-01-31 20:48:19 | ESP32

 前の記事のGPIO割込みを利用して、ロータリーエンコーダの回転方向を検出するテストをします。

 回転方向の検出は、秋月電子でロ-タリーエンコーダを買うとついてくる説明用紙に出ている方法を使います。

 ロータリーエンコーダのA端子とB端子をプルアップしておいて、エンコーダを回転させると各端子の電圧は、時計回り、反時計回りのとき、下の図のように変化します。

 前のBAの値を1ビット左にシフトした値と現在のBAの値のXORをとり、下位2ビットだけ取り出すと時計回りのときは、0か1(2未満)であり、反時計回りのときは、2か3(2以上)です。このことを使って回転方向を判定します。

 GPIO12とGPIO13を使います。回路図です。(内部でプルアップしますので、プルアップ抵抗は不要です)

 テストしたスケッチです。

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

/*
 *  ESP32 Rotary Encoder test
 *  2020.1.31
 *  JH7UBC Keiji Hata
 */

#define ENC_A 12
#define ENC_B 13

volatile int old_value = 0x11;
volatile int value = 0;
volatile int D;
volatile int count = 0;

void setup() {
  pinMode(ENC_A, INPUT_PULLUP); //内部でプルアップ
  pinMode(ENC_B, INPUT_PULLUP); //内部でプルアップ
  attachInterrupt(ENC_A, rotary, CHANGE); //GPIO割込みセット
  attachInterrupt(ENC_B, rotary, CHANGE); //GPIO割込みセット
  Serial.begin(115200); //シリアル通信初期化
}

void loop() {

}

//割込みサービスルーチン

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){
    Serial.println("Right");
    count = 0;
    }else if(count <= -4){
    Serial.println("Left");
    count = 0;
    }
  old_value = value;
  }
}

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

 時計回りのとき「Right」、反時計回りのとき「Left」とシリアルモニタに表示します。

ロータリーエンコーダを回してみます。

モニターの表示です。

回転方向と表示が逆の場合は、A,Bの配線を逆にします。

機械式ロータリーエンコーダには、チャタリングがありますので、回転と表示が若干ずれることがありますが、問題なく動作すると思います。

Arduinoのライブラリには、RotaryEncoderというものがあり、それを使う方法が簡単なのですが、使いにくい点もあるので、PICで使っている方法でテストしてみました。


ESP32 GPIO割込みテスト

2020-01-28 19:23:50 | ESP32

 ESP32の割込みには、ハードウェア割込み(GPIO割込みとタッチ割込み)とソフトウェア割込み(タイマー割込みとウォッチドックタイマ割込み)があります。

 今回は、GPIO割込みのテストをします。

 GPIO割込みでは、GPIOピンの信号値が変化したときに割込みが発生し、割込みサービスルーチン(ISR)が実行されます。この割込みはすべてのGPIOピンで利用できます。

 書式は、attachInterrupt(GPIOPin, ISR, Mode); です。

 Modeは、LOW,HIGH,CHANGE,FALLING,RISING が使えます。

 割込みを停止するには、

 detachInterrupt(GPIOPin); とします。

 「Arduinoをはじめよう」の本の外部割込みのスケッチを参考に、簡単なスケッチを作りました。

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

/*
 *  ESP32 GPIO interrupt test
 *  2020.1.28
 *  JH7UBC Keiji Hata
 */

#define LED_Pin 27
#define SW_Pin 33
volatile int state = LOW;
 
void setup() {
  pinMode(SW_Pin, INPUT_PULLUP);
  pinMode(LED_Pin, OUTPUT);
  attachInterrupt(SW_Pin, LED_blink, FALLING);
}

void loop() {
  digitalWrite(LED_Pin, state);
}

void LED_blink(){
  delay(10);
  state = !state;
}

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

配線回路図です。

ブレッドボードです。

 タクトスイッチにチャタリングがあるため、押し方によっては、誤動作しますが、スイッチを押すたびに点灯・消灯を繰り返し、GPIO割込みを確認することができました。

 ただ、入力専用のピンA0,A3,A6,A7を割込み入力に設定するとうまくいきません。

どうして?

 

1月29日追記

 入力専用ピンでの内部プルアップしたGPIO割込みが不安定なことについて、今朝実験をしてみました。

 10kΩの外付け抵抗で入力専用ピンをプルアップしたら、正常に動作しました。

 入力専用ピンは、内部プルアップ(ソフトウェアプルアップ)ができないのかもしれません。(コンパイルではエラーは出ませんが)


ESP32 PWM テスト

2020-01-25 21:15:06 | ESP32
 パソコンの修復が終了したので、再びESP32の勉強を進めます。
 今回は、ESP32のPWM(Pulse Width Moduration)のテストをします。
 PWM出力できるポート(GPIO)は
ポート GPIO
A4         32
A5         33
A10        4
A11        0
A12        2
A13      15
A14      13
A15      12
A16      14
A17      27
A18      25
A19     26
です。A0,A3,A6,A7は入力専用なので、利用できません。
 
PWMに使う関数は、
ledcSetup(chan,freq,bit_num); //チャンネル,周波数,ビット数
ledcAttachPin(pin,chan); //ピン番号,チャンネル
ledcWrite(chan,duty); //チャンネル,デューティ
です。
 
ledcSetup()で、チャンネル(0~15),PWM周波数(~40MHz),ビット数(1~16)を設定します。PWM周波数は、PWMに供給されるがクロックが80MHzですので、ビット数(分解能)によって、ビットごとの最大周波数は次のようになります。
ビット数 分解能 最大周波数(Hz)
16             65536     1220.70
15             32768     2441.41
14             16384     4882.81
13               8192     9765.63
12               4096     19531.25
11               2048     39062.50
10               1024        78125
9                   512      156250
8                   256      312500
7                   128      625000
6                     64     1250000
5                     32      2500000
4                     16      5000000
3                       8     10000000
2                       4     20000000
1                       2     40000000
 
ledcAttachPin()で、出力ピンをどのチャンネルに接続するかを設定します。
ledcWrite()で、チャンネルのデューティ比を設定します。デューティ比は、
 デューティ比=duty/bit_numです。
例として、1000HzのPWM出力をデューティ比50%で出力してみます。チャンネル0を使い、A19(GPIO26)に出力します。
 
スケッチです。
void setup() {
ledcSetup(0,1000,8);
ledcAttachPin(26, 0);
ledcWrite(0,128);
}
void loop() {
}
出力波形です。1000Hz デューティ比50%
 
ledcWrite(0,64);とするとデューティ比25%になります。
出力波形です。
 
ledcWrite(0,192);とするとデューティ比75%になります。出力波形です。
 
最大周波数を出力してみます。
ledcSetup(0,40000000,1);
ledcAttachPin(26, 0);
ledcWrite(0,1);
とすると確かに40MHzが出力されました。ただし、デューティ比は、50%だけです。
 
 それでは、任意の周波数は、出力できるでしょうか。
 freq=7000000と設定したときの出力です。
 
 かなりずれた周波数を発生させました。
 1000Hz,10000Hz,100000Hz,1000000Hzなどは、正しい周波数を発生させることができますが、周波数によっては、かなりずれを生じます。分周期のせいでしょうか。
 正確な周波数を発生させることができるなら、VFOとして使えるかな、などと期待したのですが、ちょっと無理みたいです。
 チャンネルについても、いくつか実験をしたのですが、最後に指定した1つしか使えないようです。これについて、情報がありましたら、教えてください。
 

ESP32 DACのテスト

2020-01-15 08:53:27 | ESP32

 ESP32は、2つの8ビットDAC(デジタル・アナログ・コンバータ)を内蔵しています。DAC1とDAC2です。

 DAC1はGPIO25に、DAC2はGPIO26に接続されています。

 DACに出力する関数は、dacWrite(pin,value);で、pinはGPIO番号(25か26),valueは8ビットですから0~255です。

 まず、dacWrite(25,255);で出力した場合の出力電圧です。

dacWrite(25,127);の場合

dacWrite(25,0);の場合

次に、正弦波を作ってみます。スケッチです。

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

/*
* ESP32 DAC sin wave generator
* 2020.01.14
* JH7UBC Keiji Hata
 */
 
float i = 0.0;
void setup() {
}
 
void loop() {
  dacWrite(25,128+128*sin(i));
  i += 0.1;
  if(i >= 6.28){
    i = 0;
  }
}

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

ADCから出力された波形です。

 ギザギザになりますが、まずまずの正弦波です。

 周波数は、974Hzでした。


ESP32 ADCのテスト

2020-01-14 09:11:54 | ESP32

 ESP32のADC(アナログ・デジタル・コンバータ)のテストをします。

 ESP32のADCについては、ESPRESSIFのESP-IDF Programming Guide および スイッチサイエンスのESP-WROOM-32に関するTIPSが参考になります。

 ESP32には、12ビットのADCが2つ内蔵されています。(ADC1とADC2)

 ADC1は、GPIO32-39の8チャンネルに使用され、ADC2は、GPIO 0, 2, 4, 12 - 15 及び 25 - 27の10チャンネルに使用されます。

 ただし、ADC2は、WiFiドライバで使用されるため、WiFiドライバで使用していない時のみ使用できます。

 ADC入力と各GPIOの番号は次のようになっています。

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

ADC_No  GPIO

ADC0      36

ADC3      39

ADC4      32

ADC5      33

ADC6      34

ADC7      35

ADC10     4

ADC11     0

ADC12     2

ADC13    15

ADC14    13

ADC15    12

ADC16    14

ADC17    27

ADC18    25

ADC19    26

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

 ESP32-DevKitCでADC入力テストを行い、上記のGPIOでADCが動作することを確認しました。

 ただし、GPIO 0,2,15については、他の機能に使われることがあります。

 ESP32のADCは、0~1Vを0~4095の値に変換します。

 入力ピンとADCの間には、アッテネータが入っていて、その減衰率を設定できます。デフォルトでは、減衰率が11dBに設定されています。

 11dBの場合、0~3.6Vを0~4095に変換することになります。

 デフォルトの状態で、実際にADCを動かし見ると3.28Vの時4095に変換しました。

 setup()でanalogSetAttenation(ADC_0dB);として、アッテネータの減衰率を0dB(減衰なし)に設定すると

 1.04Vで4095に変換しました。

 試しに、減衰率を6dBに設定した場合は、1.89Vで4095に変換しました。

 AD変換直線性と誤差を見るために0.2V刻みで変換値を測定してみました。(アッテネータはデフォルトの11dBです)

 ADCの変換値の1桁目はノイズのため、ばらつきます。また、0V付近と最大値付近長が大きくなりますが、精度を求めなければ、使えそうです。

 参考までに、ADCのテストに使ったスケッチです。表示用に2.2インチTFTを使っています。

(上の画面では、ADC0と表示していますが、その後下のスケッチのようにGPIOピン番号に変更しました)

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

/*
 * ESP32 ADC test
 * 2020.01.13
 * JH7UBC Keiji Hata
 */
 
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

#define TFT_DC 17
#define TFT_CS 5
#define TFT_RST 16

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST);
 
int p = 36; //GPIOの番号
 
void setup() {
  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLACK);
  tft.setTextColor(ILI9341_CYAN);
  tft.setTextSize(3);
  tft.setCursor(50, 50);
  tft.print("PIN");
}
 
void pinDisp(int pin_No){
  tft.fillRect(110,50,50,30,ILI9341_BLACK);
  tft.setTextColor(ILI9341_CYAN);
  tft.setTextSize(3);
  tft.setCursor(110, 50);
  tft.print(pin_No);
}
 
void ADC(int pin_No){
  int val = analogRead(pin_No);
  tft.fillRect(50,100,120,50,ILI9341_BLACK);
  tft.setTextColor(ILI9341_WHITE);
  tft.setTextSize(5);
  tft.setCursor(50, 100);
  tft.print(val);
}
 
void loop() {
  pinDisp(p);
  ADC(p);
  delay(500);
}