pulseIn Function - Version 0.1 (Beta)

**Current Version: 0.1.1 (Beta) 01/23/2014 **
Previous Version: 0.1 (Beta)

Here’s my first real version of a pulseIn() function for the Spark Core. Due to the blocking nature of this function, it has an intentional timeout of 10 seconds to prevent the WLAN from disconnecting. (That 10 seconds includes the time waiting for the pulse to trigger and the actual pulse length.) Therefor this function currently has a useable range from about 10uS to 10S! It’s accurate to one microsecond at the low end of the scale.

To use this function, simply copy/paste the code (below) into your sketch (after the closing brace [}] of void loop()) then call it just as you would any function.

pulseIn(PIN, STATE);
PIN: The pin to monitor for the pulse. [D0~D7|A0~A7]
STATE: The state in which to start timing the pulse. [HIGH|LOW]
Note: Make sure the pin has already been called with pinMode(PIN, INPUT); prior to calling this function, or it will fail.

I still have a small list of things to add and tweak, but I’m really pleased with this initial release! Changing digitalRead to the low-level GPIO_ReadInputDataBit took the while loop runtime down from 2.36uS to just 0.405uS!

So please, play with it, see how it works and give me your feedback! Like I said, I’ve already got a short todo list which I’ll work on over the rest of the week (I’ll post said list this afternoon). Hopefully by this weekend I can have a semi-final version ready for testing, if that goes well I’ll integrate the function into spark_wiring.cpp and submit a pull request. :smiley:

4 Likes

Thought I’d share some pictures of my testing setup, plus the code I used!


I set D4 as an INPUT and hooked it to the output of my AFG; D5 was set as an OUTPUT and connected to the AFG’s trigger input. Both of these connections were also routed to two analog inputs on my Scope.


Here’s a nice view of the Core and gear during testing.


The trigger pulse from the Core captured by the Scope. It should be 1mS but is almost 40% too short. On a positive note, the digital output from the Core doesn’t overshoot much and contains very little noise!


AFG setup to send a 25mS pulse 5mS after being triggered.


Another setup for a 500uS pulse.

int i = 0;
int disPos = 0;
void setup() {
    Wire.begin();
    pinMode(D4, INPUT);
    pinMode(D5, OUTPUT);
    pinMode(D6, OUTPUT);
    digitalWrite(D5, LOW);
    digitalWrite(D6, LOW);
}

void loop() {
    
    digitalWrite(D5, HIGH);
    delay(1);
    digitalWrite(D5, LOW);
    
    unsigned long pulseVal = pulseIn(D4, HIGH);
    unsigned long pulseRaw = pulseVal / 0.405;
    
    Wire.beginTransmission(0x4E);
    if (disPos == 0) {
        Wire.print("CL");
        disPos++;
    }
    else if (disPos == 5) {
        Wire.print("TP");
        Wire.write(0);
        Wire.write(0);
        disPos = 1;
    }
    else {
        Wire.print("TRT");
        disPos++;
    }
    Wire.print("TT");
    Wire.print(pulseVal);
    Wire.print(" / ");
    Wire.print(pulseRaw);
    Wire.write(0x00);
    Wire.endTransmission();
    delay(1000);
}

unsigned long pulseIn(uint16_t pin, uint8_t state) {
    
    GPIO_TypeDef* portMask = (PIN_MAP[pin].gpio_peripheral);
    uint16_t pinMask = (PIN_MAP[pin].gpio_pin);
    unsigned long pulseCount = 0;
    unsigned long loopCount = 0;
    unsigned long loopMax = 25000000;
    
    // While the pin is *not* in the target state we make sure the timeout hasn't been reached.
    while (GPIO_ReadInputDataBit(portMask, pinMask) != state) {
        if (loopCount++ == loopMax) {
            return 0;
        }
    }
    
    // When the pin *is* in the target state we bump the counter while still keeping track of the timeout.
    while (GPIO_ReadInputDataBit(portMask, pinMask) == state) {
        if (loopCount++ == loopMax) {
            return 0;
        }
        pulseCount++;
    }
    
    // Return the pulse time in microsecond!
    return pulseCount * 0.405; // Calculated the pulseCount++ loop to be about 0.405uS in length.
}

