Project 1: chipKIT™ Pro and I/O Control

Digital Input and Output

Using MPLAB® X Integrated Development Environment and chipKIT™ Pro MX7

Project 1: Digital Input and Output


The purpose of this project is to familiarize you with the methods of reading from and writing to the input and output (I/O) pins of the PIC™32 microcontroller. Software modeling concepts are presented using data flow diagrams and control flow diagrams.

Before you begin, you should have:


Qty Description
1 chipKIT™ Pro MX7 processor board with USB cable
1 Microchip MPLAB X IDE
1 MPLAB® XC32++ Compiler

Inputs and Outputs

Microprocessor pins are commonly either configured to be a digital input or digital output, hence are usually referred to as I/O pins. Digital input pins allow microprocessors to receive binary data from their environment. Binary data can only have two possible values: either one or zero. These two binary values can be used to represent various conditions such as on or off, high or low, and true or false. A single item of binary data is often referred to as a bit (short for binary digit). By sensing the presence or absence of a voltage, input pins can be used to detect binary events such as the pressing of a button. Individual digital output pins can be used to control single function devices such as turning an LED on or off. Alternatively, a collection of pins can be thought of as representing a “binary word.” The use of a word to represent a collection of data bits is generic and the number of bits it represents depends on both the processor and the specific C compiler being used. Having made the distinction between the use of a word to represent a collection of bits and a specific integer data type, we will follow the rules established in section 6.4 of the MPLAB XC32 C Compiler User's Guide.

The most basic means of communicating to and from a microprocessor is through I/O pins. On the PIC32 microprocessor, I/O pins are grouped in terms of ports, with 16 pins per port, and the collection of bits representing the state of the I/O pins of a designated port itself can be thought of as a word. The pins of each port are identified by a bit position in the form Rp0 to Rp15, where “p” is a letter that represents the particular I/O port on the processor and the numerical value (in the range 0 through 15) is the bit position within the word. The way in which each bit within a word should be valued or interpreted is at the discretion of the code developer. The conventional C (unsigned) integer data type uses binary encoding where the implicit “place” value associated with the position of a bit is given by 2 raised to the value of the bit's position. For example, the implicit value associated with the bit Rp3 is 8, corresponding to 2 raised to the third power. The value of a given bit is further dictated by whether the bit has an actual value of one or zero. For example, if the bit Rp3 is actually zero (' 0' or low), then its explicit value is zero (i.e., zero times the implicit place value). On the other hand, if the bit Rp3 is one ('1' or high), then its explicit value is 8 (i.e., one times the place value). Words may be interpreted, or “encoded,” in other ways. For example, signed data use two's complement encoding. Other encoding schemes include floating point (used for real numbers), ASCII (used to represent characters), and Gray code (often used to represent position), to name just a few.

