目录
前言
我发现很多的讲解都是单个中断的控制是如何实现的,但是基本没有多个中断的讲解。
ZYNQ中断
中断分类
中断一共被分为三类
1)PPI 私有中断
2)SGI 软件中断
3)SPI 共享中断
PPI 私有中断
每个CPU都有私有中断,PPI包括全局计时器、私人监督计时器、私人计时器和来自PL的FIQ/IRQ。下表是PPI的ID:
SGI 软件中断
软件生成的中断被路由到一个或者两个CPU中,如下表所示是SGI的中断ID:
SPI 共享中断
共享的外设中断(SPI)是由PS和PL中的各种I/O和内存控制器生成的,它们被路由到其中一个或两个cpu。来自PS外设的SPI中断也被路由到PL。如下表所示是SPI的中断ID:
GIC 通用中断控制器
通用中断控制器(GIC)是一个集中的资源,用于管理从PS和PL发送到cpu的中断。控制器启用、禁用、掩码和对中断源的优先级,并在CPU接口接受下一个中断时以编程的方式将它们发送到选定的CPU(或CPU)。此外,该控制器支持于实现安全感知系统的安全扩展。
从中断的结构图中可以大概理解中断可以给到不同的CPU去处理,至于代码是如何实现的就在中断初始化的代码中。
举例
使用PS端的DMA中断和口接受中断
DMA和串口的基本理论知识就不讲解了不是本篇的重点
基本配置
PS_UART初始化和中断初始化
/*
* uart.h
*
* Created on: 2021年11月19日
* Author: heiheiの
*/
#ifndef SRC_UART_H_
#define SRC_UART_H_
#include "xstatus.h"
#include "xuartps.h"
#include "xscugic.h"
#include "stdio.h"
#define UART_DEVICE_ID XPAR_PS7_UART_0_DEVICE_ID //串口设备ID
#define INTC_DEVICE_ID XPAR_SCUGIC_SINGLE_DEVICE_ID //中断ID
#define UART_INT_IRQ_ID XPAR_XUARTPS_0_INTR //串口中断ID
#define BAUD_UARTPS 115200
#define BUFFER_SIZE 8
#define BUFFER_SZE 100
extern u8 SendBuffer[BUFFER_SZE];
extern u8 RecvBuffer[BUFFER_SZE];
int Uart_Init(XUartPs *UartInstPtr);
int Uart_Intr_Init(XScuGic *IntcInstancePtr,XUartPs *UartInstPtr);
void uart_intr_handler(void *call_back_ref);
#endif /* SRC_UART_H_ */
/*
* uart.c
*
* Created on: 2021年11月19日
* Author: heiheiの
*/
#include "uart.h"
#include "ps_dma.h"
#include "sleep.h"
extern u8 uart_send[512];
extern u8 recv_total_byte;
int Uart_Init(XUartPs *UartInstPtr){
XUartPs_Config *Config;
int status;
//获取设备基础地址
Config = XUartPs_LookupConfig(UART_DEVICE_ID);
if (NULL == Config) {
return XST_FAILURE;
}
//设备驱动实例初始化
status = XUartPs_CfgInitialize(UartInstPtr, Config, Config->BaseAddress);
if (status != XST_SUCCESS) {
printf("Config Uart fail\r\n");
return XST_FAILURE;
}
status=XUartPs_SelfTest(UartInstPtr);
if (status != XST_SUCCESS) {
print("Self test Fail\r\n");
return XST_FAILURE;
}
//设置波特率
XUartPs_SetBaudRate(UartInstPtr,BAUD_UARTPS);
//设置模式
XUartPs_SetOperMode(UartInstPtr,XUARTPS_OPER_MODE_NORMAL);
XUartPs_SetFifoThreshold(UartInstPtr,32);
XUartPs_SetRecvTimeout(UartInstPtr,4);
XUartPs_SetInterruptMask(UartInstPtr, XUARTPS_IXR_RXOVR|XUARTPS_IXR_TOUT);
return XST_SUCCESS;
}
int Uart_Intr_Init(XScuGic *IntcInstancePtr,XUartPs *UartInstPtr){
u32 status;
XScuGic_Config *IntcConfig;
XScuGic_Disable(IntcInstancePtr,UART_INT_IRQ_ID);
IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
status=XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
IntcConfig->CpuBaseAddress);
//注册异常回调函数
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler) XScuGic_InterruptHandler,
IntcInstancePtr);
//使能异常回调
Xil_ExceptionEnable();
//设置串口接收中断优先级
XScuGic_SetPriorityTriggerType(IntcInstancePtr,UART_INT_IRQ_ID,32,1);
XScuGic_Connect(IntcInstancePtr, UART_INT_IRQ_ID,
(Xil_ExceptionHandler)uart_intr_handler,
(void *) UartInstPtr);
XScuGic_Enable(IntcInstancePtr,UART_INT_IRQ_ID);
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
return XST_SUCCESS;
}
void uart_intr_handler(void *call_back_ref)
{
XUartPs *InstancePtr=(XUartPs *)call_back_ref;
u32 IsrStatus;
u32 ReceivedCount;
IsrStatus = XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
XUARTPS_IMR_OFFSET);
IsrStatus &= XUartPs_ReadReg(InstancePtr->Config.BaseAddress,
XUARTPS_ISR_OFFSET);
if (IsrStatus & ((u32)XUARTPS_IXR_RXOVR)){
XUartPs_WriteReg(InstancePtr->Config.BaseAddress,XUARTPS_ISR_OFFSET, XUARTPS_IXR_RXOVR) ;
ReceivedCount=XUartPs_Recv(InstancePtr,&uart_send[recv_total_byte],500);
recv_total_byte+=ReceivedCount;
printf("uart_send1:%s\r\n",uart_send);
}
else if(IsrStatus & ((u32)XUARTPS_IXR_TOUT)){
XUartPs_WriteReg(InstancePtr->Config.BaseAddress,XUARTPS_ISR_OFFSET, XUARTPS_IXR_TOUT) ;
ReceivedCount=XUartPs_Recv(InstancePtr,&uart_send[recv_total_byte],500);
recv_total_byte+=ReceivedCount;
printf("uart_send1:%d\r\n",ReceivedCount);
for(int i=0;i<recv_total_byte;i++)
XUartPs_SendByte(STDOUT_BASEADDRESS,uart_send[i]);
recv_total_byte=0;
}
}
DMA初始化和中断函数
/*
* ps_dam.h
*
* Created on: 2021年11月17日
* Author: heiheiの
*/
#ifndef SRC_PS_DMA_H_
#define SRC_PS_DMA_H_
#include "xdmaps.h"
#include <stdlib.h>
#include "xscugic.h"
#include "xuartps_hw.h"
#include <stdio.h>
#define DMA_DEVIEC_ID XPAR_XDMAPS_1_DEVICE_ID
#define DMA_INT_DEVIEC_ID XPAR_SCUGIC_SINGLE_DEVICE_ID
#define DMA0_INT_ID XPS_DMA0_INT_ID
#define DMA1_INT_ID XPS_DMA1_INT_ID
#define DMA2_INT_ID XPS_DMA2_INT_ID
#define DMA3_INT_ID XPS_DMA3_INT_ID
#define DMA4_INT_ID XPS_DMA4_INT_ID
#define DMA5_INT_ID XPS_DMA5_INT_ID
#define DMA6_INT_ID XPS_DMA6_INT_ID
#define DMA7_INT_ID XPS_DMA7_INT_ID
#define DMA_FAULT_INTR XPAR_XDMAPS_0_FAULT_INTR
#define DMA_LENGTH 5
int PsDMA_Init(XDmaPs *DmaInstance,XDmaPs_Cmd *DmaCmd,void *Src,void *Dst);
int PsDMA_Intr_Init(XDmaPs *DmaInstance,XScuGic *GicPtr);
void IntcTypeSetup(XScuGic *InstancePtr,int intld,int intType);
#endif /* SRC_PS_DMA_H_ */
/*
* ps_dma.c
*
* Created on: 2021年11月17日
* Author: heiheiの
*/
#include "ps_dma.h"
int PsDMA_Init(XDmaPs *DmaInstance,XDmaPs_Cmd *DmaCmd,void *Src,void *Dst)
{
XDmaPs_Config *DmaCfg;
u32 status=XST_SUCCESS;
memset(DmaCmd,0,sizeof(XDmaPs_Cmd));
DmaCmd->ChanCtrl.DstBurstLen=1; // 目的释放量
DmaCmd->ChanCtrl.DstBurstSize=1; // 目的释放长度
DmaCmd->ChanCtrl.DstInc=1; // 目的释放递增或固定地址
DmaCmd->ChanCtrl.SrcBurstLen=1; //源释放量
DmaCmd->ChanCtrl.SrcBurstSize=1; //源释放长度
DmaCmd->ChanCtrl.SrcInc=1; //源的递增或固定地址
DmaCmd->BD.SrcAddr=((u32)(Src));
DmaCmd->BD.DstAddr=((u32)Dst);
DmaCmd->BD.Length=4;
DmaCfg=XDmaPs_LookupConfig(DMA_DEVIEC_ID);
if(DmaCfg == NULL)
return XST_FAILURE;
status=XDmaPs_CfgInitialize(DmaInstance,DmaCfg,DmaCfg->BaseAddress);
if(status != XST_SUCCESS)
return XST_FAILURE;
return XST_SUCCESS;
}
int PsDMA_Intr_Init(XDmaPs *DmaInstance,XScuGic *GicPtr){
u32 status=XST_SUCCESS;
XScuGic_Config *GicConfig;
Xil_ExceptionInit();
//设备初始化
GicConfig=XScuGic_LookupConfig(XPAR_SCUGIC_SINGLE_DEVICE_ID);
if(NULL == GicConfig)
return XST_FAILURE;
status=XScuGic_CfgInitialize(GicPtr,GicConfig,GicConfig->CpuBaseAddress);
if (status != XST_SUCCESS)
return XST_FAILURE;
//硬件初始化
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_IRQ_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
GicPtr);
//设置通道0优先级
XScuGic_SetPriorityTriggerType(GicPtr,DMA0_INT_ID,160,1);
//连接中断处理函数
status = XScuGic_Connect(GicPtr,DMA0_INT_ID,(Xil_InterruptHandler)XDmaPs_DoneISR_0,(void *)DmaInstance);
if (status != XST_SUCCESS)
return XST_FAILURE;
XScuGic_Enable(GicPtr, DMA0_INT_ID);
Xil_ExceptionEnable();
return XST_SUCCESS;
}
main.c 函数
#include "xparameters.h"
#include "uart.h"
#include "xil_printf.h"
#include "stdio.h"
#include "xil_types.h"
#include "xil_assert.h"
#include "xil_io.h"
#include "xil_exception.h"
#include "xil_cache.h"
#include "ps_dma.h"
#include "sleep.h"
#include <stdio.h>
u8 RecvBuffer[BUFFER_SZE]; //接收数据缓存
u8 SendBuffer[BUFFER_SZE]; //发送数据缓存
u32 rec_data = 1 ;
/*这里需要注意切记只能使能一个中断控制驱动实例,因为只有一个中断控制,如果实例化两个会卡中断*/
XScuGic Intc; //中断控制器驱动程序实例
/*********************************************************************************/
XUartPs Uart_Ps; //串口驱动程序实例
XDmaPs DmaInstance;
static int Src[DMA_LENGTH];
static u8 Dst[DMA_LENGTH];
volatile int Checked[XDMAPS_CHANNELS_PER_DEV];
u8 uart_send[512];
u8 recv_total_byte;
XDmaPs DmaXDmaps;
XDmaPs_Cmd DmaCmd;
//main函数
int main(void)
{
int status=XST_SUCCESS;
int Index;
recv_total_byte=0;
status= Uart_Init(&Uart_Ps);
print("RUN1\r\n");
status=Uart_Intr_Init(&Intc,&Uart_Ps);//初始化串口中断
status=PsDMA_Init(&DmaXDmaps,&DmaCmd,&rec_data,&Dst);
if(status!= XST_SUCCESS){
xil_printf("Error: XDMaPs_Example_W_Intr failed\r\n");
return XST_FAILURE;
}
status=PsDMA_Intr_Init(&DmaXDmaps,&Intc);//初始化DMA中断
for (Index = 0; Index < DMA_LENGTH; Index++)
Src[Index] = DMA_LENGTH-Index;
for (Index = 0; Index < DMA_LENGTH; Index++)
Dst[Index] = 0;
print("RUN\r\n");
while (1){
for (Index = 0; Index < DMA_LENGTH; Index++) {
printf("Src[%d],Dst[%d]:%d\r\n",Src[Index],Index,Dst[Index]);
}
printf("rec_data:%d\r\n",(int)rec_data);
XDmaPs_Start(&DmaXDmaps,0,&DmaCmd,0);
printf("Is Life:%d\r\n",XDmaPs_IsActive(&DmaXDmaps,0));
rec_data++;
sleep(2); //延时2s
};
return status;
}
初始化中断的时候需要切记 必须使用同一个中断控制器驱动程序实例,不然会导致卡中断!
LwIp的回环历程中加入串口
1)打开platform_zynq.c文件,修改函数platform_setup_interrupts,将函数修改为:
void platform_setup_interrupts(XScuGic *IntcTimer){
XScuGic_Config *IntcConfig;
IntcConfig=XScuGic_LookupConfig(INTC_DEVICE_ID);
XScuGic_CfgInitialize(IntcTimer,IntcConfig,IntcConfig->CpuBaseAddress);
Xil_ExceptionInit();
Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
(Xil_ExceptionHandler)XScuGic_InterruptHandler,
IntcTimer);
Xil_ExceptionEnable();
XScuGic_SetPriorityTriggerType(IntcTimer,TIMER_IRPT_INTR,16,1);
XScuGic_Connect(IntcTimer,TIMER_IRPT_INTR,
(Xil_ExceptionHandler)timer_callback,
(void *)&TimerInstance);
XScuGic_Enable(IntcTimer,TIMER_IRPT_INTR);
Xil_ExceptionEnableMask(XIL_EXCEPTION_IRQ);
}
2)在main.c中定义一个XScuGic IntcTimer变量,修改init_platform()函数里的platform_setup_interrupts函数修改后就可以正常使用。
3)在echo.c文件,其中红方框部分就是实现回环的代码将接受到的数据返回。其中的p->payload就是接受的数据,p-len表示数据长度。可以使用memcpy函数将接受的数据拷贝进一个数组中处理数据。也可以在这加printf(“%s”,p->payload),将接受的数据发送给串口。