Introduction

Xilinx® LogiCORE™ IP AXI 通用输入/输出 (GPIO) 内核为 AXI 接口提供通用输入/输出接口。 这个 32 位IP内核旨在与 AXI4-Lite 接口连接。(参考Xilinx的开发指南:AXI GPIO v2.0 LogiCORE IP Product Guide(PG144))

Features

1* 支持 AXI4-Lite 接口规范

2* 支持可配置的单或双 GPIO 通道

3* 支持 1 至 32 位 GPIO 引脚的可配置通道宽度

4* 支持将每个 GPIO 位动态编程为输入或输出

5* 支持每个通道的单独配置

6* 支持所有寄存器的每一位的独立复位值

7* 支持可选的中断请求生成

注: 具体的配置信息可见 Vivado中 AXI GPIO IP核。

Function Description

AXI GPIO 设计为 AXI4-Lite 接口提供通用输入/输出接口。

AXI GPIO 可以配置为单通道或双通道设备。 每个通道的宽度可独立配置。

通过启用或禁用三态缓冲器,端口动态配置为输入或输出。 (虚线部分为三态缓冲区)。

通道可以配置为在其任何输入发生变换时生成中断。(这里发生变换的意思就是信号从1到0或者从0到1) 。

AXI GPIO 内核的顶层框图如Figure 1-1所示。

image-20220504164845460

(1)AXI4-Lite Interface

​ AXI4-Lite 接口模块实现了一个 32 位 AXI4-Lite 从接口,用于访问 GPIO 通道寄存器。 有关 AXI4-Lite 从接口的更多详细信息,请参阅 AXI4-Lite IPIF LogiCORE IP Product Guide (PG155) 的规范使用部分。

(2)Interrupt Control

中断控制从 GPIO 通道获取中断状态并生成中断给主机。 当在 Vivado® IDE 中设置了 Enable Interrupt 选项时,它会被启用。

(3)GPIO Core

​ GPIO 内核由寄存器(Register)和多路复用器(MUX)组成,用于读取和写入 AXI GPIO 通道寄存器。 它还包括在通道输入发生变化时识别中断事件的必要逻辑。

​ 图中的三态缓冲器实际上并不是内核的一部分。 当在 Vivado Design Suite 中生成输出产品时,三态缓冲器会自动添加到顶层设计封装文件中。

Port Descriptions

表 2-3 列出并描述了 AXI GPIO I/O 信号。需要注意几个信号的有效状态。

image-20220504174433551

Register Space

Table 2-4 shows the AXI GPIO registers and their addresses.

image-20220504174900730

具体的信息

(1)AXI GPIO Data Register (GPIOx_DATA)

AXI GPIO 数据寄存器用于读取通用输入端口和写入通用输出端口。 当端口配置为输入时,写入 AXI GPIO 数据寄存器无效。

GPIO 数据寄存器有两个(GPIO_DATA GPIO2_DATA),每个通道对应一个。

通道 1 数据寄存器 (GPIO_DATA) 始终存在;仅当内核配置为双通道时,通道 2 数据寄存器 (GPIO2_DATA) 才存在。(Enable Dual Channel = 1).

AXI GPIO 数据寄存器,表 2-6 详细介绍了该寄存器的功能。

image-20220504200358476

(2)AXI GPIO 3-State Control Register (GPIOx_TRI)

AXI GPIO 三态控制寄存器用于将端口动态配置为输入或输出。

当该寄存器中的某个位被设置时,相应的I/O端口被配置为输入端口。 当一个位被清零时,对应的 I/O 端口被配置为输出端口。

有两个 AXI GPIO 三态控制寄存器(GPIO_TRI GPIO2_TRI),每个通道对应一个。

仅当内核配置为双通道 Enable Dual Channel = 1 时,通道 2 三态控制寄存器 (GPIO2_TRI) 才存在。

AXI GPIO 三态控制寄存器的寄存器功能如表 2-7 所示。

image-20220504201857262

Interrupts

AXI GPIO 内核可以在 Enable Interrupt 参数的控制下进行配置,以在任何通道输入中发生转换时生成电平中断。

GPIO 接口模块包括中断检测逻辑,以识别通道输入上的任何转换。

当检测到转换时,它会指示给中断控制器模块。

中断控制器模块实现了必要的寄存器来启用和维护中断的状态。

为了支持通道的中断功能,中断控制器模块实现了以下寄存器:

(1)Global Interrupt Enable Register (GIER) —- 为处理器或中断控制器的中断输出提供主机启用/禁用。

(2)IP Interrupt Enable Register (IPIER) —- 为每个通道实现独立的中断使能位。

(3)IP Interrupt Status Register (IPISR) —- 为每个通道实现独立的中断状态位。IP ISR 提供 Read 和 Toggle-On-Write 访问。

Toggle-On-Write 机制允许中断服务例程使用单个写事务清除一个或多个 ISR 位。 也可以手动设置 IP ISR 以生成中断以进行测试。

Global Interrupt Enable Register (GIER)

全局中断启用寄存器为处理器的中断输出提供主启用/禁用。 这是一个单比特读/写寄存器,如图 2-3 所示。