With 16 I/O pins available on a PIC32 port, values can be represented ranging from zero to 65535 (base 10), or, in hexadecimal, zero to 0xFFFF (the leading “0x” indicates a hexadecimal representation of a number where each digit can have one of 16 values [0 through F] while a leading “0b” will be used to indicate a binary representation where each digit can only have one of two values [' 0' or '1']). On the PIC32MX7 microcontroller, I/O ports are labeled A through G. Because digital ports are limited to 16 bits, values ranging from zero to 65535 can be directly read from or written to external connections. For example, if RB3 is set high and all other Port B pins are set low, then the processor is outputting a value of eight (0x0008, or in binary, 0b0000 0000 0000 1000). (See Section 6.6 of Microchip XC32 User's Guide for alternate constant expressions.)

Input pins can also be used to detect events by sensing switches, button pushes, or some other device that outputs a binary signal, provided that an appropriate voltage level is generated by the sensor device. Multiple event type inputs can be connected to a single 16-bit processor port.

I/O Specific to the PIC32 Processor

Figure 1 shows a simplified block diagram for a PIC32 I/O pin. The more detailed block diagram available here is a reproduction from section 12 of the PIC32MX User's Guide and may provide additional useful information for advanced users. The characteristics of each I/O pin are individually controlled by setting bits in four, or in some cases, five registers. The PIC32MX795 processor used on the chipKIT Pro MX7 supports 16 pins that can be used as either an analog input or a digital I/O. Ten of these analog input pins are routed to Pmod connectors, as discussed in the Analog Inputs section of chipKIT Pro MX7 reference manual. By default, when the PIC32 is reset, these pins are set as analog input. The bits in the AD1PCFG register corresponding to the register and a pin number must be set to a '1' if the pin is to be used as a digital input or output.

Figure 1. Simplified block diagram for a PIC32 I/O pin.

One may also use PORTSetPinsDigitalIn(port, bits); or PORTSetPinsDigitalOut(port, bits); as an alternative to setting the AD1PCFG register. These are provided by the XC32 peripheral library to set the pin as a digital input or output. For this project, only digital I/O will be used. Refer to the first two side tabs above for alternate methods for configuring I/O pins.

TRISi, PORTi, LATi, and ODCi are all 16-bit registers where the designation of i ranges from A through G. The TRISi, LATi, and ODCi registers control the individual I/O pins corresponding to the bit position. For example, RG6 is the designation for bit 6 of the PORTG register. The TRIS (tri-state) control determines whether the pin is to be used as an input or an output. This is commonly referred to as setting the I/O pin direction. Setting the bit position in the TRIS register to a '0' makes the pin an output. This means that the state of the pin is controlled by the values written to the LAT and ODC registers. Setting the bit position in the TRIS register to a ' 1' makes the pin an input.

                  int x;
                  x = PORTG;

Regardless of the TRIS register's settings, the logic level of the output of an I/O pin can always be determined by reading the associated port. Setting bit 6 of TRISG to a '1' makes pin RG6 an input, but this is only because the output is disconnected by the TRISG tri-state control.

The PIC32MX family of processors is designed to operate with 3.3V inputs. More specifically, an input below the threshold of 0.66V is read by the processor as a low (a value of zero or a logical false). A voltage on an input pin that is above the threshold of 2.64V is read as a high (a value of one or a logical true). All digital input-only pins are 5V tolerant, meaning that a device that outputs 5V for logic high can be connected to digital input pins and will not damage the PIC32MX processor. The maximum voltage that can be applied to I/O pins that can be used as either analog or digital I/O is 3.6V for the chipKIT Pro MX7 (corresponding to VDD+0.3V). Exceeding this voltage limit will damage the processor.

The latch (LAT) register is used to control the output level. If the TRIS register is set to configure the I/O pin as an output, a logic '0' written to the LAT register always results in a low voltage level pin. A logic '1' written to the LAT register results in the output pin being either a high voltage level (3.3V) or it results in high impedance, depending on the state of the open drain control (ODC) register. The ODC allows the output to either source up to 25mA at 3.3V or to be in the high impedance state. The default setting (reset condition) for the ODC register is all zeros, which means the outputs can both sink and source current.

The LAT register is a memory element that stores the output state of the I/O pins. The LAT register can be written to at any time regardless of the direction of the bits, as controlled by the TRIS register. To assign a value to the LAT register, one might use the following example:

                  LATG = 0xA5;

However, the TRIS register still controls which LAT bits are enabled to the I/O pins. The bits in the LAT registers can be read as well as written. For example:

                  int x;
                  x = LATG;

As previously discussed, even when a pin is declared as an output, the state of the pin can still be read by the PORT register.

There is no guarantee that a device attached to the pin is not loading the output pin to the degree that the output appears to be low when it is actually programmed to be high. Thus when one wants to know the output state that an I/O pin was previously programmed for, it is preferable to read the LAT register associated with the I/O port. For example, the preferred method for reading the logic levels previously programmed to the Port G I/O pins would be the following:

                  int x;
                  x = LATG;   // Read IO LAT register for Port G

In addition to the register assignments that result in all 16 bits of the register being set at one time, there are modifier instructions that provide bit control. The modifiers that provide atomic bit manipulation are SET, CLR, and INV. For the SET modifier, the bit positions that are set to a one in the instruction will set the register bits high, while not altering the bit in the unspecified positions. For example, the instruction LATGSET = 0x8020; will result in bits 5 and 15 being set to a one. Bits 0 through 4 and 6 through 14 will not be changed.

Similarly, the modifier CLR sets the specified bits to a zero. The instruction LATGCLR = 0x8020; will result in bits 5 and 15 being set to zero. Bits 0 through 4 and 6 through 14 will not be changed. The modifier INV toggles the selected bits. If prior to the instruction, the specified bit location was set high, it will be set low after execution of the instructions, and vice versa. The instruction LATGINV = 0x8020; will toggle bits 5 and 15.

The pins associated with the buttons and LEDs on the chipKIT Pro MX7 processor board can be determined by referring to either the chipKIT Pro MX7 reference manual or the chipKIT Pro MX7 schematic. LED1 through LED4 are connected to PORTG pins 12 through 15, respectively. BTN1 and BTN2 on the PIC32MX7 are connected to PORTG pins 6 and 7. Although BTN3 is not used in this project, it will be used in future projects, and it is worth noting that BTN3 connects to I/O Port A bit 0 and requires disabling the JTAG programmer using the instruction: DDPCONbits .JTAGEN = 0;.

Refer to the second tab, labeled Peripheral Library I/O FUnctions, on the top right and for alternative methods of using and configuring PIC32 I/O pins.

Digital I/O Signal Conditioning

The detailed block diagram of an I/O pin for a PIC32 port is shown in the tab to the right above that is labeled “PIC32 Pins.” The state of the logic at the output of any I/O pin can be read at any time using the PORT register, regardless of whether it is assigned to function as an input or an output pin. As described above, the maximum voltage that can be applied to pins that can be assigned as an analog input is 3.6V. Other pins are 5V tolerant, which means that when the pin is assigned as an input, an external voltage of up to 5V can be applied to the PIC32 processor without causing damage. The schematic diagram for the chipKIT Pro MX7 has a transient super diode with 5V clamping on each processor I/O pin that is connected to a Pmod interface jack. This diode's purpose is to mitigate transient voltages, and it is not designed to limit the voltage applied to the processor pin.

The input current of an I/O pin configured as an input is plus or minus one micro amp (±1μA), which makes the input resistance greater than 3MΩ. Active high inputs from switches should have a pull-down resistor to drain trapped charges on processor inputs. Unused inputs should be terminated with either a pull-up or pull-down resistor, or processor current consumption may increase due to transistor switching action causing noise inside the processor. An alternative to terminating unused I/O pin is to program the pins to output a low level.

The maximum current capability of each conventional I/O pin is 25 mA (sink or source), while the combined current of all I/O pins is 200 mA subject to total power constraints. I/O pins have both open drain and active source output capability. The open drain capability provided by the ODC register is useful when interfacing with switch array keypads. (See PmodKYPD™ for an example keypad device.) The outputs that drive the four LEDs on the chipKIT Pro MX7 board are limited to approximately 5mA.

Additional insight into the effects of I/O pin loading is provided by the Microchip application note AN234 and the link found at “The Basics — Output.”

Project Tasks

The only hardware required for this project is the chipKIT Pro MX7 processor board. We will be using two of the momentary pushbuttons as inputs and the four LEDs as outputs. The goal is to use combinations of buttons that are pressed to control which LED is on, based on the action described in a truth table .

Initially, generate a new project as described in Project 0 (You will have find and unzip that folder). Copy the following three common files found in the “Code” folder in the zip file: config_bits.h, chipKIT_Pro_MX7.h , and chipKIT_Pro_MX7.c. Add them to the project directory and add them to the project. Create a new C file called Project1.c and modify it as shown in the following listing:

Listing 1.

                  #include <plib.h>
                  #include "config_bits.h"
                  #include "chipKIT_Pro_MX7.h"

                  int main(void)
                     sys_init(); 	/* See the function chipKIT_Pro_MX7_Setup() in chipKIT_Pro_MX7.c 
									for details of configuration of the processor board  */

					/* Project initialization code goes here	*/

						/* Project operational code goes here */
					return 1;	// This line of code will never be executed.

The first step for any microprocessor-based design is to configure the microprocessor to accommodate the hardware platform and the application requirements. For this project, the board initialization requires only a few program statements. Port G bits 6 and 7 must be set as digital inputs to allow reading of the pushbuttons BTN1 and BTN2. This can be accomplished using the following statement:

                  PORTSetPinsDigitalIn(IOPORT_G, BIT_6 | BIT_7);

This project does not use BTN3, but if an application did require the use of BTN3, the next two lines of code are needed.

                  DDPCONbits.JTAGEN = 0;    // Disable JTAB programming
                  PORTSetPinsDigitalIn(IOPORT_A, BIT_0);

Port G bits 12 through 15 must be set as digital outputs to control LED1 through LED4. Usually, the initial value of the outputs must also be set. This can be accomplished using the following statements:

                  PORTSetPinsDigitalOut(IOPORT_G, BIT_12 | BIT_13 | BIT_14 | BIT_15);
                  PORTClearBits(IOPORT_G, BIT_12 | BIT_13 | BIT_14 | BIT_15);

Constants BIT_0 through BIT_15 are defined as constants 0x0001, 0x0002, 0x0004, ... 0x8000 in the included file plib.h. The chipKIT_Pro_MX7.h file also defines LED1 through LED4 as BIT_12 through BIT_15 as well as defining BTN1 as BIT_6, BTN2 as BIT_7, and BTN3 as BIT_0. It is then possible to use the following lines of code to set the button and LED I/O if the file chipKIT_Pro_MX7.h is included in a project:

                  PORTSetPinsDigitalIn(IOPORT_G, BTN1 | BTN2);
                  DDPCONbits.JTAGEN = 0;  // Disable JTAB programming
                  PORTSetPinsDigitalIn(IOPORT_A, BTN3);

                  PORTSetPinsDigitalOut(IOPORT_G, LED1 | LED2 | LED3 | LED4);
                  PORTClearBits(IOPORT_G, LED1 | LED2 | LED3 | LED4);

The latter method is preferable because there is more information conveyed to someone reading the program when, for example, BTN1 is used instead of BIT_6, even though they represent the same value. From a sustainable software perspective, both BTN1 and BIT_6 are preferred over using the value 0x40 or 64 to indicate a bit position. Such explicit numerical representations constitute a magic number because they appear with no explanation. Magic numbers in software programming are to be avoided and can usually be eliminated using “#define” declarations.

The first informational tab on the right side of this page (labeled “MPLAB I/O Syntax”) discusses alternatives methods for programming I/O that the reader may encounter in other PIC32 literature and reference designs.

After the processor has been initialized, the program must continually read the input to determine whether a particular button is pressed (logical value of true) or released (logical value of false), execute logic to determine which of the LEDs to turn on and off, and set the output pins accordingly. The program maps the two button inputs to one of four LED outputs using the logic expressed by the truth table shown below in Table 1. The LEDs are driven by the output pins so writing a logical '1' to the LED port will cause the LED to be lit.

The program creates an infinite loop by using a while(1) statement, as illustrated in the flow diagram shown in the figures in the tab on the upper right labeled “Software Models.” The action of using explicit software instruction to determine the state of an input is called “polling.”

Project Program Verification

After compiling your project and downloading the code to the microprocessor, the code can be verified by simply pressing BTN1 and/or BTN2 and visually observing the on/off status of the four chipKIT Pro MX7 LEDs. For example, when BTN1 and BTN2 are pressed at the same time, LED4 should be on. Never underestimate the value of information that can be gained by turning an LED on or off to indicate an action. The technique is simple to code and easily observed.

Alternatives to polling are addressed in Project 5, which deals with interrupts or preemption.

Table 1. Truth table for mapping inputs to outputs.

Try It on Your Own!

  1. Describe how the following software instrumentation provided by the MPLAB X IDE was used to assist in writing the code for this project.
    • ANSWER: Watch variables allow you to “peek” inside the processor and determine the values stored at memory locations and special function registers. The value is then compared to the expected value. Operating under the assumption that processors are 100% predictable, if you accurately know the values of the parameters used in the expression and how the expression operates, then the result can always be determined before the statement is executed. If the values are different, then either you are assuming the wrong data values or you don't understand the operation of the expression.
    • ANSWER: Break points allow you to halt the code just prior to executing a particular C instruction. This allows you to examine the contents of memory or values of variables using the Watch window. This also allows you to determine which instruction branch was chosen after a program control statement (if, for, while, or do -while).
    • ANSWER: Single stepping allows you to observe variables and how they change as each line of program code is executed. This helps you determine where the program code is not operating as expected predicted).
  2. Write a detailed description of your verification-testing plan by writing a step-by-step process for someone to follow that will show that the system functions correctly.

    ANSWER: Visually verify the proper LEDs are on when BTN1 and BTN2 are pressed.

    • When neither BTN1 nor BTN2 is pressed, only LED1 is on.
    • When only BTN1 is pressed, only LED2 is on.
    • When only BTN2 is pressed, only LED3 is on.
    • When BTN1 and BTN2 are pressed, only LED4 is on.
  3. Based on page 6 of the schematic for the chipKIT Pro MX7 processor board:
    • Assuming a 0.7V forward voltage drop for the LED, compute how many milliamps of current are supplied to light one of the four LEDs.


      \[{I_{{\rm{OUT}}}} = \frac{{3.3{\rm{V}} - 0.7{\rm{V}}}}{{470\Omega }} = 5.5{\rm{mA}}\]

    • The schematic shows that a 10 kΩ is placed in series between the pushbutton and the input pin of the PIC32 processor. What is the voltage applied to the processor pin when the button is pressed and why?

      ANSWER: The 10 kΩ (R2) and 3 MΩ PIC32 input resistance (Rint) form a voltage divider as shown in the circuit shown below. Approximately 3.3V will be applied to the input pin due to the fact that the PIC32 input resistance is much greater than the 10 kΩ resistance of R2. Resistor R1 has no effect on this voltage.

    • Schematic diagram of pushbutton input.
    • Why are there pull-down resistors attached to the buttons?

      ANSWER: Without the pull-down resistor (R1), the processor inputs are unterminated while the buttons are not pressed. The high input impedance and relatively high input capacitance of the processor pins can trap an electric charge when the button is released thus giving a false indication that the button is still pressed.

  • Other product and company names mentioned herein are trademarks or trade names of their respective companies. © 2014 Digilent Inc. All rights reserved.