Main Site Documentation

Encoders, how to support them


#1

Is there a standard encoder interface that GHI can support natively? I have never use encoders myself so I do not know much about them but I have a ruff idea on how they work.

Let assume there is some managed libraries for encoders, then how would they look like? Would this work better than InterruptPort?


#2

Hi Gus,

If you were to support encoders, something I would like to see would be that all of the interrupt processing gets done on the encoder’s board. That way, it can be very accurate without tying down the FEZ with lots of interrupts.

A for types I guess it’s really what you want to do. You can use Hall effect sensors, or optical sensors…

Personally, I would prefer Hall effect.


#3

Well, please explain what your thinking about when you say ‘encoder’, Gus. Are you thinking about a quadrature encoder support? You’ll likely want to include a small glitch filter which would allow the use of inexpensive mechanical encoders for front panel controls: http://www.soigeneris.com/EZ_MPG_M1-details.aspx .

When using an encoder to keep track of a robot wheel turning or something similar, typically you would want to keep a tick count, incremented or decremented by reading the quadrature decoder. The control program would need to read the tick count often enough to avoid errors from it rolling over multiple times. The controller would then be able to calculate a ‘delta’ tick count to know how far the wheel had moved in that time period.


#4

This didn’t answer my question Chris :frowning: What will the encoder managed code look like?

int x = Encloder.GetPosition(); ?

How would it initialize?

Encoder e = new Encoder(pin A, pin B, int count); ?


#5

I do not know! I do not use encoders and do not know how they work but if I were to talk the GHI guys to add something for encoder then how would it look like? Is this even possible to define a standard interface for all encoders?

I think it is impossible since you need the interrupt on every encoder tick but I want to hear it from someone who have in fact used encoder.


#6

Ahh encoders.

Int x = Encloder.GetPosition() Or GetCount();

Also you would have to have a couple ways to Init.

//Single pin Encoder
Encoder e = new Encoder(pin A);
//Double Pin Encoder
Encoder e = new Encoder(pin A,Pin B);

The double pins help determine direction. So you would need a get Getdirection of types if A fires first then b is forward and the exact opposite is true. So maybe some thing more generic then get direction but that should give you the idea.

The next thing would be start time tick and current time tick with a reset so you can reset encoder count and get a time when you started vs a time when it stopped.

I have Encoders popping on 4 interupts right now. It entertaining to watch the lag but is usable as long as not need to near real time.

You may also need to worry about position for other types of encoders. The ones i don’t use. Generally knob based user input.


#7

Maybe you can look at the Phidgets implementation to see how they do it ?

I don’t know if it can be useful, but one never knows…


#8

I think it is perfectly feasible to support a low resolution quadrature encoder as you only need one phase to generate an interrupt and you increment or decrement the counter based on the state of the other phase (you read it in the ISR.) This would even work for a low resolution encoder on a robot wheel, the high resolution encoders would not be very practical to do this way though.


#9

Maybe you can expand on this. Explain why and what since this way isn’t really set yet.

I would guess you are talking about ones like this
http://www.opticalencoder.com/pdf/CP-250-HHC.pdf


#10

If you want to use high count quadrature encoders then you need to use an FPGA or similar HW based solution. For low resolution you can get by having one channel, say A, generate an interrupt on one edge. In your ISR you will know the state of the input that generated the interrupt and then you increment or decrement a position counter based on if channel B is HI or LOW. (This tells you direction.) Your ISR has to be very fast so you can get in and out before the next state change.


#11

There is a 2nd way to read encoders (specially quadrature encoders). Just use one timer interrupt. The timer interrupt must be fast enough to do an oversampling of the encoder values. No edge detection, only pin level change must be detected.

I’ve used this method on an Arduino robot with 2 wheel quadrature encoders (32 ppr). That works without any problem with a timer interrupt every 1ms. But I don’t know if this is possible with a FEZ board.

For higher resolution, as Jeff_Birt mentioned, a hardware solution is better.


#12

Hi,

This is an old thread, but I would like to revive it.

Currently everybody is writing their own QuadEncoder classes. It would be nice to have it in native code though, for speed.


Quad q= new Quad(FEZ_Pin.Interrupt Q1, FEZ_Pin.Interrupt Q2)
q.Count

Should work for most people.

It would also be nice to have 3 Encoder classes.
One that checks only the rising edge of one of the pins.
One that checks rising edges on both pins.
One that checks rising and falling edges on both pins.

That will give you 1X, 2X and 4X over sampling…

Thanks,
Errol


#13

I Have a very good quadrature encoder based on the pic…

Its a STATE machine… Very good for inexpensive encoders, gives X4 sampling (not oversampling ) and doesn’t require glitch filters…

The method is to monitor, at very high speed, the state of A and B inputs… I then transmit (SPI) to my cobra…

If anyone wants the code (its in pic basic,for the 12F675 8 pin) you can have it…

I personally think just encoding with interrupts, even with glitch enabled, you can get the speed

@ Gus I would strongly advise using a state machine if you implement this in native code

Cheers Ian


#14

