Home » Blog » Projects » Electronics Projects » Arduino AD9850 Rotary Encoder Commands

Arduino AD9850 Rotary Encoder Commands

ky040As described earlier, my signal generator is controlled by LCD Keypad buttons and a rotary encoder. The KY-040 rotary encoder is cheap and reliable. You can buy them on eBay for around a dollar each and you should always keep a few around.

Rotary encoders are used to send tuning pulses to an electronic device. Your car radio probably uses rotary encoders for changing volume and frequency. They work by sending pulses when turned. These pulses are on two channels (A and B) and are sent 90 degrees out of phase with each other.

By hooking up these outputs to two digital pins on the Arduino, you can measure their state: HIGH or LOW. The pattern of the pulses is different if you are turning the knob one way or the other. This difference lets you figure out the direction of the turn. The number of pulses sent tells you the turning speed.

The KY-040 also includes a momentary press switch, which sends out a voltage on a third channel when pushed.

At the beginning of each Arduino program loop in my signal generator, I check for the state of the keypad buttons and the rotary encoder, and translate any button or encoder events into equivalent button commands.

/* UNO Digital Pin Connections for KY-040 Encoder */
#define	 KY_CLK 2 //Interrupt 0
#define  KY_DT 12
#define  KY_SW 11

void loop()
{ ...

	btn = ReadLCDBtn();
	if (btn == BUTTON_NONE) {
		btn = CheckEncoderTurning();
	}
	if (btn == BUTTON_NONE) {
		btn = CheckEncoderSwitch();
	}
	/* Deal with Menu activities, if any */
	btn = ProcessMenu(btn);
... }

 

Decoding Rotary Encoder Commands

First, let’s discuss rotary encoder turns, or pulses in either turning direction. There are are two ways to read these pulses. The first way is just to ask the Arduino pins connected to the rotary encoder channels if they are low or high. This is called “polling”. The second is to use what is called a “hardware interrupt”. An interrupt is where the Arduino secretly monitors certain pins in the background, and reacts when they change automatically, i.e. without polling. I use the interrupt approach.

The first thing you do is write what is called, not surprisingly, an interrupt service routine (ISR). You attach this ISR to an appropriate in during Setup(). Not all Arduino digital pins support interrupts. On my Arduino UNO, digital pin 2 provides interrupt 0, so I connected the clock line of my KY-040 to that pin.

void setup()
{ ...
	/* Encoder KY-040 */
	pinMode(kyCLK, INPUT);
	pinMode(kyDT, INPUT);
	attachInterrupt(0, isrEncoder, CHANGE);
	pinMode(kySW, INPUT);
...}

void isrEncoder() {
	/* Interrupt Service Routine for Encoder turns */
	Down = (digitalRead(kyCLK) == digitalRead(kyDT));
	TurnDetected = true;
}

 

Whenever the KY-040 changes pin 2, the ISR activates and does two things. First, it sets a global variable called TurnDetected to true. Second, it sets another global variable called Down either true or false, depending on the turn direction. Then, once every Arduino loop, my program checks the status of these two variables. It then knows whether or not the rotary encoder has turned one pulse, and in what direction.

int CheckEncoderTurning() {
	int retval = BUTTON_NONE;
	if (TurnDetected)	// set by isrEncoder
	{
		if (Down) {
			if (MenuOn)
			{
				retval = BUTTON_LEFT;
			}
			else
			{
				retval = BUTTON_DOWN;
			}

		}
		else {
			if (MenuOn)
			{
				retval = BUTTON_RIGHT;
			}
			else
			{
				retval = BUTTON_UP;
			}

		}
		TurnDetected = false;
	}
	return retval;
}

 

To read whether or not the KY-040 button is pushed, I used the first approach, namely polling. The following routine asks the switch if it is pushed, and if so, for how long. It then simulates pushing the keypad Select button, either short of long.

int CheckEncoderSwitch() {
	int retval = BUTTON_NONE;
	static unsigned long startTime;
	if (digitalRead(kySW) == LOW)
	{
		startTime = millis();
		while (digitalRead(kySW) == LOW) {}  //wait until released
		if ((millis() - startTime) >= PRESS_LONG)
		{
			retval = BUTTON_SELECT_LONG;	//Toggle Repeating
		}
		else
		{
			retval = BUTTON_SELECT;			//Toggle MenuOn
		}
	}
	return retval;
}

 

The KY-040 switch is usually wired with a pull-up resister, so its normal “open” value is HIGH (5V). When you push the button, it goes to LOW (~ 0V).

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.