Main Site Documentation

Pulsein()


#1

Can we have a pulsein() method added to the inputPort class?

Such a function would measure the time a certain pin is high. If the pin is high when the method is called, it will wait until the pin is low and start measuring when it is high again.

I know this can be done within managed code, but I don’t know how accurate it would be compared to if it were baked into the firmware.

Thanks!


#2

pulsein available in other systems like BS2 and Arduino is horrible! It locks down the whole system just to read a pin! For example our pulse out (we call it OutputCompare) runs in the back with interrupts and with no need to lock-down the system.

Now, while NETMF supports interrupt pins, you can create such functionality without our help, using interrupt port…so are you up for the challenge?

Anybody else is up for the challenge? I am thinking a method that can take in a buffer and log all samples in the buffer…this is perfect for reading a TV remote!

Now if you only want o measure one pulse (like pulse in does) then this can be much simpler.


#3

Hmmm…I’m thinking timestamps?


#4

For a very simple implementation, what would be the best option? I know the obvious ones, but what’s the most precise, quick and dirty way to do it?


#5

Here is a basic class I wrote with PulseIn functionality. I use it to read the pulse train output from an old Futaba RC Receiver.


using System;
using Microsoft.SPOT.Hardware;

namespace PulseIn
{
    public class PulseIn
    {
        /// <summary>
        /// Holds the time (in 100ns Ticks) that the last edge (rising or falling) was received.
        /// </summary>
        private long lastEdgeTimeTicks;

        /// <summary>
        /// Holds the amout of time (in 100ns ticks) that the pulse was high.  This value is updated on every falling
        /// edge triggered interrupt.
        /// </summary>
        private long highTimeTicks;

        /// <summary>
        /// Gets the amout of time (in 100ns ticks) that the pulse was high.  This value is updated on every falling
        /// edge triggered interrupt.
        /// </summary>
        public long HighTimeTicks
        {
            get { return (highTimeTicks); }
        }

        /// <summary>
        /// Holds the amount of time (in 100ns ticks) that the pulse was low.  This value is updated on every rising
        /// edge triggered interrupt.
        /// </summary>
        private long lowTimeTicks;

        /// <summary>
        /// Gets the amount of time (in 100ns ticks) that the pulse was low.  This value is updated on every rising
        /// edge triggered interrupt.
        /// </summary>
        public long LowTimeTicks
        {
            get { return (lowTimeTicks); }
        }


        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="pulseInPin">
        /// Pin that is to be configured to read the pulse input
        /// </param>
        public PulseIn(Cpu.Pin pulseInPin)
        {
            // Intialize the interrupt port that we want to use to measure the pulse
            InterruptPort intPort = new InterruptPort(pulseInPin, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);

            // Initialize class members.
            lastEdgeTimeTicks = DateTime.Now.Ticks;
            highTimeTicks = 0;
            lowTimeTicks = 0;

            // Add the interrupt handler
            intPort.OnInterrupt += new NativeEventHandler(IntPort_OnInterrupt);

            return;
        }

        /// <summary>
        /// Interrupt handler that gets called on falling and rising edges of the configured pulse input pin.
        /// </summary>
        /// <param name="port">
        /// Port (Pin Number?) that caused this interrupt
        /// </param>
        /// <param name="state">
        /// State (High or Low) that the pin was in after the rising or falling edge.  0 = Low, 1 = High
        /// </param>
        /// <param name="time">
        /// DateTime that the pulse triggerd the interrupt.
        /// </param>
        void IntPort_OnInterrupt(uint port, uint state, DateTime time)
        {
            if (state == 0)
            {
                // Here the pin is low.  We just counted the high time, the amount of time from rising edge to falling edge
                lock (this)
                {
                    highTimeTicks = time.Ticks - lastEdgeTimeTicks;
                }
            }
            else
            {
                // Here the pin is high.  We just counted the low time, the amount of time from falling edge to rising edge
                lock (this)
                {
                    lowTimeTicks = time.Ticks - lastEdgeTimeTicks;
                }
            }
                        
            // Save the time that this edge occured.
            lastEdgeTimeTicks = time.Ticks;

            return;
        }
    }
}

It works, but its also my first time writing code in the .Net Micro Framework.


#6

I’ve been on a learning experience second to none with my RFID reader problem http://www.tinyclr.com/forum/13/1835.

I must say that a proper PulseIn class implemented in native code would be wonderful.

