GPIO (General Purpose Input/Ouput) Outputs
As mentioned earlier, a GPIO pin is a pin that can be configured through software to be either a digital input or a digital output. A microcontroller will have a number of these GPIO pins, organized into one or more “ports," as will be discussed below.
First a note on Voltages and Ohm’s Law examples
A microcontroller requires at least one voltage source, commonly called Vcc. Some devices require more than one voltage source, often a lower voltage for their high-speed core circuitry and a higher voltage for their I/O system. In such a case, any I/O current and voltage calculations would be based on the I/O system voltage. When a Vcc voltage is required in an example calculation I will use 5V unless explicitly stated otherwise. This is simply to be consistent. Quite a few embedded systems use lower Vcc (or lower I/O Vcc), and the numbers that do so will only grow in the future. So it goes without saying that you should use your actual Vcc value (your I/O system Vcc, if your device uses more than one supply voltage) in any calculation. Be aware also that it is not uncommon to have multiple power voltage values supplying different chips on a single board, so always make sure you know which value is correct for the particular calculation.
What is an output?
GPIO outputs let you translate logical values within your program to voltage values on output pins, and it is these voltage outputs that help your μC exert control over the system into which it is embedded.
A GPIO output is a digital (2 state) output that your program can set to either ‘0’ (a low voltage, near GND) or ‘1’ (a high voltage, near Vcc). So to a good approximation, when you write a ‘0’ to an output pin, that pin will be at 0 Volts, and when you write a ‘1’ to an ouput pin, that pin will be at Vcc Volts. If it helps, you can think of an ouput (any digital ouput, not just GPIO) as a switch that can be switched to either Vcc or to GND.
What does it mean to "write" an output?
Writing an output means that your program executes an instruction designed to place the logical value of a data bit, located inside the CPU, onto an output pin as a high or low voltage.
The Concept of Ports
A port can be thought of as a data channel that comes into or goes out of the uC. There can be parallel ports (all data bits present on their own lines) and serial ports (data bits passed serially along one or more lines). A number of types of serial ports will be discussed in later chapters of this tutorial, but for now we'll just talk about GPIO ports. A GPIO port is a set of GPIO pins which are organized into a single N-bit (8/16/32 bit) word, and are thus written out and read in as operations on a single word. A uC can have one or more GPIO ports, and given port may be a full N-bits wide, or a given port may be fewer than N-bits. A shortened port (fewer than N-bits wide) usually exists because the chip designers simply ran out of pins for the extra GPIO bits. For example, a 28-pin, 8-bit microcontroller may dedicate 5 pins to power and GND, and all other pins are available as GPIO. That means 23 GPIO pins, which could be organized as two 8-bit ports and one 7-bit port.
A shortened port will still appear as a full N-bit value to the CPU, it is just that the bit positions that are not brought out to pins are unused, since they can never transmit or receive valid data. and must be ignored. Depending on the CPU design these unused bits may always read as 0s or they may read as undetermined values. The datasheet will give the details, and in any case, the unused bits should be masked out before using the port data.
You will also see this phenomenon of missing or unused bits in configuration registers. It is very common for a configuration register to have unused bits - sometimes a configuration register will only use a single bit in the register! Again, these unused bits may read as 0s or they may be indeterminate. When writing to a configuration register that has unused bits, the datasheet will tell you what values to write to those bits. Usually you are required to write 0s to the unused bits. One reason for this is that unused bit positions may become used in a future version of the device, and writing a 1 to a configuration bit is more likely to enable some feature - some feature you didn't intend, since the bit and the feature didn't even exist when you wrote the software. Just be safe and write the unused bits of any configuration register according to the datasheet. To sum up, mask out unused bits when reading ports or configuration registers, and set unused bits as per the datasheet when writing to ports or configuration registers.
Each GPIO port will have a number of data and configuration registers associated with it. These registers will access data read as inputs, hold data written as outputs, and will allow configuration settings (often for each individual GPIO pin) such as (depending on the device) data direction (input or output), built-in pullup/pulldowns, input hysteresis, output drive strength, and so on. All GPIO register information will be found in the datasheet.
Configuring a GPIO pin as an output
To configure GPIO pins as inputs or outputs there is normally a register for each port, called a Data Direction Register (DDR) or other similar term. Writing a ‘1’ to a given bit in the DDR will configure the corresponding GPIO port pin as an output, while writing a ‘0’ to a given DDR bit will configure the GPIO port pin as an input. As mentioned, each pin in a GPIO port can be independently configured as an input or an output. It is quite possible and very common to have some pins configured as inputs and other pins configured as outputs in the same port. It is not necessary to configure GPIO pins one at a time; in fact it is more common to configure an entire port at once, by writing a value to the DDR with 1s in every bit position which is to be configured as an output. A crucial point to remember is that on RESET all GPIO pins will be configured as inputs. That is to say, on RESET all DDR bits will be set to 0. By defaulting to inputs there is no possibility that the pins will conflict with any logic outputs or other voltage sources connected to them.
Since DDR bits are set to 0 on RESET, in most cases the GPIO configuration will consist of either ignoring the DDR register completely (thus leaving all GPIO pins in that port configured as inputs), or in writing a value that sets selected pins as outputs. In most cases it will not be necessary to ever write 0 bits into a DDR. The exception to this is when a pin is used alternately as both an output and as an input. In such a case, the programmer is responsible for configuring the pin to either an output or an input as required at any given stage of the program execution.
Output drive levels
Besides the output voltage itself, probably the most important specification for an output pin is the amount of current it can source (current out of the pin when it is set to ‘1’) and sink (current into the pin when it is set to ‘0’). If the output pin is driving the input of another digital logic chip the current it needs to source or sink will usually be very small (microamperes), but when driving other loads such as LEDs, bipolar transistors or relays, the output will need to source or sink non-negligible current (in the milliamperes range). The output current ratings for different uCs will typically be between 2 and 20 mA. It is important to look in the device datasheet to find the exact values for your device.
It is worth noting that in the past, it was often the case that digital ouputs could source much less current when high then they could sink when low. This lack of symmetry was simply a function of the logic design and technology of the time. The practical result of this lack of symmetry is that the circuits to which these output pins were connected were designed so that an output ‘0’ was the high-current state, that is, the state which caused higher current to flow through the load driven by the output. Taking the example of driving an LED with such an output, the LED would have been connected to the output so that when the output was high no current flowed through the LED, and when the output was low current did flow through the LED (since the output could handle higher current when low than when high). Such an LED connection requires the other end of the LED to be wired, not to GND as might be the intuitive choice, but to Vcc. Then, when the output is high, no current flows through the LED between Vcc and the high output voltage, but when the output is low, current flows through the LED between Vcc and the low output voltage. The term “active-low” is used for such an output, since the output is active (the LED turns on) when it is driven low. The opposite term is “active-high,” with the corresponding meaning - the device is active when it is driven high.
You will see such active-low output configurations in many system designs, so get used to them. Modern output circuitry does not have the output current asymmetry that older output circuitry had, so in general modern outputs are equally capable of driving active-high loads connected to GND, as of driving active-low loads connected to Vcc. This means that in many cases you can choose whether to design your output circuits to be active-low or active-high as you desire, or as other circuit factors dictate. In any case, you will need to be just as comfortable in thinking of turning on an output by writing a 0 as you are in thinking the more intuitive way of turning on an output by writing a 1. Later you will find that the same active-high/active-low issue also comes up when using digital inputs; e.g. a button push may be indicated by a 1 on an input pin or it may be indicated by a 0, depending on the how the switch is wired to the input.
While the notions of active-high and active-low are useful and apply to many outputs (and inputs), there are also situations where the concept does not apply. For example, an output may be connected to a bi-color LED that lights red if driven high and green if driven low (and yellow if driven with a 100Hz square wave). Or it may be connected to an actuator that moves a lever left if high and right if low. In cases such as these there is not one obvious active state and one inactive state, but rather there are two states that are equally "active," if it makes sense to say that.
One more point to make about active-high and active-low is that the concept does not necessarily refer to the output state which causes more current to pass through the driven load. A modern digital input sources or sinks very low current whether driven high or low, but such an input may very well correspond to a signal that is considered active-high or active-low. The RESET input to a micrcontroller or other IC is one example (and again, for technical-historical reasons, most reset inputs are active-low).
Driving an LED
We’ve been talking about driving an LED with a GPIO output, but we haven’t actually said how to connect an LED to an output. To connect an LED to an output, you will need both the LED and a current-limiting resistor (unless your LED has one already built in). The resistor is necessary because otherwise the LED will draw too much current from the output, placing a strain on both the LED and the output, and possibly damaging one or both. The possible connections are shown below. Note that the relative positions of the LED and resistor do not matter. In each of the two diagrams below, the positions of the resistor and LED could be swapped with no change in circuit operation, since the two components are connected in series. But in any case, the LEDs must be connected in the directions shown or no current will ever flow through them.
In the drawing below, diagram A shows an active-high connection (current flows through the LED when the output is high), and diagram B shows an active-low connection (current flows through the LED when the output is low).
Connecting an LED to an Output Pin
The value of the current-limiting resistor is calculated based on Vcc, the LED forward voltage (around 2V for red LEDs, 3V for yellow and green, 3.5V for blue, but check the LED datasheet to be sure), and the desired LED current. Older, less efficient LEDs required 10-20 mA for good brightness, but more modern LEDs require much less, just a few mA. The formula for the resistance is R = (Vout – Vled)/Iled. As an example, assume Vout = 4.5V, Vled = 2V, Iled = 5mA. This gives R = (4.5-2)/.005 or 500 Ohms. The nearest standard values are 470 or 560 Ohms, either of which is plenty close enough.
As shown in the diagram, you will connect the LED and the resistor in series, with one leg of this series connection going to your output pin, and the other leg going to either Vcc (if you will drive the LED active-low) or GND (if you will drive the LED active-high). Since the LED is a diode, it has polarity, and must be connected forward biased so current can flow. This means connect the anode to the more positive portion of the circuit and the cathode to the more negative. It’s easy to visualize when looking at the LED symbol – just have the LED arrow pointing away from Vcc and/or towards GND (and remembering that the output is a switch that can be switched to either Vcc or GND).
Driving higher-current and/or higher-voltage loads
There will come a time, probably sooner rather than later, when you need to drive an output load that requires more current than your GPIO output can supply. Not every electrical device can be driven with 10 or 20 mA! There will also be cases where you need to switch a voltage greater than your Vcc (if you just connected that load to your output pin the higher voltage would certainly blow the uC). In that case you need to boost your output current and/or voltage drive capability. Two common means of doing this are to use transistors and to use relays.
Using a bipolar (BJT) transistor
Bipolar transistors (a.k.a. BJTs, bipolar junction transistors) are current-amplifying devices, which have a current gain relating their “input” base current and their “output” collector (or emitter) current. To use a BJT as a switch requires that you drive the base with at least enough current to turn the transistor fully on to pass the current required by the load. Within reasonable limits, driving the base with more current will not cause any problems. BJT current gain (base to collector) can be found in the transistor datasheet; it will typically be 10-200 for a regular BJT, depending on the device and on the operating conditions.
Suppose we need to drive a 150 mA load (let’s say it’s a 12V lamp). A useful general purpose transistor for such a current level is the 2N4401. It is rated for 600 mA collector current, and 40V collector-emitter voltage, so it is a good choice. The minimum current gain according to the datasheet is >100 @ 150 mA, so our base current needs to be at least 150/100 or 1.5 mA. As a safety factor let us double that to 3 mA. Our base-current resistor is then calculated from R=(Vout-Vf)/0.003, where we will specify Vout=4.5V and Vf=0.9V. This gives us an Rb value of 1200 Ohms for our base resistor.
Up to the maximum ratings of the transistor, the limit on how much collector current you can switch with a BJT hinges on how much base current you can drive with your digital output. If you need to switch more collector current than your base drive can manage, you can use one transistor to drive another transistor, effectively multiplying the current gain of the 2nd transistor by the current gain of the 1st. Look up “Darlington transistor” to see how this can be done. You can buy such dual transistors built into a single package.
In the drawing below, diagram A shows an NPN transistor driving a load RL, and diagram B shows a PNP transistor driving a load RL. Diagram A is active-high (current flows through the load when the uC output is high) and diagram B is active-low (current flows through the load when the uC output is low). The transistors should be chosen, of course, to be able to handle the load current and voltage.
Driving Outputs with Bipolar Transistors
“Digital Transistors,” sometimes also called logic transistors, are simply BJTs with built-in base bias resistors. Since they don’t require an external base resistor, they are as easy to connect to a digital output as a logic-level MOSFET – simply drive the base lead directly from the digital output. Digital transistors can be obtained with various values of base resistance, and some models also have an additional resistance between base and emitter, to help turn the transistor off if the base drive is disconnected. This won’t happen if the transistor is connected to a regular digital output, but it can happen if the transistor is connected to a switch or a circuit that has a high-impedance state. Look at digital transistors as a luxury that you never need (you can always use a regular transistor and a base resistor) but that you may want to use in certain circumstances.
Using a metal-oxide-semiconductor field-effect (MOSFET) transistor
MOSFET transistors are transconductance devices, adjusting their drain-source resistance (and thus the current through the load) according to the voltage between gate and source. To use a MOSFET as a switch requires that you drive the gate with a high-enough voltage to turn the transistor fully on (low enough drain-source resistance) to pass the current required by the load. Within reasonable limits, driving the gate voltage even higher will not cause any problems.
In the drawing below, diagram A shows an N-channel mosfet driving a load RL, and diagram B shows a P-channel mosfet driving a load RL. Diagram A is active-high (current flows through the load when the uC output is high) and diagram B is active-low (current flows through the load when the uC output is low). The MOSFETs should be chosen that they are fully on (very low source-drain resistance) with the gate voltage that can be supplied by the uC output. They should also be chosen, of course, to be able to handle the load current and voltage.
Driving Outputs with MOSFETS