本帖最后由 suncat0504 于 2023-11-24 13:36 编辑
#申请原创# @21小跑堂
平时买个什么模块,需要测试是否正常。通常这种情况下,我优先选择用ESP8266或者其它可以在ArduinoIDE下使用的开发板来测试。原因无他,因为可以直接使用支持库、以及连接、下载、串口测试显示接收等方便,不需要额外做其他事情。所以当买来的旋转编码器在拿到手里后,立马就用ESP8266来测试了。
论坛里已经有不少伙计发了有关编码器的帖子,我这里也就不再详细说明编码器的资料了,只上干货。
先看看ESP8266的引脚图:
旋转编码器的接口:
编码器的电原理图:
输出信号:
A、B两相都输出方波,顺时针方向旋转时,A相超前B相90度,逆时针方向旋转时,B相超前A相90度;
为了方便接线和测试,我用洞洞板重新改了接线方式,使所有接线都放在一侧。使用ESP8266的D1,D2这两个口采集A、B数据。
为了方便看编码器旋转时信号的变化,首先使用以下程序进行测试:// 本程序使用ESP8266测试旋转编码器,使用D1(GPIO5)接A,D2(GPIO4)接B
// 引脚定义
#defineBMQ_A 5
#defineBMQ_B 4
// 旋转标志变量
unsignedchar flag = 0;
// 上次状态
unsignedchar status_old = 0;
// 当前状态
unsignedchar status_cur = 0;
// 旋转计数,顺时针:+1;逆时针:-1
int cnt = 0;
void setup(){
// 串口设置
Serial.begin(115200);
// LED设置
pinMode(LED_BUILTIN, OUTPUT);
// 设置D1(GPIO5)、D2(GPIO4)为数字输入口
pinMode(BMQ_A, INPUT); // D1 - A
pinMode(BMQ_B, INPUT); // D2 - B
// 设置中断,下降沿和上升沿均触发中断
//attachInterrupt(digitalPinToInterrupt(BMQ_A), a_proc, FALLING | RISING);
//attachInterrupt(digitalPinToInterrupt(BMQ_B), b_proc, FALLING | RISING);
status_old=digitalRead(BMQ_A)*2+digitalRead(BMQ_B);
}
void loop(){
status_cur=digitalRead(BMQ_A)*2+digitalRead(BMQ_B);
if(status_old != status_cur){
status_old=status_cur;
Serial.print("A:");
Serial.print(digitalRead(BMQ_A));
Serial.print(" B:");
Serial.println(digitalRead(BMQ_B));
}
}
// 中断处理函数,边沿触发
IRAM_ATTR void a_proc(){
status_cur=digitalRead(BMQ_A)*2+digitalRead(BMQ_B);
if(status_cur != status_old){
status_old=status_cur;
Serial.print("a_proc ");
Serial.print("A:");
Serial.print(digitalRead(BMQ_A));
Serial.print(" B:");
Serial.println(digitalRead(BMQ_B));
}
}
IRAM_ATTR void b_proc(){
status_cur=digitalRead(BMQ_A)*2+digitalRead(BMQ_B);
if(status_cur != status_old){
status_old=status_cur;
Serial.print("b_proc ");
Serial.print("A:");
Serial.print(digitalRead(BMQ_A));
Serial.print(" B:");
Serial.println(digitalRead(BMQ_B));
}
}
运行后,从串口收集到的信息如下:
初始化状态:A:0 B:0
1、顺时针旋转一格场合:
A:1 B:0
A:1 B:1
A:0 B:1
A:0 B:0
2、逆时针旋转一格场合
A:0 B:1
A:1 B:1
A:1 B:0
A:0 B:0
程序改为边沿中断触发处理场合(需要开放Setup中attachInterrupt的中断设置代码,注释掉loop中代码)
1、顺时针旋转一格场合:
a_proc A:1 B:0
b_proc A:1 B:1
a_proc A:0 B:1
b_proc A:0 B:0
2、逆时针旋转一格场合
b_proc A:0 B:1
a_proc A:1 B:1
b_proc A:1 B:0
a_proc A:0 B:0
和循环处理的取得数据变化是一致的。可以使用中断方式来处理。根据数据的变化规律,准备用以下逻辑进行判断处理,
1、初始化时记录A、B口数据,计算为A*2+B
2、中断发生时,采集数据,数据发生变化并且匹配顺时针或者逆时针的变化规律时,存储到标志数组,
3、采集到的变化数据够4个时,与变化规律进行比较,从而判断出是顺时针旋转,还是逆时针旋转。
整理代码如下:
// 本程序测试旋转编码器,使用D1(GPIO5)接A,D2(GPIO4)接B
// 引脚定义
#define BMQ_A 5
#define BMQ_B 4
// 一组数据采集完成的标志
unsigned char flag = 0;
// 当前数据存储位置
unsigned char pos = 0;
// 完整数据采集周期变量
unsigned char chg[8];
// 上次状态
unsigned char status_old = 0;
// 当前状态
unsigned char status_cur = 0;
// 旋转计数,顺时针:+1;逆时针:-1
long step = 0;
void setup() {
// 串口设置
Serial.begin(230400);
// LED设置
pinMode(LED_BUILTIN, OUTPUT);
// 设置D1(GPIO5)、D2(GPIO4)为数字输入口
pinMode(BMQ_A, INPUT); // D1 - A
pinMode(BMQ_B, INPUT); // D2 - B
// 设置中断,下降沿和上升沿均触发中断
attachInterrupt(digitalPinToInterrupt(BMQ_A), a_proc, FALLING | RISING);
attachInterrupt(digitalPinToInterrupt(BMQ_B), b_proc, FALLING | RISING);
status_old=digitalRead(BMQ_A)*2+digitalRead(BMQ_B);
}
void loop() {
}
// 中断处理函数,边沿触发
IRAM_ATTR void a_proc() {
//Serial.println("a_proc");
com_proc();
}
IRAM_ATTR void b_proc() {
//Serial.println("b_proc");
com_proc();
}
void com_proc() {
int val=0;
if (flag==0) {
// 当前状态
status_cur=digitalRead(BMQ_A)*2+digitalRead(BMQ_B);
if (status_cur != status_old) {
Serial.print("pos=");
Serial.print(pos);
Serial.print(" AB=");
Serial.println(status_cur);
// 有变化
status_old=status_cur;
// 保存
chg[pos++]=status_cur;
if (pos>=4) {
// 完成一组数据采集
flag=1; // 设置标志,防止下一次中断打断下面的处理
// 计算这组数据的变化顺序
val=chg[0]*1000 + chg[1]*100 +chg[2]*10 +chg[3];
// 为了能避免用户在旋转不到位时进行反方向的旋转,需要判断多种值的判定
// 正常的顺时针旋转为2310,逆时针为1320,通过切换组合顺序,可以避免漏判
if (val == 2310 || val==3102 || val==1023 || val==231) {
// 顺时针转动
step++;
Serial.print("Shun:");
Serial.println(step);
} else if (val == 1320 || val==3201 || val==2013 || val==132) {
// 逆时针转动
step--;
Serial.print("Ni:");
Serial.println(step);
}
if (val == 1020 || val==1013 || val==2023 || val==2020) {
// 转了一半不动,或者回去了,保留后两位数据
chg[0]=chg[2];
chg[1]=chg[3];
pos=2;
// 恢复标志
flag=0;
} else {
// 存储位置归位
pos=0;
// 恢复标志
flag=0;
}
}
}
}
}
以上代码是经过了以下测试,没有任何问题的,响应速度也很快:
1、慢速顺时针旋转测试
2、慢速逆时针旋转测试
3、快速顺时针旋转测试
4、快速逆时针旋转测试
5、快速旋转中顺时针、逆时针无规律快速切换测试
6、旋转一格不到,就回位,或者反转的测试
以上处理逻辑,应该同样适用于ARM、RISC等其它架构的单片机。
对不喜欢动手敲太多代码的朋友,你也可以选择安装别人做好的名为“Encoder”的库,有现成的例子可以帮助你。
|
以Arduino方式的方式测试使用旋转编码器EC11,以外部中断方式触发结合电平逻辑判断旋转方向和角度,适合低分辨率和低转速的编码器,如果高分辨率和高速旋转设备,用户可借助MCU定时器的编码器接口进行数据采集。(蓝V用户打赏已提升)