InterruptPort::EnableInterrupt - 'System.ArgumentException' occurred in Microsoft.SPOT.Hardware.dll

I am trying to use a Microsoft.SPOT.Hardware.InterruptPort based interrupt . I get an exception when the code attempts to add a delegate for the interrupt event handler.

#### Exception System.ArgumentException - 0xfd000000 (1) ####
#### Message: 
#### Microsoft.SPOT.Hardware.InterruptPort::EnableInterrupt [IP: 0000] ####
#### Microsoft.SPOT.Hardware.NativeEventDispatcher::add_OnInterrupt [IP: 0027] ####
#### IRQApp.Program::ProgramStarted [IP: 00c3] ####
#### IRQApp.Program::Main [IP: 0015] ####

A first chance exception of type ‘System.ArgumentException’ occurred in Microsoft.SPOT.Hardware.dll
A first chance exception of type ‘System.ArgumentException’ occurred in Microsoft.SPOT.Hardware.dll

Other posts suggest I may be using a GPIO which is not interrupt enabled, I believe I am using an interrupt enabled GPIO port, i.e. pin 3 on socket 5 using a FEZ Spider II board.

    public partial class Program
    {
        private GT.Socket socket5;
        private Microsoft.SPOT.Hardware.Cpu.Pin pin3;
        private Microsoft.SPOT.Hardware.InterruptPort irq;

        void ProgramStarted()
        {
            socket5 = GT.Socket.GetSocket(5,true,null,null);
            pin3 = socket5.CpuPins[3];
            irq = new InterruptPort(pin3, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLevelHigh);
            irq.OnInterrupt += new NativeEventHandler(irq_OnInterrupt);
            Debug.Print("Program Started");
        }
        
        void irq_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            Debug.Print("irq_OnInterrupt occurred: " + time.Ticks.ToString());
        } 
    } 

Is anyone able to identify what I am missing, or why the above code will not work. Thanks.

I would take a look at the button module drivers.

InterruptEdgeLevelHigh does not work for me either. Try the other options and find the best suited. I am still looking for the netmf interrupt primer.

@ njbuch - the book “Expert .NET Micro Framework” has a wealth of information in it. It’s worth taking a look. (I have found a number of additional publications on the .NETMF, most relate to non-GHI kit, and were published around 2010.)

@ Gus - I had a look at the button module driver. This driver uses GTI.InterruptInput , not Microsoft.SPOT.Hardware.InterruptPort. I have run this code, which uses GT.SocketInterfaces.InterruptInput.

    public partial class Program
    {
        private GT.SocketInterfaces.InterruptInput irq;

        void ProgramStarted()
        {
            irq = breakout.CreateInterruptInput(GT.Socket.Pin.Three, GlitchFilterMode.Off,
                ResistorMode.Disabled, InterruptMode.RisingEdge);
            irq.Interrupt += irq_Interrupt;
            Debug.Print("Program Started");
        }
        
        void irq_Interrupt(InterruptInput sender, bool value)
        {
            Debug.Print("irq_Interrupt occurred: " + time.Ticks.ToString());
        } 
    }

It works well, yet all I am able to do here in order to gain some control over the interrupt behaviour, unless I am missing something, is remove / replace the delegate. I somehow doubt whether this will prevent / disable the interrupt.

I found the code on which I based the first interrupt attempt in section 6.3 , page 30 and 31 of the Beginner guide to C# and NETMF. The code is as follows.

using System;
using Microsoft.SPOT;
using System.Threading;
using Microsoft.SPOT.Hardware;
using GHIElectronics.NETMF.FEZ;

namespace MFConsoleApplication1
{
    public class Program
    {
        // this moved out here so it can be used by other methods
        static OutputPort LED;
        public static void Main()
        {
            LED = new OutputPort((Cpu.Pin)FEZ_Pin.Digital.LED, true);
            // the pin will generate interrupt on high and low edges
            InterruptPort IntButton = new InterruptPort((Cpu.Pin)FEZ_Pin.Interrupt.LDR, true,
                Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeBoth);
            // add an interrupt handler to the pin
            IntButton.OnInterrupt += new NativeEventHandler(IntButton_OnInterrupt);
            //do anything you like here
            Thread.Sleep(Timeout.Infinite);
        }
        
