1 Peripheral Overview

Each of the Input/Output (I/O) available ports is provided with eight registers to control their operation:

  • Port registers (PORTx) allow the user to read the logic levels on the pins. For example, reading from PORTA register returns the actual I/O pin values. Pins configured as output can also be driven by writing to PORTx.
  • Output Latch registers (LATx) can be used for both writes to the port and read-modify-write operations on the values that the I/O pins are driving. Writes to LATx are equivalent with writes to the corresponding PORTx register. Reads from LATx return register values, not I/O pin values. This prevents inadvertent modification of pins that are configured both as inputs and outputs by the software.
  • Tri-State Control registers (TRISx) allow the user to configure the direction of the pins. When a bit from register is set (logic ‘1’), the port output driver is disabled for that pin. Otherwise, if a pin is cleared (logic ‘0’) the port output driver is enabled.
  • Analog Select registers (ANSELx) allow the user to enable/disable the digital input buffers or the ST and TTL input buffers for specific pins. Pins configured for analog inputs will have the digital input buffers disabled to reduce potential shoot-through currents, which can increase the operating current.
  • Weak Pull-Up registers (WPUx) are used to enable or disable weak pull-up on specific pins. This option allows the connection of one or more open collector drives to an input pin without the need for an external pull-up resistor. The option also allows the disabling of the pull-ups, when not needed, to minimize the current consumption.
  • Input Level Control registers (INLVLx) allow the user to configure either ST or TTL input thresholds to be used for port reads and Interrupt-on-Change.
  • Slew Rate Control registers (SLRCONx) allow the user to select a lower pin slew rate or configure it at a maximum rate. Slew Rate reduction is useful for limiting the EMI emissions of the pins during state transitions.
  • Open-Drain Control registers (ODCONx). The output pins can be configured as open collector outputs (drive-only sink outputs) signals, or both source and sink outputs. The desired setting can be configured using the Open-Drain Configuration registers.

Most port pins share functions with device peripherals, both analog and digital. In general, when a peripheral is enabled on a port pin, that pin cannot be used as a General Purpose output. However, the pin can still be read.

A simplified model of a generic I/O port, without interfaces to other peripherals, is shown below.

Figure 1-1. Generic I/O Port Operation

Individual pins can be independently configured to generate an interrupt. The source of the interrupt can be either a rising or falling edge of the signal on the port. The Interrupt-on-Change can also be used to wake the device from Sleep mode.