Finally, here’s the code I used for testing. In a nutshell, it fires off a 1mS trigger pulse to the AFG on D5 then runs the pulseIn function on D4 to measure a precisely timed return pulse from the AFG. At this stage the pulseIn function was only returning the number of times pulseCount++ ran in the while loop. It would then print this out as a new line (or wrap to the top) on my OLED display before delaying for one second and starting the whole process over again.

So now, I could setup my AFG to output a pulse (e.g., 500uS) then read the pulseCount++ result on the OLED and by using some simple math determine the amount of time (in microseconds) that one loop took to run.

I then slightly modified the code to display both the raw pulseCount and the microsecond output, so I could tweak the modifier a bit.

That’s pretty much it! Surprisingly this took me almost all night, mainly because I tried reading the input port register another way at first, which ended up being a dead end. Once I got to the point of using the AFG to send precisely timed pulses to the Core, it only took me about two hours to get things tweaked and optimized (and a lot of that was waiting for the core to update OTA, since I was using my iPad in the lab).

4 Likes

Worked great for me, with a (much) simpler setup - thanks!

1 Like

Awesome! Glad to see it’s working well for you. That’s a very clean breadboard layout, I must say!

Make sure you guys subscribe to my Github feed or this thread to try out the future updates and get notified when it gets submitted and goes live to the Core Build system. :smile:

I just hooked it back up to my ultrasonic sensor to start working on the Digole Display Library, and it seems to be working great! The output is pretty precise, too. :smile:

@timb I was doing a little Fast Reading tonight. If you want to put the pedal down all the way, check it out:
https://gist.github.com/technobly/8573877

1 Like

@BDub Cool! Direct port manipulation is fast as shit, isn’t it? :smiley:

Yeah, you know I went down that exact same road, however I couldn’t get PIN_MAP[pin].gpio_peripheral->IDR & PIN_MAP[pin].gpio_pin to compare to my state variable in the else loop.

I.e.,

GPIO_TypeDef* portMask = (PIN_MAP[pin].gpio_peripheral);
uint16_t pinMask = (PIN_MAP[pin].gpio_pin);

while (portMask->IDR & pinMask) == state) {
	if (loopCount++ == loopMax) {
		return 0;
	}
	pulseCount++;
}

I tried masking the state variable several ways and just as is, but despite the pin being high it would always return a 0. (For debugging I even tried just monitoring the pin with similar code and printing the result of portMask->IDR & pinMask directly to my OLED.)

I was getting pretty tired there later on, so perhaps I screwed something up? Right now the loop is at 405nS, which gives under half a microsecond resolution, which I think is pretty damn good considering. My ultimate goal with this is to eventually buff up the micros function using some assembly so it can act as an actual timer without overflowing every 7 minutes. Once this is done we can just use it to directly time the pulse length. This way the timing won’t be dependent on knowing the amount of time it takes for a particular loop to run (as in my current implementation) which will be very unreliable once the Spark Team moves all the background stuff into a separate concurrent loop.

Out of curiosity, how did you measure the timing in your Gist?

Agreed, we are coding for fun here… making it work, for now. Along the way, helping to uncover issues, and finding solutions to current problems.

The problem with portMask->IDR & pinMask is that the result is not always 0 or 1. It’s more like 0 or 0x0080 or 0x0040 or 0x0020. We haven’t bit shifted the port pin down to bit 0. That would take too much time anyway, so with just a tiny bit more code, we can have super speed:

unsigned long pulseIn(uint16_t pin, uint8_t state) {

    GPIO_TypeDef* portMask = (PIN_MAP[pin].gpio_peripheral);
    uint16_t pinMask = (PIN_MAP[pin].gpio_pin);
    unsigned long pulseCount = 0;
    unsigned long loopCount = 0;
    unsigned long loopMax = 25000000;

    if(state == HIGH) {
        // While the pin is *not* in the target state we make sure the timeout hasn't been reached.
        while ((portMask->IDR & pinMask) == 0) { // while the bit in the portmask is low
            if (loopCount++ == loopMax) {
                return 0;
            }
        }

        // When the pin *is* in the target state we bump the counter while still keeping track of the timeout.
        while (portMask->IDR & pinMask) { // while the bit in the portmask is high, might be 0x0080, 0x0040, etc..
            if (loopCount++ == loopMax) {
                return 0;
            }
            pulseCount++;
        }
    }
    else {
        // While the pin is *not* in the target state we make sure the timeout hasn't been reached.
        while (portMask->IDR & pinMask) { // while the bit in the portmask is high, might be 0x0080, 0x0040, etc..
            if (loopCount++ == loopMax) {
                return 0;
            }
        }

        // When the pin *is* in the target state we bump the counter while still keeping track of the timeout.
        while ((portMask->IDR & pinMask) == 0) { // while the bit in the portmask is low
            if (loopCount++ == loopMax) {
                return 0;
            }
            pulseCount++;
        }
    }
    
    // Return the pulse time in microsecond!
    return pulseCount * 0.405; // Calculated the pulseCount++ loop to be about 0.405uS in length.
}

I agree there are probably 15 other ways to do this with timers, capture compare inputs, interrupts… so for now it’s just fun to play around and think of different ways.

I measured the timing with my Rigol DS1053e. I will say that I took some liberties with claiming the timing… based on how you structure your code, the compiler and/or the GPIO bus seems to be delayed or sped up slightly based on what’s going on. So you should probably check the timing of the loop after calculating the K factor from 1us up to 1s. Hopefully it averages out to some number that works with a straight gain correction over the entire range.

Hi @timb
I am trying to use your library, but I get an error.
Im calling with :

float concentration = dust.getConcentration(pulseIn(D1, LOW), millis(), DUST_SAMPLE_INTERVAL_MS);

And I get the error :

function_pulseIn.o: In function `pulseIn(unsigned short, unsigned char)':
/spark/compile_server/shared/workspace/3_compile-server2/core-firmware/build/function_pulseIn.cpp:42: multiple definition of `pulseIn(unsigned short, unsigned char)'
firmware.o:/spark/compile_server/shared/workspace/3_compile-server2/core-firmware/build/function_pulseIn.cpp:42: first defined here

The error I get looks to me like Spark have implemented the pulseIn function? Except when I dont include timb’s function I get an error like pulseIn function not found.

@Rockvole, unfortunately I don’t believe @timb is active on this forum anymore. I will try and take a look at his code to see what the issue might be. How are you compile the code: IDE, CLI or locally? Can you post your code so I can get a better picture of how you are using @timb’s code?

Hi @peekay123,
Thanks for the response. I trimmed my code down to the minimum which causes the error :

#include "function_pulseIn.cpp"

void setup()
{
}

void loop() {
    pulseIn(D1, LOW);
}

I added #include <application.h>
to the top of timb’s code :

1 Like

@Rockvole, doing an include of a .cpp file does not cause the proper pre-processing that a .h file normally gets. If you paste the actual code at the top of your app before setup() or rename function_pulseIn.cpp to function_pulseIn.h and change the #include, it will compile just fine. :smile:

1 Like

Thanks for the help @peekay123 - that compiles fine now.

Strange no-one improved that obtuse error during 35 years of C++ compilers :smile:

2 Likes

the pulseIn code here works but if there is a time out, 10 seconds is long enough to disconnect the particle from the cloud, perhaps depending on what other delays you may have in your code. I have shortened by timeout counter (loopMax) to 1000000 and still get reasonable results.