ZYNQ使用AXI DMA(Scatter/Gather)模式进行PL与PS数据交互附源码(ps端移植freertos或者裸机)

这篇具有很好参考价值的文章主要介绍了ZYNQ使用AXI DMA(Scatter/Gather)模式进行PL与PS数据交互附源码(ps端移植freertos或者裸机)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

简介

AXI DMA操作需要先提供一个在内存中驻留的不变空间,用于存储需要进行的DMA操作。形容这“每一次操作”的东西叫做Buffer Descriptor,缩写叫BD,这些BD是连接成链表的形式的,因为BD会动态增加,而预先分配存储BD的空间是恒定的,因此BD被连成一个环(BD Ring),其实就是一个循环链表。

Scatter/Gather 允许一个数据包(Packet)由多个描述符(BD)来描述。官方文档指出的一个典型应用是在传输网络包时,Header和数据往往是分开存储的,利用SG模式可以较好的处理向多个目标读写的操作,提高应用吞吐量。 DB Ring中DB成链存放,为了解决环形结构带来的不知道Packet的起点和终点带来的问题,DMA使用了帧开始位 (TXSOF,TX Start of Frame bit) 和帧结束位 (TXEOF,TX End of Frame Bit)来分辨一段Packet。 当 DMA 获取设置了 TXSOF 位的描述符时,将触发Packet的开始。 Packet继续获取后续描述符,直到它获取一个设置了 TXEOF 位的描述符。

在接收 (S2MM) 通道上,当开始接收数据包时,AXI DMA会自动使用 RXSOF 标记描述符,告诉软件部分这个描述符对应的buffer是一个数据包的开头。 如果正在接收的数据包的总字节数比描述符中指定的长,则用下一个描述符接着传。 这种获取和存储过程一直持续到整个接收数据包被传输完毕。 接收到数据包结尾时正在处理的描述符由AXI DMA自动标记为RXEOF=1。表明与该描述符关联的缓冲区包含数据包的结尾。

每个描述符内部指明了该特定描述符实际传输的字节数。 软件可以通过从 RXSOF 描述符通过描述符链到 RXEOF 描述符来确定为接收数据包传输的总字节数。

Scatter Gather 操作从设置控制寄存器和描述符指针开始。

设置和启动 MM2S 通道的 DMA 具体操作如下:

  • 将起始描述符的位置写入Current Descriptor寄存器中
  • 设置运行/停止位为1(MM2S_DMACR.RS=1)启动MM2S运行
  • (可选)启用中断(MM2S_DMACR.IOC_IrqEn 和 MM2S_DMACR.Err_IrqEn)
  • 将末尾描述符位置写入Tail Descriptor寄存器中,写入后会立刻触发DMA获取描述符,如果是多通道,这一步骤会在数据包到达S2MM时开始
  • 处理当前描述符,从内存中读取数据并转化成Stream输出

S2MM通道的配置类似:

  • 将起始描述符的位置写入Current Descriptor寄存器中
  • 通过将运行/停止位设置为 1 (S2MM_DMACR.RS =1) 来启动 S2MM 通道运行,并且暂停位 (DMASR.Halted) 置低,指示 S2MM 通道正在运行
  • (可选)启用中断(MM2S_DMACR.IOC_IrqEn 和 MM2S_DMACR.Err_IrqEn)
  • 将有效地址写入尾部描述符寄存器,自动触发 DMA 从内存获取描述符
  • 处理获取的描述符,并将从 S2MM 流式通道接收到的任何数据写入内存

不说了理论知识去看其他up分享我们直接上配置教程

ZYNQ使用AXI DMA(Scatter/Gather)模式进行PL与PS数据交互附源码(ps端移植freertos或者裸机),fpga开发,交互,c语言,网络

AXI DMA 配置

ZYNQ使用AXI DMA(Scatter/Gather)模式进行PL与PS数据交互附源码(ps端移植freertos或者裸机),fpga开发,交互,c语言,网络

 AXI FIFO配置

 ZYNQ使用AXI DMA(Scatter/Gather)模式进行PL与PS数据交互附源码(ps端移植freertos或者裸机),fpga开发,交互,c语言,网络

 ZYNQ配置

ZYNQ使用AXI DMA(Scatter/Gather)模式进行PL与PS数据交互附源码(ps端移植freertos或者裸机),fpga开发,交互,c语言,网络

 ZYNQ使用AXI DMA(Scatter/Gather)模式进行PL与PS数据交互附源码(ps端移植freertos或者裸机),fpga开发,交互,c语言,网络

 ZYNQ使用AXI DMA(Scatter/Gather)模式进行PL与PS数据交互附源码(ps端移植freertos或者裸机),fpga开发,交互,c语言,网络

 配置完成我们进行AXI Stream流接口ip核的设计

上源码    (封装IP核的技能自己学一下)

module pl_write(
input               m_axis_aclk,
input               m_axis_aresetn,

input      [63:0]   pl_data  ,
input               data_en  ,

input               m_axis_tready,
output reg [31:0]   m_axis_tdata,
output     [3:0]    m_axis_tkeep,
output              m_axis_tlast,v
output reg          m_axis_tvalid
); 

parameter  DATA_0 = 4'b0000,
           DATA_1 = 4'b0001,
           DATA_2 = 4'b0011,
           DATA_3 = 4'b0010,
           DATA_4 = 4'b0110,
           DATA_5 = 4'b0111,
           DATA_6 = 4'b0101,
           DATA_7 = 4'b0100,
           DATA_8 = 4'b1100,
           DATA_9 = 4'b1101;

reg       data_en_1;
reg [63:0]pl_data_1;
reg [3:0] next_flag;

assign  m_axis_tkeep  = 4'b1111;  
assign  m_axis_tlast  = (next_flag==DATA_9)&&m_axis_tready&&m_axis_tvalid;

