One of the primary features of a microprocessor is to interact with and control external devices. These devices may be something as simple as an LED or a push button. In order to interface with such devices, almost all microprocessors have dedicated pins that can be controlled by software. We are going to examine the most basic digital interface: the Input/Output port or IO port.
IO Port Basics
A IO port is a collection of external pins on a microprocessor that can be configured to act as either an input or an output. Voltage levels present on the physical pins are converted into corresponding logic values. The supply voltage of the microprocessor, commonly called VDD, determines what voltage level is equivalent to logic 1. If the supply voltage is 3.3V, an input voltage of 3.3V will be interpreted as logic 1. Similarly, 0.0V will be interpreted as logic 0.
In the example below, we see a 4-bit IO port configured as an input. The switches on the lower 3 bits are open , so PA.0, PA.1 and PA.2 are connected to 3.3V. The 4th bit has the switch pressed which connects PA.3 to 0.0V, or logic 0. The IO port converts the analog voltage levels (3.3V & 0V) into a binary representation of 0111.
We could alternatively configure the IO port to act as an output. In this situation, software writes a logic value to the IO port which in turn places corresponding voltage on the pins. The example below illustrates how a GPIO port configured as an output can be used to turn on several LEDs. If binary 0011 is written to the GPIO port, LEDs D2 and D3 will turn on.
The processor places a voltage of 3.3V on PA.0 and PA.1. Since there is no forward voltage drop across D0 and D1, D0 and D1 do not turn on. The processor places 0.0V on PA.2 and PA.3, creating a forward voltage drop on D2 and D3 turning both LEDs on.
Pull Up/Down resistors
In some situation it might be necessary for a pull-up or pull-down resistor to be enabled on a IO pin. A pull-up/down resistor is often times used to set the state of a IO pin in the absence of a valid signal. An example of this would be a push button connected to an input pin. In the figure below, the push button connected to PA.0 is not being pressed. When a IO pin is configured as an input, the microprocessor does not drive any value on the pin. The pin is essentially in a Hi-Z state, meaning that it is neither 0 nor 1. An input pins relies on some external circuit to set the value. If software were to read the value of PA.0, the value is unknown (It could be a 0 or a 1).
In order to solve this problem, we want to add a pull-up resistor that sets the value on PA.0 to a know value when the push button is not being pressed.
When pressed, the push button connects PA.0 to GND and software would read a logic value of 0. When the push button is not pressed, the pull-up resistor pulls the PA.0 to 3.3V and software would read a logic value of 1.
Many designs use external pull-up/down resistors. This is what is shown in the figure above. As a designer you get to choose if you want to use the internal or external pull-up resistors. The benefit of internal resistors is that they are already included in the processor, consume no additional space on your printed circuit board, and have added flexibility by enabling them in software.
The down side is that it takes some finite amount of time during board initialization before internal resistors are active. Prior to the IO initialization code being executed by the application, the IO lines will float to an unknown value and could cause a system to malfunction. For this reason, I almost always use external pull-up/down resistors. I would prefer to spend a few pennies on external resistors and ensure that floating GPIO pins do not cause a system to malfunction.
De-bouncing a Push Button
A common problem encountered when using a push button is that the mechanical contacts of some push buttons will intermittently make contact, or bounce, when first pressed. The image below demonstrates this phenomenon.
If an application were to examine the IO pin in rapid succession, the application might treat a single button press as multiple button presses. In most situation, this is not the behavior that the user would expect.
In order for the application to detect only a single push button event, we need to de-bounce the button. De-bouncing a push button can be accomplished in hardware using a few different techniques such as a Schmitt triggered input or by adding an resistor-capacitor combination to the input pin.
The voltage on the IO pin cannot change instantaneously due the presence of the capacitor. Choosing the correct R and C values will act to filter out the temporary spikes in voltage due to the bouncing of the button. The other approach to take is to de-bounce the button in software. De-bouncing in software does not require an external resistor-capacitor filter but does require software to periodically sample the IO pin.