今回使用するモーターから6本のコードが出ている。このうち1と6がモーター駆動用。2と5がエンコーダーの電源用、3と4からエンコーダのパルスが出力される。ところがコードの説明が見当たらない。以前使ったGA12-N20 6V 60RPMのエンコーダーのコードと色と並びが同じだったが違っていた。Encorderの+と-が下の図とは逆だった。
今回、エンコーダーを使うに当たっては、レマの研究室さんの記事を全面的に参考にさせて頂いた。

エンコーダーの出力
モーターの回転はパルス数に比例する。回転の向きはEncoder A とEncoder B のパルスの関係で決まる。Encoder AとEncoder Bからは出力されるパルスは、1/4サイクルずれている。下図のようにEncoder A のパルスが立ち上がるときにEncoder B のパルスがLOWならば反時計回り、HIGHならば時計回りになる。

環境
マイコン | ESP32 Dev MOdule(38pin) |
DCモータードライバ | DRV8871搭載 DCモータードライバ |
DCモーター | 12V60RPM |
Arduino IDE動作PC | Raspberry Pi 5 |
Arduino ID | バージョン1.8.19 |

RPMを表示する
モーター回転のRPM(revolutions per minute)を表示するプログラムを作る。このプログラムを起動すると、モーターが一定のスピードで回転し、その時のRPMが下図のようにOLEDに表示される。1行目:プログラム名, 2行目:プログラム名(つづき), 3行目:0.5秒毎のパルスの数, 4行目:0.5秒毎のRPM。

エンコーダーからのパルスの立ち上がりを確実にとらえるために、割り込み処理を使っている。割り込み処理の設定はattachInterrupt関数で行う。
attachInterrupt(digitalPinToInterrupt(ENCO_B), encoder_func, RISING);
# digitalPinToInterrupt(ENCO_B) : ピン番号から変換した割り込み番号を指定する
# encoder_func : 割り込み発生時に呼び出す関数
# RISING : ピンの状態がLOWからHIGHに変わったときに割り込みが発生
割り込み処理関数は、フラグ処理やカウントアップなど極力短時間で終了する処理にする。ここでは排他は行っていない。また変数や関数をメモリに常駐し、優先して処理ができるようにする。参照
volatile long encoderValue = 0; // volatile : 変数をRAM からロードする
void IRAM_ATTR encoder_func() { // IRAM_ATTR : 関数をメモリに常駐する
// 処理はパルスカウンターのインクリメントかデクリメントだけ
int A_state = digitalRead(ENCO_A);
int B_state = digitalRead(ENCO_B);
if (A_state == B_state) {
encoderValue--; // clockwise
} else {
encoderValue++; // counter clockwise
}
}
ESP32_DRV8871_encoder
/* ESP32_DRV8871_encoder 2025.1.26
【概要】ESP32でモータードライバーDRV8871を使ってDCモーターのエンコーダーからrpmを表示する簡単な例
【環境】 ESP32は38pin
モータードライバーはDRV8871
モーターは、12V60RPM DCギアモーター、エンコーダー付き
【OLED表示】
1行目 プログラム名
2行目 プログラム名(つづき)
3行目 INTERVAL間隔のパルス数
4行目 INTERVAL間隔のRPM
*/
const int MOTOR_SPEED = 150; // モーターの出力
// ESP32のpin設定
const int motor1Pin1 = 12; // ドライバーDRV8871のIN1へ
const int motor1Pin2 = 13; // ドライバーDRV8871のIN2へ
const int ENCO_A = 27; // エンコーダー A のコードへ
const int ENCO_B = 26; // エンコーダー B のコードへ
const int INTERVAL = 500; // RPM計測の間隔(ms)
const int PPR_VAL = 11; // Pulses per Revolution
const int GEAR_RATIO = 168; // 減速比
#include <Wire.h> // SSD1306 --- OLED用
#include "SSD1306.h" // SSD1306用ライブラリを読み込み
SSD1306 display(0x3c, 21, 22); //SSD1306インスタンスの作成(I2Cアドレス,SDA,SCL)
//----- グローバル変数に行ごとの内容を保持
String line1 = ""; // 1行目
String line2 = ""; // 2行目
String line3 = ""; // 3行目
String line4 = ""; // 4行目
volatile long encoderValue = 0; // エンコーダーのパルスカウンター
long previousMillis = 0;
// エンコーダーのパルス発生割り込み処理
void IRAM_ATTR encoder_func() {
// A_val and B_val are HIGH / LOW
int A_state = digitalRead(ENCO_A);
int B_state = digitalRead(ENCO_B);
if (A_state == B_state) {
encoderValue--; // clockwise
} else {
encoderValue++; // counter clockwise
}
}
void setup() {
// 初期画面表示
display.init(); //ディスプレイを初期化
display.setFont(ArialMT_Plain_16); //フォントを設定
display.flipScreenVertically(); // 表示反転(ボードにLCDを固定する向きに応じて)
line1 = "ESP32_DRV8871_"; // プログラム名表示
line2 = "encoder";
display.drawString(0, 0, line1); // 1行目の表示
display.drawString(0, 17, line2); // 2行目の表示
display.drawString(0, 34, line3); // 3行目の表示
display.drawString(0, 49, line4); // 4行目の表示
display.display(); // 画面を更新
// DC motor設定
pinMode(motor1Pin1 , OUTPUT);
pinMode(motor1Pin2 , OUTPUT);
analogWrite(motor1Pin1, 0);
analogWrite(motor1Pin2, 0);
// エンコーダー設定
pinMode(ENCO_A, INPUT_PULLUP);
pinMode(ENCO_B, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(ENCO_B), encoder_func, RISING);
// とりあえず低速回転
analogWrite(motor1Pin1, 0);
analogWrite(motor1Pin2, MOTOR_SPEED);
delay(1000);
}
void loop() {
int currentMillis = millis();
// INTERVALが経過したかチェック
if (currentMillis - previousMillis > INTERVAL) {
previousMillis = currentMillis;
// INTERVAL間隔のパスる数からRPMを求める。
float rpm_val = (float)encoderValue * 120 / PPR_VAL / GEAR_RATIO;
char buf_l3[32];
sprintf(buf_l3, "enc: %ld", encoderValue);
line3 = buf_l3;
char buf_l4[32];
sprintf(buf_l4, "rpm: %.2f", rpm_val);
line4 = buf_l4;
//----- OLED表示
display.clear(); // 画面をクリア
display.drawString(0, 0, line1); // 1行目の表示
display.drawString(0, 17, line2); // 2行目の表示
display.drawString(0, 34, line3); // 3行目の表示
display.drawString(0, 49, line4); // 4行目の表示
display.display(); // 画面を更新
encoderValue = 0;
}
}