always @(posedge m_axis_aclk) begin
    if(m_axis_aresetn == 1'b0)begin
        data_en_1<=1'b0;
        pl_data_1<=64'd0;
    end
    else if(m_axis_tready) begin
        data_en_1<=data_en;
        pl_data_1<=pl_data;
    end 
end

always@(posedge m_axis_aclk)begin
    if(m_axis_aresetn == 1'b0)begin
        next_flag<=DATA_0;
        m_axis_tdata <= 32'd0;
        m_axis_tvalid<=1'b0;
    end
    else if(data_en_1) begin
            m_axis_tvalid<=1'b1;
            case (next_flag)
            DATA_0  : begin
                if(m_axis_tready)begin
                    next_flag<=DATA_1;
                    m_axis_tdata<={32'h22111111};
                end else begin
                    m_axis_tdata <= m_axis_tdata;
                    next_flag<=DATA_0;
                end
            end
            DATA_1 :begin
                if(m_axis_tready)begin
                    m_axis_tdata<={32'h22111111};
                    next_flag<=DATA_2;
                end else begin
                    m_axis_tdata <= m_axis_tdata;
                    next_flag<=DATA_1;
                end
            end
            DATA_2 :begin
                if(m_axis_tready)begin
                    m_axis_tdata<={32'h22111111};
                    next_flag<=DATA_3;
                end else begin
                    m_axis_tdata <= m_axis_tdata;
                    next_flag<=DATA_2;
                end
            end
            DATA_3 :begin
                if(m_axis_tready)begin
                    m_axis_tdata<={32'h22111111};
                    next_flag<=DATA_4;
                end else begin
                    m_axis_tdata <= m_axis_tdata;
                    next_flag<=DATA_3;
                end
            end
            DATA_4  : begin
                if(m_axis_tready)begin
                    m_axis_tdata<={8'd01,8'd12,pl_data_1[15:0]};
                    next_flag<=DATA_5;
                end else begin
                    m_axis_tdata <= m_axis_tdata;
                    next_flag<=DATA_4;
                end
            end
            DATA_5 :begin
                if(m_axis_tready)begin
                    m_axis_tdata<={8'd02,8'd12,pl_data_1[31:16]};
                    next_flag<=DATA_6;
                end else begin
                    m_axis_tdata <= m_axis_tdata;
                    next_flag<=DATA_5;
                end
            end
            DATA_6 :begin
                if(m_axis_tready)begin
                    m_axis_tdata<={8'd03,8'd12,pl_data_1[47:32]};
                    next_flag<=DATA_7;
                end else begin
                    m_axis_tdata <= m_axis_tdata;
                    next_flag<=DATA_6;
                end
            end
            DATA_7 :begin
                if(m_axis_tready)begin
                    m_axis_tdata<={8'd04,8'd12,8'd0,pl_data_1[55:48]};
                    next_flag<=DATA_8;
                end else begin
                    m_axis_tdata <= m_axis_tdata;
                    next_flag<=DATA_7;
                end
            end
            DATA_8 :begin
                if(m_axis_tready)begin
                    m_axis_tdata<={8'd05,8'd12,8'd0,pl_data_1[63:56]};
                    next_flag<=DATA_9;
                end else begin
                    m_axis_tdata <= m_axis_tdata;
                    next_flag<=DATA_8;
                end
            end
            DATA_9 :begin
                if(m_axis_tready)begin
                    m_axis_tdata<={32'hffffffff};
                    next_flag<=DATA_0;
                end else begin
                    m_axis_tdata <= m_axis_tdata;
                    next_flag<=DATA_9;
                end
            end
            default:begin
                    next_flag<=DATA_0;
                    m_axis_tdata <= m_axis_tdata;
                end 
            endcase
    end
    else begin
        m_axis_tvalid<=1'b0;
    end
end

endmodule

 数据发送IP核根据自己需求创建了

上ps端代码

注意:每一个bd可存储4字节的数据 bd串联为一个环形成一个数据包packet

// 要传输的每个packet大小
#define MAX_PKT_LEN        0x4   //缓冲个数 字节
#define MARK_UNCACHEABLE        0x701
// 每个packet对应的BD数量
#define NUMBER_OF_BDS_PER_PKT        11//一个bd 4字节
// 一共要传输的packet个数
#define NUMBER_OF_PKTS_TO_TRANSFER     1

此代码为接收代码

#include "xaxidma.h"
#include "xparameters.h"
#include "xil_exception.h"
#include "xdebug.h"
#include "xscugic.h"
#include "xaxidma_hw.h"

#define DMA_DEV_ID		XPAR_AXIDMA_0_DEVICE_ID

#define MEM_BASE_ADDR		0x01100000         //ddr地址
#define RX_INTR_ID			XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID
#define RX_BD_SPACE_BASE	(MEM_BASE_ADDR)
#define RX_BD_SPACE_HIGH	(MEM_BASE_ADDR + 0x0000FFFF)
#define RX_BUFFER_BASE		(MEM_BASE_ADDR + 0x00300000)
#define RX_BUFFER_HIGH		(MEM_BASE_ADDR + 0x00300FFF)
#define INTC_DEVICE_ID      XPAR_SCUGIC_SINGLE_DEVICE_ID

//超时计数
#define RESET_TIMEOUT_COUNTER	10000
// 要传输的每个packet大小
#define MAX_PKT_LEN		0x4   //缓冲个数 字节
#define MARK_UNCACHEABLE        0x701
// 每个packet对应的BD数量
#define NUMBER_OF_BDS_PER_PKT		11//一个bd 4字节
// 一共要传输的packet个数
#define NUMBER_OF_PKTS_TO_TRANSFER 	1
// 总共需要的BD总数
#define NUMBER_OF_BDS_TO_TRANSFER	(NUMBER_OF_PKTS_TO_TRANSFER * \
						NUMBER_OF_BDS_PER_PKT)

//中断合并阈值和延迟定时器阈值我们将合并阈值设置为包的总数。在这个例子中,接收端只会得到一个完成中断。
#define COALESCING_COUNT		NUMBER_OF_PKTS_TO_TRANSFER
#define DELAY_TIMER_COUNT		100

#define INTC		XScuGic
#define INTC_HANDLER	XScuGic_InterruptHandler
//函数申明

 int CheckData(int Length);
 void RxCallBack(XAxiDma_BdRing * RxRingPtr);
 void RxIntrHandler(void *Callback);
 int SetupIntrSystem(INTC * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 RxIntrId);
 void DisableIntrSystem(INTC * IntcInstancePtr, u16 RxIntrId);
 int RxSetup(XAxiDma * AxiDmaInstPtr);
//设备实例
XAxiDma AxiDma;
//中断控制器实例
static INTC Intc;
//中断标志
volatile int RxDone=0;
volatile int Error=0;

int main(void)
{
	int Status;
	XAxiDma_Config *Config;
	xil_printf("\r\n--- Entering strart --- \r\n");
	//查找设备配置信息
	Config = XAxiDma_LookupConfig(DMA_DEV_ID);
	if (!Config) {
		xil_printf("No config found for %d\r\n", DMA_DEV_ID);
		return XST_FAILURE;
	}
	//初始化DMA引擎
	XAxiDma_CfgInitialize(&AxiDma, Config);
	if(!XAxiDma_HasSg(&AxiDma)) {
		xil_printf("Device configured as Simple mode \r\n");
		return XST_FAILURE;
	}
	//设置接收通道,以使数据准备好接收
	Status = RxSetup(&AxiDma);
	if (Status != XST_SUCCESS) {
		xil_printf("Failed RX setup\r\n");
		return XST_FAILURE;
	}
	//设置中断
	Status = SetupIntrSystem(&Intc, &AxiDma, RX_INTR_ID);
	if (Status != XST_SUCCESS) {
		xil_printf("Failed intr setup\r\n");
		return XST_FAILURE;
	}
	return XST_SUCCESS;
}

//数据比对
 int CheckData(int Length)
{
	u8 *RxPacket;
	int Index = 0;
	Xil_DCacheFlushRange((UINTPTR)RX_BUFFER_BASE, MAX_PKT_LEN *NUMBER_OF_BDS_TO_TRANSFER);
	RxPacket = (u8 *) RX_BUFFER_BASE;
	//使指定地址缓存区失效
	Xil_DCacheInvalidateRange((UINTPTR)RxPacket, Length);
	for(Index = 0; Index < Length; Index+=4) {
			xil_printf("Data %d: %x %x %x %x\r\n", Index / 4, RxPacket[Index + 3], RxPacket[Index + 2], RxPacket[Index + 1], RxPacket[Index + 0]);
	}
	RxDone=0;
	RxSetup(&AxiDma);
	XAxiDma_IntrAckIrq(&AxiDma,XAXIDMA_IRQ_IOC_MASK,XAXIDMA_DEVICE_TO_DMA);
	return XST_SUCCESS;
}

 void RxCallBack(XAxiDma_BdRing * RxRingPtr)
{
	int BdCount;
	XAxiDma_Bd *BdPtr;
	XAxiDma_Bd *BdCurPtr;
	u32 BdSts;
	int Index;
	xil_printf("enter RxCallBack\r\n");
	//获取所有已处理bd
	BdCount = XAxiDma_BdRingFromHw(RxRingPtr, XAXIDMA_ALL_BDS, &BdPtr);
	//处理bd
	BdCurPtr = BdPtr;
	for (Index = 0; Index < BdCount; Index++) {
		//检查bd状态并判断是否有错误
		BdSts = XAxiDma_BdGetSts(BdCurPtr);
		if ((BdSts & XAXIDMA_BD_STS_ALL_ERR_MASK) ||
		    (!(BdSts & XAXIDMA_BD_STS_COMPLETE_MASK))) {
			Error = 1;
			break;
		}
		//查找下一个处理的bd
		BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(RxRingPtr, BdCurPtr);
		RxDone += 1;
	}
	CheckData(RxDone);
}

 void RxIntrHandler(void *Callback)
{
	XAxiDma_BdRing *RxRingPtr = (XAxiDma_BdRing *) Callback;
	u32 IrqStatus;
	int TimeOut;
	/* Read pending interrupts */
	IrqStatus = XAxiDma_BdRingGetIrq(RxRingPtr);
	/* Acknowledge pending interrupts */
	XAxiDma_BdRingAckIrq(RxRingPtr, IrqStatus);
	if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {return;}
	if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
		XAxiDma_BdRingDumpRegs(RxRingPtr);
		Error = 1;
		XAxiDma_Reset(&AxiDma);
		TimeOut = RESET_TIMEOUT_COUNTER;
		while (TimeOut) {
			if(XAxiDma_ResetIsDone(&AxiDma)) {break;}
			TimeOut -= 1;
		}
		return;
	}
	//调用回调
	if ((IrqStatus & (XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK))) {
		xil_printf("Generate rx interrupt\r\n");
		RxCallBack(RxRingPtr);
	}
}
//初始化中断
 int SetupIntrSystem(INTC * IntcInstancePtr,XAxiDma * AxiDmaPtr, u16 RxIntrId)
{
	//获取发送/接收 环
	XAxiDma_BdRing *RxRingPtr = XAxiDma_GetRxRing(AxiDmaPtr);
	int Status;
	XScuGic_Config *IntcConfig;
	//查找中断控制器信息
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	if (NULL == IntcConfig) {
		return XST_FAILURE;
	}
	//初始化中断控制器
	Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
					IntcConfig->CpuBaseAddress);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}
	//设置中断优先级
	XScuGic_SetPriorityTriggerType(IntcInstancePtr, RxIntrId, 0xA0, 0x3);
	//绑定中断处理函数
	Status = XScuGic_Connect(IntcInstancePtr, RxIntrId,
				(Xil_InterruptHandler)RxIntrHandler,
				RxRingPtr);
	if (Status != XST_SUCCESS) {
		return Status;
	}
	//启动中断源
	XScuGic_Enable(IntcInstancePtr, RxIntrId);
	//启动硬件中断
	Xil_ExceptionInit();
	//绑定中断异常处理函数
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
			(Xil_ExceptionHandler)INTC_HANDLER,
			(void *)IntcInstancePtr);
	//启用IRQ异常
	Xil_ExceptionEnable();
	return XST_SUCCESS;
}
//禁用dma中断
 void DisableIntrSystem(INTC * IntcInstancePtr, u16 RxIntrId)
{
	XScuGic_Disconnect(IntcInstancePtr, RxIntrId);
}
//设置读取通道
 int RxSetup(XAxiDma * AxiDmaInstPtr)
{
	XAxiDma_BdRing *RxRingPtr;
	int Status;
	XAxiDma_Bd BdTemplate;
	XAxiDma_Bd *BdPtr;
	XAxiDma_Bd *BdCurPtr;
	int BdCount;
	int FreeBdCount;
	UINTPTR RxBufferPtr;
	int Index;
	//获取接收环
	RxRingPtr = XAxiDma_GetRxRing(&AxiDma);
	//设置空间之前禁用所有读取中断
	XAxiDma_BdRingIntDisable(RxRingPtr, XAXIDMA_IRQ_ALL_MASK);
	//设置bd空间
	BdCount = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT,
				RX_BD_SPACE_HIGH - RX_BD_SPACE_BASE + 1);
	//创建bd环
	Status = XAxiDma_BdRingCreate(RxRingPtr, RX_BD_SPACE_BASE,RX_BD_SPACE_BASE,
					XAXIDMA_BD_MINIMUM_ALIGNMENT, BdCount);
	if (Status != XST_SUCCESS) {
		xil_printf("Rx bd create failed with %d\r\n", Status);
		return XST_FAILURE;
	}
	//为Rx通道设置BD模板。然后复制到每个RX BD。
	//bd归零
	XAxiDma_BdClear(&BdTemplate);
	//复制模板到创建的bd  模板为16个4字节数据uint32_t类型
	Status = XAxiDma_BdRingClone(RxRingPtr, &BdTemplate);
	if (Status != XST_SUCCESS) {
		xil_printf("Rx bd clone failed with %d\r\n", Status);
		return XST_FAILURE;
	}
	//在读取bd环上加上缓冲区以便读取数据
	FreeBdCount = XAxiDma_BdRingGetFreeCnt(RxRingPtr);
	Status = XAxiDma_BdRingAlloc(RxRingPtr, FreeBdCount, &BdPtr);
	if (Status != XST_SUCCESS) {
		xil_printf("Rx bd alloc failed with %d\r\n", Status);
		return XST_FAILURE;
	}
	BdCurPtr = BdPtr;
	RxBufferPtr = RX_BUFFER_BASE;
	for (Index = 0; Index < FreeBdCount; Index++) {
		//设置bd缓冲地址
		Status = XAxiDma_BdSetBufAddr(BdCurPtr, RxBufferPtr);
		if (Status != XST_SUCCESS) {
			xil_printf("Rx set buffer addr %x on BD %x failed %d\r\n",
			(unsigned int)RxBufferPtr,
			(UINTPTR)BdCurPtr, Status);
			return XST_FAILURE;
		}
		//为给定的bd设置子段长度
		Status = XAxiDma_BdSetLength(BdCurPtr, MAX_PKT_LEN,
					RxRingPtr->MaxTransferLen);
		if (Status != XST_SUCCESS) {
			xil_printf("Rx set length %d on BD %x failed %d\r\n",
			    MAX_PKT_LEN, (UINTPTR)BdCurPtr, Status);
			return XST_FAILURE;
		}
		//接收BDs不需要设置任何控件硬件会设置每个流的SOF/EOF位
		//设置bd控制位
		XAxiDma_BdSetCtrl(BdCurPtr, 0);
		//设置bd的id
		XAxiDma_BdSetId(BdCurPtr, RxBufferPtr);
		RxBufferPtr += MAX_PKT_LEN;
		BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(RxRingPtr, BdCurPtr);
	}
	//设置合并阈值,因此只有一个接收中断在本例中出现如果你想有多个中断发生,改变 COALESCING_COUNT是一个较小的值
	//为给定的描述符环形通道设置中断合并参数。
	Status = XAxiDma_BdRingSetCoalesce(RxRingPtr, COALESCING_COUNT,DELAY_TIMER_COUNT);
	if (Status != XST_SUCCESS) {
		xil_printf("Rx set coalesce failed with %d\r\n", Status);
		return XST_FAILURE;
	}
	//将一组bd加入到分配的硬件中
	Status = XAxiDma_BdRingToHw(RxRingPtr, FreeBdCount, BdPtr);
	if (Status != XST_SUCCESS) {
		xil_printf("Rx ToHw failed with %d\r\n", Status);
		return XST_FAILURE;
	}
	//使能所有读取中断
	XAxiDma_BdRingIntEnable(RxRingPtr, XAXIDMA_IRQ_ALL_MASK);
	//启动读取dma通道
	Status = XAxiDma_BdRingStart(RxRingPtr);
	if (Status != XST_SUCCESS) {
		xil_printf("Rx start BD ring failed with %d\r\n", Status);
		return XST_FAILURE;
	}
	return XST_SUCCESS;
}

此代码为回环 DMA需要勾选发送和接收端口并直接将zynq接收和发送端连接起来形成回环就可以了

#include "xaxidma.h"
#include "xparameters.h"
#include "xil_exception.h"
#include "xdebug.h"
#include "xscugic.h"

#define DMA_DEV_ID		XPAR_AXIDMA_0_DEVICE_ID

#define MEM_BASE_ADDR		0x01100000         //ddr地址
#define RX_INTR_ID		XPAR_FABRIC_AXIDMA_0_S2MM_INTROUT_VEC_ID
#define TX_INTR_ID		XPAR_FABRIC_AXIDMA_0_MM2S_INTROUT_VEC_ID
#define RX_BD_SPACE_BASE	(MEM_BASE_ADDR)
#define RX_BD_SPACE_HIGH	(MEM_BASE_ADDR + 0x0000FFFF)
#define TX_BD_SPACE_BASE	(MEM_BASE_ADDR + 0x00010000)
#define TX_BD_SPACE_HIGH	(MEM_BASE_ADDR + 0x0001FFFF)
#define TX_BUFFER_BASE		(MEM_BASE_ADDR + 0x00100000)
#define RX_BUFFER_BASE		(MEM_BASE_ADDR + 0x00300000)
#define RX_BUFFER_HIGH		(MEM_BASE_ADDR + 0x004FFFFF)
#define INTC_DEVICE_ID          XPAR_SCUGIC_SINGLE_DEVICE_ID

//超时计数
#define RESET_TIMEOUT_COUNTER	10000
// 要传输的每个packet大小
#define MAX_PKT_LEN		0x100    //缓冲个数
#define MARK_UNCACHEABLE        0x701
// 每个packet对应的BD数量
#define NUMBER_OF_BDS_PER_PKT		12
// 一共要传输的packet个数
#define NUMBER_OF_PKTS_TO_TRANSFER 	11
// 总共需要的BD总数
#define NUMBER_OF_BDS_TO_TRANSFER	(NUMBER_OF_PKTS_TO_TRANSFER * \
						NUMBER_OF_BDS_PER_PKT)

/*中断合并阈值和延迟定时器阈值
*有效范围为1到255
*我们将合并阈值设置为包的总数。在这个例子中,接收端只会得到一个完成中断。
*/
#define COALESCING_COUNT		NUMBER_OF_PKTS_TO_TRANSFER
#define DELAY_TIMER_COUNT		100

#define INTC		XScuGic
#define INTC_HANDLER	XScuGic_InterruptHandler
//函数申明
static int CheckData(int Length, u8 StartValue);
static void TxCallBack(XAxiDma_BdRing * TxRingPtr);
static void TxIntrHandler(void *Callback);
static void RxCallBack(XAxiDma_BdRing * RxRingPtr);
static void RxIntrHandler(void *Callback);
static int SetupIntrSystem(INTC * IntcInstancePtr,
			   XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId);
static void DisableIntrSystem(INTC * IntcInstancePtr,
					u16 TxIntrId, u16 RxIntrId);
static int RxSetup(XAxiDma * AxiDmaInstPtr);
static int TxSetup(XAxiDma * AxiDmaInstPtr);
static int SendPacket(XAxiDma * AxiDmaInstPtr);

//设备实例
XAxiDma AxiDma;
//中断控制器实例
static INTC Intc;
//中断标志
volatile int TxDone;
volatile int RxDone;
volatile int Error;

//发送数据包缓冲区,必须32位对齐使用
u32 *Packet = (u32 *) TX_BUFFER_BASE;

int main(void)
{
	int Status;
	XAxiDma_Config *Config;
	xil_printf("\r\n--- Entering strart --- \r\n");
	//查找设备配置信息
	Config = XAxiDma_LookupConfig(DMA_DEV_ID);
	if (!Config) {
		xil_printf("No config found for %d\r\n", DMA_DEV_ID);
		return XST_FAILURE;
	}
	//初始化DMA引擎
	XAxiDma_CfgInitialize(&AxiDma, Config);
	if(!XAxiDma_HasSg(&AxiDma)) {
		xil_printf("Device configured as Simple mode \r\n");
		return XST_FAILURE;
	}
	//设置发送通道,以使数据准备好发送
	Status = TxSetup(&AxiDma);
	if (Status != XST_SUCCESS) {
		xil_printf("Failed TX setup\r\n");
		return XST_FAILURE;
	}
	//设置接收通道,以使数据准备好接收
	Status = RxSetup(&AxiDma);
	if (Status != XST_SUCCESS) {
		xil_printf("Failed RX setup\r\n");
		return XST_FAILURE;
	}
	//设置中断
	Status = SetupIntrSystem(&Intc, &AxiDma, TX_INTR_ID, RX_INTR_ID);
	if (Status != XST_SUCCESS) {
		xil_printf("Failed intr setup\r\n");
		return XST_FAILURE;
	}
	//初始化标志信号
	TxDone = 0;
	RxDone = 0;
	Error = 0;
	//发送数据
	Status = SendPacket(&AxiDma);
	if (Status != XST_SUCCESS) {
		xil_printf("Failed send packet\r\n");
		return XST_FAILURE;
	}
	//检查发送接收是否完成
	while (((TxDone < NUMBER_OF_BDS_TO_TRANSFER) ||
			(RxDone < NUMBER_OF_BDS_TO_TRANSFER)) && !Error) {}
	if (Error) {
		xil_printf("Failed test transmit%s done, "
			"receive%s done\r\n", TxDone? "":" not",
					RxDone? "":" not");
		goto Done;
	}else {
		//数据比对
		Status = CheckData(MAX_PKT_LEN * NUMBER_OF_BDS_TO_TRANSFER,0xC);
		if (Status != XST_SUCCESS) {
			xil_printf("Data check failed\r\n");
			goto Done;
		}
		xil_printf("Successfully ran AXI DMA SG interrupt Example\r\n");
	}
	//关闭发送、接收中断
	DisableIntrSystem(&Intc, TX_INTR_ID, RX_INTR_ID);
Done:
	xil_printf("--- Exiting end --- \r\n");
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}
	return XST_SUCCESS;
}
//数据比对
static int CheckData(int Length, u8 StartValue)
{
	u8 *RxPacket;
	int Index = 0;
	u8 Value;

	RxPacket = (u8 *) RX_BUFFER_BASE;
	Value = StartValue;
	//使指定地址缓存区失效
	Xil_DCacheInvalidateRange((UINTPTR)RxPacket, Length);

	for(Index = 0; Index < Length; Index++) {
		if (RxPacket[Index] != Value) {
			xil_printf("Data error %d: %x/%x\r\n",
			    Index, RxPacket[Index], Value);
			return XST_FAILURE;
		}
		Value = (Value + 1) & 0xFF;
	}
	return XST_SUCCESS;
}
//发送回调函数
static void TxCallBack(XAxiDma_BdRing * TxRingPtr)
{
	int BdCount;
	u32 BdSts;
	XAxiDma_Bd *BdPtr;
	XAxiDma_Bd *BdCurPtr;
	int Status;
	int Index;
	xil_printf("enter TxCallBack\r\n");
	//获取所有已处理bd
	BdCount = XAxiDma_BdRingFromHw(TxRingPtr, XAXIDMA_ALL_BDS, &BdPtr);
	//处理bd
	BdCurPtr = BdPtr;
	for (Index = 0; Index < BdCount; Index++) {
		//检查bd状态并判断是否有错误
		BdSts = XAxiDma_BdGetSts(BdCurPtr);
		if ((BdSts & XAXIDMA_BD_STS_ALL_ERR_MASK) ||
		    (!(BdSts & XAXIDMA_BD_STS_COMPLETE_MASK))) {
			Error = 1;
			break;
		}
		//这里我们什么都不需要做。但是如果RTOS是使用时,我们可能需要释放附加的数据包缓冲区处理过的BD
		/*   NOP  */
		//查找下一个处理的bd
		BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(TxRingPtr, BdCurPtr);
	}
	//释放所有bd内存以便下一次使用
	Status = XAxiDma_BdRingFree(TxRingPtr, BdCount, BdPtr);
	if (Status != XST_SUCCESS) {
		Error = 1;
	}
	if(!Error) {
		//没有错误返回已处理bd个数
		TxDone += BdCount;
	}
}
//发送中断函数
static void TxIntrHandler(void *Callback)
{
	XAxiDma_BdRing *TxRingPtr = (XAxiDma_BdRing *) Callback;
	u32 IrqStatus;
	int TimeOut;
	//读取挂起中断
	IrqStatus = XAxiDma_BdRingGetIrq(TxRingPtr);
	//确认挂起中断
	XAxiDma_BdRingAckIrq(TxRingPtr, IrqStatus);
	if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {
		return;
	}
	if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
		XAxiDma_BdRingDumpRegs(TxRingPtr);
		Error = 1;
		//复位
		XAxiDma_Reset(&AxiDma);
		TimeOut = RESET_TIMEOUT_COUNTER;
		while (TimeOut) {
			if (XAxiDma_ResetIsDone(&AxiDma)) {break;}
			TimeOut -= 1;
		}
		return;
	}
	//无错误调用发送回调函数
	if ((IrqStatus & (XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK))) {
		xil_printf("Generate tx interrupt\r\n");
		TxCallBack(TxRingPtr);
	}
}

