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.

Compiler/MSP430G2553: Quadrature Encoder Multiple Detection Issue

Part Number: MSP430G2553

Tool/software: TI C/C++ Compiler

Hello, We have an issue with the Quadrature encoder input. There is no problem in interrupt Edge Detection, but there are multiple detection. Due to this it is causing multiple detriment or increment to the counter. Code shows the arrangement of connection between the Quadrature encoder and the MSP430G2553 TSSOP-28 pin version. Attaching the Code here: [code] // Button Input from Quadrature Encoder // // MSP430G2xx3 // ----------------- // /|\| XIN|- // | | | 32kHz // --|RST XOUT|- // | | // TXD-----|P1.2/UCA0TXD | LED 150E // | P1.6|--|>|---/\/\/\---- // RXD-----|P1.1/UCARXD | | // | | --- // | | /// // | | // | | // | P1.3/A3| #include // Encoder Line A #define LINE_A BIT3 // Encoder Line B #define LINE_B BIT4 // Get the Status of the Input Lines from Encoder #define GETLINESTATE() (P2IN & (LINE_A + LINE_B)) // Check if the button is in Pressed State #define BtnRead_Pressed() ((P1IN&BIT3) == 0) // Check if the Button is in Released State #define BtnRead_Released() ((P1IN&BIT3) == BIT3) // Button in Idle State #define BtnSTATE_IDLE 0 // Button Detected and Now Debouncing sensing loop #define BtnSTATE_SENSE 1 // Button Action being performed #define BtnSTATE_ACTION 2 // Button Waiting for Release #define BtnSTATE_TRANS 4 // Button Debounce Delay in Milliseconds #define MAX_Btn_Debounce_mS 50 // Button Start / Dection condition from Interrupt volatile uint16_t button_detect = 0; // Button Timing counter uint32_t button_counter = 0; // Button State Storage uint16_t button_state = BtnSTATE_IDLE; // Counter for Encoder volatile uint8_t encoder_pos = 0; // Encoder Time Stamp volatile uint32_t encoder_ts = 0; // Encoder Click - Indicate that there has been movement volatile uint16_t encoder_click = 0; // Milliseconds Counter volatile uint32_t millis = 0; // Time stamp for delay volatile uint32_t timer_delay_ts = 0; // Time Delay needed volatile uint32_t timer_delay_ms = 0; //****************************************************************************** // Device Initialization ******************************************************* //****************************************************************************** void initClockTo16MHz() { if (CALBC1_16MHZ == 0xFF) // If calibration constant erased { while (1) __bis_SR_register(LPM4_bits);// do not load, trap CPU!! } DCOCTL = 0; // Select lowest DCOx and MODx settings BCSCTL1 = CALBC1_16MHZ; // Set DCO DCOCTL = CALDCO_16MHZ; } void initGPIO() { __disable_interrupt(); P1SEL = BIT1 + BIT2; // P1.1 = RXD, P1.2=TXD P1SEL2 = BIT1 + BIT2; // Low Power P1DIR = ~(BIT3); // P1.3 as input P1OUT = BIT3; // P1.3 Pull-up P1REN = BIT3; // P1.3 Enable Pull-up P2DIR = ~(LINE_A | LINE_B); // LINE A and B as Input P2OUT = (LINE_A | LINE_B); // LINE A and B Pull-up P2REN = (LINE_A | LINE_B); // LINE A and B Enable Pull-up P3DIR = 0xFF; P3OUT = 0; // Configure Interrupt P1IE |= BIT3; // P1.3 interrupt enabled P1IES |= BIT3; // P1.3 Hi/lo edge P1IFG &= ~BIT3; // P1.3 IFG cleared // Configure Encoder Interrupt P2IE |= (LINE_A + LINE_B); // LINE A and B Interrupt Enabled P2IES |= LINE_A; // Configure the Edge based on current input state P2IFG = 0; // Clear the IFG } void initUART() { UCA0CTL1 |= UCSSEL_1; // ACLK UCA0BR0 = 3; // 32768Hz 9600 UCA0BR1 = 0; // 32768Hz 9600 UCA0MCTL = UCBRS_3; // Modulation UCBRSx = 3 UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine** } void initTIMERA() { // 1 Millisecond Pulse with ACLK/8 and Dual CCR interrupts // Timer in Up-count mode MC_1 // Clock input as ACLK TASSEL_1 // Input clock divided by 8 as ID_3 // --- // Final Frequency Fs = ACLK / 8 = 32768 / 8 = 4096Hz // Time Unit Ts = 1/(ACLK / 8) = 1/4096 = 244.1440uS // 1Ms = Counts * Ts, or // Counts = 1Ms / Ts = 1Ms / (1 / (ACLK/8) ) = 1MS * ACLK / 8 , or // Counts = 1MS * 32768 / 8 = 4096 / 1000 = 4.096 ~ 4 // CCR0 (TA0CCR0) = 4-1 = 3 // --- // Both Interrupts for TA0CCTL0 // CCR0 = 4 - 1; // PWM Period for 1 Ms CCTL0 = CCIE; // Enable TA0CCTL0 - Compare Interrupt millis = 0; // Clear the Counter TACTL = TASSEL_1 + MC_1 + ID_3; // ACLK/8, up mode } uint16_t button_process() { uint16_t ret = 0; switch(button_state) { case BtnSTATE_IDLE: if (button_detect == 1 && BtnRead_Pressed()) { button_state = BtnSTATE_SENSE; button_counter = millis; } break; case BtnSTATE_SENSE: if ((millis - button_counter) >= MAX_Btn_Debounce_mS) { if (BtnRead_Pressed()) { button_state = BtnSTATE_ACTION; button_counter = millis; // Restart for Release } else // Debounce Failed { __disable_interrupt(); button_detect = 0; button_counter = 0; __enable_interrupt(); button_state = BtnSTATE_IDLE; } } break; case BtnSTATE_ACTION: ret = 1; button_state = BtnSTATE_TRANS; break; case BtnSTATE_TRANS: if (((millis - button_counter) >= (MAX_Btn_Debounce_mS*2)) || BtnRead_Released()) { __disable_interrupt(); button_detect = 0; button_counter = 0; __enable_interrupt(); button_state = BtnSTATE_IDLE; } break; } return ret; } void delay(uint32_t ms) { __disable_interrupt(); timer_delay_ts = millis; timer_delay_ms = ms; __enable_interrupt(); __bis_SR_register(LPM3_bits + GIE); // Sleep the MCU } //****************************************************************************** // Main ************************************************************************ //****************************************************************************** int main(void) { //uint32_t ts; WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer initClockTo16MHz(); initGPIO(); initUART(); initTIMERA(); __enable_interrupt(); //ts = millis; while (1) { //if( (millis - ts) >= 1000) if (button_process()) { P1OUT ^= BIT6; while (!(IFG2 & UCA0TXIFG)) ; // USCI_A0 TX buffer ready? UCA0TXBUF = 0x00; __no_operation(); while (!(IFG2 & UCA0TXIFG)) ; // USCI_A0 TX buffer ready? UCA0TXBUF = millis; __no_operation(); } if (encoder_click == 1) { while (!(IFG2 & UCA0TXIFG)) ; // USCI_A0 TX buffer ready? UCA0TXBUF = 0x01; __no_operation(); while (!(IFG2 & UCA0TXIFG)) ; // USCI_A0 TX buffer ready? UCA0TXBUF = encoder_pos; __no_operation(); __disable_interrupt(); encoder_click = 0; __enable_interrupt(); } __delay_cycles(400000); } } // Timer A0 interrupt service routine #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=TIMER0_A0_VECTOR __interrupt void Timer_A(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(TIMER0_A0_VECTOR))) Timer_A (void) #else #error Compiler not supported! #endif { ++millis; if (timer_delay_ms != 0) { if ((millis - timer_delay_ts) >= timer_delay_ms) { timer_delay_ts = 0; // WAKE-UP! __bic_SR_register_on_exit(LPM3_bits); // Clear LPM3 bits from 0(SR) } } } // Port 1 interrupt service routine #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=PORT1_VECTOR __interrupt void Port_1(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(PORT1_VECTOR))) Port_1 (void) #else #error Compiler not supported! #endif { if(BtnRead_Pressed()) button_detect = 1; // Indicate Button press P1IFG &= ~BIT3; // P1.3 IFG cleared } // Port 2 interrupt service routine #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__) #pragma vector=PORT2_VECTOR __interrupt void Port_2(void) #elif defined(__GNUC__) void __attribute__ ((interrupt(PORT2_VECTOR))) Port_2 (void) #else #error Compiler not supported! #endif { uint8_t state = GETLINESTATE(); uint8_t ifg = P2IFG; ifg &= (LINE_A | LINE_B); encoder_ts = millis; // Time Stamp if( (ifg & LINE_A) == LINE_A ) // Interrupt on LINE A { if (P2IES & LINE_A) { // if falling edge on Line A if (state & LINE_B) { // Transitioned from State 3 to State 1 encoder_pos -= 1; } else { encoder_pos += 1; // Transitioned from State 2 to State 0 } } else { // else rising edge on Line A if (state & LINE_B) { // Transitioned from State 1 to State 3 encoder_pos += 1; } else { // Transitioned from State 0 to State 2 encoder_pos -= 1; } } P2IES ^= LINE_A; encoder_click = 1; } if( (ifg & LINE_B) == LINE_B ) // Interrupt on LINE B { if (P2IES & LINE_B) { // if falling edge on Line B if (state & LINE_A) { // Transitioned from State 3 to State 2 encoder_pos += 1; } else { encoder_pos -= 1; // Transitioned from State 1 to State 0 } } else { // else rising edge on Line B if (state & LINE_A) { // Transitioned from State 2 to State 3 encoder_pos -= 1; } else { // Transitioned from State 0 to State 1 encoder_pos += 1; } } P2IES ^= LINE_B; encoder_click = 1; }// P2IFG = 0; // LINE_A and LINE B IFG cleared } [/code] Please help with this, Thanking you, Regards, Ab
  • Not able to post code from Edge or Chrome.

    There is problem with the Website.

    // Button Input from Quadrature Encoder
    //
    //               MSP430G2xx3
    //            -----------------
    //        /|\|              XIN|-
    //         | |                 | 32kHz
    //         --|RST          XOUT|-
    //           |                 |
    //   TXD-----|P1.2/UCA0TXD     | LED     150E
    //           |             P1.6|--|>|---/\/\/\----
    //   RXD-----|P1.1/UCARXD      |                  |
    //           |                 |                 ---
    //           |                 |                 ///
    //           |                 |
    //           |                 |
    //           |          P1.3/A3|
    #include 
    
    // Encoder Line A
    #define LINE_A BIT3
    // Encoder Line B
    #define LINE_B BIT4
    // Get the Status of the Input Lines from Encoder
    #define GETLINESTATE() (P2IN & (LINE_A + LINE_B))
    // Check if the button is in Pressed State
    #define BtnRead_Pressed() ((P1IN&BIT3) == 0)
    // Check if the Button is in Released State
    #define BtnRead_Released() ((P1IN&BIT3) == BIT3)
    
    // Button in Idle State
    #define BtnSTATE_IDLE   0
    // Button Detected and Now Debouncing sensing loop
    #define BtnSTATE_SENSE  1
    // Button Action being performed
    #define BtnSTATE_ACTION 2
    // Button Waiting for Release
    #define BtnSTATE_TRANS  4
    
    // Button Debounce Delay in Milliseconds
    #define MAX_Btn_Debounce_mS 50
    
    // Button Start / Dection condition from Interrupt
    volatile uint16_t button_detect = 0;
    // Button Timing counter
    uint32_t button_counter = 0;
    // Button State Storage
    uint16_t button_state = BtnSTATE_IDLE;
    // Counter for Encoder
    volatile uint8_t encoder_pos = 0;
    // Encoder Time Stamp
    volatile uint32_t encoder_ts = 0;
    // Encoder Click - Indicate that there has been movement
    volatile uint16_t encoder_click = 0;
    
    // Milliseconds Counter
    volatile uint32_t millis = 0;
    // Time stamp for delay
    volatile uint32_t timer_delay_ts = 0;
    // Time Delay needed
    volatile uint32_t timer_delay_ms = 0;
    
    //******************************************************************************
    // Device Initialization *******************************************************
    //******************************************************************************
    
    void initClockTo16MHz()
    {
        if (CALBC1_16MHZ == 0xFF)            // If calibration constant erased
        {
            while (1)
                __bis_SR_register(LPM4_bits);// do not load, trap CPU!!
        }
        DCOCTL = 0;                          // Select lowest DCOx and MODx settings
        BCSCTL1 = CALBC1_16MHZ;              // Set DCO
        DCOCTL = CALDCO_16MHZ;
    }
    
    void initGPIO()
    {
        __disable_interrupt();
        P1SEL = BIT1 + BIT2;            // P1.1 = RXD, P1.2=TXD
        P1SEL2 = BIT1 + BIT2;
        // Low Power
        P1DIR = ~(BIT3);                // P1.3 as input
        P1OUT = BIT3;                   // P1.3 Pull-up
        P1REN = BIT3;                   // P1.3 Enable Pull-up
        P2DIR = ~(LINE_A | LINE_B);     // LINE A and B as Input
        P2OUT = (LINE_A | LINE_B);      // LINE A and B Pull-up
        P2REN = (LINE_A | LINE_B);      // LINE A and B Enable Pull-up
        P3DIR = 0xFF;
        P3OUT = 0;
        // Configure Interrupt
        P1IE |=  BIT3;                  // P1.3 interrupt enabled
        P1IES |= BIT3;                  // P1.3 Hi/lo edge
        P1IFG &= ~BIT3;                 // P1.3 IFG cleared
        // Configure Encoder Interrupt
        P2IE |= (LINE_A + LINE_B);      // LINE A and B Interrupt Enabled
        P2IES |= LINE_A;        // Configure the Edge based on current input state
        P2IFG = 0;                      // Clear the IFG
    }
    
    void initUART()
    {
        UCA0CTL1 |= UCSSEL_1;                     // ACLK
        UCA0BR0 = 3;                              // 32768Hz 9600
        UCA0BR1 = 0;                              // 32768Hz 9600
        UCA0MCTL = UCBRS_3;                       // Modulation UCBRSx = 3
        UCA0CTL1 &= ~UCSWRST;                     // **Initialize USCI state machine**
    }
    
    void initTIMERA()
    {
        // 1 Millisecond Pulse with ACLK/8 and Dual CCR interrupts
        //  Timer in Up-count mode MC_1
        //  Clock input as ACLK TASSEL_1
        //  Input clock divided by 8 as ID_3
        //  ---
        //  Final Frequency Fs = ACLK / 8 = 32768 / 8 = 4096Hz
        //  Time Unit Ts = 1/(ACLK / 8) = 1/4096 = 244.1440uS
        //  1Ms = Counts * Ts, or
        //  Counts = 1Ms / Ts = 1Ms / (1 / (ACLK/8) ) = 1MS * ACLK / 8 , or
        //  Counts = 1MS * 32768 / 8 = 4096 / 1000 = 4.096 ~ 4
        //  CCR0 (TA0CCR0) = 4-1 = 3
        //  ---
        //  Both Interrupts for TA0CCTL0
        //
        CCR0 = 4 - 1;                                   // PWM Period for 1 Ms
        CCTL0 = CCIE;                                   // Enable TA0CCTL0 - Compare Interrupt
        millis = 0;                                     // Clear the Counter
        TACTL = TASSEL_1 + MC_1 + ID_3;                 // ACLK/8, up mode
    }
    
    uint16_t button_process()
    {
        uint16_t ret = 0;
        switch(button_state)
        {
            case BtnSTATE_IDLE:
                if (button_detect == 1 && BtnRead_Pressed())
                {
                    button_state = BtnSTATE_SENSE;
                    button_counter = millis;
                }
                break;
            case BtnSTATE_SENSE:
                if ((millis - button_counter) >= MAX_Btn_Debounce_mS)
                {
                    if (BtnRead_Pressed())
                    {
                        button_state = BtnSTATE_ACTION;
                        button_counter = millis; // Restart for Release
                    }
                    else // Debounce Failed
                    {
                        __disable_interrupt();
                        button_detect = 0;
                        button_counter = 0;
                        __enable_interrupt();
                        button_state = BtnSTATE_IDLE;
                    }
                }
                break;
            case BtnSTATE_ACTION:
                ret = 1;
                button_state = BtnSTATE_TRANS;
                break;
            case BtnSTATE_TRANS:
                if (((millis - button_counter) >= (MAX_Btn_Debounce_mS*2)) || BtnRead_Released())
                {
                    __disable_interrupt();
                    button_detect = 0;
                    button_counter = 0;
                    __enable_interrupt();
                    button_state = BtnSTATE_IDLE;
                }
                break;
        }
        return ret;
    }
    
    void delay(uint32_t ms)
    {
        __disable_interrupt();
        timer_delay_ts = millis;
        timer_delay_ms = ms;
        __enable_interrupt();
        __bis_SR_register(LPM3_bits + GIE);             // Sleep the MCU
    }
    //******************************************************************************
    // Main ************************************************************************
    //******************************************************************************
    int main(void)
    {
        //uint32_t ts;
        WDTCTL = WDTPW + WDTHOLD;                       // Stop watchdog timer
    
        initClockTo16MHz();
    
        initGPIO();
    
        initUART();
    
        initTIMERA();
    
        __enable_interrupt();
    
        //ts = millis;
        while (1)
        {
            //if( (millis - ts) >= 1000)
            if (button_process())
            {
                P1OUT ^= BIT6;
    
                while (!(IFG2 & UCA0TXIFG))
                        ;                // USCI_A0 TX buffer ready?
                UCA0TXBUF = 0x00;
                __no_operation();
    
                while (!(IFG2 & UCA0TXIFG))
                        ;                // USCI_A0 TX buffer ready?
                UCA0TXBUF = millis;
                __no_operation();
            }
            if (encoder_click == 1)
            {
                while (!(IFG2 & UCA0TXIFG))
                        ;                // USCI_A0 TX buffer ready?
                UCA0TXBUF = 0x01;
                __no_operation();
                while (!(IFG2 & UCA0TXIFG))
                        ;                // USCI_A0 TX buffer ready?
                UCA0TXBUF = encoder_pos;
                __no_operation();
                __disable_interrupt();
                encoder_click = 0;
                __enable_interrupt();
            }
            __delay_cycles(400000);
        }
    
    }
    
    // Timer A0 interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=TIMER0_A0_VECTOR
    __interrupt void Timer_A(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(TIMER0_A0_VECTOR))) Timer_A (void)
    #else
    #error Compiler not supported!
    #endif
    {
        ++millis;
        if (timer_delay_ms != 0)
        {
            if ((millis - timer_delay_ts) >= timer_delay_ms)
            {
                timer_delay_ts = 0;
                // WAKE-UP!
                __bic_SR_register_on_exit(LPM3_bits);  // Clear LPM3 bits from 0(SR)
            }
        }
    }
    
    // Port 1 interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=PORT1_VECTOR
    __interrupt void Port_1(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(PORT1_VECTOR))) Port_1 (void)
    #else
    #error Compiler not supported!
    #endif
    {
        if(BtnRead_Pressed())
            button_detect = 1;                  // Indicate Button press
        P1IFG &= ~BIT3;                         // P1.3 IFG cleared
    }
    
    // Port 2 interrupt service routine
    #if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
    #pragma vector=PORT2_VECTOR
    __interrupt void Port_2(void)
    #elif defined(__GNUC__)
    void __attribute__ ((interrupt(PORT2_VECTOR))) Port_2 (void)
    #else
    #error Compiler not supported!
    #endif
    {
        uint8_t state = GETLINESTATE();
        uint8_t ifg = P2IFG;
        ifg &= (LINE_A | LINE_B);
        encoder_ts = millis; // Time Stamp
    
        if( (ifg & LINE_A) == LINE_A )  // Interrupt on LINE A
        {
            if (P2IES & LINE_A) {       // if falling edge on Line A
                if (state & LINE_B) {   // Transitioned from State 3 to State 1
                    encoder_pos -= 1;
                }
                else {
                    encoder_pos += 1;   // Transitioned from State 2 to State 0
                }
            }
            else {                      // else rising edge on Line A
                if (state & LINE_B) {   // Transitioned from State 1 to State 3
                    encoder_pos += 1;
                }
                else {                  // Transitioned from State 0 to State 2
                    encoder_pos -= 1;
                }
            }
    
            P2IES ^= LINE_A;
            encoder_click = 1;
        }
        if( (ifg & LINE_B) == LINE_B ) // Interrupt on LINE B
        {
            if (P2IES & LINE_B) {       // if falling edge on Line B
                if (state & LINE_A) {   // Transitioned from State 3 to State 2
                    encoder_pos += 1;
                }
                else {
                    encoder_pos -= 1;   // Transitioned from State 1 to State 0
                }
            }
            else {                      // else rising edge on Line B
                if (state & LINE_A) {   // Transitioned from State 2 to State 3
                    encoder_pos -= 1;
                }
                else {                  // Transitioned from State 0 to State 1
                    encoder_pos += 1;
                }
            }
    
            P2IES ^= LINE_B;
            encoder_click = 1;
        }//
    
        P2IFG =  0;            // LINE_A and LINE B IFG cleared
    }
    
    

    Using HTML to the Rescue

  • Hello,
    We have an issue with the Quadrature encoder input.
    There is no problem in interrupt Edge Detection, but there are multiple detection.
    Due to this it is causing multiple detriment or increment to the counter.
    Code shows the arrangement of connection between the Quadrature encoder and the MSP430G2553 TSSOP-28 pin version.
    Attaching the Code above.

    Kindly help with this

    Regards,
    Ab

  • To give us a head-start: Is there any pattern to the symptom? E.g: always, random, always-on-some-reboots? I'm supposing you're noticing this over the UART; can you provide a (brief) sequence that shows the symptom?

    The first thing I noticed:
    > P2IES |= LINE_A; // Configure the Edge based on current input state
    Per SLAU144J Table 8-2, P2IES is "unchanged" by a PUC, which I'm pretty sure implies that it is in a random state after powerup (the other day I saw it start up at 0xFF). I suggest:
    > P2IES = LINE_A|(0*LINE_B); // LINE_A high->low, LINE_B low->high
  • I don't think I've seen this kind of processing of rotary encoders.  Could someone explain how it deals with encoder switch bounce?

  • A time ago I was utterly surprised how noisy manual rotary encoder can be and Edge Detection may not be the best solution.

    I would like to suggest a different approach. 

    I used timer based sampling of encoder's inputs and a buffer to determine the correct sequence of encoder's pulses and filter the transients.

    Encoder's inputs can be treated as a 2-bit value.

    When encoder is sampled, a series of 2-bit samples may look like the following: 3-3-3-3-3-3-2-2-2-0-0-1-1-1-1-3-3-3,

    or with a longer encoder's pulse a stream of samples may look like: 3-3-3-3-2-2-2-2-2-2-2-2-0-0-0-0-0-0-0-0-1-1-1-1-1-1-1-1-3-3-3-3-3-3 

    Then 4 consecutive 2-bit samples are stored in a single byte long FIFO buffer.

    Commands:

    FIFO<<=2;

    FIFO |= sample;

    discard the oldest sample on the left and add a current sample into FIFO (on the right).

    A transition from value 3 to value 2 always occurs for a leading edge of encoder's pulse (PULSE BEGIN) therefore valid sequences of samples in FIFO are: 

    x-x-3-2  (tested with FIFO & 0x0E command)

    Similarly, a transition from value 1 to value 3 occurs for a trailing edge of encoder's pulse (PULSE END) therefore valid sequences of samples in FIFO are: 

    x-x-1-3  (tested with FIFO & 0x07 command)

    All you need now is to detect PULSE BEGIN condition and wait for PULSE END condition. You just ignore any other sequences as not valid.

    By counting number of samples between PULSE BEGIN and PULSE END you can determine the length of the pulse (speed of rotation)

    For opposite rotation you need the second set of leading and trailing sequences:

    PULSE BEGIN  x-x-3-1

    PULSE END     x-x-2-3

    With manually operated rotary encoders I determined a sampling rate around 1kHz as the best precision vs. MCU burden compromise.

    Happy coding

  • Mechanical quadrature encoders may have the same number of "pulses" as detents, or half as many pulses.  But in both cases, a pulse means a full cycle of transitions on both lines.  In effect, a pulse has four transitions, so between two detents you may have four transitions or two transitions depending on the type of encoder.  But your code appears to count every transition as an encoder "tick", which I believe is why you have multiple detections.  The solution is to set up an intermediate value which accumulates detected 1 or -1 transitions until they total 4 or -4 (2 or -2 for the other encoder type), then divide that by 4 and add the resulting 1 or -1  to encoder_pos, and only then change encoder_click to 1.

    I believe your code is also somewhat subject to false reporting of encoder switch bounce.  If you find that to be the case, you might try disabling the interrupt on the line that just triggered the interrupt, and enabling it again after the other line interrupts.  So at any time only one line would have interrupts enabled, and in theory the line that is still bouncing would not be generating any interrupts at all.

  • Thank you for your insight, I think that the double counter based mechanism would be great to solve this glitch ridden behaviour. Once implemented I would try to post the updated code. Also would like to try to try the alternating interrupt topology. Only concern is while we change the interrupts being enabled, how fast we can exit the loop would become the key point to detect next pulse. The reason being when we change the IES the IFG also gets set. Now this can be either by the Pin or by just the IES and EN changes. I would try to take the plot of the rotation also this time. Thank again for your guidance, Regards, Ab
  • Ab said:
    Thank you for your insight, I think that the double counter based mechanism would be great to solve this glitch ridden behaviour. Once implemented I would try to post the updated code. Also would like to try to try the alternating interrupt topology. Only concern is while we change the interrupts being enabled, how fast we can exit the loop would become the key point to detect next pulse. The reason being when we change the IES the IFG also gets set. Now this can be either by the Pin or by just the IES and EN changes. I would try to take the plot of the rotation also this time. Thank again for your guidance, Regards, Ab

    I  believe you are less likely to be pressed for time using alternating interrupts.  That's because none of the bouncing that takes place after a line changes state will generate any interrupts.  If anything would cause timing problems, it's the "false" interrupts that would have been generated by bouncing - which have to be serviced even if false.  When you switch the enabled interrupt to the other line, all that bouncing goes unnoticed.

    I don't believe you are correct about the flag register. Changing the enable or direction register, or both, does not set the flag.  So after processing the current interrupt, the last things you would do before returning from the interrupt would be to invert both enable bits, invert both direction bits, and then finally clear both flag bits.  Then RTI.

    A while back I wrote up this method and posted it on Github.  Unfortunately for most, the code is written in assembler.  But the PDF file gives the logic in higher level terms.  Included are .hex files you can flash to a G2231 or G2553 for testing the algorithm using a G2 Launchpad and an encoder.  Versions for both kinds of encoders are included.  Find the "Pin-Triggered" section of the PDF.  The code runs at 1 MHz, and includes some delay for LED flashing, but still seems to capture all the transitions with no problem.

    https://github.com/gbhug5a/Rotary-Encoder-Servicing-Routines

    By the way, those little files are useful for testing whether an encoder is flaky.  There's no point spending hours trying to fix your code if the real problem is a flaky encoder.  "Flaky" means both lines are bouncing at the same time.  That should not happen, and if it does, there is no code that will overcome it.

**Attention** This is a public forum