Main Site Documentation

Interrupt Port and Input Port Read Values are Wrong on First Interrupt


#1

I’m noticing some funny behavior with some rotary encoder code that I have. It’s for a quadrature encoder knob where I have device hardware debounced. The code does not work correctly ONLY on the first event. After the first event, results are consistent and correct.

Both PinA.Read() and PinB.Read() return TRUE on the first event. PinA() should always be FALSE due to the InterruptMode.InterruptEdgeLow and PinB() will vary depending on direction, but also ALWAYS returns TRUE on the first
interrupt that’s triggered.

Also, the logic analyzer shows the encoder performing as it should.

I’ve really simplified the code and maybe someone else can test it out. I’ve also tried this on several pins with the same results.

public class Program
    {
        static RotaryEncoder RE;
        static int RotCount=0;
        static InterruptPort PinA;
        static InputPort PinB;
        
        static bool PinAVal;
        static bool PinBVal;


        public static void Main()
        {
            PinA = new InterruptPort(Pins.GPIO_PIN_D0, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLow);
            PinB = new InputPort(Pins.GPIO_PIN_D1, false, Port.ResistorMode.Disabled);
            PinA.OnInterrupt += PinA_OnInterrupt;
            while (true)
            {
                Thread.Sleep(Timeout.Infinite);
            }

       
        }

        static void PinA_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            PinBVal = PinB.Read();
            PinAVal = PinA.Read();
            if (PinBVal)
            { Debug.Print("CounterClockwise!"); }
            else { Debug.Print("Clockwise!"); }

        }

Again, subsequent interrupts return correct values. I’m using a Fez Cerb 40

Any ideas?


#2

MF queues all interrupts. If you are turning the encoder fast, then the state of the A pin may be different from the state when the interrupt was queued. So, you assumption that A is low when you are in the interrupt is not always true.


#3

Hi!

Thanks for the reply.

I’m turning it slowly and making one “click” or registration of the encoder. As I said, the subsequent rotations yield expected results. Pins values show expected values.

I understand what you’re saying, but in this case, I think the MC is way faster than I’m incrementing the encoder.

I’m working on modified code to use both Interrupt Edges. This way I trap two conditions and I filter the very first incorrect result and then second interrupt is correct and I get stable results.

I think there is possibly some flaw somewhere in the firmware or netmf.


#4

@ Gismofx - check the CodeShare area, under Community, for several examples of encoder use.


#5

In general i would not use the read to get the state, just use the data1 or data2 values to set a variable in A and B and compare the variables. data1 and data2 are giving the data from the moment the interrupt was triggered at the time in the time parameter.


#6

Hi David,

Good point! I wanted to look at both values to understand the direction of rotation in a quick and dirty way… Data2 is the pin value. O or 1(Low or High) Data1 is the Pin Number. I’ve opted for this method which works very well:

void RotationInterrupt(uint data1, uint data2, DateTime time)//Data1 Is Pin Data 2 Is Pin.Read()
        {

            //There are 4 states/conditions that can occur:
            if (data1 == (uint)PinA.Id && data2 == 0)//Pin A Goes LOW
            {
                State[StateCount] = 1;
            }
            else if (data1 == (uint)PinA.Id && data2 == 1) //Pin A Goes HIGH
            {
                State[StateCount] = 2;
            }
            else if (data1 == (uint)PinB.Id && data2 == 0) //Pin B Goes LOW
            {
                State[StateCount] = 3;
            }
            else if (data1 == (uint)PinB.Id && data2 == 1) //Pin B Goes HIGH
            {
                State[StateCount] = 4;
            }
            else throw new Exception("No State recognized");

            StateCount++;
            if (StateCount == 2)//we've collected two states. Let's compare them and return a rotation direction
            {
                StateCount = 0;  //reset the state count              
                
                if (!SkipFirstInterrupt)
                {
                    if ((State[0] == 1 && State[1] == 3) || (State[0] == 2 && State[1] == 4))
                    {
                        //do counterclockwise
                        RotationEventHandler(COUNTERCLOCKWISE, 0, DateTime.Now);
                    }

                    else if ((State[0] == 3 && State[1] == 1) || (State[0] == 4 && State[1] == 2))
                    {
                        //do clockwise
                        RotationEventHandler(CLOCKWISE, 0, DateTime.Now);
                    }
                }
                SkipFirstInterrupt = !SkipFirstInterrupt;
            }

        }

#7

The problem with encoders is that the pulse is very short, and the NETMF interrupt comes delayed.
Because of this I made an RLP driver for my rotary encoder:
https://www.ghielectronics.com/community/codeshare/entry/843
This is also a nice example to get started with RLP.


#8


Will this code result in the loss of every other interrupt?

#9

Thanks! I’ve review that, and I’m very interested in RLP, but I’m happy with the performance of the C# version and as fast as I can twist my encoder, It will count it and the direction is always correct. I imagine there would be issues with an encoder coming off a motor at much higher speeds and low level is absolutely necessary.


#10

Yes. Well, In my case, the encoder has 12 detents, but 24 is pulse pairs per revolution, so each detent throws 4 pulse/pin changes. Each detent click yields 4 interrupts. So I read two, determine the direction, and then ignore the other two. Rinse/repeat

Some encoders without detents or higher quality ones might not need that line and would make benefit without that little bit.