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;
}
}