该寄存器只有在设置了 Enable Interrupt 参数时才有效。

注意:因为这是控制中断生成的主位,所以必须将其设置为生成中断,即使在 IP 中断启用寄存器 (IP IER) 中启用了中断也是如此。

全局中断使能寄存器的位定义如表 2-8 所示。

image-20220504203857077

image-20220504204238824

IP Interrupt Enable (IPIER) and IP Status Registers (IPISR)

IP 中断允许寄存器 (IPIER) 和 IP 中断状态寄存器 (IPISR),如图 2-4 所示,为每个中断提供一个位。

这些寄存器只有在设置了 Enable Interrupt 参数时才有效。

重要提示:IP 中断使能寄存器中的中断使能位与 IP 中断状态寄存器中的状态位一一对应。

中断事件由 AXI4-Lite 时钟记录在 IP 中断状态寄存器中,因此输入端口的变化必须在至少一个时钟周期内保持稳定,以保证中断捕获。

每个 IPISR 寄存器位都可以通过 Toggle-On-Write 行为通过软件设置或清除。

IP 中断使能寄存器和 IP 中断状态寄存器的位定义分别在表 2-9 和表 2-10 中给出。

image-20220504204903815

image-20220504204919951

Note 1. Toggle-On-Write (TOW) access toggles the status of the bit when a value of 1 is written to the corresponding bit.

Programming Sequence

通过以下步骤有助于访问 AXI GPIO 内核(写IDE的程序)。

对于启用中断时的输入端口,请执行以下步骤:

1.将 GPIOx_TRI 寄存器中的相应位写入 1,将端口配置为输入。

2.通过设置IP中断使能寄存器中的相应位来使能通道中断; 也可以通过将全局中断寄存器的第 31 位设置为 1 来启用全局中断。

3.接收到中断时,读取 GPIOx_DATA 寄存器中的相应位。通过将值 1 写入相应位来清除 IP 中断状态寄存器中的状态。

对于未使能中断时的输入端口,请使用以下步骤:

1.通过将 GPIOx_TRI 寄存器中的相应位写入值为 1,将端口配置为输入。

2.取 GPIOx_DATA 寄存器的对应位。

对于输出端口,使用以下步骤:

1.通过将 GPIOx_TRI 寄存器中的相应位写入值为 0,将端口配置为输出。

2.写入 GPIOx_DATA 寄存器中的相应位。

The Corresponding Code

1.设置头文件

#include “xgpio.h”

2.导入AXI_GPIO的器件ID

#define GPIO_DEVICE_ID XPAR_GPIO_0_DEVICE_ID

3.找AXI_GPIO的中断号

#define INTC_GPIO_INTERRUPT_ID XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR

( #define XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR 61U)//这里中断号为61

具体的PS-PL的共享外设中断号看:Shared Peripheral Interrupts (SPI),【UG585 7.2.3】

image-20220511200348424

4.设置通道

#define GPIO_CHANNEL1 1 //因为AXI有两个通道

5.对AXI_GPIO进行初始化

XGpio_Initialize(XGpio * InstancePtr, u16 DeviceId) //根据提供的DeviceID去初始化XGpio的实例,里面包含了查找配置和初始化的函 数,不需要在main中再次声明

XGpio * InstancePtr:针对实例的指针,用时需加&取地址符号

u16 DeviceId:AXI_GPIO器件的ID

如果函数中

6.对AXI_GPIO进行配置

(1) 将端口配置为输入

XGpio_SetDataDirection(XGpio *InstancePtr, unsigned Channel, u32 DirectionMask) //将 GPIOx_TRI 寄存器中的相应位写入 1,将端口配置为输入。

DirectionMask:是一个位掩码,指定哪些离散是输入和哪些是输出。 设置为 0 的位为输出,设置为 1 的位为输入。(value:0x00000001)

(2) 打开全局中断使能

XGpio_InterruptGlobalEnable(XGpio *InstancePtr)

(3) 打开通道中的信号对应的使能

XGpio_InterruptEnable(XGpio *InstancePtr, u32 Mask)

Mask:同上面的DirectionMask

7.设置中断系统

SetupInterruptSystem(XScuGic *GicInstancePtr, XGpioPs *AXI_Gpio, u16 AXI_GpioIntrId);

(1)查找GIC器件配置信息,并进行初始化

IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
XScuGic_CfgInitialize(GicInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);

(2)中断处理函数中“三板斧”

// 初始化ARM处理器异常语柄
Xil_ExceptionInit();
//来给IRQ异常注册处理程序
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, GicInstancePtr);
//使能处理器的中断
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);

(3)关联中断处理函数

XScuGic_Connect(IntcInstancePtr, IntrId, (Xil_ExceptionHandler)GpioHandler, InstancePtr);

(4)为AXI_GPIO器件使能中断

XScuGic_Enable(XScuGic *InstancePtr, u32 Int_Id)

(5)设置中断的优先级和、触发类型

XScuGic_SetPriorityTriggerType(XScuGic *InstancePtr, u32 Int_Id, u8 Priority, u8 Trigger);

