This discussion assumes you understand the principles of digital input. If not, read about them here.
One very common type of digital signals is one in which the information lies in the duration between changes in the signal level. Perhaps the simplest case to imagine is a magnetic or optical sensor sensing a turning shaft: whenever the shaft gets to a certain rotation angle, the sensor's output will change from LOW to HIGH, and at a different angle it will change back to HIGH. The resulting waveform whenever the shaft is constantly turning is a set of pulses. The faster the rotation, the narrower and closer together the pulses. By measuring the duration between the beginning of one pulse to the beginning of the next, we can know the amount of time it took the shaft to complete exactly one turn or, in other words, if we measure the frequency of the pulses, we can know the speed of turning.
Another common case is PWM signals (read more about them in the section about PWM Output). Those signals use the width of a pulse to represent a value within a certain range. For example, PWM signals used for hobby servos are a series of pulses, each ranging between about 1-2 milliseconds. A pulse of 1 millisecond usually means "servo arm should move all the way to one end", a pulse of 2 milliseconds means "all the way to the other end" and a pulse of 1.5 milliseconds means "center". If one wishes to decode such a signal, so that to receive input from a system which generates such servo signals, they'll have to measure the width of a pulse, or the duration between the beginning of the pulse to the end of it.
The signal in the image below is a typical PWM signal, which has a period (the duration between a rising edge to the next rising edge) and a pulse width (the duration between a rising edge and the next falling edge). Often it is convenient to talk about the frequency of the signal, which is simply the inverse of the period, its physical meaning being the number of pulses per second.
Measuring the frequency and pulse width of digital signals with IOIO is done using the Pulse Input module (sometimes referred to as "input capture"). This module has several parameters affecting its operation. The easiest was to understand the possibilities is to understand a little bit about the inner-workings of the pulse input module. Each module has a timer, which constantly counts time at a certain clock rate. The timer can be told to record (or "capture") its current value when an input pin changes its state from LOW to HIGH ("rising edge"), from HIGH to LOW ("falling edge") or on any edge. By subtracting two timer captures corresponding to two edges of interest, the amount of time that elapsed between them can be obtained. The timer has a limit to how high it can count. Whenever it reaches that limit, it rolls over, i.e. starts counting from 0 again. It is impossible to know exactly when the timer rolled over, and for that reason, if we try to measure durations that are too long (or more precisely, longer than the timer limit times its clock period), we'll get false results! For that reason, the proper choice of clock rate, timer precision and frequency scaling discussed below is critical.
The first choice affecting the module's operation is the precision. The IOIO has 3 single-precision and 3 double-precision pulse input modules (it is possible to change this division by trading-off one double-precision for two single-precision modules). Single-precision modules use a 16-bit timer and can thus measure durations up to 65535 clock cycles. Double-precision modules use a 32-bit timer and can thus measure durations up to 4.3 billion clock cycles.
Each module's clock source can be chosen to be either 16MHz, 2MHz, 256KHz or 62.5KHz. The lower the clock rate, the longer it takes for the timer to roll-over, thus the longer the longest pulse that can be measured. For example, a single-precision module, using the 256KHz clock, can measure durations up to 65535 / 256000 = 0.255[sec]. On the other hand, the slower the clock, the lower the measurement resolution, for example, when using the 62.5KHz clock, our measurements are restricted to multiples of 1 / 62500 = 16[us], so a duration of 40[us] will actually measure 48[us], which may or may not be acceptable for our application. Obviously, double-precision modules suffer much less from this trade-off, so unless one needs more than 4 concurrent channels, using double-precision is the simplest choice.
The parameter which determines what events constitute the beginning and the end of the measured period is called mode. The mode can be either positive (i.e. HIGH) pulse, negative (i.e. LOW) pulse, or frequency (i.e rising edge to rising edge). When measuring frequency, it is possible to either measure the duration of a single period, the duration of 4 consecutive periods, or the duration of 16 consecutive periods. The two latter options, called frequency scaling allow us to shift the frequency range that can be measured and/or increase the measurement resolution. It also helps smoothing up noise as result of averaging several measurements. Note, however, that the timer limit becomes stricter as we scale, i.e. the lowest frequency we can measure increases by the scaling factor. Also note that in terms of API, when using frequency scaling, the methods are still going to return the duration of a single period (or the actual, unscaled frequency of the signal). That is, the division of the measured period by the number scaling factor is done under the hood.
To summarize the possible modes, the table below gives the specifications for each possible choice of parameters. This table is for single-precision operation. To get the figures for double-precision, simply multiply the longest pulse duration by 65536 and likewise divide the lowest frequency by that amount. For convenience, the table is sorted by the longest pulse. For optimal resolution, always select the bottommost row that matches your expected maximum pulse duration.
|Clock||Scaling||Resolution||Longest pulse||Lowest frequency|
For ultimate reliability, it is generally not recommended to measure durations shorter than 20[us]. Shorter periods risk "missing" an event, and instead reporting the duration to the next event, thus providing false results. If the user is able to detect such anomalies, it is possible to measure shorter durations (5-10[us]). Frequency scaling can help greatly in measuring high frequencies, as the effective duration gets longer. For example, measuring the period of a 500KHz signal violates the requirements (it has a 2[us] period), but with using 16x scaling, the measured duration will be 32[us] which is within specification.
As the measured signals can have high rates, capturing each and every event is wasteful and normally unnecessary. For that reason, the capture rate is internally limited to 200 captures per second, per channels. In other words, measuring a signal with a frequency greater than 200Hz will skip some of the pulses, but will produce accurate measurements 200 times a second.
Using the IOIO pins as pulse inputs is done via the
PulseInput interface. An instance of this interface corresponds to a physical pin on the board, configured to work in pulse input mode, as well as to one pulse input module out of the 6 available ones (3 single-precision, 3 double-precision or as configured).
PulseInput instances are obtained by calling one of the overloads of
IOIO.openPulseInput(). The simplest form is:
PulseInput pulse = ioio.openPulseInput(pinNum, mode);
where mode can be, e.g.
PulseMode.FREQ. This opens pin number
pinNum as floating pulse input, at double-precision with a 16MHz clock, which is a good default for many applications. It is required that at the time of call, this pin is not being used for anything else, and that there is at least one free pulse input module with the designated precision. In order to open the pin with pull-up or pull-down, to use single-precision, or a different clock rate, use the longer version of the method.
Once an instance of
PulseInput is obtained, the duration of the latest pulse can be obtained by calling:
float pulseSeconds = pulse.getDuration();
When used to measure frequency, the following form is preferred:
float freqHz = pulse.getFrequency();
Both the above methods may block for a few milliseconds upon the first call, but will otherwise return immediately.
Sometimes, it is necessary to measure pulse widths without losing a single pulse. This is possible if the frequency of the incoming signal is less than 200Hz, and achieved by calling:
float pulseSeconds = pulse.waitPulseGetDuration();
This form will block until a pulse is measured and will then return its duration. A small FIFO is used internally to assure no pulses are lost, but the caller must read frequently or the FIFO will overflow and pulses will be discarded.
When you are done using the pin, call:
in order to return the pin to a "floating" state and possibly be able to re-open it in the same or in a different mode, as well as free the pulse input module. The
PulseInput instance becomes useless after this call - it is illegal to do anything with it.