Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

How to read incremental rotary (quadrature) encoder?

Status
Not open for further replies.

baileychic

Advanced Member level 3
Advanced Member level 3
Joined
Aug 2, 2017
Messages
728
Helped
56
Reputation
112
Reaction score
57
Trophy points
28
Activity points
7,033
How to read incremental rotary (quadrature) encoder?

The rotary encoder has two connections for signals namely A and B.

I have to use 3 pins of microcontroller (PIC16F) to read it.

I have used PIC16F877A and connections are also below.

A -> RB1
B -> RB2
A xor B (using xor gate) -> RB0/INT

I need to use external interrupt to detect change in rotary encoder state and increment or decrement a counter.

This is the code that I have written but the counter is always 0.


Code C - [expand]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
// LCD module connections
sbit LCD_RS at RD4_bit;
sbit LCD_EN at RD5_bit;
sbit LCD_D4 at RD0_bit;
sbit LCD_D5 at RD1_bit;
sbit LCD_D6 at RD2_bit;
sbit LCD_D7 at RD3_bit;
 
sbit LCD_RS_Direction at TRISD4_bit;
sbit LCD_EN_Direction at TRISD5_bit;
sbit LCD_D4_Direction at TRISD0_bit;
sbit LCD_D5_Direction at TRISD1_bit;
sbit LCD_D6_Direction at TRISD2_bit;
sbit LCD_D7_Direction at TRISD3_bit;
// End LCD module connections
 
sbit EncoderA_bit at RB1_bit;
sbit EncoderB_bit at RB2_bit;
sbit EncoderINT_bit at RB0_bit;
 
signed char counter = 0;
char msg[17];
char txt1[] = " ROTARY ENCODER ";
char txt2[] = "COUNT: ";
 
void interrupt() {
    if((INTE_bit) && (INTF_bit)) {
        INTF_bit = 0;
        //counter++;
        if((EncoderA_bit != RB1_bit)||(EncoderB_bit != RB2_bit)){
            if(EncoderB_bit ^ RB1_bit){
                 counter++;
            }
 
            if(EncoderA_bit ^ RB2_bit){
                 counter--;
            }
 
            EncoderA_bit = RB1_bit;
            EncoderB_bit = RB2_bit;
    }
    }
}
 
void main() {
    TRISB = 0x07;
    TRISD = 0x00;
    PORTB = 0x00;
    PORTD = 0x00;
    
    Lcd_Init();
    Lcd_Cmd(_LCD_CURSOR_OFF);
    Lcd_Cmd(_LCD_CLEAR);
    Lcd_Out(1,1,txt1);
    Lcd_Out(2,1,txt2);
    
    Delay_ms(100);
    INTEDG_bit = 0;
    INTF_bit = 0;
    INTE_bit = 1;
    PEIE_bit = 1;
    GIE_bit = 1;
    
    while(1) {
          ByteToStr(counter,msg);
          strcat(msg," ");
          Lcd_Out(2,7,msg);
    }
}



Schematic
 

Attachments

  • RIRE.png
    RIRE.png
    52.2 KB · Views: 174

You only need two signals. A and B.
1. Use one of them (lets call it A) to trigger an edge INT interrupt.
2. In the ISR, read 'B', it tells you the direction
3. change your 'counter' value accordingly.

If you also want to incorporate acceleration, reset a timer at each interrupt and look at it's value on the next interrupt. If it is small enough (= rotating fast) add more to the counter.

Brian.
 

Hi,

It depends what you want to do with it.
Brians solution is good for input devices like the ALPS EC11 encoders.

But if you want to do position control it suffers from loss of position steps during change of direction.

Klaus
 

You have a serious confusion with RB1_bit and EncoderA_bit respectively RB2_bit and EncoderB_bit. Encoderxx is defined as alias of the RBx, but should be a variable holding the previous input state.

Apart form this problem, the code should work. There's a possible complication with bouncing encoder outputs, RB1 and RB2 should be better copied to variables first, so that all instructions referring to it are using the same value.

The XOR logic and INT input can be saved by using Port B interrupt an change function.

You only need two signals. A and B.
1. Use one of them (lets call it A) to trigger an edge INT interrupt.
2. In the ISR, read 'B', it tells you the direction
3. change your 'counter' value accordingly.
Possible, but at the expense of loosing 3 of 4 available encoder steps. The simple logic also can't handle encoder rotated back and forth and gives potentially wrong counts.
 
Thanks all. Betwixt's method works fine. I don't need acceleration or position control. I am just using Rotary Encoder to select 4 different PWM frequencies for DC-DC Converter.

I removed the XOR gate and connected A to RB0/INT and B to RB1 and read in ISR.

Code is working fine now.

I need a little modification and I don't know how to implement it.

The rotary encoder has one more signal connection that is:

SW pin and it is used to provide signal for rotary encoder button press.

I want to know how I can connect

A
B
SW pins to PIC16F877A.

A needs INT pin
B needs DigitalInput pin
SW needs INT pin

and I have only one INT pin and I can't change the controller.

I need to use same INT pin for A and SW signals and counter should not increment when SW signals is given.
 
Last edited:

Hi,

Use a standard input for the SW.

Poll the SW with with a frequency higher than 20Hz (50ms).

Klaus
 
I agree with Klaus, you would need significant extra electronics to externally multiplex SW with the encoder output and for such a low priority function it isn't worth the extra effort. Just connect SW to another digital input and poll it.

Brian.
 
How about using RB4 to RB7 interrupt on change function?

Key detection needs only moderate scan rate, it's very unusual to use interrupt for it. Incremental decoder depends on the application, pulse rate can be between a few Hz and 10s of kHz.
 
Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top