ZYNQ-Vitis(SDK)裸机开发之(五)PL端AXI GPIO的使用

本文详细介绍了在ZYNQ7020芯片上使用Vivado和Vitis进行AXIGPIO的配置,包括AXIGPIOIP核的功能、BlockDesign中的添加和连线,以及如何在Vitis工程中编写代码以控制GPIO、中断和中断处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

一、ZYNQ中PL端AXI GPIO简介

1.AXI GPIO IP核介绍

二、搭建Block Design

1.添加AXI GPIO的IP核

2.Block Design连线设计

三、编写Vitis工程

1.AXI GPIO Vitis操作说明

2. 添加AXI GPIO操作代码


例程开发环境:

SOC芯片:ZYNQ7020

开发环境:Vivado2020.2,Vitis2020.2

目前网上对于AXI GPIO的详细使用方法介绍的很少,基本都是一个通道一个IO,或者几个IO,统一进行配置,并没有对不同bit的不同操作进行深入的讲解,因此才写了本篇文章,也是希望大家学习过程中少走一些弯路,坑我都替你们踩过了。

  • 一、ZYNQ中PL端AXI GPIO简介

1.AXI GPIO IP核介绍

每个AXI GPIO IP核具有两个通道GPIO和GPIO2,添加IP后,默认只开启通道1,如果使用双通道,则勾选上Enable Dual Channel

每个通道具有32bit的可控制IO,每个bit可控制一个IO的输入或输出,在IP核中可以将整个通道32bit全部设置成输入模式或者输出模式

GPIO Width:选择需要使用几个bit的IO;

Default Output Value:32个bit上电后默认输出值,也可在Vitis中通过代码配置

Default Tri State Value:32个bit的IO默认方向模式,FFFFFFFF即全部是输入模式

如果需要使用AXI GPIO的中断输入,则需要勾选上Enable Interrupt,并将irpt中断引脚连接到PS端,用于PS端读取中断状态进行控制

  • 二、搭建Block Design

本项目工程是在ZYNQ-Vitis(SDK)裸机开发之(一)串口实验工程基础上开发的,一些block design的设计方法,Vitis工程的建立方法等,均在该篇文章中进行了详细的讲解,大家可以去参考:

ZYNQ-Vitis(SDK)裸机开发之(一)串口收发使用:PS串口+PL串口、多个串口使用方法

1.添加AXI GPIO的IP核

在原有串口工程基础上添加AXI GPIO的IP核

右击block design空白处,点击Add IP,弹出窗口中输入AXI GPIO,双击添加即可

添加后,按照如下配置

在这里我需要使用两个IO,因此只需要使用通道1,并且在通道1中GPIO Width只需要填2个即可;其中bit0作为蜂鸣器的输出控制IO,bit1作为按键输入读取IO,由于不知道外部按键何时会被按键,因此需要通过中断来检测外部按键的状态,将Enable Interrupt勾选上,点击OK。

2.Block Design连线设计

AXI GPIO IP核添加后,按照下图进行连线,通过concat将AXI GPIO的中断信号连接到PS端

输出产品

生成顶层文件

在xdc文件中添加AXI GPIO引脚的约束,我这里将bit0约束到蜂鸣器,将bit1约束到外部按键

最后综合、实现、生成bit文件。

  • 三、编写Vitis工程

1.AXI GPIO Vitis操作说明

/*

 * AXI GPIO使用说明:

 * 1.每个AXI GPIO IP核有两个通道,在vivado中搭建block design时可以选择启用哪个通道,或者两个通道都启用

 * 2.如果要使用AXI GPIO中断功能,在在vivado中搭建block design时需要将中断使能勾选上

 * 3.AXI GPIO每个通道具有32个bit的可控IO,每个IO,即每个bit均可进行配置操作

 * 4.设置输出方向、设置IO值以及读取IO值时,操作对象均是对应每个bit,而不是每个通道,每个bit都可以单独设置

 * 5.设置中断启动、关闭时,是对每个通道进行操作,例如打开中断是直接打开一整个通道,也就是32bit的IO全部的中断全部打开,无法单独对某一bit进行中断配置

 * 6.该代码实现AXI GPIO读取按键中断输出,然后打开蜂鸣器,按键一次,打开蜂鸣器,再按键一次,关闭蜂鸣器,蜂鸣器:channel1bit0,按键:channel1bit1

 */

2. 添加AXI GPIO操作代码

