GPIO通过MIO连接PS,通过EMIO连接PL
GPIO分为4个bank[32 22 32 32],前两个是连接MIO,后面两个是连接EMIO(PS引脚不够用,需要EMIO扩展)
PS MIO控制led,无中断
#define MIO0_LED 11
#define MIO50_KEY 11
// 绑定PS端的管脚
int main(){
//查找gpio配置信息
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
//初始化
XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);
//配置管脚的方向 0:输入 1:输出(输出时候需要多一个输出使能函数)
// 输出示例
XGpioPs_SetDirectionPin(&Gpio, MIO0_LED, 1);
//输出时候需要多一个输出使能函数
XGpioPs_SetOutputEnablePin(&Gpio, MIO0_LED, 1);
// 输入示例
XGpioPs_SetDirectionPin(&Gpio, MIO50_KEY, 0x0);
//读取引脚的值
u32 key_value1=XGpioPs_ReadPin(&Gpio, MIO50_KEY);
//给输出引脚赋值
XGpioPs_WritePin(&Gpio, MIO0_LED,key_value1);
}
PS EMIO控制led,无中断
#define EMIO_KEY 54
// 因为GPIO前两个bank是连接MIO共32+22=54个,编号是 0-53
// 此处的key是用EMIO扩展的位宽一位PL端按键,所以编号在53基础上加1=54
// 54并不是实际的管脚,在生成bit文件之前已经给54号绑定了PL端的管脚
#define MIO0_LED 11
// 绑定PS端的管脚11
int main(){
//查找gpio配置信息
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
//初始化
XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);
//配置管脚的方向 0:输入 1:输出(输出时候需要多一个输出使能函数)
// 输出示例
XGpioPs_SetDirectionPin(&Gpio, MIO0_LED, 1);
//输出时候需要多一个输出使能函数
XGpioPs_SetOutputEnablePin(&Gpio, MIO0_LED, 1);
// 输入示例
XGpioPs_SetDirectionPin(&Gpio, EMIO_KEY, 0x0);
//读取引脚的值
u32 key_value1=XGpioPs_ReadPin(&Gpio, EMIO_KEY);
//给输出引脚赋值
XGpioPs_WritePin(&Gpio, MIO0_LED,key_value1);
}
PS gpio中断控制 流程
(只用PS端就只有MIO,如果要用PL端需要EMIO绑定PL的管脚,其他一样)
每个外设都有一个自己的中断号,需要中断控制器GIC收到中断请求之后做判断
中断服务函数IntrHandler()只是改变标志信号的值
真正的操作要在while里不断的检测中断,然后进行逻辑操作,while内部要
//先清除中断状态
XGpioPs_IntrClearPin(&Gpio, MIO50_KEY);
//再IntrHandler()中的关闭的中断打开,给gpio管脚中断 赋使能
XGpioPs_IntrEnablePin(&Gpio, MIO50_KEY);
int main(){
//查找gpio配置信息,初始化
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);
//配置管脚的方向 0:输入 1:输出(输出时候需要多一个输出使能函数)
XGpioPs_SetDirectionPin(&Gpio, MIO0_LED, 1);
XGpioPs_SetOutputEnablePin(&Gpio, MIO0_LED, 1);
XGpioPs_SetDirectionPin(&Gpio, MIO50_KEY, 0x0);
//设置PS GPIO中断系统
SetupInterruptSystem(&Intc, &Gpio, GPIO_INTERRUPT_ID);
//查找中断控制器gic配置信息,初始化
//中断固定三函数
//关联中断函数
//给gpio中断控制器 赋使能
//单独给gpio一个管脚设置中断触发类型
//单独给gpio一个管脚中断 赋使能
while(1){
//进入判断while,只要触发中断就开始执行
if(key==1){
//清除gpio管脚中断状态
XGpioPs_IntrClearPin(&Gpio, MIO50_KEY);
//执行相应的操作
FUNCTIONAL
key==0;
//给gpio管脚中断 赋使能
XGpioPs_IntrEnablePin(&Gpio, MIO50_KEY);
}
}
//设置PS GPIO中断系统
void SetupInterruptSystem(&Intc, &Gpio, GPIO_INTERRUPT_ID);
{
//查找中断控制器gic配置信息,初始化
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
//中断固定三函数
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
GicInstancePtr);
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
//关联中断函数,中断之后做出IntrHandler操作
//在IntrHandler函数操作中需要关闭引脚的中断使能,
//在后面对中断做完所有的响应之后打开
XScuGic_Connect(GicInstancePtr, GpioIntrId,
(Xil_ExceptionHandler)IntrHandler,
(void *)Gpio);
//给gpio中断控制器 赋使能
XScuGic_Enable(GicInstancePtr, GpioIntrId);
//单独给gpio一个管脚设置中断触发类型
XGpioPs_SetIntrTypePin(Gpio, MIO50_KEY, XGPIOPS_IRQ_TYPE_EDGE_FALLING);
//单独给给gpio一个管脚中断 赋使能
XGpioPs_IntrEnablePin(&Gpio, MIO50_KEY);
}
//中断服务函数 改变信号的值,在while里检测,然后才是真正的逻辑操作
void IntrHandler(){
//关闭引脚的中断使能
XGpioPs_IntrDisablePin(&Gpio, MIO50_KEY);
//中断触发后做出的响应
XXXXXXXXXX;
key=1;//中断操作
}
AXI_GPIO中断
AXI GPIO是ZYNQ的一个IP核,它能够将PS侧的AXI4-Lite接口转成PL侧的IO口,可解决PS侧IO口不够用的问题。
软核:出场时不存在的电路,是pl端新建的
硬核:出场时就已经存在的电路
PL到PS的中断需要在zynq中配置打开interrupts,选中PL-PS,PL的中断有16个ID
//先给AXI gpio中断控制器 赋使能
XScuGic_Enable(GicInstancePtr, AXI_GpioIntrId);
//然后把AXI的中断打开分两步:打开全局中断、打开通道中断
//打开全局中断
XGpio_InterruptGlobalEnable(AXI_Gpio);
//打开通道中断
XGpio_InterruptEnable(AXI_Gpio, u32 Mask);
注意:AXI GPIO的中断是高电平敏感类型的信号
int main(){
(led)
//查找PS端gpio配置信息,初始化
ConfigPtr = XGpioPs_LookupConfig(GPIO_DEVICE_ID);
XGpioPs_CfgInitialize(&Gpio, ConfigPtr,ConfigPtr->BaseAddr);
(key)
//查找AXI gpio配置信息,初始化
XGpio_Initialize(&AXI_Gpio, AXI_GPIO_DEVICE_ID);
//配置PS gpio管脚的方向
//0:输入 1:输出(输出时候需要多一个输出使能函数)
XGpioPs_SetDirectionPin(&Gpio, MIO0_LED, 1);
XGpioPs_SetOutputEnablePin(&Gpio, MIO0_LED, 1);
//配置AXI gpio的方向
//DirectionMask 0:输出 1:输入
XGpio_SetDataDirection(&AXI_Gpio, GPIO_CHANNEL1,
u32 DirectionMask);
//给AXI GPIO设置中断系统
SetupInterruptSystem(&Intc, &AXI_Gpio, AXI_GPIO_INTERRUPT_ID);
while(1){
//进入判断while,
if(key==1){
//先读取和AXI_GPIO相连的信号电平状态,进行判断,如何操作
if(XGpio_DiscreteRead(&AXI_Gpio, unsigned Channel)){
//清除AXI gpio中断状态
XGpio_InterruptClear(&AXI_Gpio, u32 Mask);
//执行相应的操作
FUNCTIONAL
//打开通道中断
XGpio_InterruptEnable(&AXI_Gpio, u32 Mask);
}
}
}
//给AXI GPIO设置中断系统
SetupInterruptSystem(&Intc, &AXI_Gpio, AXI_GPIO_INTERRUPT_ID)
{
//查找中断控制器gic配置信息,初始化
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
XScuGic_CfgInitialize(GicInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
//中断固定三函数
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
GicInstancePtr);
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
//关联中断函数,中断之后做出IntrHandler操作
//在IntrHandler函数操作中需要关闭引脚的中断使能,
//在后面对中断做完所有的响应之后打开
XScuGic_Connect(GicInstancePtr, AXI_GpioIntrId,
(Xil_ExceptionHandler)IntrHandler,
(void *)AXI_Gpio);
//给AXI gpio中断控制器 赋使能
XScuGic_Enable(GicInstancePtr, AXI_GpioIntrId);
//Priority 0 8 16 32... 248,数值越小优先级越高
//Trigger 触发类型,看手册
XScuGic_SetPriorityTriggerType(XScuGic *InstancePtr, u32 Int_Id,
u8 Priority, u8 Trigger)
//把AXI的中断打开分两步:打开全局中断、打开通道中断
//打开全局中断
XGpio_InterruptGlobalEnable(AXI_Gpio);
//打开通道中断,一个通道32位
//Mask 0:关闭中断 1:打开中断
XGpio_InterruptEnable(AXI_Gpio, u32 Mask);
}
//中断服务函数
IntrHandler(){
//关闭通道中断使能
XGpio_InterruptDisable(&AXI_Gpio, u32 Mask);
//中断触发后做出的响应
XXXXXXXXXX;
key=1;
}
AXI IO 自定义IP核
slv_reg0、1、2、3寄存器存储信号的数据,可以写可以读
从sdk写数据到slv_reg,slv_reg在ip核的verilog代码模板里连接这自己的逻辑功能模块的输入
在AXI接口的ip核模板里添加自己的逻辑代码,注意自己逻辑代码的parameter和引出的信号
MYSELF_IP_mWriteReg,MYSELF_IP_mReadReg这两个函数控制slv_reg的值,函数里的BaseAddress参数需要去 **_bsp - include - xparameter.h找到自己的ip核基地址
#include "myself_ip.h"
#include "xparameters.h"// 函数中用到的ip核基地址存在这里
#include "xil_io.h"// write read需要这个头文件
#include "stdio.h"
#include "sleep.h"
// 寄存器地址偏移存在myself_ip.h
#define SLV_REG0_OFFSET MYSELF_IP_S00_AXI_SLV_REG0_OFFSET
#define SLV_REG1_OFFSET MYSELF_IP_S00_AXI_SLV_REG1_OFFSET
// ip核基地址存在xparameters.h
#define BaseAddress XPAR_MYSELF_IP_0_S00_AXI_BASEADDR
int main(){
printf("myip_open\n\r");
int led_status=0;
//ip核verilog的逻辑是ctrl2=100 led亮
//MYSELF_IP_mWriteReg(BaseAddress, SLV_REG1_OFFSET, 0x00000064);//100
//下面是使用write read
while(1){
led_status=MYSELF_IP_mReadReg(BaseAddress, SLV_REG0_OFFSET);
printf("%d\n\r",led_status);
if(led_status==0){
MYSELF_IP_mWriteReg(BaseAddress, SLV_REG0_OFFSET, 0x00000001);
printf("on\n\r");
led_status=MYSELF_IP_mReadReg(BaseAddress, SLV_REG0_OFFSET);
printf("%d\n\r",led_status);
sleep(2);
MYSELF_IP_mWriteReg(BaseAddress, SLV_REG0_OFFSET, 0x00000000);
printf("off\n\r");
led_status=MYSELF_IP_mReadReg(BaseAddress, SLV_REG0_OFFSET);
printf("%d\n\r",led_status);
sleep(2);
}
}
return 0;
}
ddr3没写入数据之前,里面的数据是随机值
DMA
PL到DDR之间的数据传输,所以一般表示方向就有了 MM2S和 S2MM
MM2S:从地址映射的内存到 数据流接口,具体说从 DDR搬运数据到 PL。
S2MM:从数据流接口到地址映射的内存接口,具体说从 PL搬运数据到 DDR。