GPIO

Setup and use of Blackboard's GPIO devices

1596

The term “General Purpose Input/Output” (GPIO) generally refers to digital signals that are either inputs driven by two-state devices like slide switches or pushbuttons, or outputs that drive an indicator device (like an LED). Blackboard’s General Purpose I/O (GPIO) devices include 12 single-pole double-throw (SPDT) slide switches, 6 momentary single-pole single-throw (SPST) pushbutton switches, 10 single-color LEDs, and 3 tricolor (RGB) LEDs. Most of these GPIO devices are connected to the FPGA, but two pushbuttons and one RGB LED are connected to the Processing System (PS). The ARM processor can access the PS-connected devices at any time, whether or not the FPGA has been programmed. But it can only access the FPGA-connected devices after the FPGA has been configured (note: the ZYNQ device works just fine as an ARM system without the FPGA being programmed – they are independent systems). Ultimately, all GPIO devices are accessed through register reads and writes, but the PS GPIO devices use different registers, and have different programming requirements than the FPGA GPIO.

The slide switches constantly output a ‘0’ or a ‘1’ depending on their position. The pushbuttons are normally open, and so normally output a ‘0’ when not pressed, and output a ‘1’ only while actively pressed. All switches are connected to ZYNQ pins through series resistors to prevent damage from inadvertent short circuits.


Figure 1. Top-level GPIO Diagram
Figure 1. Top-level GPIO Diagram

All LED signals are active high. The individual/discrete LEDs are connected to FPGA pins via 330-ohm resistors, and so a logic high signal (3.3V) will illuminate them with about 3mA of current. RGB LEDs have higher voltage thresholds and different current requirements, so they cannot be connected directly to the FPGA like the discrete LEDs. RGB LEDs are driven by NPN bipolar transistors, and the transistor bases are connected to the FPGA. A logic high on any RGB LED signal will illuminate the LED with about 5mA of current.

Each RGB LED device includes three individual LEDs that are located very close together, giving the appearance of a single LED. Each LED is driven independently, and this allows different colors to be created. By simply turning LEDs on or off, seven different colors can be created. But by controlling the brightness levels of individual LEDs, a broad range of colors can be created. LED brightness levels are typically controlled using pulse-width modulated (PWM) signals. PWM signals are commonly used as simple “digital to analog” signals - when used to control LED brightness, the PWM signals turn individual LEDs on and off at a frequency higher than the human eye can detect, with different lengths of “on” time to create the appearance of different brightness levels (you can learn more about PWM circuits/signals in the linked documents). Since the RGB LEDs are connected to the FPGA, users can define custom PWM IP blocks to drive the LEDs. The Blackboard hardware definition file includes RGB LED PWM circuits as discussed below. You can read more about PWM circuits here: PWM signals

Processing system (PS) connected GPIOs

ZYNQ’s processing system contains about 20 IP cores that add additional features to the ARM processor. IP cores include controllers for USB, SPI, I2C, CAN, UART, SDIO and Ethernet busses, as well as a GPIO controller block. Between them, these cores have hundreds of I/O signals – far too many to route to external pins on the ZYNQ chip package. Not every system needs all these IP cores, and most systems will use only a few of them. The ZYNQ chip has only 54 “multiplexed I/O” (MIO) pins that the IP cores can use to connect to external devices. System designers must choose the IP cores and functions they need, and then carefully allocate the MIO pins to make sure all critical functions are supported. The Blackboard uses several of the cores, including SPI, I2C, UART, SDIO, USB, and GPIO; the GPIO IP core uses just five MIO pins to connect two pushbuttons and one RGB LED. Like all IP cores, the GPIO block is accessed through a set of predefined/permanent registers (discussed below).

FPGA-connected GPIOs

The FPGA in ZYNQ has access to many more I/O pins than the processing system does. On the Blackboard, FPGA pins connect to several peripherals, including a seven-segment display, an HDMI connector, several sensors and expansion connectors, and 32 GPIO devices (12 slide switches, 4 pushbuttons, 10 LEDs, and 2 RGB LEDs). The ARM processor accesses all FPGA-connected devices through registers in user-programmed custom IP blocks. These custom IP blocks are not a permanent part of the processing system – rather, they are designed by the user and must programmed into the FPGA prior to use. Because these custom IP blocks are located in the FPGA, they can easily be modified to include new features or accommodate user alterations as desired. The Blackboard_standard_configuration defines custom IP blocks that can be used to connect to the FPGA GPIO devices.