头文件axi_gpio_hdl.h:

  1. 定义一些位操作的接口,因为AXI GPIO的官方接口都是按整个通道进行读写操作的,但是一个通道有32bit的IO,因此需要在自己代码中再将IO操作封装一层,细化到按bit操作
  2. 定义AXI GPIO设备ID号、中断ID号、通道号、通道中使用的bit号等
  3. 定义高低电平的枚举
  4. 定义AXI GPIO操作配置的结构体实例
  5. 声明AXI GPIO驱动初始化、中断初始化、中断处理、写操作、读操作的方法

具体头文件代码如下:

/*!
    \file    axi_gpio_hdl.h
    \brief   firmware functions to manage intr
    \version 2024-03-12, V1.0.0
	\author  tbj
*/

#ifndef AXI_GPIO_HDL_H
#define AXI_GPIO_HDL_H

#include "crc_cal.h"
#include "xgpio.h"

#ifdef __cplusplus
 extern "C" {
#endif

//位操作定义
#define SETBIT(x,y)  x|=(1<<y)  //把x中的第y位置1,从0开始数
#define CLRBIT(x,y)  x&=~(1<<y)  //把x中的第y位置0,从0开始数
#define RESERVEBIT(x,y) x^=(1<<y)  //把x中的第y位取反,从0开始数
#define GETBIT(x,y)  ((x)>>(y)&1)  //获取x中第y位的值,移位运算符的优先级大于逻辑按位运算符优先级

//AXI_GPIO设备ID
#define AXI_GPIO_DEVICE_ID 		XPAR_AXI_GPIO_0_DEVICE_ID
//AXI_GPIO中断ID
#define AXI_GPIO_INT_ID 		XPAR_FABRIC_GPIO_0_VEC_ID
//AXI_GPIO使用的通道号(一个AXI_GPIO一共两个通道,每个通道有32bit I/O 可控)
#define AXI_GPIO_CHANNEL1 		XGPIO_IR_CH1_MASK
//定义外设连接AXI GPIO对应哪个bit
#define AXI_GPIO_BEEP			0
#define AXI_GPIO_KEY			1

typedef enum{
	AXI_GPIO_VALUE_OFF = 0,
	AXI_GPIO_VALUE_ON,
}AXI_GPIO_VALUE;

//PL端 AXI GPIO 驱动实例
XGpio axi_gpio;

//AXI GPIO外设初始化函数
int axi_gpio_init(XGpio *axi_gpio_ptr, u8 channel_num, u32 direction);
//AXI GPIO中断初始化函数
int axi_gpio_intr_init(XScuGic *gic_inst_ptr, XGpio *axi_gpio_ptr, u16 AXI_GpioIntrId);
//AXI GPIO输入中断处理函数
void axi_gpio_key_Intr_Hander(void *CallBackRef);

//读取AXI GPIO相应通道值
u8 axi_gpio_read(XGpio *axi_gpio_ptr, u8 channel_num, u8 gpio_bit);
//设置AXI GPIO相应通道值
void axi_gpio_write(XGpio *axi_gpio_ptr, u8 channel_num, u8 gpio_bit, u8 gpio_bit_val);

#ifdef __cplusplus
}
#endif

#endif /* AXI_GPIO_HDL_H */

源文件axi_gpio_hdl.c:

  1. 对头文件中AXI GPIO驱动初始化、中断初始化、中断处理、写操作、读操作的方法进行实现

具体代码如下:

/*!
    \file    axi_gpio_hdl.c
    \brief   firmware functions to manage axi_gpio
    \version 2024-04-11, V1.0.0
	\author  tbj
*/

/*
 * AXI GPIO使用说明:
 * 1.每个AXI GPIO IP核有两个通道,在vivado中搭建block design时可以选择启用哪个通道,或者两个通道都启用
 * 2.如果要使用AXI GPIO中断功能,在在vivado中搭建block design时需要将中断使能勾选上
 * 3.AXI GPIO每个通道具有32个bit的可控IO,每个IO,即每个bit均可进行配置操作
 * 4.设置输出方向、设置IO值以及读取IO值时,操作对象均是对应每个bit,而不是每个通道,每个bit都可以单独设置
 * 5.设置中断启动、关闭时,是对每个通道进行操作,例如打开中断是直接打开一整个通道,也就是32bit的IO全部的中断全部打开,无法单独对某一bit进行中断配置
 * 6.该代码实现AXI GPIO读取按键中断输出,然后打开蜂鸣器,按键一次,打开蜂鸣器,再按键一次,关闭蜂鸣器,蜂鸣器:channel1bit0,按键:channel1bit1
 */

