File: InterruptPortMM
Details
File: InterruptPortMM.cs
Date: Thu, Mar 7, 2019
using System;
using System.IO;
using System.Threading;
/// +------------------------------------------------------------------------------------------------------------------------------+
/// | TERMS OF USE: MIT License |
/// +------------------------------------------------------------------------------------------------------------------------------|
/// |Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation |
/// |files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, |
/// |modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software|
/// |is furnished to do so, subject to the following conditions: |
/// | |
/// |The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.|
/// | |
/// |THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
/// |WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR |
/// |COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
/// |ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
/// +------------------------------------------------------------------------------------------------------------------------------+
namespace BBBCSIO
{
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Provides the Interrupt Port functionality for a BeagleBone Black
/// (MemoryMapped Version)
///
/// Q: What is really happening here!
/// A: These are not true interrupts. They have much more in common with
/// a polled event although the event will "interrupt" the Main() thread
/// and execute code in an object listening on them.
///
/// All GPIOs are associated with one of four banks. The act of creating
/// an InterruptPort on a GPIO will start an event monitoring thread for
/// that bank. This GPIO bank event monitoring thread is continuously
/// computable and there is only one thread per bank. If possible, it is
/// much more efficient to have all of your interrupts concentrated on one
/// bank (hence starting only one monitoring thread) rather than scattered
/// over all four gpio banks.
///
/// The InterruptPort registers with the GPIO bank event monitoring thread
/// and when a pin state change of significance is detected the SendInterruptEvent
/// function in this class will be called. The SendInterruptEvent function
/// will place the relevant information into an EventData object and then
/// send it on to anybody listening on the InterruptPorts OnInterrupt delegate
///
/// Be aware that the OnInterrupt handler method which accepts the data from
/// the C# event is executing in the Gpio banks event monitoring thread
/// - not the main() thread. You have to be careful what you do in there to
/// avoid the many issues associated with multi-threaded applications.
/// Also, it is not advisable to take too long to process the incoming event
/// data - you cannot receive another interrupt while processing that one.
///
/// PRIORITYS: THe GPIO bank event monitoring thread will detect changes
/// and then run down its list of registered InterruptPort until it finds
/// the correct one for that pin state change. The order of these registered
/// InterruptPorts is the Priority - highest first. Thus, if two pins change
/// state simultaneously then only one will get processed. If on a subsequent
/// pass the second interrupts pin state is still changed, it will get
/// processed then. If it has changed back it will not be processed and the
/// interrupt event will be lost.
///
/// CLOSING DOWN: Be aware that a call to DisableInterrupt() does not stop
/// the worker thread. You need to do an explicit ClosePort() call to stop
/// the thread and release any resources the InterruptPort is using. Always
/// explicitly Dispose() all ports when you are done with them.
///
/// </summary>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public class InterruptPortMM : PortMM, IComparable
{
private InterruptMode eventInterruptMode = InterruptMode.InterruptEdgeBoth;
private bool interruptIsEnabled = false;
private bool interruptClearToSend = false;
public event InterruptEventHandlerMM OnInterrupt;
// a user specified code which is included with every event sent
private int evCode=0;
private int interruptEventsReceived = 0;
private int interruptEventsSent = 0;
// the priority of this InterruptPort relative to other
// Interrupt Ports on the same bank. Larger value means
// higher priority and hence is serviced first. Equal
// priorities imply it is undefined which gets serviced first
private int interruptEventPriority = 0;
// Values to support interrupt event detection. These are public - we need
// speed of access here and I do not want to add the overhead of a property get accessor
// this is the state of the entire GPIO bank DATAIN register on the last interrupt.
public int LastInterruptRegisterState=0;
// a mask to detect our ports bit in the register. zero for disabled interrupt
public int InterruptMask = 0;
// a mask to detect if we are interested in a hit state. zero for not interested
public int ActiveHighMask = 0;
// a mask to detect if we are interested in a low state. zero for not interested
public int ActiveLowMask = 0;
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Constructor
/// </summary>
/// <param name="eventInterruptModeIn">The interrupt mode</param>
/// <param name="gpioIn">The GPIO this interrupt is on.
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public InterruptPortMM (GpioEnum gpioIn, InterruptMode eventInterruptModeIn) : base(gpioIn)
{
EventInterruptMode = eventInterruptModeIn;
// setup for event detection
SetEventMasks();
// open the port and turn on event detection
OpenPort();
}
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Constructor
/// </summary>
/// <param name="eventInterruptModeIn">The interrupt mode</param>
/// <param name="gpioIn">The GPIO this interrupt is on.</param>
/// <param name="evCodeIn">A user specified code which appears in the events</param>
/// <param name="evPriorityIN">the event priority, higher numbers processed first</param>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public InterruptPortMM (GpioEnum gpioIn, InterruptMode eventInterruptModeIn, int evPriorityIn, int evCodeIn) : base(gpioIn)
{
EventInterruptMode = eventInterruptModeIn;
evCode = evCodeIn;
interruptEventPriority = evPriorityIn;
// setup for event detection
SetEventMasks();
// open the port and turn on event detection
OpenPort();
}
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Sets the event masks from the ports GpioCfgObject
/// </summary>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
private void SetEventMasks()
{
// set these flags, we set them pre-set for speed during event detection
InterruptMask = GpioCfgObject.GpioMask;
if ((EventInterruptMode == InterruptMode.InterruptEdgeLevelHigh) || (EventInterruptMode == InterruptMode.InterruptEdgeBoth))
{
// this will be used to detect a high bit set
ActiveHighMask = GpioCfgObject.GpioMask;
}
else
{
// we are not interested in a high bit so all bits 0
ActiveHighMask = 0;
}
if ((EventInterruptMode == InterruptMode.InterruptEdgeLevelLow) || (EventInterruptMode == InterruptMode.InterruptEdgeBoth))
{
// this will be used to detect a low bit set
ActiveLowMask = GpioCfgObject.GpioMask;
}
else
{
// we are not interested in a low bit so all bits 0
ActiveLowMask = 0;
}
}
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Gets the PortDirection
/// </summary>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public override PortDirectionEnum PortDirection()
{
return PortDirectionEnum.PORTDIR_INPUT;
}
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Gets/Sets the event priority. For InterruptPorts on the same bank,
/// the InterruptPort with the higher priority will be activated in
/// preference to the others if there simultaneous events
/// </summary>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public int InterruptEventPriority
{
get
{
return interruptEventPriority;
}
set
{
interruptEventPriority = value;
}
}
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Gets/Sets the event code. This is a userdefined value specified when
/// the interrupt port was created.
/// </summary>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public int EvCode
{
get
{
return evCode;
}
}
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Gets/Sets the count of events received
///
/// NOTE: events triggered and lost because the processing of another event
/// took too long are not included in this count.
///
/// </summary>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public int InterruptEventsReceived
{
get
{
return interruptEventsReceived;
}
set
{
interruptEventsReceived = value;
}
}
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Gets/Sets the cound of events sent. It is possible to receive an event
/// and not send it if the port is not enabled when the event is received.
/// </summary>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public int InterruptEventsSent
{
get
{
return interruptEventsSent;
}
set
{
interruptEventsSent = value;
}
}
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Gets the events interrupt mode
/// </summary>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public InterruptMode EventInterruptMode
{
get
{
return eventInterruptMode;
}
set
{
eventInterruptMode = value;
}
}
// #########################################################################
// ### Event Manipulation Code
// #########################################################################
#region Event Manipulation Code
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Opens the port. Throws an exception on failure.
///
/// This is really just starting a worker thread which does all the work
///
/// </summary>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
protected override void OpenPort()
{
// perform base operations
base.OpenPort();
// make sure that we are looking for events on the
// GPIO bank that this port belongs to
MMDevMem.StartEventThreadForInterruptPort(this);
// activate interrupt events for this port
MMDevMem.ActivateInterrupt(this);
}
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Closes the port. Throws an exception on failure
/// </summary>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public override void ClosePort()
{
OnInterrupt = null;
interruptIsEnabled = false;
interruptClearToSend = false;
// deactivate interrupt events for this port
MMDevMem.DeactivateInterrupt(this);
// close in the base class too
base.ClosePort();
}
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Enables the interrupt
/// </summary>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public void EnableInterrupt()
{
interruptIsEnabled = true;
// start cleared to send
ClearInterrupt();
}
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Disables the interrupt
/// </summary>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public void DisableInterrupt()
{
interruptIsEnabled = false;
}
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Clears the interrupt. Must be called after every interrupt.
/// </summary>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public void ClearInterrupt()
{
interruptClearToSend = true;
}
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// A function to send an interrupt event. This function expects to be called
/// from within our interrupt event monitoring thread. It fills in all the
/// required parts and sends event off to the registered subscribers.
///
/// NOTE: you are NOT in the main form thread here. You are in the thread
/// which processes the interrupts over in MemoryMapDevMem
///
/// NOTE: we do not need to check for the InterruptMode. This is done
/// in the thread worker and we would not be called if it wasn't
/// an event we had to send on.
///
/// </summary>
/// <param name="evValue">The current state of the pin 0 or 1</param>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public void SendInterruptEvent(uint evValue)
{
EventData evData;
// count it
interruptEventsReceived++;
// some diagnostics for testing
// Console.WriteLine("interruptEventsReceived=" + interruptEventsReceived.ToString() +", value="+evValue.ToString());
// send on the data - if we should
if (OnInterrupt == null) return;
if (interruptIsEnabled == false) return;
if (interruptClearToSend == false) return;
// build our event data structure
evData = new EventData(this, evCode, evValue);
// set flag
interruptClearToSend=false;
// send the data
OnInterrupt(this.GpioID, evData.EvState, evData.EventDateTime, evData);
// count it
interruptEventsSent++;
}
#endregion
// #########################################################################
// ### IComparable Code
// #########################################################################
#region IComparable Code
/// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=
/// <summary>
/// Designed to compare priorities. This makes it possible to sort a list
/// of InterruptPorts based on priorities
/// </summary>
/// <history>
/// 28 Aug 14 Cynic - Originally written
/// </history>
public int CompareTo(object obj)
{
if ((obj is InterruptPortMM) == false) return 0;
if ((obj as InterruptPortMM).InterruptEventPriority < this.InterruptEventPriority)
{
return -1;
}
if ((obj as InterruptPortMM).InterruptEventPriority > this.InterruptEventPriority)
{
return 1;
}
// The orders are equivalent.
return 0;
}
#endregion
}
}