编码器的原理以及在arduino中的使用


原始链接

《How Rotary Encoder Works and Interface It with Arduino》
https://lastminuteengineers.com/rotary-encoder-arduino-tutorial/


在这里插入图片描述
Rotary encoders are used in the myriad of common devices we see every day. From printers and photographic lenses to CNC machines and robotics, rotary encoders are closer to us than we think. The most popular example of the use of a rotary encoder in everyday life is the volume control knob of a car radio.
A rotary encoder is a type of position sensor that converts the angular position (rotation) of a knob into an output signal that is used to determine which direction the knob is being turned.

There are two types of rotary encoders – absolute and incremental. The absolute encoder reports the exact position of the knob in degrees while the incremental encoder reports how many increments the shaft has moved.

The rotary encoder used in this tutorial is of the incremental type.

编码器分为绝对编码器和增量式编码器,本文采用的是增量式编码器

Rotary Encoders Vs Potentiometers

Rotary encoders are the modern digital equivalent of potentiometers and are more versatile than them.
They can rotate 360° without stopping whereas potentiometers can only rotate 3/4 of the circle.
Potentiometers are used in situations where you need to know the exact position of the knob. Whereas, rotary encoders are used in situations where you need to know the change in position rather than the exact position.

旋转式编码器比电位器更优秀,可以630度旋转。

How Rotary Encoders Work?

Inside the encoder is a slotted disc that is connected to the common ground pin C. It also contains two contact pins A and B, as shown below.
在这里插入图片描述
When you turn the knob, A and B come into contact with the common ground pin C in a particular order according to the direction in which you are turning the knob.

When they come into contact with common ground, signals are generated. These signals are 90° out of phase with each other as one pin comes into contact with the common ground before the other pin. This is called quadrature encoding.
在这里插入图片描述

When you turn the knob clockwise the A pin connects to the ground before the B pin. And when you turn the knob counterclockwise, the B pin connects to the ground before the A pin.

By tracking when each pin connects to or disconnects from ground, we can determine which direction the knob is being rotated. You can do this by simply observing the state of B when A changes state.
旋转的交码盘。如果是光编码器,码盘上会有黑白的线,白色能透光,黑色不能透光。
一旦经过白色的线,就会产生一个脉冲。A,B的脉冲顺序,代表了旋转的方向。

When A changes state:

  1. if B != A, then the knob is turned clockwise
    在这里插入图片描述
  2. if B = A, the knob is turned counterclockwise.
    在这里插入图片描述

Rotary Encoder Pinout

The pinouts of the rotary encoder are as follows:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Wiring a Rotary Encoder to an Arduino

Now that we know everything about the rotary encoder it’s time to put it to use!

Let’s connect the rotary encoder to the Arduino. The connections are quite simple. Start by connecting the +V pin on the module to 5V on the Arduino and the GND pin to Ground.

Now connect the CLK and DT pins to digital pins #2 and #3 respectively. Lastly connect the SW pin to digital pin #4.

The following illustration shows the wiring.
在这里插入图片描述

Arduino Code – Reading Rotary Encoders

Now that you have connected your encoder you will need a sketch to make it work.
The following sketch detects which direction the encoder is being rotated and when the button is being pushed.
Try this sketch first and then we will go into detail about it.

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

int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir ="";
unsigned long lastButtonPress = 0;

void setup() {
	
	// Set encoder pins as inputs
	pinMode(CLK,INPUT);
	pinMode(DT,INPUT);
	pinMode(SW, INPUT_PULLUP);

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

	// Read the initial state of CLK
	lastStateCLK = digitalRead(CLK);
}

void loop() {
	
	// Read the current state of CLK
	currentStateCLK = digitalRead(CLK);

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

		// If the DT state is different than the CLK state then
		// the encoder is rotating CCW so decrement
		if (digitalRead(DT) != currentStateCLK) {
			counter --;
			currentDir ="CCW";
		} else {
			// Encoder is rotating CW so increment
			counter ++;
			currentDir ="CW";
		}

		Serial.print("Direction: ");
		Serial.print(currentDir);
		Serial.print(" | Counter: ");
		Serial.println(counter);
	}

	// Remember last CLK state
	lastStateCLK = currentStateCLK;

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

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

		// Remember last button press event
		lastButtonPress = millis();
	}

	// Put in a slight delay to help debounce the reading
	delay(1);
}

If all goes well, you will see an output like this on the serial monitor.
在这里插入图片描述

If the rotation being reported is the opposite of what you expect, try swapping the CLK (Output A) and DT (Output B) pins.

Code Explanation:

The sketch begins by declaring the Arduino pins to which the CLK, DT and SW pins of the encoder are connected.

#define CLK 2
#define DT 3
#define SW 4

After this some integers are defined.

  1. The counter variable will count each time the knob is rotated one detent (click).
  2. The currentStateCLK and lastStateCLK variables hold the state of the CLK output and are used to determine the amount of rotation.
  3. A string named currentDir will be used to print the current direction of rotation on the serial monitor.
  4. The lastButtonPress variable is used to debounce a switch.
int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir ="";
unsigned long lastButtonPress = 0;

In the setup section, we first configure the connections to the rotary encoder as inputs, then we enable the input pullup resistor on the SW pin. We also setup the serial monitor.

