ZYNQ学习之路(1)——中断(代码详细注释)

ZYNQ学习之路(1)——中断

1.GPIO中断

  • ZYNQ 7000系列GPIO直接接到管脚端口有54个,64个接口接到PL侧,64个接口可以通过PL侧的IO口进行输入输出。共享中断PS和PL端总共有60个中断。

  • 本次实验使用EMIO,按键中断控制LED灯

在这里插入图片描述

  • 设置中断步骤如下

GPIO的中断号为52

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xgpiops.h"
#include "xparameters.h"
#include "xscugic.h"
//在"xparameters.h"中找到XPAR_PS7_GPIO_0_DEVICE_ID
#define	Gpio_DEV_ID	XPAR_PS7_GPIO_0_DEVICE_ID
//选择EMIO时,自动选择BANK2的前两个接口
//54的原因是BANK2开始序号是54
#define LED0	54
#define SW0		55
//在"xparameters.h"中找到XPAR_PS7_SCUGIC_0_DEVICE_ID
#define	GIC_DEV_ID XPAR_PS7_SCUGIC_0_DEVICE_ID
//GPIO中断号
#define Inter_Id_GPIO	52
#define GPIO_BANK	2

static	XGpioPs 	GpioPs;
static 	XGpioPs_Config	*GpioConfig;

static 	XScuGic		GicPs;
static 	XScuGic_Config	*GicPsConfig;
static 	int 		led	=	1;

int	GpioInit();
int	SetupInterruptSystem();
void InterHandler(void * CallBackRef);
int main()
{
	GpioInit();
    print("Hello World\n\r");
    SetupInterruptSystem();
    while(1);
    return 0;
}

//initialize GPIO
int	GpioInit(){
	int status;
	int	led = 1;
	XGpioPs * GpioPsPtr = &GpioPs;
	GpioConfig = XGpioPs_LookupConfig(Gpio_DEV_ID);
	status = XGpioPs_CfgInitialize(GpioPsPtr,GpioConfig,GpioConfig->BaseAddr);
	if(status != XST_SUCCESS){
		return status;
	}
    //设置EMIO的方向
	XGpioPs_SetDirectionPin(GpioPsPtr,LED0,1);
    //使能EMIO
	XGpioPs_SetOutputEnablePin(GpioPsPtr,LED0,1);
    //给EMIO赋值
	XGpioPs_WritePin(GpioPsPtr,LED0,led);
	XGpioPs_SetDirectionPin(GpioPsPtr,SW0,0);
    //默认情况下OutputEnablePin是0,不是输出,不是输出就是出入,所以这里是输入
	XGpioPs_SetOutputEnablePin(GpioPsPtr,SW0,0);
	return	status;
}
//GPIO中断回调函数
void InterHandler(void * CallBackRef){
	XGpioPs * gpio = (XGpioPs *)CallBackRef;
	int pin_status;
    //GPIO中断发生后会进入两次InterHandler函数,因为EMIO有两个BANK,每个BANK进入一回
    //XGpioPs_IntrGetStatusPin先观察中断是否由是否SW0引起
	pin_status = XGpioPs_IntrGetStatusPin(gpio,SW0);
	if(pin_status == 0){
		return;
	}
	int cnt = 0;
	u32 readSW = 0;
	//关闭EMIO中断
	XGpioPs_IntrClearPin(gpio,SW0);
	XGpioPs_IntrDisablePin(gpio,SW0);
	//进行消抖处理
	while(cnt < 100){
        //读取SW0状态,按键按下为低电平,判断低电平是否保持100*1000us(100ms)
		readSW = XGpioPs_ReadPin(gpio,SW0);
		if(readSW == 0){
			cnt ++;
		}
		else{
			cnt = 0;
		}
		usleep(1000);
	}
    //读取SW0数值
	readSW = XGpioPs_ReadPin(gpio,SW0);
    //为0则led进行反转
	if(readSW == 0){
		led = ~led;
		XGpioPs_WritePin(gpio,LED0,led);
	}
	//离开回调函数后,开启中断
	XGpioPs_IntrEnablePin(gpio,SW0);
}
//对GPIO(EMIO)中断进行初始化设置
int	SetupInterruptSystem(){
    //设置状态
	int status;
	//1.exception初始化
	Xil_ExceptionInit();
	//2.GIC初始化
	GicPsConfig = XScuGic_LookupConfig(GIC_DEV_ID);
	if(NULL == GicPsConfig){
		return	XST_FAILURE;
	}
	status = XScuGic_CfgInitialize(&GicPs,GicPsConfig,GicPsConfig->CpuBaseAddress);
	if(status != XST_SUCCESS){
		return	status;
	}
	//3.exception的类型有很多,需要告诉exception现在要注册的是什么类型的异常,中断异常//XScuGic_InterruptHandler
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,(void *)&GicPs);
	//4.定义中断类型,GPIO类型中断
	XScuGic_Connect(&GicPs,Inter_Id_GPIO,(Xil_InterruptHandler)XGpioPs_IntrHandler,(void *)&GpioPs);
	//5.interrupt set
    //参数含义GPIO_BANK第几个bank中断,边沿中断,下降沿中断
	XGpioPs_SetIntrType(&GpioPs,GPIO_BANK,0XFFFFFFFF,0X00,0X00);
	//6.设置中断函数,第二个参数&GpioPs作为InterHandler函数的参数传入
	XGpioPs_SetCallbackHandler(&GpioPs,&GpioPs,InterHandler);
	//7.使能中断,BANK2中第二个引脚使能中断
	XGpioPs_IntrEnable(&GpioPs,GPIO_BANK,1<<(SW0 - 54));
	//8.使能GIC
	XScuGic_Enable(&GicPs,Inter_Id_GPIO);
	//9.使能Exception
	Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
	return status;

}
  • 附图

