返回列表 发新帖我要提问本帖赏金: 60.00元(功能说明)

[其它] 以Arduino方式学习使用旋转编码器EC11

[复制链接]
 楼主| suncat0504 发表于 2023-11-24 13:20 | 显示全部楼层 |阅读模式
<
本帖最后由 suncat0504 于 2023-11-24 13:36 编辑

#申请原创# @21小跑堂
      平时买个什么模块,需要测试是否正常。通常这种情况下,我优先选择用ESP8266或者其它可以在ArduinoIDE下使用的开发板来测试。原因无他,因为可以直接使用支持库、以及连接、下载、串口测试显示接收等方便,不需要额外做其他事情。所以当买来的旋转编码器在拿到手里后,立马就用ESP8266来测试了。
论坛里已经有不少伙计发了有关编码器的帖子,我这里也就不再详细说明编码器的资料了,只上干货。
先看看ESP8266的引脚图:
b4931a292112529db433688252d4dc37
旋转编码器的接口:
c060851a6fd86445db1e4545dcdf20fa
编码器的电原理图:
5b5b9bc6faa38d6bb15a610965d17aeb
输出信号:
cf3c42ee4a2ac1749287feb7b2ad550e
A、B两相都输出方波,顺时针方向旋转时,A相超前B相90度,逆时针方向旋转时,B相超前A相90度;
为了方便接线和测试,我用洞洞板重新改了接线方式,使所有接线都放在一侧。使用ESP8266的D1,D2这两个口采集A、B数据。 2.jpg
为了方便看编码器旋转时信号的变化,首先使用以下程序进行测试:
  1. // 本程序使用ESP8266测试旋转编码器,使用D1(GPIO5)接A,D2(GPIO4)接B
  2. // 引脚定义
  3. #defineBMQ_A  5
  4. #defineBMQ_B  4
  5. // 旋转标志变量
  6. unsignedchar flag = 0;
  7. // 上次状态
  8. unsignedchar status_old = 0;
  9. // 当前状态
  10. unsignedchar status_cur = 0;
  11. // 旋转计数,顺时针:+1;逆时针:-1
  12. int cnt = 0;
  13. void setup(){
  14.   // 串口设置
  15.   Serial.begin(115200);
  16.   
  17.   // LED设置
  18.   pinMode(LED_BUILTIN, OUTPUT);
  19.   // 设置D1(GPIO5)、D2(GPIO4)为数字输入口
  20.   pinMode(BMQ_A, INPUT);    // D1 - A
  21.   pinMode(BMQ_B, INPUT);    // D2 - B
  22.   // 设置中断,下降沿和上升沿均触发中断
  23.   //attachInterrupt(digitalPinToInterrupt(BMQ_A), a_proc, FALLING | RISING);
  24.   //attachInterrupt(digitalPinToInterrupt(BMQ_B), b_proc, FALLING | RISING);
  25.   status_old=digitalRead(BMQ_A)*2+digitalRead(BMQ_B);
  26. }
  27. void loop(){
  28.    status_cur=digitalRead(BMQ_A)*2+digitalRead(BMQ_B);
  29.    if(status_old != status_cur){
  30.      status_old=status_cur;
  31.      Serial.print("A:");
  32.      Serial.print(digitalRead(BMQ_A));
  33.      Serial.print(" B:");
  34.      Serial.println(digitalRead(BMQ_B));
  35.    }
  36. }
  37. // 中断处理函数,边沿触发
  38. IRAM_ATTR void a_proc(){
  39.   status_cur=digitalRead(BMQ_A)*2+digitalRead(BMQ_B);
  40.   if(status_cur != status_old){
  41.     status_old=status_cur;
  42.     Serial.print("a_proc ");
  43.     Serial.print("A:");
  44.     Serial.print(digitalRead(BMQ_A));
  45.     Serial.print(" B:");
  46.     Serial.println(digitalRead(BMQ_B));
  47.   }
  48.    
  49. }
  50. IRAM_ATTR void b_proc(){
  51.   status_cur=digitalRead(BMQ_A)*2+digitalRead(BMQ_B);
  52.   if(status_cur != status_old){
  53.     status_old=status_cur;
  54.     Serial.print("b_proc ");
  55.     Serial.print("A:");
  56.     Serial.print(digitalRead(BMQ_A));
  57.     Serial.print(" B:");
  58.     Serial.println(digitalRead(BMQ_B));
  59.   }
  60. }


