前言
编码器(encoder) 是将信号(如 比特流 )或 数据 进行编制、转换为可用以通讯、传输和存储的信号形式的设备。. 编码器把角位移或直线位移转换成电信号,前者称为码盘 ,后者称为码尺。.
按照读出方式编码器可以分为接触式和非接触式两种;按照工作原理编码器可分为增量式和绝对式两类。. 增量式编码器是将位移转换成周期性的电信号,再把这个电信号转变成计数脉冲,用脉冲的个数表示位移的大小。绝对式编码器 的每一个位置对应一个确定的数字码,因此它的示值只与测量的起始和终止位置有关,而与测量的中间过程无关。
本文主要介绍旋转编码器在Arduino开发中的应用。
Arduino ESP32
-
旋转编码器
编码器接线图:
KY-040 模块包括两个内部上拉电阻,将引脚 CLK 和 DT 上拉到 VCC(+5V)。
引脚定义:
CLK | Rotary encoder pin A |
DT | Rotary encoder pin B |
SW | Push button pin. Normally openundefined shorted to GND on press |
VCC | Voltage supply |
GND | Ground |
旋转编码器提供两种交互方式:
-
旋转- 您可以通过单击箭头来旋转旋钮。上箭头顺时针旋转一级,下箭头逆时针旋转一级。旋转旋钮将在 DT 和 CLK 引脚上产生数字信号,如下所述。
-
按钮- 单击旋钮以按下按钮。按下时,按钮将 SW 引脚与 GND 引脚连接起来。
每次用户旋转旋钮时,它都会在 DT 和 CLK 引脚上产生一个 LOW 信号:
-
顺时针旋转会导致 CLK 引脚首先变低,然后 DT 引脚也变低。
-
逆时针旋转会导致 DT 引脚先变低,然后 CLK 引脚变低。
两个引脚将在几毫秒内返回高电平。下图说明了这一点:
使用wokwi仿真:
#define ENCODER_CLK 4
#define ENCODER_DT 2
void setup() {
Serial.begin(115200);
pinMode(ENCODER_CLKundefined INPUT);
pinMode(ENCODER_DTundefined INPUT);
}
int lastClk = HIGH;
void loop() {
int newClk = digitalRead(ENCODER_CLK);
if (newClk != lastClk) {
// There was a change on the CLK pin
lastClk = newClk;
int dtValue = digitalRead(ENCODER_DT);
if (newClk == LOW && dtValue == HIGH) {
Serial.println("Rotated clockwise ⏩");
}
if (newClk == LOW && dtValue == LOW) {
Serial.println("Rotated counterclockwise ⏪");
}
}
}
但是在实际过程中,我们得到的效果并没有如仿真一样,编码器会发生抖动,读取时并不准确。
在使用Arduino 开发ESP32设计旋转编码器程序时。
使用到的库:BasicEncoder
跑一个测试程序试试:
#include <Arduino.h>
#include <BasicEncoder.h>
const int8_t pinA = 2;
const int8_t pinB = 5;
BasicEncoder encoder(pinAundefined pinB);
void setup() {
Serial.begin(115200);
Serial.println(F("Polling in loop()"));
}
void loop() {
encoder.service();
int encoder_change = encoder.get_change();
if (encoder_change) {
Serial.println(encoder.get_count());
}
// Even a short delay here will affect performance.
// Uncomment and change the delay to see what happens.
//delay(10);
}
如果在编译过程中遇到报错,有提到SREG,那就在库函数里帮它定义一下。
找到库所在的位置打开并进行修改,其他的坑我还没有碰到过。
说了那么多,感觉上面的手不好用,试试下面的:
#include "OneButton.h"
// 编码器 A B 引脚
const int8_t Encoder_IOA = 26;
const int8_t Encoder_IOB = 27;
#if defined(ESP32)
#define PIN_INPUT 14 // 编码器按键
void IRAM_ATTR checkTicks() {
button.tick();
}
#endif
OneButton button(PIN_INPUT, true); // 创建按键工程
// 单击
void singleClick() {
Serial.println("singleClick() detected.");
}
// 双击
void doubleClick() {
Serial.println("doubleClick() detected.");
}
void setup() {
Serial.begin(115200);
pinMode(Encoder_IOA, INPUT_PULLUP);
pinMode(Encoder_IOB, INPUT_PULLUP);
// 中断配置
attachInterrupt(digitalPinToInterrupt(Encoder_IOA), IOA_attachInterrupt, FALLING);
attachInterrupt(digitalPinToInterrupt(Encoder_IOB), IOB_attachInterrupt, FALLING);
attachInterrupt(digitalPinToInterrupt(PIN_INPUT), checkTicks, FALLING);
// 按键单击 双击初始化
button.attachClick(singleClick);
button.attachDoubleClick(doubleClick);
}
void loop() {
button.tick();
}
// 编码器 中断函数
void IOA_attachInterrupt() {
if(digitalRead(Encoder_IOA) == 0) {
if(digitalRead(Encoder_IOB) == 0) {
Serial.println("Encoder_Count++");
}
}
}
// 编码器 中断函数
void IOB_attachInterrupt() {
if(digitalRead(Encoder_IOB) == 0) {
if(digitalRead(Encoder_IOA) == 0) {
Serial.println("Encoder_Count--");
}
}
}