【zynq课程笔记】【裸机】【第12课 】【AXI_GPIO原理与应用】
更多课程笔记请查看:【zynq裸机编程课程笔记合集】http://www.corecourse.cn/forum.php?mod=viewthread&tid=29095
1 介绍
在前面课程中,我们带领大家一起学习了PS GPIO的原理及基本使用。并最终通过C编程,实现了按键控制LED的效果。
但是Zynq上GPIO无论是MIO还是EMIO,都是属于PS侧的资源,相当于是硬核。而作为一个PS与PL相互协作的平台,当PS侧的GPIO硬核不够用或者无法使用的场合,我们能否使用PL端的逻辑资源来构建一个或多个GPIO软核呢?当然是可以的, Xilinx官方为我们提供了一个名为AXI GPIO的软核。该核使用标准的AXI总线与PS交互,用户可以通过AXI总线,控制AXI GPIO的输入输出模式、输出值、读取指定引脚等。该IP核的结构如下:
图 3 4AXI GPIO框图
从图中可以看出,最右侧有两个具有三态输出功能的端口,分别为GPIO和GPIO2,这也就意味着该控制器可以最多提供2个通道的GPIO。
每个通道的GPIO都有3个标准的信号,也就是输出值(GPIO_O)、输入值(GPIO_I)以及管脚输出使能控制信号(GPIO_T)。
对于输出值,使用一个名为GPIO_DAT的寄存器/D触发器存储所需要输出的值
对于输入值,使用一个名为GPIO_DATA_IN的寄存器/D触发器存储GPIO Pin管脚上的值。
对于输出使能控制信号,使用一个名为GPIO_TRI的寄存器/D触发器存储设置状态。
这两个三态缓冲器工作时,其输入输出状态受GPIO_T信号控制。当GPIO_T=0时,为输出态,三态缓冲器会输出GPIO_O的值;当GPIO_T=1时,为输入态,此时GPIO_I的值会输入到AXI GPIO中。
因此,我们只需要通过对这3个寄存器进行读写,就能够实现对该Pin的状态的控制和读取。
这些寄存器由谁读写,又是通过什么方式读写呢?这个就是我们刚刚提到的,PS通过AXI-Lite总线来读写这些寄存器。
另外该控制器还提供了中断检测逻辑,以及中断控制寄存器,用于中断检测以及中断使能/屏蔽/状态显示。
2 课程目的
接下来我们将以该IP核为例,通过拨码开关控制LED的设计,带领大家了解AXI GPIO核的使用,体会在使用时与PS GPIO的异同点。
本次设计中,将会使用到AXI GPIO的两个通道,其中一整个通道被设置为输入,用于获取拨码开关的电平;另一整个通道被设置为输出,用于驱动LED显示。
3 所包含硬件
由于开发板上PL端仅有一个LED灯,且不包含拨码开关,不能单独完成本次设计,因此我们还需要用到一个专门为0基础学习FPGA和ARM编程的用户设计的EDA拓展板。板上包含8个拨码开关和8个可由用户控制的LED灯,非常适合本次设计。
拓展板的更多资料可以查看该帖:
http://www.corecourse.cn/forum.php?mod=viewthread&tid=28643
4 系统框图
在开始设计之前我们首先要对整个系统有个大致的认识,这里我给大家做了一张简易设计图。
在设计时,AXI GPIO的通道1(GPIO)被配置为输入模式,用来接拨码开关,通道2(GPIO2)被设置为输出模式,用来接LED灯。AXI GPIO会捕获用户通过拨码开关设置的电平,PS则通过AXI4-Lite接口读取对应寄存器,以获知该电平值,然后ARM将需要设置的LED的亮灭状态值通过AXI4-Lite接口传输回AXI GPIO,作为通道2(GPIO2)的输出,驱动LED的亮灭显示。
5 创建工程
介绍完了系统的模型之后,我们就可以使用Vivado和SDK来进行系统的设计和实现了。
1. 创建一个名为AXI_GPIO的Vivado工程
2. 新建blockdesign设计
3. 添加ZYNQ、AXI GPIO IP核
6 IP配置与端口连接
(1) Zynq核
配置DDR型号,MT41K128M16 JT-125
(2) AXI GPIO核
勾选Enable Dual Channel,使能双通道
设置两个通道的GPIO Width为8,对应8个拨码开关,8个LED
另外介绍一点,在GUI配置界面可以将某一通道设置为全输入/输出模式,这种情况下,该通道的GPIO就只能输入或者输出了,这种配置在GPIO功能和使用场景明确的情况下可以有效降低PL逻辑资源的使用虑。本实验考虑到灵活性以及让大家对GPIO的各项功能有较为明确的了解,这里不设置单输入或者输出模式。
(3) 端口连接
点击“Run Block Automation”和“Run Connection Automation”
7 管脚约束
当完成block design设计之后,我们还需要对该block design生成输出文件(generate output products),并生成一个例化了该设计的HDL格式的顶层文件。
当顶层文件生成好之后,点击“Open Elaborated Design”对设计进行详细的分析,分析完成后就可以自动打开管脚约束界面进行管脚约束,如果显示的界面不是管脚分配,大家可以通过手动在右上角的下拉列表中选择I/O planning选项卡以切换到IO分配界面。关于ACZ702开发版各项功能对应的管脚信息,可以直接在下述帖子中查看。
ACZ702开发板管脚信息表
http://www.corecourse.cn/forum.php?mod=viewthread&tid=29058
以下为本次实验所用功能对应的引脚分配表:
Pin Name
Signial Name
Pin NO.
Pin Name
Signial Name
Pin NO.
SW7
gpio_rtl_0_tri_io
E17
LED7
gpio_rtl_1_tri_io
K18
SW6
gpio_rtl_0_tri_io
D18
LED6
gpio_rtl_1_tri_io
H17
SW5
gpio_rtl_0_tri_io
H15
LED5
gpio_rtl_1_tri_io
J18
SW4
gpio_rtl_0_tri_io
F16
LED4
gpio_rtl_1_tri_io
K19
SW3
gpio_rtl_0_tri_io
J14
LED3
gpio_rtl_1_tri_io
G18
SW2
gpio_rtl_0_tri_io
G14
LED2
gpio_rtl_1_tri_io
G20
SW1
gpio_rtl_0_tri_io
L15
LED1
gpio_rtl_1_tri_io
G19
SW0
gpio_rtl_0_tri_io
K14
LED0
gpio_rtl_1_tri_io
G17
分配完成后将引脚电平标准修改为LVCMOS33,然后保存约束文件,命名为AXI_GPIO.xdc,
到此,我们已经完成了整个系统的硬件系统部分的设计,接下来,我们就可以点击generate bitstream来生成本系统的PL配置文件,也就是我们常说的bit文件。
8 查看编译报告
等待比特流生成完成,我们就可以通过点击Project Summary图标,并切换到Dashboard选项卡中,来查看当前设计的资源使用等信息。
9 创建SDK工程并添加实例源码
通过前面的操作,我们已经完成了本系统的硬件系统的设计,并得到了相应的FPGA配置文件(bit)。
接下来我们还需要针对该硬件系统,生成对应的描述文件,只有基于这些描述文件,我们才能在SDK中对该系统进行正确的编程。
在菜单栏中点击File->Export->Export Hardware,以生成本系统的硬件描述文件。导出时请务必勾选包含bitstream文件,因为本系统已经使用到了FPGA侧的逻辑资源(AXI_GPIO软核)。
然后我们就可以点击File->Launch SDK来打开Vivado提供的SDK集成开发软件。然后在软件中创建一个空白模板的SDK工程,创建好之后,将我们为本节课准备好的例程源码直接拷贝到工程中。
(添加头文件包含路径)
这样,我们的应用程序就创建完了,程序写完之后,我们首先运行一次程序,带领大家体验一下实验效果,然后再来回头分析我们的程序内容,通过分析程序实现的思路,带领大家掌握和学习AXI_GPIO的使用方法。
10 硬件连接
本次硬件连接如下图所示,
将EDA拓展板连接在开发板40pin拓展接口上并连接好调试接口即可。由于本实验只进行简单的LED驱动,所以整体设计功耗不高,因此不需要使用独立供电,使用调试口Type-C供电就足够。使用调试口供电时,右侧的电源开关需要拨到上端USB侧。
(Run Configuration - SystemDebugger)
11 运行程序
现象:拓展板上拨码开关控制与之对应的LED灯亮灭(SW0控制LED0...SW7控制LED7)
12 代码分析
完成了程序效果的演示,接下来一起就来看看,我们的程序是按照怎样的思路来控制AXI_GPIO,实现我们想要的功能的。
#include"COMMON.h"
int main(void)
{
uint32_t State;
AXI_GPIO_Init(&AXI_GPIO0,GPIO_0_ID); //初始化AXI GPIO0
AXI_GPIO_Set_Channel(&AXI_GPIO0, XGPIO_IR_CH1_MASK, 0xFF, 0);//设置通道1为输入
while(1){
State = XGpio_DiscreteRead(&AXI_GPIO0,XGPIO_IR_CH1_MASK);//读取通道1输入的值
AXI_GPIO_Set_Channel(&AXI_GPIO0, XGPIO_IR_CH2_MASK, 0, State);//将通道2设置为输出,输出从通道1读取的值
}
return 0;
}
12.1 头文件分析:
对AXI GPIO的操作属于PL端的操作,所以包含的头文件为xgpio.h(GPIO对应xgpiops.h),该头文件包含Xilinx 通用 I/O (XGpio) 设备驱动程序的软件 API 定义。
12.2 代码分析:
1. 根据xparameters.h中查找到的器件ID查询配置表初始化AXI GPIO
2. 使用AXI_GPIO_Set_Channel()函数设置通道1为输入,并设置输出值为0
进入函数分析,这里分两步,首先是设置方向,本质使用的是XGpio_SetDataDirection()函数,底层是调用的XGpio_Out32()函数对指定通道的3态控制器寄存器直接写操作
XGpio_WriteReg(InstancePtr->BaseAddress,
((Channel - 1) * XGPIO_CHAN_OFFSET) + XGPIO_TRI_OFFSET,
DirectionMask);
接下来是输出值,这里使用的XGpio_DiscreteWrite()函数,通过XGpio_WriteReg()函数实现,底层是调用的XGpio_Out32()函数对指定通道的数据寄存器直接写操作
XGpio_WriteReg(InstancePtr->BaseAddress,
((Channel - 1) * XGPIO_CHAN_OFFSET) + XGPIO_DATA_OFFSET,
Data);
这里由于我们已经将通道1设置为了输入模式,此时对寄存器进行写操作无效。
3. 读取通道1状态,保存在state中。这里使用的是XGpio_DiscreteRead()函数,对通道1的数据寄存器读操作(输入状态,读操作能正常完成)
return XGpio_ReadReg(InstancePtr->BaseAddress,
((Channel - 1) * XGPIO_CHAN_OFFSET) +
XGPIO_DATA_OFFSET);
4. 再次使用AXI_GPIO_Set_Channel()函数,设置通道2 为输出,并将State的值作为通道2输出值。
寄存器空间及涉及寄存器描述
表 1寄存器映射
偏移地址
寄存器名
访问类型
默认值
描述
0x0000
GPIO_DATA
R/W
0x0
通道1 AXI GPIO数据寄存器
0x0004
GPIO_TRI
R/W
0x0
通道1 AXI GPIO3态控制寄存器
0x0008
GPIO2_DATA
R/W
0x0
通道2 AXIGP[IO数据寄存器
0x000C
GPIO2_TRI
R/W
0x0
通道2 AXI GPIO3态寄存器
0x011C
GIER
R/W
0x0
全局中断使能寄存器
0x0128
IP IER
R/W
0x0
IP中断使能寄存器
0x0120
IP ISR
R/TOW
0x0
IP中断状态寄存器
表 2AXI GPIO数据寄存器描述
位
字段名
访问类型
复位值
描述
GPIOx_DATA
R/W
对应通道默认输出值
AXI GPIO数据寄存器
对于每个被配置为输入的I/O:
读:读取输入引脚的值
写:无效
对于每个被配置为输出的I/O:
读:读取这些位将会返回0
写:将值写入相应寄存器位和输出引脚。
这里需要注意的一点是,官方手册中给出的描述是对于每个被配置为输出的I/O进行读操作时都会返回0。如下图
但是在实际操作中我们发现,只有在IP核中将通道设置为“All Outputs”时,读取该通道的值才会为0;而如果是通过软件配置的,则能够正常读取。
表 3AXI GPIO 3态寄存器位描述
位
字段名
访问类型
复位值
描述
GPIOx_TRI
R/W
对应通道默认3态输出值
AXI GPIO 3态控制寄存器
每个 I/O 引脚都可单独编程为输入或输出。
对于每一位:
0=I/O引脚配置为输出
1=I/O引脚配置为输入
编程方法和思路:
到了这里,我们就完成了AXI GPIO 的拨码开关控制LED亮灭的实验,从这个实验我们可以看到,在进行编程设计时,无论是PS GPIO还是AXI GPIO,使用前都需要进行如下几个步骤:
1. 初始化GPIO驱动程序
2. 设置指定引脚方向
3. 读/写指定管脚的值/状态
不同点则在于PS GPIO在输出指定管脚的值/状态时,需要先使能该引脚作为输出。而在硬件逻辑系统设计方面,AXI GPIO则是与EMIO类似,由于使用的是PL端引脚,在导出后还需要进行管脚分配和约束。
13 思考题
13.1 总结
到这里,本节课程内容就已经基本结束了,在本次的设计中,我们通过软件编程,将AXI GPIO的两个通道分别设置为了输入和输出,以方便代码的设计。实际上AXI GPIO核每个通道的每一位都是能独立设置其输入/输出模式并单独读写的,具体的实现方式可以参考我们在AXI GPIO应用库中提前封装好的AXI_GPIO_SetPin_Dir()、AXI_GPIO_SetPin()以及AXI_GPIO_GetPin()函数,三个函数功能如下:
AXI_GPIO_SetPin_Dir() 修改AXI GPIO某通道的某PIN的输入输出模式
AXI_GPIO_SetPin() 控制指定GPIO口输出高/低电平
AXI_GPIO_GetPin() 读取指定GPIO口的输入电平
关于我们为大家提供的示例程序的内容,就给大家简单介绍到这里。接下来,给大家布置两个思考题,或者说是课后小作业。
13.2 课后习题
配置AXI GPIO为单通道16位位宽,通过软件编程,将高8位配置为输入,获取拨码开关电平。将低8位设置为输出,使用从拨码开关获取的电平控制LED,实现拨码开关控制LED亮灭设计。
有能力的同学尝试根据对寄存器的理解,或者对我们提供的示例程序的理解,使用直接操作寄存器的方式来实现本实验。
关于本节课程所使用到的文件、文档和程序等内容,大家可以到下述链接下载。
【zynq裸机编程课程笔记合集】
http://www.corecourse.cn/forum.php?mod=viewthread&tid=29095
或者直接到www.corecourse.cn中搜索“zynq裸机编程课程笔记合集”
课程完整工程文件包括Vivado工程和SDK中的源码(工程发布前已经执行了reset_project操作,故大家下载后需要先执行generate bitstream操作)
C源码
src路径下源码
ACZ702_Lib路径下源码
AXI_GPIO
SCU
新课程解锁,开始追番
本楼层作者占位,主要用来统一解答大家学习本节课程过程中遇到的常见问题,后续会再次编辑完善。学习者有疑问的,请在本楼层之后开始提问和讨论。 本帖最后由 CJC 于 2022-8-20 15:33 编辑
作业1: 发现小梅哥的代码简洁又好用 等等再补上
原代码#include "./BSP/AXI_GPIO/axi_gpio.h"
#include "sleep.h"
XGpio xgpio;
#define AXI_GPIO_ID XPAR_GPIO_0_DEVICE_ID
#define SW0 axi_gpio_getpin(&xgpio,AXI_GPIO_CHANNEL_1,7)
#define SW1 axi_gpio_getpin(&xgpio,AXI_GPIO_CHANNEL_1,6)
#define SW2 axi_gpio_getpin(&xgpio,AXI_GPIO_CHANNEL_1,9)
#define SW3 axi_gpio_getpin(&xgpio,AXI_GPIO_CHANNEL_1,8)
#define SW4 axi_gpio_getpin(&xgpio,AXI_GPIO_CHANNEL_1,11)
#define SW5 axi_gpio_getpin(&xgpio,AXI_GPIO_CHANNEL_1,10)
/* 高八位是拨码开关SW7-SW2对应着11-6
* 低八位是LED7-LED2对应着0-5
* */
int main(void)
{
axi_gpio_init(&xgpio,AXI_GPIO_ID);
/* 高八位设置为输入,低八位设置输出 */
XGpio_SetDataDirection(&xgpio, AXI_GPIO_CHANNEL_1,0XFC0);
while(1)
{
if (SW0 == 1)
{
axi_gpio_setpin(&xgpio,AXI_GPIO_CHANNEL_1,5,0x1);
}
else
{
axi_gpio_setpin(&xgpio,AXI_GPIO_CHANNEL_1,5,0x0);
}
if (SW1 == 1)
{
axi_gpio_setpin(&xgpio,AXI_GPIO_CHANNEL_1,4,0x1);
}
else
{
axi_gpio_setpin(&xgpio,AXI_GPIO_CHANNEL_1,4,0x0);
}
if (SW2 == 1)
{
axi_gpio_setpin(&xgpio,AXI_GPIO_CHANNEL_1,3,0x1);
}
else
{
axi_gpio_setpin(&xgpio,AXI_GPIO_CHANNEL_1,3,0x0);
}
if (SW3 == 1)
{
axi_gpio_setpin(&xgpio,AXI_GPIO_CHANNEL_1,2,0x1);
}
else
{
axi_gpio_setpin(&xgpio,AXI_GPIO_CHANNEL_1,2,0x0);
}
if (SW4 == 1)
{
axi_gpio_setpin(&xgpio,AXI_GPIO_CHANNEL_1,1,0x1);
}
else
{
axi_gpio_setpin(&xgpio,AXI_GPIO_CHANNEL_1,1,0x0);
}
if (SW5 == 1)
{
axi_gpio_setpin(&xgpio,AXI_GPIO_CHANNEL_1,0,0x1);
}
else
{
axi_gpio_setpin(&xgpio,AXI_GPIO_CHANNEL_1,0,0x0);
}
}
return 0;
}
这里记录下学习中遇见的问题,AXI_GPIO硬件信息文件不是xgpio_hw.h而是xgpio_l.h,这个l是什么意思还没想明白
作业二,受到硬件的限制,SW和LED各配置了6位完成
#define AXI_GPIO_BASEADDR XPAR_AXI_GPIO_0_BASEADDR
#define SW0 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW0_PIN) & 0x1)
#define SW1 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW1_PIN) & 0x1)
#define SW2 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW2_PIN) & 0x1)
#define SW3 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW3_PIN) & 0x1)
#define SW4 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW4_PIN) & 0x1)
#define SW5 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW5_PIN) & 0x1)
#define SW0_PIN 7
#define SW1_PIN 6
#define SW2_PIN 9
#define SW3_PIN 8
#define SW4_PIN 11
#define SW5_PIN 10
#define LED0 5
#define LED1 4
#define LED2 3
#define LED3 2
#define LED4 1
#define LED5 0
int main(void)
{
uint32_t reg_val = 0;
uint32_t data = 0;
/* 高八位设置为输入,低八位设置输出 */
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_TRI_OFFSET,0XFC0);
while(1)
{
if (SW0 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED0);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED0));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW1 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED1);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED1));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW2 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED2);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED2));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW3 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED3);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED3));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW4 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED4);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED4));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW5 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED5);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED5));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
}
return 0;
} 这里记录下学习中遇到的问题,AXIGPIO的硬件文件不是xgpio_hw.h,二是xgpio_l.h,这个l代表什么意思还没想明白
#define AXI_GPIO_BASEADDR XPAR_AXI_GPIO_0_BASEADDR
#define SW0 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW0_PIN) & 0x1)
#define SW1 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW1_PIN) & 0x1)
#define SW2 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW2_PIN) & 0x1)
#define SW3 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW3_PIN) & 0x1)
#define SW4 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW4_PIN) & 0x1)
#define SW5 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW5_PIN) & 0x1)
#define SW0_PIN 7
#define SW1_PIN 6
#define SW2_PIN 9
#define SW3_PIN 8
#define SW4_PIN 11
#define SW5_PIN 10
#define LED0 5
#define LED1 4
#define LED2 3
#define LED3 2
#define LED4 1
#define LED5 0
int main(void)
{
uint32_t reg_val = 0;
uint32_t data = 0;
/* 高八位设置为输入,低八位设置输出 */
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_TRI_OFFSET,0XFC0);
while(1)
{
if (SW0 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED0);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED0));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW1 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED1);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED1));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW2 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED2);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED2));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW3 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED3);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED3));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW4 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED4);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED4));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW5 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED5);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED5));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
}
return 0;
} 作业二: 需要提醒的是AXIGPIO的硬件信息文件在xgpio_l.h中,但不太理解这个l是什么意思
用寄存器的方式要注意 使用读改写的编程方式,否则很容易影响到其他位
#define AXI_GPIO_BASEADDR XPAR_AXI_GPIO_0_BASEADDR
#define SW0 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW0_PIN) & 0x1)
#define SW1 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW1_PIN) & 0x1)
#define SW2 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW2_PIN) & 0x1)
#define SW3 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW3_PIN) & 0x1)
#define SW4 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW4_PIN) & 0x1)
#define SW5 ((Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET) >> SW5_PIN) & 0x1)
#define SW0_PIN 7
#define SW1_PIN 6
#define SW2_PIN 9
#define SW3_PIN 8
#define SW4_PIN 11
#define SW5_PIN 10
#define LED0 5
#define LED1 4
#define LED2 3
#define LED3 2
#define LED4 1
#define LED5 0
int main(void)
{
uint32_t reg_val = 0;
uint32_t data = 0;
/* 高八位设置为输入,低八位设置输出 */
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_TRI_OFFSET,0XFC0);
while(1)
{
if (SW0 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED0);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED0));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW1 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED1);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED1));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW2 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED2);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED2));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW3 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED3);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED3));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW4 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED4);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED4));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
if (SW5 == 1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val | (1 << LED5);
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
else
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val & (~(1 << LED5));
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
}
return 0;
}
CJC 发表于 2022-8-20 10:57
作业二: 需要提醒的是AXIGPIO的硬件信息文件在xgpio_l.h中,但不太理解这个l是什么意思
用寄存器的方式要注 ...
这是看了小梅哥AXIGPIO视频(一)写出来的代码
下面是看了小梅哥编程思路后的代码(寄存器方式) PS:我的拨码开关和LED只有6个
int main(void)
{
uint32_t reg_val = 0;
uint32_t data = 0;
/* 高六位设置为输入,低六位设置输出 */
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_TRI_OFFSET,0XFC0);
while(1)
{
reg_val = Xil_In32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET);
data = reg_val >> 6;
Xil_Out32(XPAR_AXI_GPIO_0_BASEADDR + XGPIO_DATA_OFFSET,data);
}
}
CJC 发表于 2022-8-19 21:40
作业1: 发现小梅哥的代码简洁又好用 等等再补上
原代码#include "./BSP/AXI_GPIO/axi_gpio.h"
#include "sl ...
改进后:
XGpio xgpio;
#define AXI_GPIO_ID XPAR_GPIO_0_DEVICE_ID
int main(void)
{
uint32_t reg_val = 0;
uint32_t data = 0;
axi_gpio_init(&xgpio,AXI_GPIO_ID);
/* 高六位设置为输入,低六位设置输出 */
XGpio_SetDataDirection(&xgpio, AXI_GPIO_CHANNEL_1,0XFC0);
while(1)
{
reg_val = XGpio_DiscreteRead(&xgpio, AXI_GPIO_CHANNEL_1);
data = reg_val >> 6;
XGpio_DiscreteWrite(&xgpio, AXI_GPIO_CHANNEL_1, data);
}
}
页:
[1]
2