Main Site Documentation

Interrupt/Event Help


#1

I’m sure the answer to this is so silly I’m going to smack myself but I’ve having an issue here.

Create InterruptPort:


                btnRed = new InterruptPort((Cpu.Pin)17, false, Port.ResistorMode.PullDown, Port.InterruptMode.InterruptEdgeBoth);
                btnRed.OnInterrupt += new NativeEventHandler(BTN_OnInterrupt);

Fire Event when pressed:

                        if (state == 0)
                            RedReleased();
                        else
                            RedPressed();

Nicely wrapped up in a class called InputManager.
Now we create an instance and subscribe to the event

InputManager IM = new InputManager();
IM.RedPressed += new InputManager.RedPressedEventHandler(RedPressed);

Works so nice…

Now let’s say I call out to another class and pass over the InputManager. Easy no? First I unsubscribe:

IM.RedPressed += RedPressed;

Then I pass along the instance and subscribe on the new class. When I exit the class I resubscribe. But does that work properly? Noooooo. InputManager holds on to every freaking event and doesn’t fire jack until I get back to the first class. Setting a break point I see the InterruptPort doesn’t even fire?!

Halp?


#2

Unsubscribe is with -=


#3

Sorry that is what I’m doing, typo on the post not in the code


#4

Maybe, because of an exception in the handler, the InterruptPort stops working.

Subscribe with code you showed as unsubscribe, so not with that ‘new’ stuff. And unsubscribe with -=. That way you subscribe and unsubscribe with the same instances.

Also in the nativehandler, don’t rely on the state (I’ve seen it fail many times when I was writing my SD card detection class, see code) parameter that is passed in, but reread the actual pin state.


#5

Also, when calling an event, make sure there is at least one subscriber.


if (!btnRed.Read())
{ 
   if (RedReleased != null) 
      RedReleased();
}
else
{
   if (RedPressed != null)
      RedPressed();
}


#6

Thanks I’ll give it a try.


#7

I suggest, this is a synchronisation problem.
Have you take a look at weak delegates ?

Like :
MyEvent = (myEventHandler)WeakDelegate.Combine(MyEvent, value);

MyEvent = (myEventHandler)WeakDelegate.Remote(MyEvent, value);

cu
Andreas


#8

OK I’ve tried both suggestions separately and together; doesn’t work. Here’s a code you can try.

Once you enter the SampleClass the InterruptPort won’t hit the OnInterrupt defined method until you manually exit the SampleClass.

Substitute pin for 0,30 or 4 to use up/down/sel buttons on cobra.


using System;
using System.Collections;
using System.Runtime.CompilerServices;
using System.Threading;
using System.IO;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Touch;

using GHIElectronics.NETMF.Hardware;

namespace ButtonFreakingTest
{
    public class Program
    {
        private static InputManager IM;

        public static void Main()
        {
            IM = new InputManager();
            CaptureEvents();
            Thread.Sleep(-1);
        }

        private static void CaptureEvents()
        {
            IM.GreenPressed += IM_GreenPressed;
        }

        private static void ReleaseEvents()
        {
            IM.GreenPressed -= IM_GreenPressed;
        }

        private static void IM_GreenPressed()
        {
            ReleaseEvents();
            SampleClass SC = new SampleClass(IM);
            CaptureEvents();
        }
    }

    public class SampleClass
    {

        private bool bLoop;

        public SampleClass(InputManager IM)
        {
            IM.GreenPressed += IM_GreenPressed;
            bLoop = true;
            while (bLoop)
                ;
            Debug.Print("Exited");
            IM.GreenPressed -= IM_GreenPressed;
        }

        private void IM_GreenPressed()
        {
            bLoop = false;
        }

    }

    public class InputManager : MarshalByRefObject
    {
        private InterruptPort btnGreen;

        public InputManager()
        {
            btnGreen = new InterruptPort((Cpu.Pin)20, false, Port.ResistorMode.PullDown, Port.InterruptMode.InterruptEdgeBoth);
            btnGreen.OnInterrupt += BTN_OnInterrupt;
        }

        private GreenPressedEventHandler m_GreenPressedEvent;
        public delegate void GreenPressedEventHandler();
        public event GreenPressedEventHandler GreenPressed
        {
            [MethodImpl(MethodImplOptions.Synchronized)]
            add
            {
                m_GreenPressedEvent = (GreenPressedEventHandler)WeakDelegate.Combine(m_GreenPressedEvent, value);
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            remove
            {
                m_GreenPressedEvent = (GreenPressedEventHandler)WeakDelegate.Remove(m_GreenPressedEvent, value);
            }
        }
        private void RaiseGreenPressed()
        {
            GreenPressedEventHandler t = m_GreenPressedEvent;
            if (t != null) t();
        }

        private GreenReleasedEventHandler m_GreenReleasedEvent;
        public delegate void GreenReleasedEventHandler();
        public event GreenReleasedEventHandler GreenReleased
        {
            [MethodImpl(MethodImplOptions.Synchronized)]
            add
            {
                m_GreenReleasedEvent = (GreenReleasedEventHandler)WeakDelegate.Combine(m_GreenReleasedEvent, value);
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            remove
            {
                m_GreenReleasedEvent = (GreenReleasedEventHandler)WeakDelegate.Remove(m_GreenReleasedEvent, value);
            }
        }
        private void RaiseGreenReleased()
        {
            GreenReleasedEventHandler t = m_GreenReleasedEvent;
            if (t != null) t();
        }

        private void BTN_OnInterrupt(uint pin, uint state, DateTime time)
        {
            Debug.Print("Pin: " + pin + ", State: " + state);
            if (state == 0)
                RaiseGreenReleased();
            else
                RaiseGreenPressed();
        }

    }
}


#9

I have the world’s stupidest fix ever for this for now but if anyone comes up with something please let me know.

Is seems no matter what you do once you subscribe to an interruptport that class/thread OWNs it. I’ve tried:

  1. Removing the event subscription
  2. Disabling the interrupt
  3. Disposing of the pin
  4. Nulling the InputManager
  5. Launching in a new thread

And none of it works. I :wall:

You want to hear something funny? If you do all of those things and then exit the class, you can recreate the InputManager on the original class/thread and it gets all those messages it didn’t raise in the second class.


#10

I’ve tried your code. With IO30 and PullUP on Cobra.

The first Pressed state is detected, and after that it stops working. Before I dig into it further, is that your problem?

I also don’t see a reason why you subscribe to the same event currently being handled, this seems not a good thing to do… (it’s like adding a item to a list you are currently iterating) When I comment out the code in the IM_GreenPressed handler, the thing continues to work…


#11

That’s the issue. The reason I’m doing subscribes/unsubscribes is the info is needed by different sections of the engine. Games can be loaded into a new AppDomain, etc, so while they’re running the UI needs to stop processing the buttons and let the engine take over.

For now what I’ve done is simply have the main app (GUI) keep the events and bubble them up using a flag to determine whether to process them or kick em up the line.


#12

Made a small modification, and it works.

You can redirect the handler to whatever you want, but just don’t subscribe/unsubscibe to/from an event while you currently handling it. I can’t see why your game engine would wait to subscribe to button events until the button was pressed once…


using System;
using System.Collections;
using System.Runtime.CompilerServices;
using System.Threading;
using System.IO;

using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using Microsoft.SPOT.Presentation;
using Microsoft.SPOT.Presentation.Media;
using Microsoft.SPOT.Touch;

using GHIElectronics.NETMF.Hardware;
using GHIElectronics.NETMF.FEZ;

namespace ButtonFreakingTest
{
    public class Program
    {
        private static InputManager IM;

        public static void Main()
        {
            IM = new InputManager();
            CaptureEvents();

            ReleaseEvents();
            SampleClass SC = new SampleClass(IM);
            CaptureEvents();
            
            Thread.Sleep(-1);
        }

        private static void CaptureEvents()
        {
            IM.GreenPressed += IM_GreenPressed;
        }

        private static void ReleaseEvents()
        {
            IM.GreenPressed -= IM_GreenPressed;
        }

        private static void IM_GreenPressed()
        {
            Debug.Print("IM_GreenPressed()");
        }
    }

    public class SampleClass
    {

        private bool bLoop;

        public SampleClass(InputManager IM)
        {
            IM.GreenPressed += IM_GreenPressed;
            bLoop = true;
            while (bLoop)
                ;
            Debug.Print("Exited");
            IM.GreenPressed -= IM_GreenPressed;
        }

        private void IM_GreenPressed()
        {
            bLoop = false;
        }

    }

    public class InputManager : MarshalByRefObject
    {
        private InterruptPort btnGreen;

        public InputManager()
        {
            btnGreen = new InterruptPort((Cpu.Pin)FEZ_Pin.Interrupt.IO30, false, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeBoth);
            btnGreen.OnInterrupt += BTN_OnInterrupt;
        }

        private GreenPressedEventHandler m_GreenPressedEvent;
        public delegate void GreenPressedEventHandler();
        public event GreenPressedEventHandler GreenPressed
        {
            [MethodImpl(MethodImplOptions.Synchronized)]
            add
            {
                m_GreenPressedEvent = (GreenPressedEventHandler)WeakDelegate.Combine(m_GreenPressedEvent, value);
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            remove
            {
                m_GreenPressedEvent = (GreenPressedEventHandler)WeakDelegate.Remove(m_GreenPressedEvent, value);
            }
        }
        private void RaiseGreenPressed()
        {
            GreenPressedEventHandler t = m_GreenPressedEvent;
            if (t != null) t();
        }

        private GreenReleasedEventHandler m_GreenReleasedEvent;
        public delegate void GreenReleasedEventHandler();
        public event GreenReleasedEventHandler GreenReleased
        {
            [MethodImpl(MethodImplOptions.Synchronized)]
            add
            {
                m_GreenReleasedEvent = (GreenReleasedEventHandler)WeakDelegate.Combine(m_GreenReleasedEvent, value);
            }

            [MethodImpl(MethodImplOptions.Synchronized)]
            remove
            {
                m_GreenReleasedEvent = (GreenReleasedEventHandler)WeakDelegate.Remove(m_GreenReleasedEvent, value);
            }
        }
        private void RaiseGreenReleased()
        {
            GreenReleasedEventHandler t = m_GreenReleasedEvent;
            if (t != null) t();
        }

        private void BTN_OnInterrupt(uint pin, uint state, DateTime time)
        {
            Debug.Print("Pin: " + pin + ", State: " + state);
            if (state == 0)
                RaiseGreenReleased();
            else
                RaiseGreenPressed();
        }

    }
}


#13

Haha that wasn’t actual game code just something I slapped together to illustrate the issue, thanks for the help!