//Priority是 IRQ 资源的新优先级。 0 是最高优先级,0xF8(248) 是最低优先级。 支持 32 个优先级,步长为 8。因此支持的优先级为 0、8、16、32、40 …、248。

//Trigger 触发器是 IRQ 资源的新触发器类型。每个位对描述一个 INT_ID 的配置。

(SFI Read Only b10 always

PPI Read Only depending on how the PPIs are configured.

b01 Active HIGH level sensitive

b11 Rising edge sensitive

SPI LSB is read only.

b01 Active HIGH level sensitive

b11 Rising edge sensitive)

实验要求

本章的实验任务是通过调用 AXI GPIO IP 核,使用中断机制,实现正点原子xc7010领航者底板上 PL 端按键控制核心板上 PS 端 LED 的功能。

系统框图

image-20220523101922480

实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include "stdio.h"
#include "xparameters.h"
#include "xscugic.h"
#include "xgpiops.h"
#include "sleep.h"
#include "xgpio.h"
#define GPIO_DEVICE_ID XPAR_XGPIOPS_0_DEVICE_ID//the id information of gpio
#define AXI_GPIO_ID XPAR_GPIO_0_DEVICE_ID//the id information of axi gpio
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID//the id information of Gic
#define AXI_GPIO_INTERRUPT_ID XPAR_FABRIC_AXI_GPIO_0_IP2INTC_IRPT_INTR//AXI GPIO 的中断号是61

//the led on ps
#define MIO0_LED 0
//the key on pl
#define MIO12_KEY 12
//AXI GPIO channel1
#define GPIO_CHANNEL1 1

XGpioPs_Config * ConfigPtr;
XScuGic_Config *IntcConfig; /* Instance of the interrupt controller */

XGpioPs Gpio;
XScuGic Intc;
XGpio AXI_Gpio; /* The Instance of the GPIO Driver */
void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpio *AXI_Gpio, u16 AXI_GpioIntrId);
void IntrHandler();
u32 key_press = 0;
int main(){
u32 led_value = 0;
printf("GPIO AXI yyds!\n\r");

//初始化GPIO
//根据器件的ID、查找器件的配置信息(配置PS端的GPIO),初始化GPIO驱动
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
XGpioPs_CfgInitialize(&Gpio, ConfigPtr, ConfigPtr->BaseAddr);
//对AXI GPIO进行初始化
XGpio_Initialize(&AXI_Gpio, AXI_GPIO_ID);

//把PS GPIO的方向设置成输出,并设置打开输出使能
XGpioPs_SetDirectionPin(&Gpio, MIO0_LED, 1);
XGpioPs_SetOutputEnablePin(&Gpio, MIO0_LED, 1);

//对AXI GPIO的方向设置
XGpio_SetDataDirection(&AXI_Gpio, GPIO_CHANNEL1, 0x00000001);//把最低位设置为输入

//设置中断系统
SetupInterruptSystem(&Intc, &AXI_Gpio, AXI_GPIO_INTERRUPT_ID);
//写数据到GPIO的输出引脚(调用函数)

while(1){
if(key_press)
{
led_value = ~led_value;
key_press = 0;

//清楚中断缓存
XGpio_InterruptClear(&AXI_Gpio, 0x00000001);
//将产生中断的值写入GPIO端口
XGpioPs_WritePin(&Gpio, MIO0_LED, led_value);
//按键消抖,在下一次初始化来之前延时一段时间
usleep(200000);
//重新打开通道一中断使能
XGpio_InterruptEnable(&AXI_Gpio, 0x00000001);
}
}
return 0;
}

void SetupInterruptSystem(XScuGic *GicInstancePtr, XGpio *AXI_Gpio,
u16 AXI_GpioIntrId)
{
//查找GIC器件配置信息,并进行初始化
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
XScuGic_CfgInitialize(GicInstancePtr, IntcConfig, IntcConfig->CpuBaseAddress);


// 初始化ARM处理器异常语柄
Xil_ExceptionInit();
//来给IRQ异常注册处理程序
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT, (Xil_ExceptionHandler)XScuGic_InterruptHandler, GicInstancePtr);
//使能处理器的中断
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);


//关联中断处理函数
XScuGic_Connect(GicInstancePtr, AXI_GpioIntrId,(Xil_ExceptionHandler)IntrHandler, (void *)AXI_Gpio);
/* Enable the interrupt for the AXI_GPIO device. */
XScuGic_Enable(GicInstancePtr, AXI_GpioIntrId);
//0xA0:中断优先级,0x1是:中断类型是高电平有效
XScuGic_SetPriorityTriggerType(IntcInstancePtr, IntrId, 0xA0, 0x3);
//打开AXI_GPIO IP的中断使能
XGpio_InterruptGlobalEnable(AXI_Gpio);//打开全局中断
XGpio_InterruptEnable(AXI_Gpio, 0x00000001);//打开通道信号对应的使能,把外设里面的中断打开
}

void IntrHandler(){
printf("interrupt is coming\n\r");
key_press = 1;
//关闭通道1使能信号
XGpio_InterruptDisable(&AXI_Gpio, 0x00000001);
}