Since MIO pins are so limited, and since many systems may need more than just 54 pins to connect ZYNQ’s hard-IP peripheral circuits to physical devices, ZYNQ includes a bypass method that allows PS peripherals to use FPGA pins directly. This is called the “Extended MIO” (EMIO) interface, and the Blackboard takes advantage of that for the SPI bus (see the SPI reference for more information).


Figure 2. GPIO signal routing
Figure 2. GPIO signal routing

Accessing FPGA GPIO devices

The FPGA-connected GPIO devices are shown in the figure below, together with the registers defined in the Blackboard_standard_configuration file. The buttons, switches and LEDs are accessed through simple registers, but the RGB LEDs use pulse-width-modulators so that a wide spectrum of colors can be created.


Figure 3. FPGA-connected GPIO
Figure 3. FPGA-connected GPIO

Buttons (0x41200000): Pushbutton data (read only). Bits will be a’1’ when corresponding button is being actively pressed.

BUTTONS: Button press data (1 when actively pressed)


LEDs (0x41210000): LED data (write only). Writing a’1’ will illuminate the corresponding LED.

LEDS: LED data (1 to illuminate)


Switches (0x41220000): Switch data (read only). Bits read as 1 or 0 depending on switch state.

SWITCH: Switch data (1/0 depending on switch state)


RGB_EN (0x43C000XX (XX-00/10/20/30/40/50)): Enable individual RGB LEDs (write only). Bit0 turns on/off corresponding RGB LED.

EN: Enable LED (1 to enable). Six identical registers for six RGB LEDs.


RGB_PERIOD (0x43C000XX (XX-04/14/24/34/44/54)): RGB LED Period definition (write only). 32-bit divisor to establish PWM frequency.

PERIOD: Divisor value applied to system clock (100MHz) used to establish PWM period/window frequency.


RGB_WIDTH (0x43C000XX (XX-08/18/28/38/48/58)): RGB LED PWM pulse-width (write only). 32-bit value determines pulse length from start of PWM period.

WIDTH: Length of PWM pulse in system clocks (100MHz).

Accessing Processing System (PS) GPIO devices

The ZYNQ processing system defines 118 GPIO signals; 54 are assigned to MIO pins, and 64 are assigned to the FPGA. All GPIO signals are bidirectional, meaning they can be driven as outputs, read as inputs, or dynamically switched between being inputs and outputs. Every GPIO signal has a “pin driver” circuit that uses several flip-flops – one to store output data, another to define data direction, another to store output enables, and so on. These flip-flips are gathered into 32-bit registers that are located in a “GPIO IP block” that can be accessed by the ARM processor. Available registers include control registers that can define GPIO signals as inputs or outputs; control registers that can enable or disable output drive (three-state control); write data registers that can drive logic levels on GPIO output signals; read data registers whose bits are defined by GPIO input signals; and registers to setup interrupts.

Within the GPIO IP block, signals and their corresponding registers are divided into four banks. The first 32 MIO signals are in bank 0, and the next 22 MIO signals are in bank 1. Banks 2 and 3 are for the 64 EMIO/FPGA signals. Here, we’re interested in the MIO signals in banks 0 and 1.

Figure 4. GPIO Pin Driver Circuits and MIO Signal connections
Figure 4. GPIO Pin Driver Circuits and MIO Signal connections

Configuration bits for 118 GPIO signals require four registers (three 32-bit registers and one 22-bit register). The GPIO block includes four registers to store output values, four more to define pin directions, four more to store enable outputs, etc. Each register is assigned to bank 0, 1, 2, or 3 depending on whether they are used with MIO signals or EMIO/FPGA signals. Many of the most useful registers are discussed below.

Blackboard’s PS-connected pushbuttons are attached to MIO_50 and MIO_51, and the three RGB LED signals attached to MIO_16 (blue), MIO_17 (red), and MIO_18 (green). These pin/signal assignments result from the physical pin connections on the Blackboard, and they cannot be changed without redesigning the circuit board (any GPIO device connected to ZYNQ pin B13 would be assigned to MIO_50). GPIO input signals are always active and can always be read, so pushbutton inputs can be read by simply reading bits 18 and 19 in the Bank1 input register. To drive an RGB LED output, the output data, direction, and enable bits (16, 17 and 18 in Bank0 control registers) must all be written.