I implemented a managed version using interrupts and timestamps and got reasonable results. Simply set up an interrupt on both edges and store the timestamp in an array. A little trick I used was to store the low-going timestamps as negative timestamp numbers and the high-going as positive. You can reconstruct the signal very easily that way. I also saved a little memory by using 16 bit deltas of the timestamps and not the full 32. It comes at the cost of processing time however.

The drawback is the sample rate is pretty low at around 10kHz but it does work.

At the moment I’m looking into using the SSP (SPI) port and DMA. The thinking is that the hardware should be able to “clock in” a signal at a known rate. You can then read out the signal afterwards. Not sure if this will work. I use SPI because it guarantees a regular sampling frequency - even if you don’t use the clock signal.

Let me know if anyone makes progress with a native version of this.


#7

        /// <summary>
        /// Create the PWM pin
        /// </summary>
        /// <param name="PWMpin">Pin to control the Speed Controller</param>
        public SpeedController(FEZ_Pin.PWM PWMpin)
        {
            // Set up the PWM port
            esc = new PWM((PWM.Pin)PWMpin);
        }

        /// <summary>
        /// Create the PWM pin and RPM Count Interrupt Pin
        /// </summary>
        /// <param name="PWMpin">Pin to control the Speed Controller</param>
        /// <param name="InterruptPin">Interrupt Pin to perform RPM Count</param>
        public SpeedController(FEZ_Pin.PWM PWMpin, FEZ_Pin.Interrupt InterruptPin) 
            : this(PWMpin)
        {
            interrupt = new InterruptPort((Cpu.Pin)InterruptPin, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeHigh);
            interrupt.OnInterrupt += new NativeEventHandler(rpmCount);
        }

        /// <summary>
        /// Counts the RPM using RPM Sensors
        /// </summary>
        /// <param name="data1">not used</param>
        /// <param name="data2">not used</param>
        /// <param name="time">The time the interrupt was triggered, in ticks (1e-7 seconds)</param>
        private void rpmCount(uint data1, uint data2, DateTime time)
        {
            timeInterval = (time.Ticks - lastInterruptTime) * 1e-7;
            rpmReading = 60 / timeInterval;
            this.DutyCycle = this.updateDutyCycle();
            lastInterruptTime = time.Ticks;
        }

This is extracted from my version of ESC driver, which includes RPM counting capabilities. You can see how I am using the interrupt pins.


#8

I think this may deserve a composable solution in general for scanning pin changes. It is observer pattern over a stream (so would be perfect for something like Rx if existed for MF). Where the hw is the IObservable and your lamda is an observer. It also keeps your context local so you not chasing events and shared state all over you program. So I came up with this port.Subscribe() pattern which seems to feel good. The first overload just passes the DateTime or each event as normal, the second overload is just a helper when your more interested in the time span between pulses.


public delegate bool PinChange(bool state, DateTime time);
public delegate bool PinChangeSpan(bool state, long tickSpan);
public static class Extentions
{
    /// <summary>
    /// This overload passes tick span between pin changes.
    /// </summary>
    public static void Subscribe(this InterruptPort port, PinChangeSpan predicate)
    {
        if (predicate == null) throw new ArgumentNullException("predicate");
        long lastTick = -1;
        port.Subscribe((bool state, DateTime dt) =>
            {
                if (lastTick < 0)
                {
                    lastTick = dt.Ticks;
                    // The first "real" pin change has no delta yet.
                    return predicate(state, -1);
                }
                long delta = dt.Ticks - lastTick;
                lastTick = dt.Ticks;
                return predicate(state, delta);
            }, 1); // Always skip first as dirty because no idea when it was set.
    }

    /// <summary>
    /// This overload passes DateTime of current pin state.
    /// </summary>
    /// <param name="predicate">User predicate.</param>
    /// <param name="skipCount">Number of changes to skip.</param>
    public static void Subscribe(this InterruptPort port, PinChange predicate, uint skipCount=0)
    {
        if (predicate == null) throw new ArgumentNullException("predicate");
        NativeEventHandler del = null;
        del = (uint data1, uint data2, DateTime time) =>
        {
            if (skipCount > 0)
            {
                skipCount--;
                return;
            }

            if (del == null)
                return; // Already unregisted. Not sure why this needed to avoid extra callback. MF Bug?

            bool state = data2 == 0 ? false : true;
            // If user predicate not want more, then unregister.
            if (! predicate(state, time))
            {
                port.OnInterrupt -= del;
                del = null;
            }
        };

        // Register observer.
        port.OnInterrupt += del;
    }
}