        static void IntButton_OnInterrupt(uint port, uint state, DateTime time)
        {
            // set LED to the switch state
            LED.Write(state == 0);
        }
    }
}

I also found a similar example in on page 81 of “Expert .NET Micro Framework”.

using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;

namespace GpioInterruptPortEdgeSample
{
    public class Program
    {
        private static InterruptPort interruptPort;
        
        public static void Main()
        {
            interruptPort = new InterruptPort(Cpu.Pin.GPIO_Pin2, false, //no glitch filter
                Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeLevelLow);
            interruptPort.OnInterrupt += new GPIOInterruptEventHandler(port_OnInterrupt);
            Thread.Sleep(Timeout.Infinite);
        }
        
        private static void port_OnInterrupt(Cpu.Pin port, bool state, TimeSpan time)
        {
            Debug.Print("Pin=" + port + " State=" + state + " Time=" + time);
            interruptPort.ClearInterrupt();
        }
    }
}

The reason I wish to use Microsoft.SPOT.Hardware.InterruptPort is so that I am able to use EdgeLevelHigh and EdgeLevelLow interrupts that emulate oneshot behaviour, and also to be able to enable and disable the interrupt.

Any further insights into what I am missing and / or how to get this working , i.e. to get past the System.ArgumentException that occurs when executing “irq.OnInterrupt += new NativeEventHandler(irq_OnInterrupt);” will be appreciated.

I am emulating that by this simple snippets of code (snippets!):

        
private readonly static InterruptPort _GateTrigger = new InterruptPort(G120.P0_26, true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeBoth);
 
_GateTrigger.OnInterrupt += new NativeEventHandler(IntButton_OnInterrupt);

static void IntButton_OnInterrupt(uint port, uint state, DateTime time)
        {
            if (_GateTrigger.Read())
            {
                Debug.Print("Switch released");
            }
            else
            {
                Debug.Print("Switch pressed");
            }
            
        }

It seems to work pretty well, not sure why the GT interrupts exits? Anyone?

And thanks for the interesting research @ Oldevel !

@ njbuch - I have similar code working. My code does not work when I attempt to use Port.InterruptMode as .InterruptEdgeLevelLow or .InterruptEdgeLevelHigh.

This will only work with certain External Interrupt capable ports on the LPC178x/7x.

In section 7.8.2 of the LPC178x/7x datasheet, page 44, the following is mentioned.
"Any pin on port 0 and port 2 regardless of the selected function can be programmed to generate an interrupt on a rising edge, a falling edge, or both. "

Then starting on page 32, section 3.3.15 in the user manual UM10470, mention is made of the External Interrupts EINT0…EINT4.

On the FEZ Spider II, these map as follows if I have this correct.
[EINT0, P0_29 and P2_10 on Socket8, Pin3] , [EINT2, P2_12 on Socket14, Pin3] , and [EINT3, P2_13 on Socket12, Pin3].
EINT1 is exposed via P0_30 and P2_11, yet these ports are not exposed on the Spider II (if working according to the FEZ Spider II schematic).
According to my understanding of the Spider II schematic and LPC178x/7x datasheet and User Manual, EINT0, EINT2, and EINT3 should be available on the given pins.

I am trying to use External Interrupts as they are slightly different, and allow oneshot behaviour. Here the interrupt triggers once, and requires a reset before it will trigger again. Using this type of interrupt, which is referred to as a level-triggered interrupt, allows one to minimize the overhead impact of the interrupt to a minimum as opposed to GPIO edge-triggered interrupt which will trigger continuously.

If one inspects the Microsoft.SPOT.Hardware.InterruptPort it is easy to see that provision is made for level-triggered interrupts.

I have yet to find out whether this can be used on the Spider II, and have posted a question about it.

1 Like

in the past, the level interrupts were not implemented in MF. have they been added in recent versions of MF?

1 Like

