Open MCUdude opened 7 years ago
I never used pulseIn() before, so when you first posted this and tagged me I didn't pay any attention to it. Now that I've taken a closer look, I see it is simply a software pin-change timer. Here's a back-of-the-napkin implementation in 7 asm instructions:
clr count_lo
clr count_hi
wait:
sbis PORTB, pin
brne wait
pulseIn: ; 5 cycles/loop
adiw count_lo, 1
sbic PORTB, pin
brne pulseIn
It counts multiples of 5 cycles, and waits for up to 65536 * 5 = 327680 cycles.
I'd be interested in a function that acts just like the original one. It should start by waiting for the previous event to stop, then wait for the next even to start, and then count the number of loops/cycles. The timeout should also be "long enough", at least a couple of minutes. Is this doable you think?
I can see the benefit of having the function return uS to maintain API compatibility with the Arduino core. However what is the necessity of supporting minute-long pulses? From a quick search I did, it seems the function is commonly used to measure echo times from ultrasonic distance sensors, which have pulse times of ms, not minutes.
However what is the necessity of supporting minute-long pulses?
I want to keep MicroCore as close as possible to the original Arduino functions as possible. I've seen usecases where an Arduino has to wait minutes for a short pulse to arrive. I'd prefer a pulseIn function that was able to measure minute long pulses and timeouts.
Bringing up a "dead" thread...
Issue #78 is caused by the pulseIn() function being very inaccurate. I tried to tune it, but I can't get something that's fairly accurate for all clocks. I used a signal generator connected to PB3 to "simulate" the internal oscillator. You can easily "trick" the IDE to use a 9.6 MHz external clock by burning bootloader for 16 MHz external clock, but compiling and uploading for the 9.6 MHz internal.
As for @nerdralph's comment, a new implementation doesn't have to be 100% compatible with the "official" one. What's important is that the maximum timeout is fairly long (a few seconds or more). It's also important that it returns microseconds (if possible).
It's basically this code that should be rewritten:
// Wait for the pulse to stop
while (!!(PINB & _BV(pin)) == state)
{
if(numloops++ == maxloops) {return 0;}
width++;
asm("nop \n");
asm("nop \n");
asm("nop \n");
}
// Convert the reading to microseconds.
return clockCyclesToMicroseconds(width << 4); // Same as multiplying by 16
So I've been working with optimizing the functions used in MicroCore. One of the goals was to redo the pulseIn function, as it relies on micros to work. I think I've created a good replacement for the pulseIn function, but LTO has to be enabled in order for the timing to be right. LTO also reduces the size of the function dramatically.
This core has the option to disable LTO (which is enabled by default). I'd like to take the output from the compiler (with LTO enabled) and make an inline assembly function out of it, so that the timing can be guarateed no matter what compiler flag is used. We're also working multiple clock frequencies, so this also have to be taken into account.
I have very little (no) experience with assembly, so any help would be appreciated. @nerdralph is it possible to use the output below to re-create the pulseIn function in assembly that isn't hard coded to a particular F_CPU?