Figure 5. GPIO IP Block Registers
Figure 5. GPIO IP Block Registers

DATA_X (0xE000A00XX (XX = 40/44/48/4C))) Output data

Bits in this register drive the corresponding GPIO signal to the value written (0 or 1) if the direction is set to output and the output is enabled. Bank0 bits map to MIO signals 0-31; Bank1 bits map to MIO signals 32-53.

DATA_X_R0 (0xE000A00XX (XX = 60/64/68/6C))) Input data

Bits in this register always show the current value on the pin. Bank0 bits map to MIO signals 0-31; Bank1 bits map to MIO signals 32-53.

DIR_X (0xE000A0XXX (XX = 204/244/284/2C4)) Pin Direction.

Bits in this register define the pin direction (1 for output). Bank0 bits map to MIO signals 0-31; Bank1 bits map to MIO signals 32-53.

OE_X (0xE000A0XXX (XX = 208/248/288/2C8)) Pin Direction.

Bits in this register enable outputs (1 to enable). To drive an output, the corresponding bit in this register and the DIR_X register must both be a 1 (they are anded). Bank0 bits map to MIO signals 0-31; Bank1 bits map to MIO signals 32-53.

Blackboards PS-connected GPIO circuits

To setup GPIO pins, the physical pin characteristics must first be defined. All 54 MIO pins have a corresponding register that defines how the pin is configured). GPIO pins must be configured before they can be defined and used as inputs or outputs.

Figure 5. GPIO IP Block Registers
Figure 5. GPIO IP Block Registers

MIO_XX (0x00000XXX): MIO pin configuration registers