@ Mike -

#region Assembly Microsoft.SPOT.Hardware.dll, v4.3.1.0

    public enum InterruptMode
    {
        InterruptNone = 0,
        InterruptEdgeLow = 1,
        InterruptEdgeHigh = 2,
        InterruptEdgeBoth = 3,
        InterruptEdgeLevelHigh = 4,
        InterruptEdgeLevelLow = 5,
    }

@ willgeorge - Interesting as it does not seem to work…

to be clear, the ENUMs were always available, but the “edge level” modes were not implemented.

1 Like

@ Brett -

Cannot comment on Spider etc. Mine are at a friends so I cannot check.

However, I checked on my G30HDR (It has TinyCLR 4.3.7.9 As received from GHI)

All the Interrupts work on mine. (With limited testing)

Example with EdgeLevel High and Low:


static InterruptPort interruptPortLevelHigh = null;
static InterruptPort interruptPortLevelLow = null;
static Cpu.Pin PA1pinHigh = GHI.Pins.G30HDR.Gpio.PA1;
static Cpu.Pin PA0pinLow = GHI.Pins.G30HDR.Gpio.PA0;

//
The Output pins that sent a logic level on a button press event.
These are just a N.O. button with a resistor to 3.3 or Ground depending on the output level wanted.

PA1pinHigh - Output sent is 1
PA0pinLow  - Output sent is 0
//

/* Whenever a level interrupt (InterruptEdgeLevelHigh or InterruptEdgeLevelLow) is triggered
it sets a flag which disables the interrupt from re-occuring until the flag is cleared
by the ClearInterrupt method. */

	//You cannot use the same Cpu Pin for High and Low - You need different pin numbers.
	try
	{
		interruptPortLevelHigh = new InterruptPort(PA1pinHigh, true, Port.ResistorMode.Disabled, 
		interruptPortLevelHigh.OnInterrupt += interruptPortLevelHigh_OnInterrupt;
	}
	catch (ArgumentException)
	{
		Debug.Print("Error creating PA1 Interrupt Edge Level High");
	}

	try
	{
		interruptPortLevelLow = new InterruptPort(PA0pinLow, true, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeLevelLow);
		interruptPortLevelLow.OnInterrupt += interruptPortLevelLow_OnInterrupt;
	}
	catch (ArgumentException)
	{
		Debug.Print("Error creating PA0 Interrupt Edge Level Low");
	}
	//

	static void interruptPortLevelHigh_OnInterrupt(uint data1, uint data2, DateTime time)
	{
		Thread.Sleep(500); //This works if you quickly press and then release the button
                           //If the button is press longer than wait you get multiple events
		
		Debug.Print("PA1 Interrupt Edge High Port Pin: " + data1 + "  State: " + data2);
	}
	//

	static void interruptPortLevelLow_OnInterrupt(uint data1, uint data2, DateTime time)
	{
		Thread.Sleep(500); //This works if you quickly press and then release the button
                           //If the button is press longer than wait you get multiple events
		
		Debug.Print("PA0 Interrupt Edge Low Port Pin: " + data1 + "  State: " + data2);
	}
	//

Debug.Print Output
ClearInterrupt not used so interrupt is received only once.

Hello! I am GHI Electronics G30HDR
PA1 Interrupt Edge High Port Pin: 1  State: 1
PA0 Interrupt Edge Low Port Pin: 0  State: 0

If you use ClearInterrupt you receive multiple events (As long as the button is pressed).

Hello! I am GHI Electronics G30HDR
PA1 Interrupt Edge High Port Pin: 1  State: 1
PA1 Interrupt Edge High Port Pin: 1  State: 1
PA1 Interrupt Edge High Port Pin: 1  State: 1
PA1 Interrupt Edge High Port Pin: 1  State: 1
PA1 Interrupt Edge High Port Pin: 1  State: 1

PA0 Interrupt Edge Low Port Pin: 0  State: 0
PA0 Interrupt Edge Low Port Pin: 0  State: 0
PA0 Interrupt Edge Low Port Pin: 0  State: 0
PA0 Interrupt Edge Low Port Pin: 0  State: 0
PA0 Interrupt Edge Low Port Pin: 0  State: 0


EDIT: I forgot to add the following with ClearInterrupt()

static void interruptPortLevelHigh_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            Thread.Sleep(500); //This works if you quickly press and then release the button
                               //If the button is press longer than wait you get multiple events
            interruptPortLevelHigh.ClearInterrupt();
            Debug.Print("PA1 Interrupt Edge High Port Pin: " + data1 + "  State: " + data2);
        }
        //

        static void interruptPortLevelLow_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            Thread.Sleep(500); //This works if you quickly press and then release the button
            //If the button is press longer than wait you get multiple events
            interruptPortLevelLow.ClearInterrupt();
            Debug.Print("PA0 Interrupt Edge Low Port Pin: " + data1 + "  State: " + data2);
        }