在这里插入图片描述

2.软中断(CPU0、CPU1)

  • 软中断有16个,都是上升沿触发,不用选择触发方式,可以核间中断或者自己中断自己。本次演示核间中断。

  • CPU0/1代码如下

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_mmu.h"
#include "xscugic.h"
#include "xparameters.h"
//此处为片上ram(OCM)地址,CPU0和CPU1都可以对其进行访问,实现数据交互,下面附图
#define COMM_VAL	(*(volatile	unsigned long * )(0xFFFF0000))
//gic的device id
#define	Gic_Dev_ID	XPAR_PS7_SCUGIC_0_DEVICE_ID
//软中断的中断号为0-15,选取两个
#define CPU0_INTER	0X0D
#define CPU1_INTER	0X0E

static 	XScuGic	GicPs;
static	XScuGic_Config *	Gic_Config;

int	software_inter_init();
int	cpu0InterHandler(void * callReference);

int main()
{
	COMM_VAL = 0;
    //关闭cache,cpu写入ram时,先进入cache,后由cache写入ram
	Xil_SetTlbAttributes(0xFFFF0000,0x14de2);
	software_inter_init();
	while(1){
		print("Hello cpu0\n\r");
		//开始发送中断,CPU1_INTER目标中断号,XSCUGIC_SPI_CPU1_MASK中断目标
		XScuGic_SoftwareIntr(&GicPs,CPU1_INTER,XSCUGIC_SPI_CPU1_MASK);

		COMM_VAL = 1;
		while(COMM_VAL == 1);
	}

    return 0;
}

int	software_inter_init(){
	int	status;
	Xil_ExceptionInit();
	Gic_Config = XScuGic_LookupConfig(Gic_Dev_ID);
	status = XScuGic_CfgInitialize(&GicPs,Gic_Config,Gic_Config->CpuBaseAddress);
	if(status !=	XST_SUCCESS){
		return	status;
	}
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&GicPs);
    //cpu0InterHandler中断回调函数
    //这句话将软中断CPU0_INTER号与CPU0连接起来
	status = XScuGic_Connect(&GicPs,CPU0_INTER,(Xil_ExceptionHandler)cpu0InterHandler,&GicPs);
	XScuGic_Enable(&GicPs,CPU0_INTER);
	Xil_ExceptionEnable();
	return	status;
}

int	cpu0InterHandler(void * callReference){
	printf("cpu1 interrupt cpu0!\n\r");
}

在这里插入图片描述

3.PL中断(16个中断)

  • 核心板调出中断接口,用于接收外部PL的数据输出
    在这里插入图片描述

  • CPU0代码如下:

#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_mmu.h"
#include "xscugic.h"
#include "xparameters.h"

#define COMM_VAL	(*(volatile	unsigned long * )(0xFFFF0000))
#define	Gic_Dev_ID	XPAR_PS7_SCUGIC_0_DEVICE_ID
//软中断
#define CPU0_INTER	0X0D
#define CPU1_INTER	0X0E
#define	DISTBASS	XPAR_PS7_SCUGIC_0_DIST_BASEADDR
#define	TypeTri		0X03
//PL中断号61
#define Inter_Cpu0_PL	61

static 	XScuGic	GicPs;
static	XScuGic_Config *	Gic_Config;

int	software_inter_init();
int	cpu0InterHandler(void * callReference);
int	PL_cpu0_handler(void * call);

int main()
{
	COMM_VAL = 1;
	print("Hello cpu0\n\r");
	Xil_SetTlbAttributes(0xFFFF0000,0x14de2);
	software_inter_init();
	while(1){
		//XScuGic_SoftwareIntr(&GicPs,CPU1_INTER,XSCUGIC_SPI_CPU1_MASK);
		while(COMM_VAL == 1);
	}

    return 0;
}

