Interfacing rotary encoder to Atmega32

Recently I was working on a project that involved rotary encoder. I thought I’d share some thoughts on how rotary encoder can be interfaced and programmed. Actually it is easy to work with rotary encoders – interfacing is simple – only three wires are required to connect to microcontroller (two for signal (quadrature outputs) and one for reference (GND)). When turned there is a Grey code produced on outputs that allows tracking turn speed and direction. These features allow having convenient user interface with single knob. Many rotary encoders also comes with button – so menu navigation couldn’t be easier. In our project we are going to use a 12-step mechanical rotary encoder from sparkfun.Interfacing rotary encoder to Atmega32
You can find many projects in internet where one of rotary encoder pins is connecter to microcontroller interrupt pin. This enables easier detection of encoder turn. Using endless loop isn’t recommended because it occupies lots of MCU resources and is inefficient. Another logical solution is to use Timer that periodically generates interrupts so it could check if rotary encoder have been turned or button have been pressed. Lets stick with timer option as it allows connecting encoder to any available microcontroller pins. Encoder is connected to Atmega32 as follows:Schematic Interfacing rotary encoder to Atmega32
Encoder A pin to atmega32 pin B2; encoder pin B to MCU pin B3 and button to pin B4. We are going to use atemga32 internal pull-ups on these pins, so other encoder pins have to be connected to ground.
Lets write simple library for reading rotary encoder. This will make code more reusable and modular. To do so we create two empty files rotary.c and rotary.h. In rotary.h we define our port pins and library function prototypes:
#ifndef ROTARY_H
#define ROTARY_H
#include <avr/io.h>
//define port where encoder is connected
#define ROTPORT PORTB
#define ROTDDR DDRB
#define ROTPIN PINB
//define rotary encoder pins
#define ROTPA PB2
#define ROTPB PB3
#define ROTPBUTTON    PB4
//define macros to check status
#define ROTA !((1<<ROTPA)&ROTPIN)
#define ROTB !((1<<ROTPB)&ROTPIN)
#define ROTCLICK !((1<<ROTPBUTTON)&ROTPIN)
//prototypes
void RotaryInit(void);
void RotaryCheckStatus(void);
uint8_t RotaryGetStatus(void);
void RotaryResetStatus(void);
#endif
In library source file we declare following functions. First of all initialize pins where rotary encoder is connected:
void RotaryInit(void)
{
//set pins as input
ROTDDR &= ~((1<<ROTPA)|(1<<ROTPB)|(1<<ROTPBUTTON));
//enable interrnal pullups;
ROTPORT |= (1<<ROTPA)|(1<<ROTPB)|(1<<ROTPBUTTON);
}
we set selected port pins as inputs and enable internal pull-up resistors.
Then follows our rotary check status function which sets our internal variable to some variable.
void RotaryCheckStatus(void)
{
//reading rotary and button
//check if rotation is left
     if(ROTA & (!ROTB)){
        loop_until_bit_is_set(ROTPIN, ROTPA);
        if (ROTB)
            rotarystatus=1;
        //check if rotation is right
        }else if(ROTB & (!ROTA)){
            loop_until_bit_is_set(ROTPIN, ROTPB);
            if (ROTA)
                rotarystatus=2;
        }else if (ROTA & ROTB){
            loop_until_bit_is_set(ROTPIN, ROTPA);
            if (ROTB)
                rotarystatus=1;
             else rotarystatus=2;
        }
    //check button status
    if (ROTCLICK)
        {
            rotarystatus=3;
        }
}
if know was turned left – rotary status is set to 1, if right then value is 2 and if button was pressed – status is set to 3.
Following two functions simply returns status and resets it:
//return button status
uint8_t RotaryGetStatus(void)
{
return rotarystatus;
}
//reset status
void RotaryResetStatus(void)
{
rotarystatus=0;
}
We are using a graphical LCD based on ks0108 controller for displaying messages. How co control it and how to set up library follow this post. We won’t go in to details on this.
To check rotary encoder status we are using Timer2 overflow interrupts. Microcontroller is running at 16MHz so with prescaller 256 overflow interrupt occur 122 times/s. Speed seems to be suitable for fluent operation.
void Timer2_Start(void)
{
    TCCR2|=(1<<CS22)|(1<<CS21); //prescaller 256 ~122 interrupts/s
    TIMSK|=(1<<TOIE2);//Enable Timer0 Overflow interrupts
}
After timer has been started we can put RotarryCheckStatus function inside it and read encoder actions:
ISR(TIMER2_OVF_vect)
{
//reading rotary and button
RotaryCheckStatus();
}
For more detail: Interfacing rotary encoder to Atmega32


About The Author

Ibrar Ayyub

I am an experienced technical writer holding a Master's degree in computer science from BZU Multan, Pakistan University. With a background spanning various industries, particularly in home automation and engineering, I have honed my skills in crafting clear and concise content. Proficient in leveraging infographics and diagrams, I strive to simplify complex concepts for readers. My strength lies in thorough research and presenting information in a structured and logical format.

Follow Us:
LinkedinTwitter

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top