Take the 2-minute tour ×
Electrical Engineering Stack Exchange is a question and answer site for electronics and electrical engineering professionals, students, and enthusiasts. It's 100% free, no registration required.

I connected two AMT 103 rotary encoders (mounted on two separate DC motors) to a dspic30f4011 and read the values using interrupts (instead of qei as the dspic only has one). However, the error in the values increase the farther and faster we go when running the motors simultaneously. One of the theories is that since the interrupts have different priorities one of them overrides the other and causes the error, note that when only one motor is run we don't have an error. Is this causing the error? If yes how can it be fixed? Will using one qei and one interrupt fix this? Here's the dspic code:

#include "p30f4011.h"
#include <delay.h>
#include <stdlib.h>
#include <math.h>

_FWDT(WDT_OFF)

#define InterruptFlag0_A    IFS0bits.INT0IF //Rising Edge on Signal A, Encoder 1
#define InterruptFlag1_A    IFS1bits.INT1IF //Rising Edge on Signal A, Encoder 2
#define Encoder0_B  PORTBbits.RB0 // Signal B of Encoder 1
#define Encoder1_B  PORTBbits.RB1 // Signal B of Encoder 2

//Variables for Calculations
long T1=0;
int counter=0;
int NR=0;
long T2=0;
int counterone=0;
int NRone=0;
long i;
//-------------------------


int main()
{
    ADCON1=0x0F;//digital
    TRISBbits.TRISB0=1;//RA inputs
    TRISBbits.TRISB1=1;//RA inputs

    //Interrupts init
    IFS1bits.INT1IF = 0;    /*Reset INT1 interrupt flag */
    IEC1bits.INT1IE = 1;    /*Enable INT1 Interrupt Service Routine */
    IFS0bits.INT0IF = 0;    /*Reset INT1 interrupt flag */
    IEC0bits.INT0IE = 1;    /*Enable INT1 Interrupt Service Routine */

    while(1) //NEGATIVE ROTATIONS ARE CW. POSITIVE ROTATIONS ARE CCW
    {   

        T1= ((long)NR*10000)+(long)counter;
        T2= ((long)NRone*10000)+(long)counterone;

    }
    return 0;
}

void __attribute__((__interrupt__)) _INT0Interrupt(void);
void __attribute__((__interrupt__, auto_psv)) _INT0Interrupt(void)
{   
    if(InterruptFlag0_A==1) {
        if(Encoder0_B==1) {
            counter=counter-1;
            InterruptFlag0_A=0;
        } else if (Encoder0_B==0) {
            counter=counter+1;
            InterruptFlag0_A=0;
        }
    }
    if(counter==10000) {
        counter=0;
        NR=NR+1;
    } else if(counter==-10000) {
        counter=0;
        NR=NR-1;
    }
}

void __attribute__((__interrupt__)) _INT1Interrupt(void);
void __attribute__((__interrupt__, auto_psv)) _INT1Interrupt(void)
{   
    if(InterruptFlag1_A==1) {
        if(Encoder1_B==1) {
            counterone=counterone+1;
            InterruptFlag1_A=0;
        } else if (Encoder1_B==0) {
            counterone=counterone-1;
            InterruptFlag1_A=0;
        }
    }
    if(counterone==10000) {
        counterone=0;
        NRone=NRone+1;
    } else if(counterone==-10000) {
        counterone=0;
        NRone=NRone-1;
    }
}

Thanks

share|improve this question
    
This is not really an answer, but for your next project yo might consider using the PSoC3 or PSoC5 from Cypress Semiconductor. It can easily handle 8 encoder inputs without using any CPU time. –  Rocketmagnet Apr 24 '12 at 18:03
add comment

2 Answers 2

Don't ever tie external inputs like this directly to interrupt pins. Interrupt pins should be used with "safe" inputs which can't run away and leave the controller a gibbering puddle of ooze as it valiantly tries to service endless interrupts.

(you won't actually kill the MCU, but your embedded application will simply not work)

For things like encoders it's best to use counter hardware and then have a periodic interrupt to look at the counts and determine how many counts per interval of time you're seeing, which will then tell you how fast the motor is turning. If you need directional information as well you could run the I/Q encoder outputs to the clock/direction inputs of standard up/down counter ICs and then poll them with the microcontroller.

I know this is probably not the kind of answer you were hoping for, but using interrupts to count encoder pulses is bad design.

share|improve this answer
3  
Connecting external inputs like buttons or encoders directly to maskable interrupts can be a fine thing to do, if the maskable interrupt when triggered disables itself but sets a timer to re-enable it after some minimum time. –  supercat Apr 24 '12 at 18:41
    
Good catch. You are correct, and in fact this is typical for low power designs in order to "wake up" and handle the event. –  akohlsmith Apr 25 '12 at 0:16
add comment

I think for this design you would be better off using the input capture peripheral on your microcontroller instead of external interrupts. Briefly, the input capture peripheral works in conjunction with a timer to time external events. You can connect one encoder channel to each input capture channel (two quadrature encoders = 4 total lines and luckily the 4011 you're using has four input capture lines). The input capture peripheral can be configured to trigger off of a variety of conditions including:

  • Every rising edge in the signal
  • Every falling edge
  • Every 4th rising edge
  • Every 16th rising edge
  • Every rising and falling edge

(This comes from your chip's datasheet, pg78)

When a trigger condition occurs the peripheral reads the current value of a timer that you specify (I believe either Timer 2 or Timer 3 for your chip). It stores that value in a FIFO that is four 16-bit words deep. It can also generate an interrupt, or you can watch the FIFO and see when it gets full. Possibly you can cause an interrupt to occur when the FIFO is full as well - I haven't checked.

The application of this to a quadrature encoder would be to use two input capture channels with the peripheral utilizing a timer with a suitable frequency. Probably set both channels to use a rising edge condition, or possibly every 4th or 16th rising edge. The choice of these depends on the level of accuracy you need, the frequency of the timer and the speeds you will be trying to measure. There's a lot of factors that you can play with to get better accuracy out of this system. For this example, the difference between two rising edges on one channel will be proportional to speed, and the difference between the rising edges of one channel to another will tell you what direction the motor is spinning.

If you can spare a timer and the input capture peripheral then this is the best way to interface two encoders.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.