@ Mike
Circuit(but using 3.3 instead of 5v):
[url]https://hifiduino.files.wordpress.com/2010/10/analogdeb.jpg[/url]
Main Code:
using System;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using GHI.Pins;
using Hardware.RotaryEncoderDriver;
namespace TestEncoder
{
public class Program
{
static RotaryEncoder encoder;
static Cpu.Pin EncoderPinA = Generic.GetPin('C', 12);
static Cpu.Pin EncoderPinB = Generic.GetPin('D', 2);
static int RotationCount = 0;
static string direction;
public static void Main()
{
encoder = new RotaryEncoder(EncoderPinA, EncoderPinB);
encoder.RotationEventHandler += encoder_RotationEventHandler;
while (true)
{
Thread.Sleep(100);
}
}
static void encoder_RotationEventHandler(uint data1, uint data2, DateTime time)
{
{
RotationCount++;
if (data1 == 1)
{
direction = "Clockwise";
Debug.Print(direction);
}
else
{
direction = "Counter-Clockwise";
Debug.Print(direction);
}
Debug.Print("Count: " + RotationCount.ToString());
}
}
}
}
Encoder Driver:
using System;
using Microsoft.SPOT;
using System.Threading;
using Microsoft.SPOT.Hardware;
namespace Hardware.RotaryEncoderDriver
{
/// <summary>
/// </summary>
public class RotaryEncoder : IDisposable
{
#region Things Used Here Globals/Publics/Privates
/// <summary>/// Pin A Interrupt Port/// </summary>
private static InterruptPort PinA = null;
/// <summary>/// Pin B Interrupt Port/// </summary>
private static InterruptPort PinB = null;
/// <summary>/// Rotary Push button is momentary/// </summary>
private static InterruptPort RotaryButton = null;
/// <summary>
/// Default Value is 80ms
/// I use hardware debounce
/// </summary>
public TimeSpan DebounceInterval
{
get { return _DebounceInterval; }
set { _DebounceInterval = value; }
}
public static uint DebounceMillis = 5;
private TimeSpan _DebounceInterval = TimeSpan.FromTicks(DebounceMillis * TimeSpan.TicksPerMillisecond);
public uint ButtonPressAndHoldTimeMillis = 3000;
//private int ButtonEventStartMillis;
//private byte ButtonInterruptCount;//will never exceed value of 4
/// <summary>
/// Subscriber Event for Rotary Interrupts
/// This is the Public EventHandler that is passed through the code and eventually handed off with rotation results.
/// </summary>
public event NativeEventHandler RotationEventHandler = null;
/// <summary>
/// Subscribe for Button Interrupts
/// This will return SingleClick 1, DoubleClick 2[not implemented] , and PressAndHold 0 Events in data1
/// </summary>
public event NativeEventHandler MomentaryButtonEventHandler=null;
public static byte CLOCKWISE = 1;
public static byte COUNTERCLOCKWISE = 0;
//public struct ResultSet
//{
// public bool PinA;
// public bool PinB;
//}
//private ResultSet[] results = new ResultSet[2];//why use result set? read only?
//private bool InProcess; //no longer used.
private static byte StateCount;
/// <summary>/// This Holds the 4 states of a "click" of the momentary/// </summary>
private static byte[] State = new byte[4];
private static bool SkipFirstInterrupt;//This is used
private DateTime button_timestamp = DateTime.Now;
#endregion
#region Constructors
/// <summary>
/// Only if Rotary Encoder is being used.
/// </summary>
/// <param name="pinA"></param>
/// <param name="pinB"></param>
public RotaryEncoder(Cpu.Pin pinA, Cpu.Pin pinB) : this(pinA, pinB, Cpu.Pin.GPIO_NONE) { }
/// <summary>
/// This is the main constructor. Rotary Encoder + Momentary Button
/// </summary>
/// <param name="pinA"></param>
/// <param name="pinB"></param>
/// <param name="buttonPin">Pin for the Momentary Switch</param>
public RotaryEncoder(Cpu.Pin pinA, Cpu.Pin pinB, Cpu.Pin buttonPin=Cpu.Pin.GPIO_NONE)
{
//InProcess = false;
SkipFirstInterrupt = true;
StateCount = 0;
PinA = new InterruptPort(pinA, true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeBoth);//Pins are pulled high.
PinB = new InterruptPort(pinB, true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeBoth);//Interrupt Needed here.
PinA.OnInterrupt += RotationInterrupt;
PinB.OnInterrupt += RotationInterrupt;
#region Unused Example Code
////RotationEvent = new NativeEventHandler(RotationEvent_OnInterrupt);
//NativeEventHandler RotationEvent =
// delegate { NewRotationResult(PinA.Read(), PinB.Read(), ref InProcess, ref results, RotationEventHandler); };//Pin A will always be low because interrupt edge};
////Alternative definition
////NativeEventHandler RotationEvent = (data1, data2, time) =>
////{
//// NewNotaryResult(PinA.Read(), PinB.Read(), ref InProcess, ref results,RotationEventHandler);
////};
//PinA.OnInterrupt += RotationEvent;//Use Deletage NewRotatationResult
//PinB.OnInterrupt += RotationEvent;//not needed but will leave here
#endregion
//setup button
// Setup and initialize the Button Event
if (buttonPin != Cpu.Pin.GPIO_NONE)//more generic definition//Pins.GPIO_NONE)
{
//play here
RotaryButton = new InterruptPort(buttonPin,true, Port.ResistorMode.PullUp, Port.InterruptMode.InterruptEdgeLow);
RotaryButton.OnInterrupt += RotaryButton_OnInterrupt;
//RotaryButton.OnInterrupt += (data1, data2, time) =>
//{
// OnRaiseButtonEvent(time, MomentaryButtonEventHandler, ref button_timestamp, this.DebounceInterval);
//};
}
}
#endregion
#region Built-In Momentary Push Button Methods
/// <summary>
///
/// </summary>
/// <param name="data1">Pin Number [Not Used]]</param>
/// <param name="data2">Pin Value on Interrupt 0 or 1 // High or Low</param>
/// <param name="time">TimeStamp</param>
private void RotaryButton_OnInterrupt(uint data1, uint data2, DateTime time)
{
Thread.Sleep(100);//let things settle
//MomentaryButtonEventHandler(1, 0, time);
if (RotaryButton.Read() == false)//button is still held low
{
//DateTime.Now.AddSeconds(4);
//int millisnow = DateTime.Now.Ticks+100000;
while (DateTime.Now < time.AddSeconds(3))
{
if (RotaryButton.Read() == true)
{
MomentaryButtonEventHandler(1, 0, time);
break;
}
}
if (RotaryButton.Read() == false) { MomentaryButtonEventHandler(0, 0, time); }
}
else
{
MomentaryButtonEventHandler(1, 0, time);
}
}
/// <summary>
/// Creates the event for subscribers
/// </summary>
private static void OnRaiseButtonEvent(DateTime time, NativeEventHandler handler, ref DateTime buttonTimestamp, TimeSpan debounceInterval)
{
// Event will be null if there are no subscribers
if (handler != null)
{
if (time > buttonTimestamp + debounceInterval)
{
buttonTimestamp = time;
handler(0, 0, DateTime.Now);
}
}
}
#endregion
#region Rotary Methods
/// <summary>
/// This Handles all Interrupts From the Rotary Encoder
/// </summary>
/// <param name="data1">The Pin Number {uint}, which Generated the Interrupt</param>
/// <param name="data2">The Pin.Read() Value at time of Interrupt 0 or 1 (LOW or HIGH)</param>
/// <param name="time">Timestamp</param>
private void RotationInterrupt(uint data1, uint data2, DateTime time)//Data1 Is Pin Data 2 Is Pin.Read()
{
//Debug.Print("Pin A: " + PinA.Read().ToString() + " || " + "Pin B: " + PinB.Read().ToString());
if (RotationEventHandler == null)//Check to see if user has added an external event handler
{
throw new Exception("You must define a handler in your main code. Ex: RotaryEncoder.RotationEventHanlder+=DoSomething;");
}
//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)) //|| (State[0] ==1 && State[2] == 4))
{
//do counterclockwise
RotationEventHandler(COUNTERCLOCKWISE, 0, DateTime.Now);
}
else if ((State[0] == 3 && State[1] == 1) || (State[0] == 4 && State[1] == 2) ) //|| (State[0] ==3 && State[2] == 2) )
{
//do clockwise
RotationEventHandler(CLOCKWISE, 0, DateTime.Now);
}
else
{
Debug.Print ("Error in Encoder: State0: " + State[0].ToString() + " || State1: " + State[1].ToString());
}
}
SkipFirstInterrupt = !SkipFirstInterrupt;
}
}
#endregion
/// <summary>
/// Frees all pins and disposes this object
/// </summary>
public void Dispose()
{
PinA.Dispose();
PinB.Dispose();
RotaryButton.Dispose();
}
}
}
//EXAMPLE EVENT HANDLER
//static void RE_RotationEventHandler(uint data1, uint Rotation, DateTime time)
//{
// //throw new NotImplementedException();
// RotCount++;
// if (Rotation==1)
// Debug.Print(RotCount.ToString() + " Rotation Clockwise!");
// else
// Debug.Print(RotCount.ToString() + " Rotation Counter-Clockwise!");
//}
//Quadrature Signal Timing Offset
// __
// |__| |__|
// __
// |__| |__|