风筝
发表于: 2022-7-20 18:02:16 | 显示全部楼层

旋转编码器(Rotary Encoder)是一种位置传感器,可将旋钮的角度位置(旋转)转换为输出信号,用于确定旋钮的旋转方向。


由于它们的稳健性和精细的数字控制;它们用于许多应用,包括机器人、CNC机器和打印机。


市场上有两种类型的旋转编码器 - 绝对式和增量式。绝对编码器以度为单位为我们提供旋钮的精确位置,而增量编码器报告轴移动了多少增量。本文中使用的旋转编码器是增量型的。


旋转编码器与电位器

旋转编码器是等效于现代数字的电位器,比电位器更通用。


它们可以完全旋转没有末端,而电位器只能旋转大约3/4圈。


电位器最适合需要知道旋钮确切位置的情况。但是,旋转编码器最适合您需要知道位置变化而不是确切位置的情况。


旋转编码器的工作原理

编码器内部是一个开槽圆盘,连接到公共接地引脚C,以及两个接触引脚A和B,如下图所示。

rotary-encoder-internal-structure.jpg

当您转动旋钮时,A和B会与公共接地引脚C接触,具体顺序取决于您转动旋钮的方向。


当它们与公共接地接触时,它们会产生信号。当一个引脚在另一个引脚之前接触时,这些信号彼此相位相差 90°。这称为正交编码。

rotary-encoder-working-animation.gif


顺时针转动旋钮时,首先连接A引脚,然后连接B引脚。当您逆时针转动旋钮时,首先连接B引脚,然后连接A引脚。


通过跟踪每个引脚何时连接和断开接地,我们可以使用这些信号变化来确定旋钮旋转的方向。您可以通过在A更改状态时简单地观察B的状态来做到这一点。


当A改变状态时:

如果 B != A,则旋钮顺时针旋转。

rotary-encoder-output-pulses-in-clockwise-rotation.jpg


如果 B = A,则旋钮逆时针旋转。

rotary-encoder-output-pulses-in-anticlockwise-rotation.jpg


旋转编码器引脚

旋转编码器的引脚分配如下:

rotary-encoder-module-pinout.jpg


GND 是接地连接。

VCC 是电源电压,通常为3.3或5V。

SW 是低电平有效按钮开关输出。按下旋钮时,电压变为低电平。

DT(输出 B)与 CLK 输出相同,但它比 CLK 滞后 90° 相移。该输出可用于确定旋转方向。

CLK(输出 A)是确定旋转量的主要输出脉冲。每次将旋钮沿任一方向旋转一个棘爪(咔嗒声)时,“CLK”输出都会经历一个从高电平到低电平的循环。


旋转编码器与Arduino开发板的硬件连接

现在我们已经了解了有关旋转编码器的一切,是时候使用它了!


让我们将旋转编码器连接到 Arduino。连接相当简单。首先将模块上的+V引脚连接到Arduino上的5V,GND引脚连接到地。


现在将CLK和DT引脚分别连接到数字引脚#2 和#3。最后,将SW引脚连接到数字引脚#4。


下图显示了接线。

wiring-rotary-encoder-with-arduino-uno.jpg


读取旋转编码器的Arduino代码

现在您已经连接了编码器,您需要一个草图才能使其全部工作。


以下草图检测编码器何时旋转,确定其旋转方向以及按钮是否被按下。试试草图; 然后我们将详对其介绍。

  1. // Rotary Encoder Inputs
  2. #define CLK 2
  3. #define DT 3
  4. #define SW 4

  5. int counter = 0;
  6. int currentStateCLK;
  7. int lastStateCLK;
  8. String currentDir ="";
  9. unsigned long lastButtonPress = 0;

  10. void setup() {
  11.         
  12.         // Set encoder pins as inputs
  13.         pinMode(CLK,INPUT);
  14.         pinMode(DT,INPUT);
  15.         pinMode(SW, INPUT_PULLUP);

  16.         // Setup Serial Monitor
  17.         Serial.begin(9600);

  18.         // Read the initial state of CLK
  19.         lastStateCLK = digitalRead(CLK);
  20. }

  21. void loop() {
  22.         
  23.         // Read the current state of CLK
  24.         currentStateCLK = digitalRead(CLK);

  25.         // If last and current state of CLK are different, then pulse occurred
  26.         // React to only 1 state change to avoid double count
  27.         if (currentStateCLK != lastStateCLK  && currentStateCLK == 1){

  28.                 // If the DT state is different than the CLK state then
  29.                 // the encoder is rotating CCW so decrement
  30.                 if (digitalRead(DT) != currentStateCLK) {
  31.                         counter --;
  32.                         currentDir ="CCW";
  33.                 } else {
  34.                         // Encoder is rotating CW so increment
  35.                         counter ++;
  36.                         currentDir ="CW";
  37.                 }

  38.                 Serial.print("Direction: ");
  39.                 Serial.print(currentDir);
  40.                 Serial.print(" | Counter: ");
  41.                 Serial.println(counter);
  42.         }

  43.         // Remember last CLK state
  44.         lastStateCLK = currentStateCLK;

  45.         // Read the button state
  46.         int btnState = digitalRead(SW);

  47.         //If we detect LOW signal, button is pressed
  48.         if (btnState == LOW) {
  49.                 //if 50ms have passed since last LOW pulse, it means that the
  50.                 //button has been pressed, released and pressed again
  51.                 if (millis() - lastButtonPress > 50) {
  52.                         Serial.println("Button pressed!");
  53.                 }

  54.                 // Remember last button press event
  55.                 lastButtonPress = millis();
  56.         }

  57.         // Put in a slight delay to help debounce the reading
  58.         delay(1);
  59. }