1 Like

Thanks for the comments.

@ willgeorge - thanks for your code sample. From what I have read it should be possible on the Spider II.

It would be really helpful if GHI could comment on whether this is possible or not, and if is possible, provide a snippet which works on the Spider II…

@ willgeorge - Interesting codesample. Not sure I understand how to use TWO pins for one button. Are they both connected to the same wire?

@ njbuch -

Not sure I understand how to use TWO pins for one button.

Sorry… My very poor wording.

For my test I used two different buttons. Both were Normally Open contacts.

One button goes from Ground to +3.3 on button press.
PA1pinHigh - Output sent is 1. (InterruptPort interruptPortLevelHigh)

One goes from +3.3 to Ground on a button press.
PA0pinLow - Output sent is 0. (InterruptPort interruptPortLevelLow)

Pull-Up and Pull-Down resistors were 4.7K (What I had at hand)

@ njbuch - The button could be connected to both pins, i.e. the pins are inputs which are connected together, and to the button circuit. Then one interrupt will trigger when the button is pressed, the other when it is released, given adequate provision is made for switch debounce, and oneshot interrupts are used (else the interrupt could trigger continuously while the button is in it’s released state). Come to think of it, edge triggering will work as well.

@ willgeorge - I tried the following on the Spider II, as per your sample code.

    public class Program
    {
        static InterruptPort interruptPortLevelLow = null;
        static Cpu.Pin pin3 = (Cpu.Pin) 74; // socket 8 pin 3 on the Spider II
        static int cnt = 0;
        public static void Main()
        {
            try
            {
                //interruptPortLevelLow = new InterruptPort(pin3, true, Port.ResistorMode.Disabled,
                //    Port.InterruptMode.InterruptEdgeLevelLow);
                interruptPortLevelLow = new InterruptPort(pin3, true, Port.ResistorMode.Disabled,
                    Port.InterruptMode.InterruptEdgeLow);
                interruptPortLevelLow.OnInterrupt += interruptPortLevelLow_OnInterrupt;
            }
            catch (Exception)
            {
                Debug.Print("Error creating pin3 Interrupt Edge Level Low");
            }

            Debug.Print("Program Started");
            Thread.Sleep(Timeout.Infinite);
        }

        static void interruptPortLevelLow_OnInterrupt(uint data1, uint data2, DateTime time)
        {
            Debug.Print("pin3 Interrupt Edge Low Port Pin: " + data1 + "  State: " + data2);
            //interruptPortLevelLow.ClearInterrupt();
        }
    }

When I set Port.InterruptMode.InterruptEdgeLevelLow or Port.InterruptMode.InterruptEdgeLevelHigh , I receive an exception as previously mentioned.
When I set Port.InterruptMode.InterruptEdgeLow or Port.InterruptMode.InterruptEdgeHigh or Port.InterruptMode.InterruptEdgeBoth, the code executes.

@ Oldevel -

Sort of looks like we need one of GHI’s Heavyweights to respond!

I would look further but I do not have a Spider II.

FWIW - InterruptEdgeLevelHigh works fine in NETM4.4 on STM’s

@ Oldevel - Level interrupts are not currently supported on the G120.

@ John - I will work around it, thanks.