static void RxCallBack(XAxiDma_BdRing * RxRingPtr)
{
	int BdCount;
	XAxiDma_Bd *BdPtr;
	XAxiDma_Bd *BdCurPtr;
	u32 BdSts;
	int Index;
	xil_printf("enter RxCallBack\r\n");
	//获取所有已处理bd
	BdCount = XAxiDma_BdRingFromHw(RxRingPtr, XAXIDMA_ALL_BDS, &BdPtr);
	//处理bd
	BdCurPtr = BdPtr;
	for (Index = 0; Index < BdCount; Index++) {
		//检查bd状态并判断是否有错误
		BdSts = XAxiDma_BdGetSts(BdCurPtr);
		if ((BdSts & XAXIDMA_BD_STS_ALL_ERR_MASK) ||
		    (!(BdSts & XAXIDMA_BD_STS_COMPLETE_MASK))) {
			Error = 1;
			break;
		}
		//查找下一个处理的bd
		BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(RxRingPtr, BdCurPtr);
		RxDone += 1;
	}
}

static void RxIntrHandler(void *Callback)
{
	XAxiDma_BdRing *RxRingPtr = (XAxiDma_BdRing *) Callback;
	u32 IrqStatus;
	int TimeOut;
	/* Read pending interrupts */
	IrqStatus = XAxiDma_BdRingGetIrq(RxRingPtr);
	/* Acknowledge pending interrupts */
	XAxiDma_BdRingAckIrq(RxRingPtr, IrqStatus);
	if (!(IrqStatus & XAXIDMA_IRQ_ALL_MASK)) {return;}
	if ((IrqStatus & XAXIDMA_IRQ_ERROR_MASK)) {
		XAxiDma_BdRingDumpRegs(RxRingPtr);
		Error = 1;
		XAxiDma_Reset(&AxiDma);
		TimeOut = RESET_TIMEOUT_COUNTER;
		while (TimeOut) {
			if(XAxiDma_ResetIsDone(&AxiDma)) {break;}
			TimeOut -= 1;
		}
		return;
	}
	//调用回调
	if ((IrqStatus & (XAXIDMA_IRQ_DELAY_MASK | XAXIDMA_IRQ_IOC_MASK))) {
		xil_printf("Generate rx interrupt\r\n");
		RxCallBack(RxRingPtr);
	}
}
//初始化中断
static int SetupIntrSystem(INTC * IntcInstancePtr,
			   XAxiDma * AxiDmaPtr, u16 TxIntrId, u16 RxIntrId)
{
	//获取发送/接收 环
	XAxiDma_BdRing *TxRingPtr = XAxiDma_GetTxRing(AxiDmaPtr);
	XAxiDma_BdRing *RxRingPtr = XAxiDma_GetRxRing(AxiDmaPtr);
	int Status;
	XScuGic_Config *IntcConfig;
	//查找中断控制器信息
	IntcConfig = XScuGic_LookupConfig(INTC_DEVICE_ID);
	if (NULL == IntcConfig) {
		return XST_FAILURE;
	}
	//初始化中断控制器
	Status = XScuGic_CfgInitialize(IntcInstancePtr, IntcConfig,
					IntcConfig->CpuBaseAddress);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}
	//设置中断优先级
	XScuGic_SetPriorityTriggerType(IntcInstancePtr, TxIntrId, 0xA0, 0x3);
	XScuGic_SetPriorityTriggerType(IntcInstancePtr, RxIntrId, 0xA0, 0x3);
	//绑定中断处理函数
	Status = XScuGic_Connect(IntcInstancePtr, TxIntrId,
				(Xil_InterruptHandler)TxIntrHandler,
				TxRingPtr);
	if (Status != XST_SUCCESS) {
		return Status;
	}
	Status = XScuGic_Connect(IntcInstancePtr, RxIntrId,
				(Xil_InterruptHandler)RxIntrHandler,
				RxRingPtr);
	if (Status != XST_SUCCESS) {
		return Status;
	}
	//启动中断源
	XScuGic_Enable(IntcInstancePtr, TxIntrId);
	XScuGic_Enable(IntcInstancePtr, RxIntrId);
	//启动硬件中断
	Xil_ExceptionInit();
	//绑定中断异常处理函数
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
			(Xil_ExceptionHandler)INTC_HANDLER,
			(void *)IntcInstancePtr);
	//启用IRQ异常
	Xil_ExceptionEnable();
	return XST_SUCCESS;
}
//禁用dma中断
static void DisableIntrSystem(INTC * IntcInstancePtr,
					u16 TxIntrId, u16 RxIntrId)
{
	XScuGic_Disconnect(IntcInstancePtr, TxIntrId);
	XScuGic_Disconnect(IntcInstancePtr, RxIntrId);
}
//设置读取通道
static int RxSetup(XAxiDma * AxiDmaInstPtr)
{
	XAxiDma_BdRing *RxRingPtr;
	int Status;
	XAxiDma_Bd BdTemplate;
	XAxiDma_Bd *BdPtr;
	XAxiDma_Bd *BdCurPtr;
	int BdCount;
	int FreeBdCount;
	UINTPTR RxBufferPtr;
	int Index;
	//获取接收环
	RxRingPtr = XAxiDma_GetRxRing(&AxiDma);

	//设置空间之前禁用所有读取中断
	XAxiDma_BdRingIntDisable(RxRingPtr, XAXIDMA_IRQ_ALL_MASK);
	//设置bd空间
	BdCount = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT,
				RX_BD_SPACE_HIGH - RX_BD_SPACE_BASE + 1);
	//创建bd环
	Status = XAxiDma_BdRingCreate(RxRingPtr, RX_BD_SPACE_BASE,RX_BD_SPACE_BASE,
					XAXIDMA_BD_MINIMUM_ALIGNMENT, BdCount);
	if (Status != XST_SUCCESS) {
		xil_printf("Rx bd create failed with %d\r\n", Status);
		return XST_FAILURE;
	}
	//为Rx通道设置BD模板。然后复制到每个RX BD。
	//bd归零
	XAxiDma_BdClear(&BdTemplate);
	//复制模板到创建的bd  模板为16个4字节数据uint32_t类型
	Status = XAxiDma_BdRingClone(RxRingPtr, &BdTemplate);
	if (Status != XST_SUCCESS) {
		xil_printf("Rx bd clone failed with %d\r\n", Status);
		return XST_FAILURE;
	}
	//在读取bd环上加上缓冲区以便读取数据
	FreeBdCount = XAxiDma_BdRingGetFreeCnt(RxRingPtr);
	Status = XAxiDma_BdRingAlloc(RxRingPtr, FreeBdCount, &BdPtr);
	if (Status != XST_SUCCESS) {
		xil_printf("Rx bd alloc failed with %d\r\n", Status);
		return XST_FAILURE;
	}
	BdCurPtr = BdPtr;
	RxBufferPtr = RX_BUFFER_BASE;
	for (Index = 0; Index < FreeBdCount; Index++) {
		//设置bd缓冲地址
		Status = XAxiDma_BdSetBufAddr(BdCurPtr, RxBufferPtr);
		if (Status != XST_SUCCESS) {
			xil_printf("Rx set buffer addr %x on BD %x failed %d\r\n",
			(unsigned int)RxBufferPtr,
			(UINTPTR)BdCurPtr, Status);
			return XST_FAILURE;
		}
		//为给定的bd设置子段长度
		Status = XAxiDma_BdSetLength(BdCurPtr, MAX_PKT_LEN,
					RxRingPtr->MaxTransferLen);
		if (Status != XST_SUCCESS) {
			xil_printf("Rx set length %d on BD %x failed %d\r\n",
			    MAX_PKT_LEN, (UINTPTR)BdCurPtr, Status);
			return XST_FAILURE;
		}
		//接收BDs不需要设置任何控件硬件会设置每个流的SOF/EOF位
		//设置bd控制位
		XAxiDma_BdSetCtrl(BdCurPtr, 0);
		//设置bd的id
		XAxiDma_BdSetId(BdCurPtr, RxBufferPtr);
		RxBufferPtr += MAX_PKT_LEN;
		BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(RxRingPtr, BdCurPtr);
	}

	//设置合并阈值,因此只有一个接收中断在本例中出现如果你想有多个中断发生,改变 COALESCING_COUNT是一个较小的值
	//为给定的描述符环形通道设置中断合并参数。
	Status = XAxiDma_BdRingSetCoalesce(RxRingPtr, COALESCING_COUNT,DELAY_TIMER_COUNT);
	if (Status != XST_SUCCESS) {
		xil_printf("Rx set coalesce failed with %d\r\n", Status);
		return XST_FAILURE;
	}
	//将一组bd加入到分配的硬件中
	Status = XAxiDma_BdRingToHw(RxRingPtr, FreeBdCount, BdPtr);
	if (Status != XST_SUCCESS) {
		xil_printf("Rx ToHw failed with %d\r\n", Status);
		return XST_FAILURE;
	}
	//使能所有读取中断
	XAxiDma_BdRingIntEnable(RxRingPtr, XAXIDMA_IRQ_ALL_MASK);
	//启动读取dma通道
	Status = XAxiDma_BdRingStart(RxRingPtr);
	if (Status != XST_SUCCESS) {
		xil_printf("Rx start BD ring failed with %d\r\n", Status);
		return XST_FAILURE;
	}
	return XST_SUCCESS;
}
//设置发送
static int TxSetup(XAxiDma * AxiDmaInstPtr)
{
	XAxiDma_BdRing *TxRingPtr = XAxiDma_GetTxRing(&AxiDma);
	XAxiDma_Bd BdTemplate;
	int Status;
	u32 BdCount;

	XAxiDma_BdRingIntDisable(TxRingPtr, XAXIDMA_IRQ_ALL_MASK);

	BdCount = XAxiDma_BdRingCntCalc(XAXIDMA_BD_MINIMUM_ALIGNMENT,
			(UINTPTR)TX_BD_SPACE_HIGH - (UINTPTR)TX_BD_SPACE_BASE + 1);

	Status = XAxiDma_BdRingCreate(TxRingPtr, TX_BD_SPACE_BASE,
				     TX_BD_SPACE_BASE,
				     XAXIDMA_BD_MINIMUM_ALIGNMENT, BdCount);
	if (Status != XST_SUCCESS) {

		xil_printf("Failed create BD ring\r\n");
		return XST_FAILURE;
	}

	XAxiDma_BdClear(&BdTemplate);
	Status = XAxiDma_BdRingClone(TxRingPtr, &BdTemplate);
	if (Status != XST_SUCCESS) {

		xil_printf("Failed clone BDs\r\n");
		return XST_FAILURE;
	}

	Status = XAxiDma_BdRingSetCoalesce(TxRingPtr, COALESCING_COUNT,
			DELAY_TIMER_COUNT);
	if (Status != XST_SUCCESS) {

		xil_printf("Failed set coalescing"
		" %d/%d\r\n",COALESCING_COUNT, DELAY_TIMER_COUNT);
		return XST_FAILURE;
	}
	XAxiDma_BdRingIntEnable(TxRingPtr, XAXIDMA_IRQ_ALL_MASK);
	Status = XAxiDma_BdRingStart(TxRingPtr);
	if (Status != XST_SUCCESS) {
		xil_printf("Failed bd start\r\n");
		return XST_FAILURE;
	}
	return XST_SUCCESS;
}

