Read GPIO from PS
The second step in using a GPIO is to read digital signals. At this level, I will take the simplest way: poll the channel periodically, check its value and take the proper action.
System requirements
The system is implemented in a Cora Z7-07S evaluation board. The PL is not used, but the AXI controller is used to access the two buttons and LEDs on the board.
While the button BTN1 is pushed, the blue LED in LD1 turns on, and BTN0 does the same for the red LED in LD0. When the button is released, the corresponding LED turns off. If both buttons are pressed simultaneously, both LEDs turn on.
Hardware configuration
LEDs and buttons are hardwired into the evaluation board and are connected to the AXI module. The relevant connections are summarised in the table below.
| Board element | Board signal name | Chip signal name | Chip pin number | Diagram signal name |
|---|---|---|---|---|
| LD0 red | LED0_R | IO_L21P_T3_DQS_AD14P_35 | N15 | rgb_leds[0] |
| LD1 blue | LED1_B | IO_0_35 | G14 | rgb_leds[5] |
| BTN0 | BTN0 | IO_L4N_T0_35 | D20 | btns_2bits[0] |
| BTN1 | BTN1 | IO_L4P_T0_35 | D19 | btns_2bits[1] |
Table 1: LEDs and button connections. In addition, the Elaborated Design window in Vivado shows another set of names for these signals. For example, BTN0 has the name btns_2bits_tri_i[0] and it is mapped to the Board Part Pin btns_2bits_tri_i_0.
I created a new Block design in Vivado, similar to the one used in the previous step, but this time, I also selected btns 2bits in the GPIO2 IP interface. Then, create the .xsa module file with the following steps:
- Use the Run Block Automation and RUN Connection Automation with All Automation selected.
- Validate Design.
- Regenerate Layout to organized it better.
- Generate Block Design, Use Global as Synthesis Options.
- Create HDL Wrapper. Find the block design file in the Sources tab of the Project Manager. The option is in the right-click menu.
- Check the pin assignments, voltage standards and pin direction by Open Elaborated Design.
- Generate Bitstream.
- Export Hardware from File menu. Include the bitstream. This step will create the .xsa from which a new platform will be created in Vitis.
Software
There are three projects to create in Vitis. First, the Platform project takes the .xsa file and builds the platform. The second is the Application project, which will have the code. I start the project with an Empty Application (C) template and then create the main.c file. At the same time, Vitis will create a System project named with the suffix _system after the application project name. I don’t know what this project is for; I guess it is an advanced feature.
The complete code is the following:
#include "xparameters.h"
#include "xgpio.h"
#include "xil_types.h"
#define GPIO_ID XPAR_AXI_GPIO_0_DEVICE_ID
#define LED_CHANNEL 1
#define BTN_CHANNEL 2
int main() {
u32 led_mask = 0b100001; // 0bBGRBGR
XGpio_Config *cfg_ptr;
XGpio gpio_device;
u32 btns;
u32 leds;
// Initialize AXI GPIO device
cfg_ptr = XGpio_LookupConfig(GPIO_ID);
XGpio_CfgInitialize(&gpio_device, cfg_ptr, cfg_ptr->BaseAddress);
// Set direction of the GPIO bits.
// Bits set to 0 are output and bits set to 1 are input.
XGpio_SetDataDirection(&gpio_device, LED_CHANNEL, 0);
XGpio_SetDataDirection(&gpio_device, BTN_CHANNEL, 0b11);
while (1)
{
// Read both buttons
btns = XGpio_DiscreteRead(&gpio_device, BTN_CHANNEL);
// Find which button is pressed and turn on the corresponding LED
if (btns == 0b01) {
leds = led_mask & 0b000001;
}
else if (btns == 0b10) {
leds = led_mask & 0b100000;
}
else if (btns == 0b11) {
leds = led_mask;
}
else {
leds = 0;
}
XGpio_DiscreteWrite(&gpio_device, LED_CHANNEL, leds);
}
}
Header
The new part here is the definition of the buttons channel.
#define BTN_CHANNEL 2
This tells us that the GPIO device is divided into two channels, one for the LEDs (channel 1) and another for the buttons (channel 2). It seems obvious, but keep in mind that this reflects the structure of the AXI GPIO module we used in the Block diagram in Vivado. Another alternative is to use two AXI modules, with one channel each. Both LED_CHANNEL and BTN_CHANNEL will point to channel 1 of two different GPIO_ID. Both configurations work for this simple example, but using one or two AXI modules has consequences on other elements, such as interruptions.
Initialisation of the AXI GPIO module
There is nothing new in the initialisation. Just notice that the initialisation happens for the LEDs and button channels simultaneously.
Only the port direction has to be set independently.
// Bits set to 0 are output and bits set to 1 are input.
XGpio_SetDataDirection(&gpio_device, LED_CHANNEL, 0);
XGpio_SetDataDirection(&gpio_device, BTN_CHANNEL, 0b11);
Read buttons state
In the main loop, each run polls the state of the buttons.
btns = XGpio_DiscreteRead(&gpio_device, BTN_CHANNEL);
Every time, the values are returned in an array of two elements.
Turn on the LEDs
The function to control the LEDs state is XGpio_DiscreteWrite(&gpio_device, LED_CHANNEL, leds). The variable leds has the value for all six LEDs in LD0 and LD1 combined. Here, we only use the first and sixth bit of the variable. The logic is summarised in the following table.
| BTN0 | BTN1 | leds |
|---|---|---|
| 0 | 0 | 0b000000 |
| 1 | 0 | 0b000001 |
| 0 | 1 | 0b100000 |
| 1 | 1 | 0b100001 |
Table 2: Logic table mapping the status of buttons and LEDs.
Compiling and uploading
In the Explorer tab, select the project name and Build from the hammer or right-click menu.
To upload and run the code, open again the right-click menu and select Run As.. > Run Hardware. Select the Configuration and OK.
Enjoy Reading This Article?
Here are some more articles you might like to read next: