This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

MSP430G2553: Rotary encoder direction

Part Number: MSP430G2553

Hello all,

I'm using a 2 phase quadrature increamental rotary encoder, with open collector NPN output and 600 pulses per revolution. I'm sensing both A and B values at ports 1.2 and 1.4.  The program is shown below,

#include <msp430g2553.h>

#define PHASEA BIT2
#define PHASEB BIT4

int clockwise;
int anticlockwise;

void update(void);

int main(void)
{
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer

clockwise = 0;
anticlockwise= 0;

P1DIR &= ~(PHASEA + PHASEB); // Set P1.2 and P1.4 as inputs
P1IFG &= ~(PHASEA ); // clear interrrupt flags for P1.2 

P1IE |= PHASEA ; //  P1.2 interrupt enabled
P1IES &= ~PHASEA ; // interrupt arises on rising edge
__enable_interrupt(); // enable all interrupts
}


#pragma vector=PORT1_VECTOR
__interrupt void Port_1(void)
{
if (PHASEA != PHASEB)

{ clockwise++; }

else { anticlockwise++; }

P1IFG =0;
}

When the encoder shaft is rotated 360 degree clockwise,  variable clockwise shows the exact count of 600. But  when rotated 360 degree in counter clockwise direction, variable clockwise shows 1200.Variable anticlockwise doesn't change but clockwise does.Any suggestions ?

  • Besides being incorrect, that is a pretty horrible way to use a quadrature encoder. What with ignoring the quadrature part and all. My code uses interrupts on both rising and  falling edges for both inputs. It is a lot more complicated but counts all edges.

    Your interrupt routine would work better if the comparison was not (BIT != BIT4) which is constant and always false. Instead, test if (P1IN & PHASEB) is zero or one.

  • Hello David,

                 Thank you for your response. My query is why use interrupts on both rising and falling edges, when we can count the no of pulses just from rising edges?

  • I agree on PHASEA and PHASEB, which are just defined constant values, not the current state of the pins. Test if (P1In & PHASEB) = 0

    But aside from that, I'm curious why this wouldn't work. Well, first, you have to assume that the encoder has no bounce, or it's dealt with otherwise in hardware. But if that's true, then when Bit2 goes high and triggers an interrupt, either the other bit is also high, which indicates movement in one direction, or the other bit is still low, which indicates the other direction. It seems this should work, and there should be no need to trigger interrupts on any other transitions.

    It's swtich bounce that complicates life. All my experience with rotary encoders so far is with the mechanical switch type, which bounce a lot. But I'd guess the 600 ppr type don't have that problem. So his method seems workable to me. No?
  • There are four times as many edges as there are pulses so you get four times the resolution.

    The mechanical encoder I am using for user input has an edge for every mechanical detent. If I  just counted pulses that would mean one change of state for every four clicks which would make no sense at all to the user.

    It  could be that you are OK with trading  simpler  software for resolution.

  • That sounds like an interesting encoder.  The encoders I use have the same number of pulses per revolution as detents per revolution.  So between any two successive detents, both switches go through a complete close/open cycle, and both are always open at the detent itself.  In that situation, the simpler code works fine, with no loss of resolution.  On the other hand, if it's not one-for-one, then you would definitely need more ambitious code so you can detect what's going on mid-pulse.  Otherwise, you could go to the next detent, and nothing would register as a click.  But I have no idea where the 600 ppr encoder he's using fits into this.  That would be the key.

  • Hello David,

    I've changed the interrupt sequence. The motor needs to be stopped after a certain count, by making P1.6 =0. Even though I've put the sequence inside a while(1) loop, the motor doesn't stop. Any suggestions?

    #include "msp430G2553.h"
    #define DEGREE ( 95)
    #define PULSES ( DEGREE*2 )
    #define PHASEA BIT2
    #define PHASEB BIT4

    int pos;

    signed int required_pulses = PULSES;

    void main(void)
    {
    WDTCTL = WDTPW + WDTHOLD; // Stop WDT

    P1DIR |= BIT5 + BIT6; // p1.5 and P1.6 are output
    P1SEL &= ~BIT5 ; // P1.5 is a port , P1.6 to TA0.1
    P1SEL |= BIT6 ;
    P1SEL2 &= ~BIT5 + ~BIT6 ;

    pos = 0;

    P1DIR &= ~(PHASEA + PHASEB); // Set P1.2 and P1.4 as inputs
    P1IFG &= ~(PHASEA ); // clear interrrupt flags for P1.2
    P1IE |= PHASEA ; // P1.1 and P1.2 interrupt enabled
    P1IES &= ~PHASEA ;


    TA0CCR0 = 1000-1; // Set maximum count value (PWM Period)
    TA0CCTL1 = OUTMOD_7; // set output on counter reset, clear output on CCR1
    TA0CCR1 = 500; // initialise counter compare value
    TA0CTL = TASSEL_2 + MC_1 ; // smclk, upmode


    __enable_interrupt(); // enable all interrupts


      while(1)                                   //        Stop the motor 
          { if (pos == required_pulses)
             {P1OUT &= ~BIT6;}
           }
    }

    #pragma vector=PORT1_VECTOR
    __interrupt void Port_1(void)
    {
            if (!(P1IN & PHASEB) )
                { pos--; }

            else { pos++; }

               P1IFG =0;

    }

**Attention** This is a public forum