static int SendPacket(XAxiDma * AxiDmaInstPtr)
{
	XAxiDma_BdRing *TxRingPtr = XAxiDma_GetTxRing(AxiDmaInstPtr);
	u8 *TxPacket;
	u8 Value;
	XAxiDma_Bd *BdPtr, *BdCurPtr;
	int Status;
	int Index, Pkts;
	UINTPTR BufferAddr;
	//限制报文长度
	if (MAX_PKT_LEN * NUMBER_OF_BDS_PER_PKT >
			TxRingPtr->MaxTransferLen) {
		xil_printf("Invalid total per packet transfer length for the "
		    "packet %d/%d\r\n",
		    MAX_PKT_LEN * NUMBER_OF_BDS_PER_PKT,
		    TxRingPtr->MaxTransferLen);
		return XST_INVALID_PARAM;
	}
	TxPacket = (u8 *) Packet;
	//发送数据
	Value = 0xC;
	for(Index = 0; Index < MAX_PKT_LEN * NUMBER_OF_BDS_TO_TRANSFER;
								Index ++) {
		TxPacket[Index] = Value;
		Value = (Value + 1) & 0xFF;
	}
	//发送之前刷新缓冲区
	Xil_DCacheFlushRange((UINTPTR)TxPacket, MAX_PKT_LEN *
							NUMBER_OF_BDS_TO_TRANSFER);
	Xil_DCacheFlushRange((UINTPTR)RX_BUFFER_BASE, MAX_PKT_LEN *
							NUMBER_OF_BDS_TO_TRANSFER);
	Status = XAxiDma_BdRingAlloc(TxRingPtr, NUMBER_OF_BDS_TO_TRANSFER,
								&BdPtr);
	if (Status != XST_SUCCESS) {
		xil_printf("Failed bd alloc\r\n");
		return XST_FAILURE;
	}
	BufferAddr = (UINTPTR)Packet;
	BdCurPtr = BdPtr;
	/*
	 * Set up the BD using the information of the packet to transmit
	 * Each transfer has NUMBER_OF_BDS_PER_PKT BDs
	 */
	for(Index = 0; Index < NUMBER_OF_PKTS_TO_TRANSFER; Index++) {
		for(Pkts = 0; Pkts < NUMBER_OF_BDS_PER_PKT; Pkts++) {
			u32 CrBits = 0;
			//设置缓冲区地址
			Status = XAxiDma_BdSetBufAddr(BdCurPtr, BufferAddr);
			if (Status != XST_SUCCESS) {
				xil_printf("Tx set buffer addr %x on BD %x failed %d\r\n",
				(unsigned int)BufferAddr,
				(UINTPTR)BdCurPtr, Status);
				return XST_FAILURE;
			}
			//设置长度字段
			Status = XAxiDma_BdSetLength(BdCurPtr, MAX_PKT_LEN,
						TxRingPtr->MaxTransferLen);
			if (Status != XST_SUCCESS) {
				xil_printf("Tx set length %d on BD %x failed %d\r\n",
				MAX_PKT_LEN, (UINTPTR)BdCurPtr, Status);
				return XST_FAILURE;
			}
			//起始bd设置sof
			if (Pkts == 0) {
				CrBits |= XAXIDMA_BD_CTRL_TXSOF_MASK;
			}
			//最后一个bd设置eof和ioc
			if(Pkts == (NUMBER_OF_BDS_PER_PKT - 1)) {
				CrBits |= XAXIDMA_BD_CTRL_TXEOF_MASK;
			}

			XAxiDma_BdSetCtrl(BdCurPtr, CrBits);
			XAxiDma_BdSetId(BdCurPtr, BufferAddr);
			BufferAddr += MAX_PKT_LEN;
			BdCurPtr = (XAxiDma_Bd *)XAxiDma_BdRingNext(TxRingPtr, BdCurPtr);
		}
	}

	//将bd交给硬件
	Status = XAxiDma_BdRingToHw(TxRingPtr, NUMBER_OF_BDS_TO_TRANSFER,
						BdPtr);
	if (Status != XST_SUCCESS) {
		xil_printf("Failed to hw, length %d\r\n",
			(int)XAxiDma_BdGetLength(BdPtr,
					TxRingPtr->MaxTransferLen));
		return XST_FAILURE;
	}
	return XST_SUCCESS;
}