运行后,从串口收集到的信息如下:
初始化状态: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个时,与变化规律进行比较,从而判断出是顺时针旋转,还是逆时针旋转。
整理代码如下:

  1. // 本程序测试旋转编码器,使用D1(GPIO5)接A,D2(GPIO4)接B

  2. // 引脚定义
  3. #define BMQ_A 5
  4. #define BMQ_B 4

  5. // 一组数据采集完成的标志
  6. unsigned char flag = 0;
  7. // 当前数据存储位置
  8. unsigned char pos = 0;
  9. // 完整数据采集周期变量
  10. unsigned char chg[8];

  11. // 上次状态
  12. unsigned char status_old = 0;
  13. // 当前状态
  14. unsigned char status_cur = 0;

  15. // 旋转计数,顺时针:+1;逆时针:-1
  16. long step = 0;

  17. void setup() {
  18.   // 串口设置
  19.   Serial.begin(230400);
  20.   
  21.   // LED设置
  22.   pinMode(LED_BUILTIN, OUTPUT);

  23.   // 设置D1(GPIO5)、D2(GPIO4)为数字输入口
  24.   pinMode(BMQ_A, INPUT);    // D1 - A
  25.   pinMode(BMQ_B, INPUT);    // D2 - B

  26.   // 设置中断,下降沿和上升沿均触发中断
  27.   attachInterrupt(digitalPinToInterrupt(BMQ_A), a_proc, FALLING | RISING);
  28.   attachInterrupt(digitalPinToInterrupt(BMQ_B), b_proc, FALLING | RISING);

  29.   status_old=digitalRead(BMQ_A)*2+digitalRead(BMQ_B);
  30. }

  31. void loop() {

  32. }

  33. // 中断处理函数,边沿触发
  34. IRAM_ATTR void a_proc() {
  35.   //Serial.println("a_proc");
  36.   com_proc();
  37. }

  38. IRAM_ATTR void b_proc() {
  39.   //Serial.println("b_proc");
  40.   com_proc();
  41. }

  42. void com_proc() {
  43.   int val=0;
  44.   if (flag==0) {
  45.     // 当前状态
  46.     status_cur=digitalRead(BMQ_A)*2+digitalRead(BMQ_B);
  47.     if (status_cur != status_old) {
  48.       Serial.print("pos=");
  49.       Serial.print(pos);
  50.       Serial.print("  AB=");
  51.       Serial.println(status_cur);
  52.       // 有变化
  53.       status_old=status_cur;
  54.       // 保存
  55.       chg[pos++]=status_cur;
  56.       if (pos>=4) {
  57.         // 完成一组数据采集
  58.         flag=1;   // 设置标志,防止下一次中断打断下面的处理

  59.         // 计算这组数据的变化顺序
  60.         val=chg[0]*1000 + chg[1]*100 +chg[2]*10 +chg[3];
  61.         // 为了能避免用户在旋转不到位时进行反方向的旋转,需要判断多种值的判定
  62.         // 正常的顺时针旋转为2310,逆时针为1320,通过切换组合顺序,可以避免漏判
  63.         if (val == 2310 || val==3102 || val==1023 || val==231) {
  64.           // 顺时针转动
  65.           step++;
  66.           Serial.print("Shun:");
  67.           Serial.println(step);
  68.         } else if (val == 1320 || val==3201 || val==2013 || val==132) {
  69.           // 逆时针转动
  70.           step--;
  71.           Serial.print("Ni:");
  72.           Serial.println(step);
  73.         }
  74.         if (val == 1020 || val==1013 || val==2023 || val==2020) {
  75.           // 转了一半不动,或者回去了,保留后两位数据
  76.           chg[0]=chg[2];
  77.           chg[1]=chg[3];
  78.           pos=2;
  79.           // 恢复标志
  80.           flag=0;
  81.         } else {
  82.           // 存储位置归位
  83.           pos=0;
  84.           // 恢复标志
  85.           flag=0;
  86.         }
  87.       }  
  88.     }
  89.   }
  90. }

以上代码是经过了以下测试,没有任何问题的,响应速度也很快:
1、慢速顺时针旋转测试
2、慢速逆时针旋转测试
3、快速顺时针旋转测试
4、快速逆时针旋转测试
5、快速旋转中顺时针、逆时针无规律快速切换测试
6、旋转一格不到,就回位,或者反转的测试

以上处理逻辑,应该同样适用于ARM、RISC等其它架构的单片机。
对不喜欢动手敲太多代码的朋友,你也可以选择安装别人做好的名为“Encoder”的库,有现成的例子可以帮助你。
图片1.png


打赏榜单

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

评论

以Arduino方式的方式测试使用旋转编码器EC11,以外部中断方式触发结合电平逻辑判断旋转方向和角度,适合低分辨率和低转速的编码器,如果高分辨率和高速旋转设备,用户可借助MCU定时器的编码器接口进行数据采集。(蓝V用户打赏已提升)  发表于 2023-11-28 15:27
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

158

主题

4504

帖子

6

粉丝
快速回复 在线客服 返回列表 返回顶部
认证:大连伊飞特信息技术有限公司软件工程师
简介:本人于1993年毕业于大连理工大学。毕业后从事单片机开发工作5年,之后转入软件开发工作至今。

158

主题

4504

帖子

6

粉丝
快速回复 在线客服 返回列表 返回顶部