Finally we read the current value of the CLK pin and store it in the lastStateCLK variable.

pinMode(CLK,INPUT);
pinMode(DT,INPUT);
pinMode(SW, INPUT_PULLUP);

Serial.begin(9600);

lastStateCLK = digitalRead(CLK);

In the loop section, we check the CLK state again and compare it with the lastStateCLK value. If they are different it means the knob has turned. We also check if currentStateCLK is 1 to react to only one state change and to avoid double counting.

currentStateCLK = digitalRead(CLK);

if (currentStateCLK != lastStateCLK  && currentStateCLK == 1){

Inside the if statement we find out the direction of rotation. To do this we simply read the DT pin and compare it with the current state of the CLK pin.

  1. If these two values ​​are different, it means that the knob is turned counterclockwise. We then decrement the counter and set the currentDir to “CCW”.
  2. If these two values ​​are equal, it means that the knob is turned clockwise. We then increment the counter and set the currentDir to “CW”.
if (digitalRead(DT) != currentStateCLK) {
    counter --;
    currentDir ="CCW";
} else {
    counter ++;
    currentDir ="CW";
}

Then we print our results on the serial monitor.

Serial.print("Direction: ");
Serial.print(currentDir);
Serial.print(" | Counter: ");
Serial.println(counter);

Outside the if statement, we update lastStateCLK with the current state of CLK.

lastStateCLK = currentStateCLK;

Next comes the logic of reading and debouncing the push button switch. We first read the current button state, when it goes LOW, we wait 50ms for the push button to debounce.

If the button remains LOW for more than 50ms, it means that the button is really pressed. So we print the message “Button pressed!” on the serial monitor.

int btnState = digitalRead(SW);

if (btnState == LOW) {
    if (millis() - lastButtonPress > 50) {
        Serial.println("Button pressed!");
    }
    lastButtonPress = millis();
}

Then we do it all over again.

Arduino Code – Using Interrupts

In order for the rotary encoder to work, we need to continuously monitor changes in the DT and CLK signals.

One way to know when these changes happen is to continuously poll them (like we did in our last sketch). However, it is not the best solution for the following reasons.

  1. We have to busily perform checking to see if the value has changed. There will be a waste of cycles if signal level does not change.
  2. There will be latency from the time the event occurs to the time we check. If we need to react quickly, we will be delayed because of this latency.
  3. It is possible to miss the signal change entirely, if the duration of the change is short.

A widely adopted solution is the use of interrupts.

With interrupts you don’t need to continuously poll the specific event. This frees up the Arduino to do some other work while not missing the event.

Wiring

Since most Arduino boards (including Arduino UNO) have only two external interrupts, we can only monitor changes in the DT and CLK signals. So remove the connection for the SW pin from the previous wiring diagram.

So now the wiring looks like this:
在这里插入图片描述
Some boards (like the Arduino Mega 2560) have more external interrupts. If you have one of those, you can keep the connection for the SW pin and extend the sketch below to include the code for the push button.

Arduino Code

Here is a sketch that demonstrates how to read a rotary encoder using interrupts.

// Rotary Encoder Inputs
#define CLK 2
#define DT 3

int counter = 0;
int currentStateCLK;
int lastStateCLK;
String currentDir ="";

void setup() {
	
	// Set encoder pins as inputs
	pinMode(CLK,INPUT);
	pinMode(DT,INPUT);

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

	// Read the initial state of CLK
	lastStateCLK = digitalRead(CLK);
	
	// Call updateEncoder() when any high/low changed seen
	// on interrupt 0 (pin 2), or interrupt 1 (pin 3)
	attachInterrupt(0, updateEncoder, CHANGE);
	attachInterrupt(1, updateEncoder, CHANGE);
}

void loop() {
	//Do some useful stuff here
}

void updateEncoder(){
	// Read the current state of CLK
	currentStateCLK = digitalRead(CLK);

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

		// If the DT state is different than the CLK state then
		// the encoder is rotating CCW so decrement
		if (digitalRead(DT) != currentStateCLK) {
			counter --;
			currentDir ="CCW";
		} else {
			// Encoder is rotating CW so increment
			counter ++;
			currentDir ="CW";
		}

		Serial.print("Direction: ");
		Serial.print(currentDir);
		Serial.print(" | Counter: ");
		Serial.println(counter);
	}

	// Remember last CLK state
	lastStateCLK = currentStateCLK;
}

Notice that the main loop of this program is kept empty so Arduino will be busy doing nothing.

Now try turning the knob. You will see an output like this on the serial monitor.
在这里插入图片描述

Code Explanation:

This program watches digital pin 2 (corresponding to interrupt 0) and digital pin 3 (corresponding to interrupt 1) for a change in value. In other words, it looks for a voltage change going from HIGH to LOW or LOW to HIGH, which happens when you turn the knob.
When a change occurs, the function updateEncoder() (known as the Interrupt Service Routine or just ISR) is called automatically. The code within this function is executed and then the program returns back to what it was doing before.

The following two lines are responsible for all this. The function attachInterrupt() tells the Arduino which pin to monitor, which ISR to execute when the interrupt is triggered and what type of trigger to look for.

attachInterrupt(0, updateEncoder, CHANGE);
attachInterrupt(1, updateEncoder, CHANGE);
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值