The InterruptPortMM class is designed to send an .NET event to a subscriber when the state (high or low) of a GPIO pin on the Beaglebone Black changes. The BBBCSIO library usually provides classes for both SYSFS and Memory Mapped access however the InterruptPort functionality is only available in MemoryMapped mode.
The GPIO on which the InterruptPortMM class operates must be configured as an input in exactly the same way as if it were to be used with any of the InputPort classes.
The example code below uses a GPIO value of 49 which is exposed as pin 23 on the P9 header. You CAN NOT assume that this pin is available as a GPIO on your Beaglebone Black and even if it is a GPIO you still cannot assume it is set as a GPIO input (as opposed to being an output). Any one GPIO can be used for any of about half a dozen different peripherals (UART, SPI bus, PWM, A/D, HDMI, EMMC etc) and it is not a given that a GPIO used in these examples or any specific GPIO is available. The GPIO's used in these examples may be used for other things on your Beaglebone Black.
Before using an InterruptPort class on a GPIO you will need to ensure that the GPIO is configured as an input. There is no code you can call in the BBBCSIO library which does this. You MUST configure the GPIO externally prior to running your executable - usually this is done by editing the device tree.
Always be aware that the armhf Linux running on the Beaglebone Black is not a real-time operating system. This means, for example, although you might set up an InterruptPort() object to watch for pin state changes it is possible some changes may be missed as the process is pre-emptively swapped in and out by the kernel.
The code below illustrates how to use the InterruptPortMM class to monitor the state of a GPIO pin and send an event to a remote method (which can be in an entirely different class). The example interrupt handler simply outputs a summary of the event to the console.
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// Creates and monitors an interrupt port. /// </summary> /// /// NOTE: /// This code assumes that the port associated with the gpioID has been /// properly configured in the device tree as an input. If it is not /// then the interrupt may not work correctly. See the associated documentation /// /// NOTE: /// Be aware of the BBB max input voltage and pullup/pulldown resistors. /// Interrupt or not it the pin is still an input. Be really careful about /// the voltage you provide here. In general it is usually a good idea to /// run the input through some sort of buffer to cap the max input at the /// BBB maximum. /// /// <param name="eventInterruptModeIn">The interrupt mode</param> /// <param name="gpioIn">The gpio this port represents. </param> /// <history> /// 28 Aug 14 Cynic - Originally written /// </history> public void SimpleInterruptPortMM(GpioEnum gpioIn, InterruptMode eventInterruptModeIn) { if (gpioIn == GpioEnum.GPIO_NONE) { throw new Exception ("Bad interrupt port GpioEnum.GPIO_NONE"); } // create an interrupt port. InterruptPortMM ipPortGPIO = new InterruptPortMM(gpioIn, eventInterruptModeIn); // Hook up an event handler to the OnInterrupt event // the IpPortGPIO_OnInterrupt function will receive the incoming event data ipPortGPIO.OnInterrupt += new InterruptEventHandlerMM(IpPortGPIO_OnInterrupt); // we will not get any data if it is not enabled ipPortGPIO.EnableInterrupt(); // run until we get key press on the console while (Console.KeyAvailable == false) { // sleep for a second Thread.Sleep(1000); } // this just stops all transmissions. The interrupt can be enabled again // NOTE: disabling the interrupt does not release the event resources ipPortGPIO.DisableInterrupt(); // close the port ipPortGPIO.ClosePort(); // usually a good idea to dispose() ipPortGPIO.Dispose(); // we never can use this object again after it is disposed. To use it // again we would have to re-create it ipPortGPIO = null; }The above code subscribes to the InterruptPort's OnInterrupt event and any events will appear in the named
IpPortGPIO_OnInterrupt
handler. Example code for the IpPortGPIO_OnInterrupt
handler is given below. You can
route more than one InterruptPort to the same handler - however to do anything useful you will need to
use the information passed in during the event to figure out which event was triggered.
Note that the code below executes in a different thread than the one in which the InterruptPortMM was created. You should not do any lengthy processing in the interrupt handler thread as no other interrupts can be processed until the current interrupt is cleared.
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+= /// <summary> /// An InterruptPort interrupt event handler. This is where the data from /// the interrupt arrives. Note the EventData object contains more /// detailed information than the passed in arguments - including a /// reference to the InterruptPort object which generated the event. /// /// NOTE: /// Be aware that this handler method which accepts the data from /// an InterruptPort event is executing in the InterruptPort's event /// thread - not the main() thread. You have to be careful what you do /// in here to avoid the many issues associated with multi-threaded /// applications. /// /// NOTE: Be Quick. It is not advisable to take to long to process the /// incoming event data - you cannot receive another interrupt while /// processing the current one and the fact that a lost interrupt /// happened will not be recorded /// /// </summary> /// <param name="evGpio">The gpio the event is configured on</param> /// <param name="evState">The event state (1 or 0)</param> /// <param name="evTime">The event time</param> /// <param name="evData">The event data structure</param> /// <history> /// 28 Aug 14 Cynic - Originally written /// </history> public void IpPortGPIO_OnInterrupt(GpioEnum evGpio, bool evState, DateTime evTime, EventData evData) { // here we process and take action on the incoming data // in this particular example we just write it out to the // console if (evData != null) { // this function could receive data from multiple // interrupt ports. To decide what action to take we could // look at the user specified evCode or the GPIO number // down in the evData object. // just output some diagnostics Console.WriteLine (evData.ToString ()); // if we have to call ClearInterrupt() on the port // we can do it this way if (evData.PortObject != null) { evData.PortObject.ClearInterrupt(); } } }In the
SimpleInterruptPortMM
call, the gpioID
and eventInterruptModeIn
are passed in when the function was called.
These values are members of the
GpioEnum
and InterruptMode
enumerations.
The called would be made via a line which looks like:
SimpleInterruptPortMM(GpioEnum.GPIO_49, InterruptMode.InterruptEdgeBoth);