REVIEW
关于PS端已经学习过: zynq PS端 GPIO-CSDN博客 zynq PS点灯-CSDN博客 C基础与SDK调试方法-CSDN博客 |
Zynq上GPIO无论是MIO还是EMIO,都是属于PS侧的资源,相当于是硬核。 而作为一个PS与PL相互协作的平台,当PS侧的GPIO硬核不够用或者无法使用的场合,我们能否使用PL端的逻辑资源来构建一个或多个GPIO软核呢? AXI_GPIO |
1. 今日摸鱼任务
简单学习对 AXI GPIO 核,
并以 AXI GPIO 核为基础,
完成 EDA 拓展板上拨码开关控制 LED 亮灭实验。
|
小梅哥视频: 12A_AXI_GPIO原理与应用(一)_哔哩哔哩_bilibili 12B_AXI_GPIO原理与应用(二)_哔哩哔哩_bilibili |
小梅哥课程笔记:【zynq课程笔记】【裸机】【第12课 】【AXI_GPIO原理与应用】 - ACZ702开发板 - 芯路恒电子技术论坛 - Powered by Discuz! (corecourse.cn) |
小梅哥教材: 03_【裸机教程】基于C编程的Zynq裸机程序设计与应用教程v2.4.5.pdf 第三章 基于 AXI GPIO 的 LED 亮灭 |
2. AXI 总线接口
AXI ,即 Advanced Extensible Interface(高级可扩展接口),它是由 Arm 定义的接口协议,包含在“高级微控制器总线架构 AMBA”标准中。
AXI是一种总线协议,是一种面向高性能、高带宽、低延迟的片内总线。
它的地址/控制和数据相位是分离的,支持不对齐的数据传输,同时在突发传输中,只需要首地址,同时分离的读写数据通道、并支持
Outstanding
传输访问和乱序访问,并更加容易进行时序收敛。
| |
在
ZYNQ7000
系列开发板上
AXI 协议,通常指
AXI4 协议。该协议共包含三种接口,也可以说是三种总线,分别为 AXI4
、
AXI-Lite
、
AXI-Stream
,在
ZYNQ
内部实现着
PS
与
PL 之间的交互,不同接口面向不同的应用场景,分别如下:
| |
AXI4:
(AXI4-Full)
|
主要面向
高性能地址映射
(
memory map)通信的需求,是面向地址映射的接口,在单地址传输的情况下最大允许 256 个时钟周期的数据突发长度。
AXI4
总线允许符合
AXI4 的系统实现非常高的数据吞吐量,同时还支持数据大小调整、多个
outstanding
操作和乱序事务处理。
在硬件级别,
AXI4
允许为每个
AXI
主从对使用不同的时钟构建系统。
此外,
AXI4 协议允许插入寄存器片(通常称为流水线级)以帮助时序收敛。
为了与
AXI4-Lite
区分,
AXI4
也被叫作
AXI4-Full
。
|
AXI4-Lite:
|
用于
简单、低吞吐量的内存映射
通信
(例如,与控制寄存器和状态寄存器之间的通信)
是一个轻量级的地址映射单次传输接口,占用很少的逻辑单元。
该接口是
AXI4
接口的简化版,突发长度从 256 被限制到
1,
也就意味着无法进行突发传输,逻辑资源的减少,
也就导致无法实现较为复杂功能。
|
AXI4-Stream:
|
主要面向
高速流数据传输
与 AXI4 的区别是没有了地址接口,因此不涉及读写数据的概念,数据只是进行简单的接收与发送。
这种方式减少了传输时的延时,允许无限制的数据突发传输规模。
|
虽然
ZYNQ
支持这三种总线,
但是在
ZYNQ
的
PS 与 PL
之间仅支持
AXI4
与
AXI4-Lite
总线,
AXI4-Stream
总线只能在
PL
中实现。
|
3. AXI_GPIO
Xilinx官方为我们提供了一个名为AXI GPIO的软核。该核使用标准的AXI总线与PS交互,用户可以通过AXI总线,控制AXI GPIO的输入输出模式、输出值、读取指定引脚等。该IP核的结构如下: |
从图中可以看出,最右侧有两个具有三态输出功能的端口,分别为GPIO和GPIO2,这也就意味着该控制器可以最多提供2个通道的GPIO。 |
每个通道的GPIO都有3个标准的信号,也就是输出值(GPIO_O)、输入值(GPIO_I)以及管脚输出使能控制信号(GPIO_T)。 |
输出值GPIO_O : 使用一个名为GPIO_DAT的寄存器/D触发器存储所需要输出的值 |
输入值GPIO_I : 使用一个名为GPIO_DATA_IN的寄存器/D触发器存储GPIO Pin管脚上的值 |
输出使能控制信号GPIO_T : 使用一个名为GPIO_TRI的寄存器/D触发器存储设置状态 |
这两个三态缓冲器工作时,其输入输出状态受GPIO_T信号控制。 当GPIO_T=0时,为输出态,三态缓冲器会输出GPIO_O的值; 当GPIO_T=1时,为输入态,此时GPIO_I的值会输入到AXI GPIO中。 |
因此,我们只需要通过对这3个寄存器进行读写,就能够实现对该Pin的状态的控制和读取。 这些寄存器由谁读写,又是通过什么方式读写呢? 这个就是我们刚刚提到的,PS通过AXI4-Lite总线来读写这些寄存器。 |
另外该控制器还提供了中断检测逻辑,以及中断控制寄存器, 用于中断检测以及中断使能/屏蔽/状态显示。 |
4. 设计流程
本次实验框图: |
①配置设置 |
基础流程可以参照:zynq PS点灯-CSDN博客 本次只记录一些重点: |
添加IP核:ZYNQ + AXI GPIO |
本次ZYNQ只需要配置DDR: |
配置AXI_GPIO核:两通道8位: |
快进到配置引脚:使用的是xc7z010clg400-1 + EDA扩展版 |
生成.xdc .bit .hdf文件 |
②Launch SDK |
打开
COMMON.h
文件:
添加 AXI_GPIO 的头文件声明:
#include
"AXI_GPIO.h"
随后在用户宏定义下添加
AXI GPIO
的器件
ID
宏定义:
#define
GPIO_0_ID XPAR_AXI_GPIO_0_DEVICE_ID
|
main.c |
#include"COMMON.h" int main(void) { uint32_t State; AXI_GPIO_Init(&AXI_GPIO0,GPIO_0_ID); //初始化 AXI GPIO0 //设置通道 1 为输入 AXI_GPIO_Set_Channel(&AXI_GPIO0, XGPIO_IR_CH1_MASK, 0xFF, 0); //设置通道 2为输出 AXI_GPIO_Set_Channel(&AXI_GPIO0, XGPIO_IR_CH2_MASK, 0x00, 0); while(1) { //读取通道 1 输入的值 State = XGpio_DiscreteRead(&AXI_GPIO0,XGPIO_IR_CH1_MASK); //将通道 2 设置为输出,输出从通道 1 读取的值 AXI_GPIO_Set_Channel(&AXI_GPIO0, XGPIO_IR_CH2_MASK, 0, State); } return 0; } |
在弹幕里面学到的,字体放大: Ctrl + Shift + '+' |
此时的效果: |
拨码开关可控制对应的LED亮灭 |
5. 代码解析
AXI_GPIO_Init(XGpio *InstPtr, uint16_t DeviceId) |
/***************************************************************************** ** @brief 初始化AXI_GPIO ** @param InstPtr GPIO实例指针 ** @param DeviceId GPIO设备ID ** Sample: AXI_GPIO_Init(&AXI_GPIO0,GPIO_0_ID); //初始化AXI GPIO0 *****************************************************************************/ void AXI_GPIO_Init(XGpio *InstPtr, uint16_t DeviceId) { XGpio_Initialize(InstPtr, DeviceId); } |
AXI_GPIO_Set_Channel(XGpio *InstPtr, uint8_t Channel, uint32_t Dri, uint32_t Data) |
/***************************************************************************** ** @brief 设定AXI GPIO某通道的模式与状态 ** @param InstPtr GPIO实例指针 ** @param Channel GPIO通道,1或2 ** @param Dir 输入/输出:0为输出,1为输入,每一位表示一个引脚 ** @param Data 输出电平高低:0为低,1为高,每一位表示一个引脚 ** Sample: //设置Gpio0的通道1全为输出模式,且全输出为低电平 ** AXI_GPIO_Set_Channel(&AXI_GPIO0, XGPIO_IR_CH1_MASK, 0, 0); *****************************************************************************/ void AXI_GPIO_Set_Channel(XGpio *InstPtr, uint8_t Channel, uint32_t Dri, uint32_t Data) { XGpio_SetDataDirection(InstPtr, Channel, Dri); XGpio_DiscreteWrite(InstPtr, Channel, Data); } |
XGpio_DiscreteRead(XGpio * InstancePtr, unsigned Channel) |
/****************************************************************************/ return XGpio_ReadReg(InstancePtr->BaseAddress, |
算法流程大致相同: |
初始化GPIO驱动程序 设置指定引脚方向 读/写指定管脚的值/状态 |
不同点 |
PS GPIO在输出指定管脚的值/状态时,需要先使能该引脚作为输出。 而在硬件逻辑系统设计方面,AXI GPIO则是与EMIO类似,由于使用的是PL端引脚,在导出后还需要进行管脚分配和约束。 |
尝试控制单个Pin |
#include"COMMON.h" return 0; |
6. 小作业
配置AXI GPIO为单通道16位位宽, 通过软件编程,将高8位配置为输入,获取拨码开关电平; 将低8位设置为输出,使用从拨码开关获取的电平控制LED, 实现拨码开关控制LED亮灭设计。 |