笔者使用它进行传感器数据搬运到ddr  ps端移植freertos进行oled显示与控制

ZYNQ使用AXI DMA(Scatter/Gather)模式进行PL与PS数据交互附源码(ps端移植freertos或者裸机),fpga开发,交互,c语言,网络文章来源地址https://www.toymoban.com/news/detail-700119.html

附上freertos的代码吧官方只是移植了一个框架使用重点在任务间的数据传递以及调度器的使用

//
/* FreeRTOS includes. */
#include "FreeRTOS.h"
#include "task.h"
#include "queue.h"
#include "timers.h"

/* Xilinx includes. */
#include "xil_printf.h"
#include "xparameters.h"
#include "sleep.h"
#include "oled_driver.h"
/* user includes. */
#include "sg_dma.h"

/*-----------------------------------------------------------*/
static void dma_rx_Task( void *pvParameters );
static void oled_Task( void *pvParameters );
static void led_Task(void *pvParameters);

static TaskHandle_t dma_RxTask;
static TaskHandle_t  oled;
static TaskHandle_t  led;
static QueueHandle_t xQueue = NULL;
static QueueHandle_t led_flag = NULL;
extern int rx_data_buf[4];

int main( void )
{
	xil_printf( "Hello from Intelligent home system\r\n" );
	sg_dma_init();
	xTaskCreate( 	dma_rx_Task,
					( const char * ) "DMA_RX",
					configMINIMAL_STACK_SIZE,
					NULL,
					tskIDLE_PRIORITY,
					&dma_RxTask );

	xTaskCreate( oled_Task,
				 ( const char * ) "oled",
				 configMINIMAL_STACK_SIZE,
				 NULL,
				 tskIDLE_PRIORITY+1 ,
				 &oled );

	xTaskCreate( led_Task,
				 ( const char * ) "led",
				 configMINIMAL_STACK_SIZE,
				 NULL,
				 tskIDLE_PRIORITY+2,
				 &led );
	//创建队列
	xQueue = xQueueCreate( 	1,sizeof( rx_data_buf ) );
	led_flag = xQueueCreate( 1,3);
	//检查队列是否创建
	configASSERT( xQueue );

	vTaskStartScheduler();

	for( ;; );
}

