发新帖本帖赏金 60.00元(功能说明)我要提问
返回列表
打印
[其它]

以Arduino方式学习使用旋转编码器EC11

[复制链接]
3866|1
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 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”的库,有现成的例子可以帮助你。



使用特权

评论回复

打赏榜单

21小跑堂 打赏了 60.00 元 2023-11-28
理由:恭喜通过原创审核!期待您更多的原创作品~

评论
21小跑堂 2023-11-28 15:27 回复TA
以Arduino方式的方式测试使用旋转编码器EC11,以外部中断方式触发结合电平逻辑判断旋转方向和角度,适合低分辨率和低转速的编码器,如果高分辨率和高速旋转设备,用户可借助MCU定时器的编码器接口进行数据采集。(蓝V用户打赏已提升) 

相关帖子

发新帖 本帖赏金 60.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

认证:大连伊飞特信息技术有限公司软件工程师
简介:本人于1993年毕业于大连理工大学。毕业后从事单片机开发工作5年,之后转入软件开发工作至今。

130

主题

3932

帖子

5

粉丝