int	software_inter_init(){
	int	status;
	Xil_ExceptionInit();
	Gic_Config = XScuGic_LookupConfig(Gic_Dev_ID);
	status = XScuGic_CfgInitialize(&GicPs,Gic_Config,Gic_Config->CpuBaseAddress);
	if(status !=	XST_SUCCESS){
		return	status;
	}
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&GicPs);
    //软中断设置
	status = XScuGic_Connect(&GicPs,CPU0_INTER,(Xil_ExceptionHandler)cpu0InterHandler,&GicPs);
	XScuGic_Enable(&GicPs,CPU0_INTER);
	//PL中断设置,Inter_Cpu0_PL中断号,PL_cpu0_handler回调函数
	status = XScuGic_Connect(&GicPs,Inter_Cpu0_PL,(Xil_ExceptionHandler)PL_cpu0_handler,&GicPs);
    //设置触发类型,DISTBASS基地址,偏移多少就可以配置上升沿之类的,Inter_Cpu0_PL中断号,0X20优先级,TypeTri 11为上升沿有效,01高电平有效

	XScuGic_SetPriTrigTypeByDistAddr(DISTBASS,Inter_Cpu0_PL,0X20,TypeTri);
    //void XScuGic_InterruptMaptoCpu(XScuGic *InstancePtr, u8 Cpu_Id, u32 Int_Id)
    //Cpu_Id是PL中断的CPU编号,Inter_Cpu0_PL是PL中断中断号
	XScuGic_InterruptMaptoCpu(&GicPs,0,Inter_Cpu0_PL);
	XScuGic_Enable(&GicPs,Inter_Cpu0_PL);
	Xil_ExceptionEnable();
	return	status;
}

int	cpu0InterHandler(void * callReference){
	printf("cpu1 interrupt cpu0!\n\r");
}
//PL中断回调函数
int	PL_cpu0_handler(void * call){
	//printf("cpu0 has been interrupted by FPGA");
	printf("0\n\r");
}

4.uart串口中断

  • uart0串口中断号59
  • CPU0代码如下
#include <stdio.h>
#include "platform.h"
#include "xil_printf.h"
#include "xil_mmu.h"
#include "xscugic.h"
#include "xparameters.h"
#include "xuartps.h"
#include "AXIRegList.h"
//OCM地址
#define COMM_VAL	(*(volatile	unsigned long * )(0xFFFF0000))
#define	Gic_Dev_ID	XPAR_PS7_SCUGIC_0_DEVICE_ID
//软中断
#define CPU0_INTER	0X0D
#define CPU1_INTER	0X0E
#define	DISTBASS	XPAR_PS7_SCUGIC_0_DIST_BASEADDR
#define	TypeTri		0X03
//PL中断
#define Inter_Cpu0_PL	61
//XPAR_PS7_UART_0_INTR为uart串口中断
#define	UART_INTR	XPAR_PS7_UART_0_INTR
#define	UART_DEV_ID	XPAR_PS7_UART_0_DEVICE_ID

//AXI BASE ADDR
#define	RAMA_ADDR	0x43C00000
#define	RAMb_ADDR	0x43C10000

static 	XScuGic	GicPs;
static	XScuGic_Config *	Gic_Config;

static	XUartPs	UartPs;
static	XUartPs_Config	*	UartConfig;
//source data,uart串口接收到的数据存入uartBuffer
unsigned	char	uartBuffer[32];//32byte
//destination	data to uart
//从ramb读出来的数据存到destBuffer
unsigned 	char 	destBuffer[32];

int	software_inter_init();
int	cpu0InterHandler(void * callReference);
int	PL_cpu0_handler(void * call);
//串口初始化
void	uartInit();
//串口回调函数
void uart_0_handler(void *CallBackRef, u32 Event,u32 EventData);

//AXIREGList	write,AXIREGLIST写函数,SourceData写入rama的数据数组指针,ByteNum写入多少字节
void RAMA_Write(unsigned char * SourceData,u32 ByteNum);
//AXIREGLIST读函数,DestData为从ramb读出的数据存放数组指针,ByteNum读出多少字节
void RAMB_Read(unsigned char * DestData,u32	ByteNum);

int main()
{
	COMM_VAL = 1;
	//print("Hello cpu0\n\r");
	Xil_SetTlbAttributes(0xFFFF0000,0x14de2);
	software_inter_init();
	uartInit();
	while(1){
		//XScuGic_SoftwareIntr(&GicPs,CPU1_INTER,XSCUGIC_SPI_CPU1_MASK);
		while(COMM_VAL == 1);
	}

    return 0;
}