复制代码

如果一切正常,您应该在串口监视器上看到以下输出。

rotary-encoder-output-on-serial-monitor.jpg


代码说明

首先在草图中声明编码器的CLK、DT和SW引脚连接到的Arduino引脚。

  1. #define CLK 2
  2. #define DT 3
  3. #define SW 4
复制代码

接下来,定义了几个整数变量。变量counter表示每次将旋钮旋转一个定位器时将修改的计数。currentStateCLKlastStateCLK变量保存CLK输出的状态,并用于确定旋转量。


在串口监视器上打印当前旋转方向时,将使用一个名为currentDir的字符串。


lastButtonPress变量用于消除开关的抖动。

  1. int counter = 0;
  2. int currentStateCLK;
  3. int lastStateCLK;
  4. String currentDir ="";
  5. unsigned long lastButtonPress = 0;
复制代码

现在在setup()函数中,我们首先将与编码器的连接定义为输入,然后我们启用SW引脚上的输入上拉电阻。我们还设置了串口监视器。


最后,我们读取CLK引脚的当前值并将其存储在lastStateCLK变量中。

  1. pinMode(CLK,INPUT);
  2. pinMode(DT,INPUT);
  3. pinMode(SW, INPUT_PULLUP);

  4. Serial.begin(9600);

  5. lastStateCLK = digitalRead(CLK);
复制代码

loop()函数中,我们再次检查CLK状态并将其与lastStateCLK值进行比较。如果它们不同,则表示旋钮已转动并出现脉冲。我们还检查 currentStateCLK 的值是否为 1,以便仅对一个状态更改做出反应以避免重复计数。

  1. currentStateCLK = digitalRead(CLK);

  2. if (currentStateCLK != lastStateCLK  && currentStateCLK == 1){
复制代码

在if条件语句中,我们确定旋转方向。为此,我们只需读取编码器模块上的DT引脚并将其与CLK引脚的当前状态进行比较。


如果它们不同,则表示旋钮逆时针旋转。然后我们递减计数器并将 currentDir 设置为“CCW”。


如果两个值相同,则表示旋钮顺时针旋转。然后我们增加计数器并将 currentDir 设置为“CW”。

  1. if (digitalRead(DT) != currentStateCLK) {
  2.     counter --;
  3.     currentDir ="CCW";
  4. } else {
  5.     counter ++;
  6.     currentDir ="CW";
  7. }
复制代码

然后我们在串口监视器上打印结果。

  1. Serial.print("Direction: ");
  2. Serial.print(currentDir);
  3. Serial.print(" | Counter: ");
  4. Serial.println(counter);
复制代码

在if条件语句之外,我们用CLK的当前状态更新lastStateCLK。

  1. lastStateCLK = currentStateCLK;
复制代码

接下来是读取和去抖动按钮开关的逻辑。我们首先读取当前按钮状态,如果它为LOW,我们等待50毫秒来消除按钮的抖动。


如果按钮保持低电平超过50毫秒,我们会在串口监视器上打印“Button pressed!”。

  1. int btnState = digitalRead(SW);

  2. if (btnState == LOW) {
  3.     if (millis() - lastButtonPress > 50) {
  4.         Serial.println("Button pressed!");
  5.     }
  6.     lastButtonPress = millis();
  7. }
复制代码

跳转到指定楼层
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题 714 | 回复: 1501



手机版|

GMT+8, 2024-12-21 23:54 , Processed in 0.045378 second(s), 6 queries , Gzip On, MemCache On. Powered by Discuz! X3.5

YiBoard一板网 © 2015-2022 地址:河北省石家庄市长安区高营大街 ( 冀ICP备18020117号 )

快速回复 返回顶部 返回列表