/* * Copyright 2011 Ytai Ben-Tsvi. All rights reserved. * * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ARSHAN POURSOHI OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied. */ package ioio.lib.api; import ioio.lib.api.exception.ConnectionLostException; import java.io.Closeable; /** * An interface for pulse width and frequency measurements of digital signals. * <p> * PulseInput (commonly known as "input capture") is a versatile module which * enables extraction of various timing information from a digital signal. There * are two main use cases: pulse duration measurement and frequency measurement. * In pulse width measurement we measure the duration of a positive ("high") or * negative ("low") pulse, i.e. the elapsed time between a rise and a fall or * vice versa. This mode is useful, for example, for decoding a PWM signal or * measuring the delay of a sonar return signal. In frequency measurement we * measure the duration between a rising edge to the following rising edge. This * gives us a momentary reading of a signal's frequency or period. This is * commonly used, for example, in conjunction with an optical or magnetic sensor * for measuring a turning shaft's speed. * <p> * {@link ioio.lib.api.PulseInput} instances are obtained by calling * {@link ioio.lib.api.IOIO#openPulseInput(ioio.lib.api.DigitalInput.Spec, ioio.lib.api.PulseInput.ClockRate, ioio.lib.api.PulseInput.PulseMode, boolean)} * . When created, some important configuration decisions have to be made: the * precision (single or double), the clock rate and the mode of operation. Modes * are straightforward: {@link ioio.lib.api.PulseInput.PulseMode#POSITIVE} is used for measuring a * positive pulse, {@link ioio.lib.api.PulseInput.PulseMode#NEGATIVE} a negative pulse, and * {@link ioio.lib.api.PulseInput.PulseMode#FREQ} / {@link ioio.lib.api.PulseInput.PulseMode#FREQ_SCALE_4} / * {@link ioio.lib.api.PulseInput.PulseMode#FREQ_SCALE_16} are used for measuring frequency. The * difference between the three scaling modes is that without scaling, the * frequency is determined by measurement of a single * (rising-edge-to-rising-edge) period. In x4 scaling, 4 consecutive periods are * measured and the time is divided by 4, providing some smoothing as well as * better resolution. Similarly for x16 scaling. Note that scaling affects the * range of signals to be measured, as discussed below. * <p> * The choice of single vs. double-precision is important to understand: IOIO * internally uses either 16-bit counters or 32-bit counters for the timing. 16- * counters force us to either limit the maximum duration (and the minimum * frequency) or compromise accuracy as compared to 32-bit counters. However, if * you need many concurrent pulse measurements in your application, you may have * no choice but to use single-precision. * <p> * The clock rate selection is important (and even critical when working in * single-precision) and requires the user to make some assumptions about the * nature of the measured signal. The higher the clock rate, the more precise * the measurement, but the longest pulse that can be measured decreases (or * lowest frequency that can be measured increases). Using the scaling option * when operating in frequency mode also affects these sizes. combinations. It * is always recommended to choose the most precise mode, which exceeds the * maximum expected pulse width (or inverse frequency). If a pulse is received * whom duration exceeds the longest allowed pulse, it will be "folded" into the * valid range and product garbage readings. * <p> * The following table (sorted by longest pulse) summarizes all possible clock / * mode combinations. The table applies for <b>single-precision</b> operation. * For double-precision, simply multiply the longest pulse by 65536 and divide * the lowest frequency by the same amount. Interestingly, the number written in * [ms] units in the longest pulse column, roughly corresponds to the same * number in minutes when working with double precsion, since 1[min] = * 60000[ms]. * <table border="1"> * <tr> * <th>Clock</th> * <th>Scaling</th> * <th>Resolution</th> * <th>Longest pulse</th> * <th>Lowest frequency</th> * </tr> * <tr> * <td>62.5KHz</td> * <td>1</td> * <td>16us</td> * <td>1.048s</td> * <td>0.95Hz</td> * </tr> * <tr> * <td>250KHz</td> * <td>1</td> * <td>4us</td> * <td>262.1ms</td> * <td>3.81Hz</td> * </tr> * <tr> * <td>62.5KHz</td> * <td>4</td> * <td>4us</td> * <td>262.1ms</td> * <td>3.81Hz</td> * </tr> * <tr> * <td>250KHz</td> * <td>4</td> * <td>1us</td> * <td>65.54ms</td> * <td>15.26Hz</td> * </tr> * <tr> * <td>62.5KHz</td> * <td>16</td> * <td>1us</td> * <td>65.54ms</td> * <td>15.26Hz</td> * </tr> * <tr> * <td>2MHz</td> * <td>1</td> * <td>500ns</td> * <td>32.77ms</td> * <td>30.52Hz</td> * </tr> * <tr> * <td>250KHz</td> * <td>16</td> * <td>250us</td> * <td>16.38ms</td> * <td>61.0Hz</td> * </tr> * <tr> * <td>2MHz</td> * <td>4</td> * <td>125ns</td> * <td>8.192ms</td> * <td>122.1Hz</td> * </tr> * <tr> * <td>16MHz</td> * <td>1</td> * <td>62.5ns</td> * <td>4.096ms</td> * <td>244.1Hz</td> * </tr> * <tr> * <td>2MHz</td> * <td>16</td> * <td>31.25ns</td> * <td>2.048ms</td> * <td>488.3Hz</td> * </tr> * <tr> * <td>16MHz</td> * <td>4</td> * <td>15.6ns</td> * <td>1.024ms</td> * <td>976.6Hz</td> * </tr> * <tr> * <td>16MHz</td> * <td>16</td> * <td>3.9ns</td> * <td>256us</td> * <td>3.906KHz</td> * </tr> * </table> * * <p> * In some applications it is desirable to measure every incoming pulse rather * than repetitively query the result of the last measurement. For that purpose * the {@link #waitPulseGetDuration()} method exists: every incoming pulse width * is pushed into a small internal queue from which it can be read. The client * waits for data to be available, then reads it and data that comes in in the * meanwhile is stored. The queue has limited size, so it is important to read * quickly if no pulses are to be lost. Note that once a pulse is detected, the * next one must have its leading edge at least 5ms after the leading edge of * the current one, or else it will be skipped. This throttling has been * introduced on purpose, in order to prevent saturation the communication * channel when the input signal is very high frequency. Effectively, this means * that the maximum sample rate is 200Hz. This rate has been chosen as it * enables measure R/C servo signals without missing pulses. * * <p> * Typical usage (servo signal pulse width measurement): * * <pre> * {@code * // Open pulse input at 16MHz, double-precision * PulseInput in = ioio.openPulseInput(3, PulseMode.POSITIVE); * ... * float widthSec = in.getDuration(); * OR: * float widthSec = in.waitPulseGetDuration(); * ... * in.close(); // pin 3 can now be used for something else. * } * </pre> * * <p> * Typical usage (frequency measurement): * * <pre> * {@code * // Signal is known to be slightly over 150Hz. Single precision can be used. * PulseInput in = ioio.openPulseInput(3, * ClockRate.RATE_2MHz, * PulseMode.FREQ_SCALE_4, * false); * ... * float freqHz = in.getFrequency(); * ... * in.close(); // pin 3 can now be used for something else. * } * </pre> */ public interface PulseInput extends Closeable { /** An enumeration for describing the module's operating mode. */ public enum PulseMode { /** Positive pulse measurement (rising-edge-to-falling-edge). */ POSITIVE(1), /** Negative pulse measurement (falling-edge-to-rising-edge). */ NEGATIVE(1), /** Frequency measurement (rising-edge-to-rising-edge). */ FREQ(1), /** Frequency measurement (rising-edge-to-rising-edge) with 4x scaling. */ FREQ_SCALE_4(4), /** Frequency measurement (rising-edge-to-rising-edge) with 16x scaling. */ FREQ_SCALE_16(16); /** The scaling factor as an integer. */ public final int scaling; private PulseMode(int s) { scaling = s; } } /** Suported clock rate enum. */ public enum ClockRate { /** 16MHz */ RATE_16MHz(16000000), /** 2MHz */ RATE_2MHz(2000000), /** 250KHz */ RATE_250KHz(250000), /** 62.5KHz */ RATE_62KHz(62500); /** The value in Hertz units. */ public final int hertz; private ClockRate(int h) { hertz = h; } } /** * Gets the pulse duration in case of pulse measurement mode, or the period * in case of frequency mode. When scaling is used, this is compensated for * here, so the duration of a single cycle will be returned. * <p> * The first call to this method may block shortly until the first data * update arrives. The client may interrupt the calling thread. * * @return The duration, in seconds. * @throws InterruptedException * The calling thread has been interrupted. * @throws ioio.lib.api.exception.ConnectionLostException * The connection with the IOIO has been lost. */ public float getDuration() throws InterruptedException, ConnectionLostException; /** * Reads a single measurement from the queue. If the queue is empty, will * block until more data arrives. The calling thread may be interrupted in * order to abort the call. See interface documentation for further * explanation regarding the read queue. * <p> * This method may not be used if the interface has was opened in frequency * mode. * * @return The duration, in seconds. * @throws InterruptedException * The calling thread has been interrupted. * @throws ioio.lib.api.exception.ConnectionLostException * The connection with the IOIO has been lost. */ public float waitPulseGetDuration() throws InterruptedException, ConnectionLostException; /** * Gets the momentary frequency of the measured signal. When scaling is * used, this is compensated for here, so the true frequency of the signal * will be returned. * <p> * The first call to this method may block shortly until the first data * update arrives. The client may interrupt the calling thread. * * @return The frequency, in Hz. * @throws InterruptedException * The calling thread has been interrupted. * @throws ioio.lib.api.exception.ConnectionLostException * The connection with the IOIO has been lost. */ public float getFrequency() throws InterruptedException, ConnectionLostException; }