#include "axi_gpio_hdl.h"

/* @brief:初始化GPIO外设
 * @param:AXI GPIO对象指针
 * @param:AXI GPIO通道号
 * @param:AXI GPIO通道输入输出方向
 * @return:初始化结果
 */
int axi_gpio_init(XGpio *axi_gpio_ptr, u8 channel_num, u32 direction)
{
	int Status;

	//初始化 PL端 AXI GPIO 驱动
	Status = XGpio_Initialize(axi_gpio_ptr, AXI_GPIO_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	//设置 AXI GPIO通道方向
	XGpio_SetDataDirection(axi_gpio_ptr, channel_num, direction);

	return XST_SUCCESS;
}


/* @brief:初始化AXI GPIO的中断
 * @param:中断控制器对象指针
 * @param:AXI GPIO对象指针
 * @param:AXI GPIO中断号
 * @return:初始化结果
 */
int axi_gpio_intr_init(XScuGic *gic_inst_ptr, XGpio *axi_gpio_ptr, u16 AXI_GpioIntrId)
{
	//设置中断优先级和中断触发方式,0x01高电平中断,0x02是总触发中断,0x03是上升沿儿中断
	XScuGic_SetPriorityTriggerType(gic_inst_ptr, AXI_GpioIntrId, 0xA0, 0x01);
	//关联中断 ID 和中断处理函数
	XScuGic_Connect(gic_inst_ptr, AXI_GpioIntrId,
					(Xil_ExceptionHandler) axi_gpio_key_Intr_Hander, (void *) axi_gpio_ptr);

	//使能来自于 axi_Gpio 器件的中断
	XScuGic_Enable(gic_inst_ptr, AXI_GpioIntrId);
	//使能AXI GPIO通道1的中断
	XGpio_InterruptEnable(axi_gpio_ptr, AXI_GPIO_CHANNEL1);
	//使能全局中断-AXI GPIO中断依赖于全局中断
	XGpio_InterruptGlobalEnable(axi_gpio_ptr);

	return XST_SUCCESS;
}

/* @brief:AXI GPIO中断处理函数
 * @param:回调指针
 * @return:初始化结果
 */
void axi_gpio_key_Intr_Hander(void *CallBackRef){

	//记录上次蜂鸣器状态
	static u8 beep_value_temp = 0;
	//本次配置蜂鸣器的状态
	u8 beep_value = 0;
	XGpio *axi_gpio_ptr = (XGpio *)CallBackRef;

	usleep(20000); //延时 20ms,按键消抖
	//通过读取AXI GPIO channel1的bit1来判断按键有没有被按下
	if (axi_gpio_read(axi_gpio_ptr, AXI_GPIO_CHANNEL1, AXI_GPIO_KEY) == 0) {
		beep_value = RESERVEBIT(beep_value_temp, 0);
		//改变蜂鸣器状态
		axi_gpio_write(axi_gpio_ptr, AXI_GPIO_CHANNEL1, AXI_GPIO_BEEP, beep_value);
		XGpio_InterruptDisable(axi_gpio_ptr, AXI_GPIO_CHANNEL1);
	}
	//清除中断并重新使能中断
	XGpio_InterruptClear(axi_gpio_ptr, AXI_GPIO_CHANNEL1);
	XGpio_InterruptEnable(axi_gpio_ptr, AXI_GPIO_CHANNEL1);
}


/* @brief:读取AXI GPIO相应通道值
 * @param:AXI GPIO对象指针
 * @param:AXI GPIO通道号
 * @param:AXI GPIO需要操作的bit
 * @return:返回AXI GPIO对应bit的值
 */
u8 axi_gpio_read(XGpio *axi_gpio_ptr, u8 channel_num, u8 gpio_bit){
	u32 read_val = 0;
	read_val = XGpio_DiscreteRead(axi_gpio_ptr, channel_num);
	return GETBIT(read_val, gpio_bit);
}

/* @brief:设置AXI GPIO相应通道值
 * @param:AXI GPIO对象指针
 * @param:AXI GPIO通道号
 * @param:AXI GPIO需要操作的bit
 * @param:AXI GPIO需要操作的bit写入的值
 * @return:无
 */
void axi_gpio_write(XGpio *axi_gpio_ptr, u8 channel_num, u8 gpio_bit, u8 gpio_bit_val){

	u32 write_val = 0;
	//先读取AXI GPIO整个通道的值,防止写入某一bit将其余bit清除掉
	write_val = XGpio_DiscreteRead(axi_gpio_ptr, channel_num);
	//判断写1还是写0
	if(gpio_bit_val == 1){
		SETBIT(write_val, gpio_bit);
	}else if(gpio_bit_val == 0){
		CLRBIT(write_val, gpio_bit);
	}else{
		;
	}
	//将对应值写入AXI GPIO寄存器
	XGpio_DiscreteWrite(axi_gpio_ptr, channel_num, write_val);
}

最后在main.c中调用AXI GPIO初始化相关功能

int main()
{
	//AXI GPIO测试
#ifdef AXI_GPIO_Test
	//初始化AXI GPIO每个bit的输入输出方向,0:输出,1:输入;0x020即bit0为输出,bit1为输入
	u32 axi_gpio_dir = 0x02;
	axi_gpio_init(&axi_gpio, AXI_GPIO_CHANNEL1, axi_gpio_dir);
	//初始化AXI GPIO中断
	axi_gpio_intr_init(&GicIntrDevice, &axi_gpio, AXI_GPIO_INT_ID);
#endif

	while(1){

	}
    return 0;
}

 创作不易,希望大家点赞、收藏、关注哦!!!ヾ(o◕∀◕)ノ

对于想要在Zynq ZC706评估板上实现自定义AXI GPIO接口的工程师来说,首先需要熟悉Zynq-7000 XC7Z045 SoC的结构和AXIGPIO IP核的功能。以下是实现该接口所需的基本步骤和示例代码: 参考资源链接:[Zynq ZC706评估板详解与应用概述](https://wenku.csdn.net/doc/88c4meyr5q?spm=1055.2569.3001.10343) 1. 首先,在Xilinx Vivado设计套件中启动一个新项目,并选择Zynq ZC706评估板作为目标设备。 2. 在Vivado中,创建一个新的IP封装,选择AXIGPIO IP核,并根据需求配置其参数,如通道宽度、中断等。 3. 将自定义的AXIGPIO IP核集成到Zynq SoC的设计中,确保AXI接口与Zynq的处理器系统PS(Processing System)正确连接。 4. 生成并导出自定义的IP核和硬件描述文件,然后在Vivado中进行综合、实现以及生成比特流文件。 5. 使用Vivado SDKVitis创建一个新的软件应用项目,并将比特流文件加载到评估板上。 6. 编写软件驱动和应用程序代码,配置GPIO引脚为输入或输出模式,并实现基本的读写操作。 示例代码(VHDL): ```vhdl library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity custom_axi_gpio is Port ( s_axi_aclk : in std_logic; s_axi_aresetn : in std_logic; s_axi_awaddr : in std_logic_vector(11 downto 0); s_axi_awvalid : in std_logic; s_axi_awready : out std_logic; s_axi_wdata : in std_logic_vector(31 downto 0); s_axi_wstrb : in std_logic_vector(3 downto 0); s_axi_wvalid : in std_logic; s_axi_wready : out std_logic; s_axi_bresp : out std_logic_vector(1 downto 0); s_axi_bvalid : out std_logic; s_axi_bready : in std_logic; s_axi_araddr : in std_logic_vector(11 downto 0); s_axi_arvalid : in std_logic; s_axi_arready : out std_logic; s_axi_rdata : out std_logic_vector(31 downto 0); s_axi_rresp : out std_logic_vector(1 downto 0); s_axi_rvalid : out std_logic; s_axi_rready : in std_logic; gpio_io_o : out std_logic_vector(31 downto 0); gpio_io_i : in std_logic_vector(31 downto 0); gpio_io_t : out std_logic_vector(31 downto 0) ); end custom_axi_gpio; ``` 通过以上步骤和示例代码,您可以在Zynq ZC706评估板上实现自定义的AXI GPIO接口,进而控制和访问外部设备。如果您希望更深入地了解Zynq-7000 SoC的应用和开发,推荐阅读《Zynq ZC706评估板详解与应用概述》,该资源为工程师提供了全面的指导和实用的技术信息,从硬件配置到软件开发,都有详尽的说明和示例,能够帮助您在使用Zynq ZC706评估板时更加得心应手。 参考资源链接:[Zynq ZC706评估板详解与应用概述](https://wenku.csdn.net/doc/88c4meyr5q?spm=1055.2569.3001.10343)
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

披着假发的程序唐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值