The GPIO registers and the configurations they provide are described below, along with some basic settings using these registers.

  • Set the Direction of an I/O Pin

    In order to configure an I/O pin as an input or an output, the TRISx register is used. A logic ‘1’ sets the corresponding bit as an input, while a logic ‘0’ sets the bit as an output.

    For example, in order to set TRISA4 pin as an output without changing the rest of the register, the following code must be used:
    TRISAbits.TRISA4 = 0; /* Configure the TRISA4 pin as output */
    The following code configures the TRISA3 pin as an input:
    TRISAbits.TRISA3 = 1; /* Configure the TRISA4 pin as input*/ 
  • Drive Output Pins Values

    To set the output values, in order to drive the output of the pins high or low, either PORTx or LATx registers can be used.

    There is, however, an advantage to using the LATx register. For example, writing a value to the PORTx register can be translated into a read-modify-write low level operation. This can cause a problem when a pin is configured sometimes as an input or as an output. If a read-modify-write is executed when the pin is an input, the current state of the pin (as an input) is copied into the output latch. When the pin is again set as an output, the state of that output is unknown.

    To avoid this issue, the user must write to the LATx registers and the values will be written directly to the PORTx registers. By reading from the LATx registers, register values will be returned.

    For example, to drive the RA4 pin high, the following code is recommended:
    LATAbits.LATA4 = 1; /* Drive the output high */

    To drive the RA4 pin low, the following code is recommended:

    LATAbits.LATA4 = 0; /* Drive the output low */
  • Enable/Disable the Digital Input Buffers

    If an I/O pin is used as a digital input, the ANSELxn corresponding bit must be cleared. This will allow the digital input signals to reach the digital peripherals, as shown in Figure 1-1. If the respective pin is used as an analog input, the ANSELxn corresponding bit must be set in order to limit current consumption.

    For example, to configure the RA0 pin as a digital input and have the RA1 pin as an analog input, the following code must be used (considering the pins are already configured as inputs):
    ANSELAbits.ANSELA0 = 0; /* ST and TTL input buffers are enabled */
    ANSELAbits.ANSELA1 = 1; /* Digital input buffers are disabled*/
  • Enable Weak Pull-up

    When using an I/O pin as an input, the pin state must be reliable. In order to avoid an input pin to enter a floating state, the user must enable the weak pull-up. This way, the pin state will be a logic ‘1’ by default since it will be internally connected to the source voltage, using a weak pull-up resistance value. The main use for WPU is to eliminate the current draw of a pull-up on a grounded input. Since the WPUs can be enabled/disabled in code, the pull-up can be turned on only when it is necessary to read the input. This saves current consumption that can appear when using a fixed external pull-up resistor.

    This is an important feature when using, for example, a button and without using an external pull-up resistor.

    The weak pull-up can be enabled using the WPUx register, as presented in the following code:
    WPUAbits.WPUA0 = 1; /* Enable weak pull-up */ 
  • Configure the Open Drain Control

    The PIC® GPIO pins can be configured as sink-only output, or to both source and sink current. The Open-Drain Control Register (ODCONx) controls the open-drain feature of the port. When an ODCONx bit is set, the corresponding port output becomes an open-drain driver capable of sinking current only. When an ODCONx bit is cleared, the corresponding port output pin is the standard push-pull drive capable of sourcing and sinking current.

    Some advantages to this mode include the following:

    • Multiple outputs can be tied together in a logic OR configuration.
    • There are bi-directional open collector buses that both send and receive through a single pin.
    • It is possible to create a 555 timer IC using comparators, a CLC Flip Flop and the OD output mode.
    A code example on how to use the ODCONx registers is presented below (assuming the pins are already configured as output pins)
    ODCONAbits.ODCA4 = 1; /* Sink current only */
    ODCONAbits.ODCA5 = 0; /* Source and sink current */
  • Configure the Slew Rate

    The SLRCONx registers change the slew rate option for each pin. It allows the user to limit the slew rate, or to drive a slew rate at the maximum possible rate. Decreasing the slew rate reduces the amount of noise induced by capacitive coupling to adjacent signals (a capacitor is formed when two conductors are in proximity).

    A code example on how to use the SLRCONx registers is presented below (assuming the pins are already configured as output pins):
    SLRCONAbits.SLRA4 = 1; /* Slew rate is limited */
    SLRCONAbits.SLRA5 = 0; /* Maximum slew rate */
  • Configure the Input Threshold Voltage

    The INLVLx registers control the input voltage threshold for each of the available PORTx input pins. A selection between the Schmitt Trigger CMOS and the TTL compatible thresholds is available.

    The input threshold is important in determining the value of a read of the PORTx register and the level at which an interrupt-on-change occurs, if that feature is enabled.
    Table 1-1. Input Level Configurations
    TTL LevelsSchmitt Trigger (ST)
    Logic ‘0VIN<0.8VVIN<0.2VDD
    Logic ‘1VIN>2.0VVIN>0.8VDD

    TTL uses fixed voltages but has a lower noise margin (1.2V), while Schmitt Trigger inputs are proportional to the supply voltage (2V for 3.3V supply and 3V for 5V supply).

    A code example on how to use the SLRCONx registers is presented below (assuming the pins are already configured as input pins):
    /* ST input used for port reads and interrupt-on-change */
    INLVLAbits.INLVLA0 = 1; 
    /* TTL input used for port reads and interrupt-on-change */
    INLVLAbits.INLVLA1 = 0;