I call it oversampling as encloders are specified as X pulses per rev, mine is 180p/r.
Sensing all four edges gives you 4X pulses, ie 720p/r … :slight_smile:

I found code on the net a few years ago, can’t remember where so I can’t give credit where it is due, but it uses a “switch” statement. It is very efficient code, I could count 60kHz pulses on a 4MHz pic.

I must still port it to MF though.

Thanks,
Errol


#15

I’d like to see pololu’s supported, they are rather prolific in robotics


#16

Just to bring up another option since encoders and interrupts are going to be tricky with managed code. An absolute rotary encoder can take the place of a quadrature encoder. The simplest being a continuous rotation potentiometer. There are potentiometers available that have up to 357 degrees of electrical range. Yes the dead band can be a problem. This becomes an A to D programming exercise instead of a interrupt and timing problem. Just have to do at least one A to D per revolution. Avago makes a magnetic absolute rotary position sensor that can be read by SPI with 12 bits of resolution. Austria micro also makes these devices. Unfortunately some other good devices are 5 volt only. Measuring an analog voltage can be much easier than messing with interrupts.


#17

Going analog you must take at least 3 readings to determine speed and direction…
1 reading will give you instantaneous posision, nothing more.
2 readings can give you speed, but the calculated speed will depend on the direction. IE, getting two readings of 5 and 350, either the motor is running slowly in revirse(5, 4,3,2,1…350) or fast forward(5,6,7,8…350)
3 readings will give you readings of 5 and 180 and 350. Then you know it is forward and you can calc speed.

My reason for going Quad is that my motors has them built in…

How does this code look? I have not tested it on Panda yet. I just ported it to C# although I have used it a lot in embedded C.


    class Encoder
    {
        InterruptPort QA;
        InterruptPort QB;
        public long Count;
        byte CurrentState;

        public Encoder(FEZ_Pin.Interrupt Q1, FEZ_Pin.Interrupt Q2)
        {
            QA = new InterruptPort((Cpu.Pin)Q1, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);
            QB = new InterruptPort((Cpu.Pin)Q2, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);
            QA.OnInterrupt += new NativeEventHandler(OnInterrupt);
            QB.OnInterrupt += new NativeEventHandler(OnInterrupt);
        }

        void OnInterrupt(uint data1, uint data2, DateTime time)
        {
            CurrentState = (byte)((CurrentState << 2) + 
                        (QA.Read() ? 2 : 0) + (QB.Read() ? 1 : 0));//Shift state up by two to make space for new state
                                                                   //And add state
            CurrentState = (byte)(CurrentState & 0x0f);            //Limit state history to only 2 states
            Count = Count + GetState(CurrentState);                //Check if state moved up or down.                                        
        }

        int GetState(byte State)
        {//Compare previous state with current state and return +1/-1/0
            switch (State)
            {                  // Previous   Current                     
                //goto    NO   //; 0 0       0 0             
                case 0: return 0;
                //goto    UP   //; 0 0       0 1             
                case 1: return +1;
                //goto    DN   //; 0 0       1 0            
                case 2: return -1;
                //goto    ERR  //; 0 0       1 1             
                case 3: return 0;

                //goto    DN   //; 0 1       0 0             
                case 4: return -1;
                //goto    NO   //; 0 1       0 1            
                case 5: return 0;
                //goto    ERR  //; 0 1       1 0             
                case 6: return 0;
                //goto    UP   //; 0 1       1 1            
                case 7: return +1;

                //goto    UP   //; 1 0       0 0            
                case 8: return +1;
                //goto    ERR  //; 1 0       0 1             
                case 9: return 0;
                //goto    NO   //; 1 0       1 0             
                case 10: return 0;
                //goto    DN   //; 1 0       1 1            
                case 11: return -1;

                //goto    ERR  //; 1 1       0 0             
                case 12: return 0;
                //goto    DN   //; 1 1       0 1             
                case 13: return -1;
                //goto    UP   //; 1 1       1 0            
                case 14: return +1;
                //goto    NO   //; 1 1       1 1             
                case 15: return 0; 
            }
            return 0;
        }
    }

Should the code throw an exception if the state is invalid, ie if the code missed a pulse? If pulses are missed then the Count isn’t really valid anymore anyway…


#18

Do you want to add this to fezzer.com so it doesn’t get lost?


#19

Gus, I am looking at an an application where a Cobra would fit in nicely.
It is somewhat realtime constrained, but 100ms response time would be enough for all cases except a quadrature encoder. It needs to keep track of ~7000 pulses (state changes) a second, so a robot can crawl at a centimeter per second (you need a high resolution encoder for that) and move with 1 m/s (so the same encoder spams pulses).
Did you already implement this in firmware (your proposed interface looks OK) or would it be feasible to manage this using RLP interrupts?


#20

I am not sure I understand the question but I will try to answer… 100ms response is too long so our devices would have no problem doing that. About the encoder, there is nothing specific for encoders in our firmware because this is highly dependent on the encoder you select. I can’t see why you can’t use any encoder with RLP but I am not encoder expert myself.

Did I answer your question? Please let me know if you need further help