static void dma_rx_Task( void *pvParameters )
{
	for( ;; )
	{
		rx_data();
		xil_printf("task1\r\n");
		xQueueSend( xQueue,
				rx_data_buf,
					0UL );
	}
}

static void oled_Task( void *pvParameters )
{
	int Recdstring[4] = {0};
	u8 key_val;
	u8 led_data[3]={0};
	for( ;; )
	{
		xil_printf("task2\r\n");
		xQueueReceive( 	xQueue,
						Recdstring,
						portMAX_DELAY );
		OLED_ShowNum(34,0,Recdstring[0],2,16);
		OLED_ShowNum(34,2,Recdstring[1],2,16);
		OLED_ShowNum(98,2,Recdstring[2],1,16);
		OLED_ShowNum(98,0,Recdstring[3],3,16);

		if (READ == 0){
			usleep(20000);
			if (READ == 0)
			key_val = ~key_val;}while(READ == 0){}
		if(key_val){
		if(Recdstring[3]<200)
		{
			led_data[0]=1;

		}else{

			led_data[0]=0;
		}
		if(Recdstring[0]>28)
		{
			led_data[1]=1;
		}else{
			led_data[1]=0;
		}
		if(Recdstring[2]>50)
		{
			led_data[2]=1;

		}else{
			led_data[2]=0;
		}
		xQueueSend( led_flag,
					led_data,
					0UL );
		}
	}
}

