diff --git a/targets/arduino/wiring.c b/targets/arduino/wiring.c index 3b93286d7..5a6da0e63 100755 --- a/targets/arduino/wiring.c +++ b/targets/arduino/wiring.c @@ -381,21 +381,38 @@ unsigned long pulseIn(int pin, int state) } */ +/* Measures the length (in microseconds) of a pulse on the pin; state is HIGH + * or LOW, the type of pulse to measure. Works on pulses from 10 microseconds + * to 3 minutes in length, but must be called at least N microseconds before + * the start of the pulse. */ unsigned long pulseIn(int pin, int state) { - unsigned long width = 0; + // cache the port and bit of the pin in order to speed up the + // pulse width measuring loop and achieve finer resolution. calling + // digitalWrite() instead yields much coarser resolution. int r = port_to_input[digitalPinToPort(pin)]; int bit = digitalPinToBit(pin); int mask = 1 << bit; + unsigned long width = 0; + // compute the desired bit pattern for the port reading (e.g. set or + // clear the bit corresponding to the pin being read). the !!state + // ensures that the function treats any non-zero value of state as HIGH. state = (!!state) << bit; + // wait for the pulse to start while ((_SFR_IO8(r) & mask) != state) ; - + + // wait for the pulse to stop while ((_SFR_IO8(r) & mask) == state) width++; - + + // convert the reading to microseconds. the slower the CPU speed, the + // proportionally fewer iterations of the loop will occur (e.g. a + // 4 MHz clock will yield a width that is one-fourth of that read with + // a 16 MHz clock). each loop was empirically determined to take + // approximately 23/20 of a microsecond with a 16 MHz clock. return width * (16000000UL / F_CPU) * 20 / 23; }