-
Notifications
You must be signed in to change notification settings - Fork 355
Digital IO
Digital (logic-level) input and output is the most basic building blocks of digital hardware systems. It is also commonly referred to as GPIO.
A logic-level signal has two states: LOW and HIGH, respectively 0 and 1 or false
and true
.
The actual representations of these states in terms of voltage and current vary depending on the circuits that generate them, and in turn, influence the circuits that consume them. Understanding the different possible representations (or at least the common ones) is important. The explanation below attempts to give some background to those of you who have not dealt with digital circuitry before. Bear with us - it is simple and fun! The following explanations have been slightly simplified to the level where you can do something practical with this knowledge possibly overlooking some subtleties and corner cases.
Each of the IOIO pins can function as a digital input or a digital output, depending on the client's choice in software, as demonstrated below. The on-board stat LED is also considered a pin, and can be controlled as a digital output.
When used as output, in their default operation modes, the IOIO pins represent the logical levels as following:
- A logical LOW will cause the pin to force a voltage of 0V ("ground") on whatever is connected to it.
- A logical HIGH will cause the pin to force a voltage of 3.3V on whatever is connected to it.
Many digital electronics will accept this representation, correctly reading the LOWs as LOWs and the HIGHs as HIGHs. As an example, an LED connected between a digital output pin (LED anode), through a 220ohm resistor to ground will light when the pin state is high.
For some components an "open-drain" operation is required. In "open drain" mode the pins will represent the logical levels slightly differently:
- A logical LOW will cause the pin to force a voltage of 0V ("ground") on whatever is connected to it, just like in normal mode.
- A logical HIGH will cause the pin to not force anything on whatever is connected to it, as if it were physically disconnected.
Open-drain can be useful in several situations. The most common is that some electronic components require a 0V / 5V signal rather than 0V / 3.3V. The way to achieve that is very simple. First, for this purpose you need to select a 5V tolerant pin. If you attempt to implement this method on a non-5V-tolerant pin, you risk permanently damaging your pin or the entire board. 5V-tolerant pins are designated on the bottom of the board with circles around the pin. You can also consult the table at the bottom of this page.
The following circuit, comprised of only a single resistor ("pull up resistor"), will cause the voltage to toggle between 0V / 5V when used as open-drain.
Another, less common situation for using open-drain, is when connecting several components' output pins to a single point, called a "bus". Since we never want any two pins to try to force different voltage levels on a single net, thus damaging each other, we use them all in open-drain mode, and use a single pull-up resistor to either 3.3V or 5V. When one (or more) of the pins on the bus "pulls low" by forcing 0V, the bus voltage will be 0V and all pins listening on that bus can detect it. When all of the pins are HIGH, i.e. virtually disconnected, the bus voltage gets "pulled up" by the resistor to whatever voltage it is connected to.
When the IOIO forces a certain voltage on one of its pins, it is what's connected to this pin (the "load") that determines how much current the IOIO needs to source (provide) or sink (consume). Different loads have different relationships between the voltage they get and the current they force. For example, a resistor has a linear relationship between the voltage and current depending on its resistance. The smaller the resistance, the higher the current per given voltage. In other words, small resistors are "heavy loads" (when trying to force voltage).
There is a limitation for the amount of current the IOIO pins can source or sink. The recommended maximum current is about 20mA. Trying to force more current than 20mA risks the pin and/or the entire board. To give some intuition, this means that any resistor smaller than 150ohm on a 3.3V would exceed the recommendation.
In real life, one has to understand the load they are connecting to determine how much current it will consume. As a rule of thumb, interfacing with logic-level components (microcontrollers, sensors, servo controls, etc.) is generally not a problem. Other load types, such as motors and high-power LEDs consume a lot of current (and typically require higher voltage levels too). Such loads will require a driver circuit, with proper specifications. We won't cover specific loads and drivers here, just know they exist and you should look for them. Typically, there are standard driver modules for different loads with a few parameters you should match against your load.
When used as inputs, the IOIO pins never force any voltage on whatever is connected to them, but rather sense the voltage level and report their state accordingly.
- When 0V is detected, the pin reads LOW.
- When 3.3V is detected, the pin read HIGH. 5V-tolerant pins, will tolerate up to 5V, and also treat it as HIGH.
- When nothing forces a voltage on the pin, the pins can be told to either:
- Not influence the pin in any way. The read value will be rather chaotic and can't be relied on. This is called "floating" mode, and it is the default mode for input pins.
- "Gently" pull the pin to 3.3V, so that the value will be read HIGH unless someone forces 0V on it. This is called "pull-up" mode.
- "Gently" pull the pin to 0V so that the value will be read LOW unless someone forces 3.3V (or 5V in designated pins) on it. This is called "pull-down" mode.
Pull up and pull down are useful for a whole bunch of stuff. A very frequent use-case is for connecting a switch. Connect the switch as in the circuit below. No additional components needed! Use the pin in pull-up mode. When the switch is open, the pin is disconnected, and the pull-up mode will cause the pin to sense HIGH. When the switch is closed, it will force 0V on the pin, and it will read LOW. Likewise, one could work in pull-down mode and connect the switch between the pin and 3.3V.
Using the IOIO pins as digital outputs is done via the DigitalOutput
interface. An instance of this interface corresponds to a physical pin on the board, configured to work in digital output mode. DigitalOutput
instances are obtained by calling one of the overloads of IOIO.openDigitalOutput()
. The simplest form is:
DigitalOutput out = ioio.openDigitalOutput(pinNum);
This opens pin number pinNum
for digital output, in the normal (i.e. not open-drain) mode. It is required that at the time of call, this pin is not being used for anything else. In order to open a pin for digital output in open-drain mode (see discussion above), the following form can be used:
DigitalOutput out = ioio.openDigitalOutput(pinNum, DigitalOutput.Spec.Mode.OPEN_DRAIN, startValue);
where startValue
will determine the initial value (LOW or HIGH) of the pin as soon as it is being set for output. This can be used to prevent momentary undesired pin states. Consult the Javadocs of the IOIO
interface for other available variants of this method.
The on-board 'stat' LED is used as a normal digital output pin. Open it using:
DigitalOutput led = ioio.openDigitalOutput(IOIO.LED_PIN);
The LED is wired so that LOW turns it on and HIGH turns it off.
Once an instance of DigitalOutput
is obtained, the pin can be set to LOW by calling:
out.write(false);
or to HIGH by calling:
out.write(true);
When you are done using the pin, call:
out.close();
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. The pin becomes useless after this call - it is illegal to do anything with it.
Using the IOIO pins as digital inputs is done via the DigitalInput
interface. An instance of this interface corresponds to a physical pin on the board, configured to work in digital input mode. DigitalInput
instances are obtained by calling one of the overloads of IOIO.openDigitalInput()
. The simplest form is:
DigitalInput in = ioio.openDigitalInput(pinNum);
This opens pin number pinNum
for digital input in floating mode. It is required that at the time of call, this pin is not being used for anything else. In order to open a pin for digital input in pull-up or pull-down mode (see discussion above), the following form can be used:
DigitalInput in = ioio.openDigitalInput(pinNum, DigitalInput.Spec.Mode.PULL_UP);
or PULL_DOWN
, respectively. Consult the Javadocs of the IOIO
interface for other available variants of this method.
Once an instance of DigitalInput
is obtained, the pin value can be read by:
boolean value = in.read();
returning false
for LOW and true
for HIGH. This method might block for a few milliseconds if called immediately after the pin is open.
Another interesting usage of DigitalInput
is for blocking until a certain value is obtained. Other than periodically polling for the value, and possibly missing narrow pulses, it is preferred to use:
in.waitForValue(true);
in order to wait for the value to become HIGH. Likewise with false
for LOW.
This is very useful, for example, for waiting until a button is pressed by the user.
When you are done using the pin, call:
in.close();
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. The pin becomes useless after this call - it is illegal to do anything with it.