static void led_Task(void *pvParameters)
{
	u8 led[3];
	for( ;; )
	{
		xil_printf("task3\r\n");
		xQueueReceive( 	led_flag,
						led,
						portMAX_DELAY );
		if(led[0]){
			LIGHT_HGIH;
		}else{
			LIGHET_LOW;
		}
		if(led[1]){
			AIR_HIGH;
		}else{
			AIR_LOW;
		}
		if(led[2]){
			flag_HIGH;
		}else{
			flag_LOW;
		}
	}
}

到了这里,关于ZYNQ使用AXI DMA(Scatter/Gather)模式进行PL与PS数据交互附源码(ps端移植freertos或者裸机)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ZYNQ7000 PL与PS交互(一): PL到PS中断的使用与实现

    翻译以xilinx用户手册ug585,知道ZYNQ7000有几类中断即可。 PS基于ARM架构,使用了两个Cortex-A9处理器和GIC PL390中断控制器。中断结构与CPU密切相关,并接收来自IO外设和可编程单元PL的中断请求。 本章主要信息: 私有、共享和软件中断。 GIC功能介绍 中断优先级和处理 1.1.1 Zynq 中

    2024年04月15日
    浏览(52)
  • 自定义ZYNQ的PL端数据处理器,通过DMA等进行交互(附GitHub源码)

    摘要:在ZYNQ中设计了自定义的PL端数据处理器,通过DMA连接到AXI总线,完成了PS和该PL端的数据交互等功能。 开发板型号:Zynq-7000 SoC XC7Z305 FPGA 开发平台:Vivado 2019.1; Vivado SDK 2019.1 Github源码 :https://github.com/CY0807/Vivado_FIFO_Test.git (1)vivado_project存放了vivado和sdk原始工程文件

    2024年01月21日
    浏览(44)
  • AXI_UART调试说明-PS使用AXI_Uartlite拓展PL端串口资源

    ** ** 注:本例程是在xc7z010clg400_1实现,若导入至复旦微电子需更改为xc7z045iffg900-2L (目录中带*号的可略过) 背景:PS端UART资源有限,难以满足实际运用中多串口的需求。 具体方法:PS通过AXI总线调用PL的资源进行UART的拓展,本说明采用vivado自带的IP核AXI Uartlite完成,属于AX

    2024年02月05日
    浏览(107)
  • zynq pl访问ps ddr

    在 xilinx mpsoc 平台上进行 Linux 软件开发,不可避免的会涉及到 PS 与 PL 之间的数据交互。这个 系列 介绍一种 基于 DDR 的信息交互方式 。 这篇文章首先介绍下 如何从系统中“偷”内存 。 交互流程: PS 写入数据到 DDR 中,使用中断通知 PL,PL 从协商好的 DDR 中读取数据; PL 写

    2024年02月01日
    浏览(49)
  • FPGA----UltraScale+系列的PS侧与PL侧通过AXI-HP交互(全网唯一最详)附带AXI4协议校验IP使用方法

    1、之前写过一篇关于ZYNQ系列通用的PS侧与PL侧通过AXI-HP通道的文档,下面是链接。 FPGA----ZCU106基于axi-hp通道的pl与ps数据交互(全网唯一最详)_zcu106调试_发光的沙子的博客-CSDN博客 大家好,今天给大家带来的内容是,基于AXI4协议的采用AXI-HP通道完成PL侧数据发送至PS侧(PS侧数

    2024年02月13日
    浏览(49)
  • zynq板上PS端给PL端复位

    最近接触zynq板-zcu104,记录一些实验笔记。 LED闪烁 在vivado中新建一个工程,命名为led-test。添加top.v文件。 其中,top模块接入zcu104开发板的clk_300_p/n信号,通过clock wizard转换为单端始终clock,接入到led闪烁模块led.v 其中,led模块通过一个24bit的计数器,经过相等间隔厚,让led灯

    2024年02月11日
    浏览(38)
  • zynq的PL向PS提供时钟和复位

    最近买了一块矿卡蚂蚁T9+,它的资源比EBAZ4205丰富。 需要矿卡资料包的朋友可以从这下载。里面包含蚂蚁T9+和EBAZ4205原理图和几个EBAZ4205例程,还有一些相关的pdf文档。 link 首先从fpga学起,可惜PL没有焊晶振,只好从PS端引,下面以点灯为例。 打开vivado,工具栏file -- project -

    2024年02月15日
    浏览(47)
  • ZYNQ学习笔记(四):PL与PS数据交互——基于BRAM IP 核的(PS端读写+PL端读)控制实验

    说起PS、PL数据交互,常见的有IO方式:MIO EMIO GPIO,还有利用BRAM或FIFO等,在上一篇文章ZYNQ学习笔记(三):PL与PS数据交互—— UART串口+AXI GPIO控制DDS IP核输出实验咱们学会了如何利用AXI GPIO IP核来实现PS(写)与PL(读)的数据交互,那么这篇文章来学习如何使用BRAM~ 本次实验

    2024年02月05日
    浏览(53)
  • FPGA - ZYNQ 基于EMIO的PS和PL交互

    Xilinx ZYNQ 系列的芯片,GPIO分为 MIO 、 EMIO 、 AXI_GPIO 三种方式。 MIO    :固定管脚,属于PS端,也就是ARM端。 EMIO   :通过PL扩展,使用时需要分配 PL ( FPGA ) 管脚,消耗PL端资源。 AXI_GPIO :封装好的IP核,PS通过M_AXI总线可以控制PL的IO接口,使用时,消耗管脚资源和逻辑资源。

    2024年04月26日
    浏览(36)
  • ZYNQ AXI-DMA Linux Cache 一致

    平台为 ZYNQ MPSOC 项目使用到AXI-DMA ,ADC模块传输数据到DDR,应用层进行数据的读取。在此做些记录 用到了AXI-Stream , IP核用的 米联客的ui_axisbufw,可以把流数据转为AXI-Stream 接口 比较重要的参考链接 1.UltraScale+MPSoC+Cache+Coherency https://xilinx-wiki.atlassian.net/wiki/spaces/A/pages/18842098/Zynq+

    2024年02月03日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包