int	software_inter_init(){
	int	status;
	Xil_ExceptionInit();
	Gic_Config = XScuGic_LookupConfig(Gic_Dev_ID);
	status = XScuGic_CfgInitialize(&GicPs,Gic_Config,Gic_Config->CpuBaseAddress);
	if(status !=	XST_SUCCESS){
		return	status;
	}
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,(Xil_ExceptionHandler)XScuGic_InterruptHandler,&GicPs);
	status = XScuGic_Connect(&GicPs,CPU0_INTER,(Xil_ExceptionHandler)cpu0InterHandler,&GicPs);
	XScuGic_Enable(&GicPs,CPU0_INTER);

	status = XScuGic_Connect(&GicPs,Inter_Cpu0_PL,(Xil_ExceptionHandler)PL_cpu0_handler,&GicPs);
	XScuGic_SetPriTrigTypeByDistAddr(DISTBASS,Inter_Cpu0_PL,0X20,TypeTri);
	XScuGic_InterruptMaptoCpu(&GicPs,0,Inter_Cpu0_PL);
	//XScuGic_Enable(&GicPs,Inter_Cpu0_PL);

	/init_uart
	XScuGic_Connect(&GicPs,UART_INTR,(Xil_InterruptHandler)XUartPs_InterruptHandler,&UartPs);
	XScuGic_Enable(&GicPs,UART_INTR);
	/
	Xil_ExceptionEnable();
	return	status;
}

int	cpu0InterHandler(void * callReference){
	//printf("cpu1 interrupt cpu0!\n\r");
	RAMB_Read(destBuffer,8);
	XUartPs_Send(&UartPs,destBuffer,8);
}

int	PL_cpu0_handler(void * call){
	//printf("cpu0 has been interrupted by FPGA");
	//printf("0\n\r");
}
//串口中断配置
void	uartInit(){
	int	status;
	u32	intrMask = 0;
	UartConfig = XUartPs_LookupConfig(UART_DEV_ID);
	status = XUartPs_CfgInitialize(&UartPs,UartConfig,UartConfig->BaseAddress);
	//设置串口波特率
	status = XUartPs_SetBaudRate(&UartPs,115200);
    //设置串口中断回调函数
	XUartPs_SetHandler(&UartPs,(XUartPs_Handler)uart_0_handler,&UartPs);
	//设置中断方式,一个数据之后超过多少时间没有再来数据,及判定为中断
	intrMask = XUARTPS_IXR_TOUT;
    //设置中断mask
	XUartPs_SetInterruptMask(&UartPs,intrMask);
    //操作模式XUARTPS_OPER_MODE_NORMAL
	XUartPs_SetOperMode(&UartPs,XUARTPS_OPER_MODE_NORMAL);
    //Timeout duration = RecvTimeout x 4 x Bit Period
    //RecvTimeout=8,有32个bit时间没有来新的数据,即被判定为中断。
	XUartPs_SetRecvTimeout(&UartPs,8);
    //uartBuffer为uart接收数据将其放入的数组,32为预期能接收到的字节数
    //监听事件
	XUartPs_Recv(&UartPs,uartBuffer,32);
}

//uart	recive	interrupt
void uart_0_handler(void *CallBackRef, u32 Event,u32 EventData){
	u32	recvCnt;
	//define	event
    //判断event时间是不是属于XUARTPS_EVENT_RECV_TOUT
	if(Event == XUARTPS_EVENT_RECV_TOUT){
        //EventData为接收到的数据字节个数
		recvCnt = EventData;
        //接收到8个字节后,判断接收到的前两个字节
		if(recvCnt == 8 && uartBuffer[0] == 0x55 && uartBuffer[1] == 0x55){
			//printf("recive frame\n\r");
            //将数据写入rama
			RAMA_Write(uartBuffer,recvCnt);
			XScuGic_SoftwareIntr(&GicPs,CPU1_INTER,XSCUGIC_SPI_CPU1_MASK);
		}
        //!!!!!!!!!!!必须要重新启动监听时间,否则EventData函数不会清0,会一直累加
		XUartPs_Recv(&UartPs,uartBuffer,32);
	}
}

void RAMA_Write(unsigned char * SourceData,u32 ByteNum){
	int i;
	for(i = 0;i < ByteNum;i = i + 1){
		AXIREGLIST_mWriteReg(RAMA_ADDR, i*4, *(SourceData + i));
	}
}

void RAMB_Read(unsigned char * DestData,u32	ByteNum){
	int i = 0;
	unsigned char data;
	for(i = 0;i < ByteNum;i = i + 1){
		data = AXIREGLIST_mReadReg(RAMb_ADDR, i * 4);
		DestData[i] = (unsigned char)data;
	}
}
  • 1
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值