Zynq GPIO Module

A look at using the Zynq's General Purpose Input/Output Module

1644

The GPIO module allows MIO pins to be used as inputs or outputs; reading or driving digital values.

The registers in the GPIO module configures if the pin is a input or an output, the value of the pin as an output. The read value of the pin, when configured as an input, is also accessible through these registers. A diagram of a single GPIO channel is shown below

(GPIO Channel Figure from TRM goes here)

The diagram shows a single channel for GPIO, but channels are arranged in logical groups called banks.

There are 4 banks; banks 0-3. Banks 0 and 1 connect to the MIO interface and goes to external pins on the chip. Banks 2 and 3 Go through the EMIO and are routed to the PL fabric

Each bank is 32-bits wide, with the exception of Bank1 which is 22 bits wide. Each bank has the registers shown in the single channel diagram, and each bitfield of each register corresponds to a a GPIO channel. Bank 0 [31:0] maps to MIO [31:0], Bank 1 [21:0] to MIO [53:32], Bank 3 [31:0] to EMIO [31:0], and Bank 4 [31:0] to EMIO [63:32].

The registers are described below, [n] is the bank number

DATA_RO[n] Reads the input values of pins configured as inputs

DATA[n] Holds the output values of the bank.

DIRM[n] Configures which channels in the bank are outputs or inputs.

OEN[n] Enables Channels in the bank that are configured as output to be driven.

There are additional registers for configuring GPIO interrupts. These are covered in a seperate document.

Accessing the GPIO module through C

GPIO Addresses

Here are C preprocessor definitions for the GPIO module registers. Make sure when you write your code, break up your code into multiple header-source pairs.

//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)

GPIO Peripherals on the Blackboard

There are 2 extra buttons and an extra RGB LED on the Blackboard, not accessible through the same method as the other buttons and LEDS. These are instead only reachable through the MIO pins, and thus through the GPIO module.

The RGB’s blue diode can be found on MIO 16, the red on MIO 17, green on MIO 18.

Button 4 can be found on MIO 50, and button 5 can be found on MIO 51.

The RGB LED can be reached through GPIO bank 0 in bits 16-18, and the buttons can be found in GPIO bank 1 in bits 18 and 19.

GPIO as output

To start, lets define a constant bitmask for the channels linked to the RGB LED. This will make our code more readable and it also becomes easy to make changes to the mask, should we want to.

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

Now lets define a few basic operations

//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
}

Now we can use our basic operations to setup the module, and initialize the value of the LEDs

//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();
}

GPIO as input

Now we will configure some GPIO channels as inputs.

Here are some functions which configure the GPIO banks. This time, however, it’s a little more general, so the code can be reused. These functions take both the bank number and maks of the channels you want configured.

//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);
}

As Before let’s define constant bitmasks for the buttons.

//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

To use these, we can just use our constants to configure and read the values for the seperate channels.

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 );
}