DR: Disable Input Buffer (1 to disable)
PU: Enable pull-up resistor on input (1 to enable)
IO_TYPE: Set I/O buffer type: 001 for LVCMOS18; 010 for LVCMOS25; 011 for LVCMOS33; 100 for HSTL
SP: Set buffer speed (0 for slow CMOS edge, 1 for fast)
L3_SEL: Level3 mux select (000 for GPIO)
L2_SEL: Level 2 mux select (00 for Level 3 mux; 01 for SRAM/NOR chip select; 10 for NAND chip select; 11 for SDIO
L1_SEL: 0 for L2 mux select

To setup the MIO pins for the Blackboard GPIO devices, the registers for MIO pins 16, 17, 18, 50 and 51 should be written with “0x600” to select LVCMOS33. The input buffers on the RGB LED output pins (16, 17 and 18) can optionally be disabled by writing “0x1600”. Note these five registers are programmed in the Blackboard_standard_configuration file with the code shown below, so there is no need to program them again.

Sample Code

The fist code segment illustrates setting up the MIO physical pins. Note that defining physical pin characteristics improperly can damage the ZYNQ device (or other devices). The registers that setup physical pins are protected system-level registers, and access to them must be unlocked before they can be changed. After setting up the physical pins, the LED MIO pins in Bank0 are setup as outputs and then enabled, and all the bits in Bank1 are set to inputs (so the pushbuttons on bts 51 and 51 are inputs).

// System Level Control Register Definitions
#define SLCR_LOCK	*((uint32_t *) 0xF8000004)
#define SLCR_UNLOCK	*((uint32_t *) 0xF8000008)
#define UNLOCK_KEY	0xDF0D
#define LOCK_KEY	0x767B

//  MIO pins register definitions
#define MIO_16		*((uint32_t *) 0xF8000740;
#define MIO_17		*((uint32_t *) 0xF8000744;
#define MIO_18		*((uint32_t *) 0xF8000748;
#define MIO_50		*((uint32_t *) 0xF80007C8;
#define MIO_51		*((uint32_t *) 0xF80007CC;

SLCR_UNLOCK = UNLOCK_KEY;	//unlock SLCRs
                           
// Configure MIO pins
*((uint32_t*) MIO_50) = 0x00000600; // BTN4 is LVCMOS33
*((uint32_t*) MIO_51) = 0x00000600; // BTN5 is LVCMOS33
*((uint32_t*) MIO_16) = 0x00001600; // RGB_LED_B is LVCMOS33 with disabled input buffer
*((uint32_t*) MIO_17) = 0x00001600; // RGB_LED_R is LVCMOS33 with disabled input buffer
*((uint32_t*) MIO_18) = 0x00001600; // RGB_LED_G is LVCMOS33 with disabled input buffer

SLCR_LOCK = LOCK_KEY;	//relock SLCRs

*((uint32_t*) GPIO_DIR_0) = 0x00070000; //Set bits 16,17,18 in DIR_0 reg to set output direction
*((uint32_t*) GPIO_OE_0) = 0x00070000;  // Set bits 16,17,18 in OE_0 reg to enable outputs
*((uint32_t*) GPIO_DIR_1) = 0x00000000; // Set bits in DIR_1 to inputs

The code example below sets up GPIO control registers, defines a bit-mask constant that can be used to setup the RGB LEDs, and defines some basic functions to work with GPIOs. The last few lines show examples of using the functions.

//output data register
#define GPIO_DATA(n)		*(((uint32_t*) 0xE000A040)+n)

//input data register
#define GPIO_DATA_RO(n) 	*(((uint32_t*) 0xE000A060)+n)

//direction register
#define GPIO_DIRM(n) 		*(((uint32_t*) 0xE000A204) + 16*n)

//output enable register
#define GPIO_OEN(n) 		*(((uint32_t*) 0xE000A208) + 16*n)

//RGB LED channels are bits 16-18, in bank 0
#define RGB_MASK 0x70000	

//configures MIO 16,17,18 as outputs
void set_GPIO_RGB_output()
{ 
	GPIO_DIRM(0) = RGB_MASK ;
}

//configures MIO 16,17,18 as inputs
void set_GPIO_RGB_input()
{
	GPIO_DIRM(0) &= ~RGB_MASK ; 
}

//enables the output of bank0 16,17,18
void en_GPIO_RGB_output(){

	GPIO_OEN(0) |= RGB_MASK ; 
}

//disables output of bank0 16-18
void dis_GPIO_RGB_output()
{
	GPIO_OEN(0) &= ~RGB_MASK;
}

//writes the passed value into the 3 outputs for the RGB LED's
void write_GPIO_RGB(uint32_t val)
{
	val = (0x7&val)<<16;	//use only bottom 3 bits, and shift to place
	GPIO_DATA(0) = (GPIO_DATA(0)&~RGB_MASK) | val;	//change only RGB bits
}


//Configures RGB Connected GPIO as outputs on channels
//initializes value to zero and enables output
void init_GPIO_RGB()
{
	dis_GPIO_RGB_output();
	set_GPIO_RGB_output();	//configure as output
	write_GPIO_RGB(0);	//clear value of RGBs
	en_GPIO_RGB_output();
}

The following code uses parameters to define the GPIO register bank, and the mask to select the bits within a register that will be modified. This makes the code more modular and more generally applicable, so it can be reused later. It configures the pushbutton input signals, and then shows an example of reading the appropriate registers and bits.

//Sets the channls in the mask, in bank n, as outputs
void set_GPIO_output(int n, uint32_t mask)
{
	GPIO_DIRM(n) |= mask; 
}

//sets the channels in the mask, in bank n, as inputs
void set_GPIO_input(int n, uint32_t mask)
{
	GPIO_DIRM(n) &= ~mask;
}

//gets the value of only the bits in the mask
int read_GPIO_input(int n, uint32_t mask)
{
	return (GPIO_DATA_RO(n) & mask);
}

//btn4 is 18 in bank 1, btn5 is 19 in bank 1
//define bit masks for easy access in banks
#define BT4_MASK 0x40000
#define BT5_MASK 0x80000
//define the bank the buttons are in
#define BT4_BANK 1
#define BT5_BANK 1

void configure_buttons(void)
{
	set_GPIO_input(BT4_BANK, BT4_MASK);
	set_GPIO_input(BT5_BANK, BT5_MASK);
}

//another way to write the above function
void configure_buttons_alt(void)
{
	set_GPIO_input(BT4_BANK, BT5_MASK | BT5_MASK);
}

//returns the value of button 4 1:on, 0:off
int get_btn4_val(void)
{
	return ( read_GPIO_input(BT4_BANK, BT4_MASK) !=0 );
}

//returns the value of button 5 1:on, 0:off
int get_btn5_val(void)
{
	return ( read_GPIO_input(BT5_BANK, BT5_MASK) !=0 );
}