Usage: The first sample is just stopping after first 5 highs. The second, I think, answers OP question on delay between first true low and next high.


InterruptPort inPort = new InterruptPort((Cpu.Pin)FEZ_Pin.Interrupt.Di13, false, Port.ResistorMode.Disabled, Port.InterruptMode.InterruptEdgeBoth);
            
// Note:
// 1) Event is raised right away with current state of pin.
// 2) Do not sleep or block inside a pin callback.
// 3) Pin callback is not reentrant. Blocking inside callback will stall other events.
// 4) NETMF callback can be called again *after unregistering event. Not clear on why yet.
int highCount = 0; // local state var.
inPort.Subscribe((isHigh, dt) =>
{
    if (isHigh && ++highCount >= 5)
    {
        Debug.Print("Pin high 5 times. Do something.");
        return false; // unsubscribe.
    }
    return true; // keep subscription active.
});

// PulseIn sample:
// Milliseconds span between first low and high.
// Note: to start getting good deltas, we need to skip 2 events.
// The first is skiped inside PulseIn, the second is skipped here as delta is -1 on first.
inPort.Subscribe((bool isHigh, long ticks) =>
{
    if (ticks == -1)
        return true; // Skip first, because we have no delta time context yet. 
    if (isHigh)
    {
        Debug.Print("\nDelay between first low and first high: " + ticks / 10000);
        return false; // done.
    }
    else
        return true;
});

Also, the callback from chip into the .Net event is super fast with almost no overhead I can see. Any overhead is in the code inside the callback which should normally only be a few lines. So not sure how much a native solution would help. You still have a callback and have to marsal stuff back into .net eventually.


#9

Also. Not sure if bug or side-effect in unsubscribe NativeEventHandler event. On my system, I get 1 more callback after unsubscribing (workaround in the code). Could this be some issue with unsubscribing *inside the callback itself?


#10

This is good stuff. I like the idea of expressing the pattern you’re looking for and then letting the software do the work. There should be a nice generic pattern in there to handle different types of streams. RC PWM or IR or software UART - all derivates of the same idea - but with specific start, transition and end patterns.

The problem I still have is speed. Unless I’m doing something really wrong, I’m not getting much lower than about 50uS per instruction cycle or about 150-200uS to do something useful like putting a value into an array and incrementing a counter. That’s just fine for RC PWM but not fast enough for software UART at 15kbaud (which happens to be what I’m trying to capture).

In my opinion a nice solution would be a native implementation of the capturing with a callback into a managed handler when it’s done.

How about something like this… (pseudo code - don’t shoot me over the correctness please :))



// Set up a Pulse Recorder to capture 2 channels of an RC PWM pulse.
// Because the total pulse length is 30ms (30000us), it can reset itself after that period if no signal is found.
// A resolution of 1us gives 1000 positions.

PulseRecorder pr = new PulseRecorder(Pin=(Cpu.Pin)InterruptPin, EdgeTrigger=true, EdgeCount=4, Resolution=1, TimeOut=30000, CallBack=RcPwmDecoder)

pr.Start();

Thread.Sleep(-1);

void RcPwmDecoder(uint[] edgeTicks)
{
  int ch1duty = (edges[1] - edges[0]) / TICKSPERMS;
  int ch2duty = (edges[3] - edges[2]) / TICKSPERMS;
}


The idea is that the recorder can use GPIO DMA and really low level stuff to record the edge transitions sequence at great speed (Megahertz) and then hand it over to a callback routine to decode. It just passes an array with the edge transition timestamps (in microseconds) - much like what OutputCompare does for generating signals. In fact it will be nice to be able to pass the same array back to OutputCompare to output the same signal.


#11

realiser, we had this already implemented in 3.0 but we removed in 4.0 for teh sake of adding something better…but then we never got to it


#12

Gus, will it make it back in anytime? PulseIn can be used for allot of things, and is quite handy to decode protocols.


#13

I keep asking for it to come back…I actually need it for a project I am working on! I should go on strike until GHI adds it back…lol

The point is we need to make this non-blocking call but the guys are working on more important features.

I will bug them again about it


#14

I’d like to have this too, but i think there are more important features like the updated CAN control.