zynq 使用AXI_dma 传输==pl到ps,linux驱动开发,应用层处理DMA数据(二)

本文详细描述了在ZYNQ平台上使用AXI_DMA进行PL到PS的数据传输,涉及DMA初始化、通道设置、数据读取至内存以及DMA资源的释放过程,旨在解决上文提到的异常退出问题并提供完整的代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

背景

经过上一篇的研究,可以简单使用dma。但是上一篇中重复读取dma存在异常退出问题,因此接下来对其代码进行封装。

dma的fpga工程以及引脚绑定,axi_dma驱动移植,编译等,请看上一篇:

zynq 使用AXI_dma 传输==pl到ps,linux驱动开发,应用层处理DMA数据(一)_zynq linux dma-优快云博客

1.初始化dma

axidma_dev_t axidma_dev1;
struct dma_transfer gTrans;
    
int initDma()
{
    
    // 初始化AXIDMA设备
    axidma_dev1 = axidma_init();
    if (axidma_dev1 == NULL) 
    {
        printf("Error: Failed to initialize the AXI DMA device.\n");
        return -1;
    }
     printf("Succeed to initialize the AXI DMA device.\n");

        
    return 0;
}

2.初始化通道

const array_t *tx_chans, *rx_chans;
int initInChannel()
{  
    // 如果还没有指定tx和rx通道,则获取收发通道
    tx_chans = axidma_get_dma_tx(axidma_dev1);
    
    
    if (tx_chans->len < 1) {
        printf("Error: No transmit channels were found.\n");
    }
    rx_chans = axidma_get_dma_rx(axidma_dev1);
    
    if (rx_chans->len < 1) {
        printf("Error: No receive channels were found.\n");
    }
    
     /* 如果用户没有指定通道,我们假设发送和接收通道是编号最低的通道。 */
    if (gTrans.input_channel != -1 && gTrans.output_channel != -1) 
    {
        gTrans.input_channel = tx_chans->data[0];
        gTrans.output_channel = rx_chans->data[0];
        printf("user system cfg:\n");
    }
    else
    {
        gTrans.input_channel = 0;
        gTrans.output_channel = 1;
        printf("user custom:\n");
    }

    printf("AXI DMAt File Transfer Info:\n");
    printf("\tTransmit Channel: %d\n", gTrans.input_channel);
    printf("\tReceive Channel: %d\n", gTrans.output_channel);
    printf("\tInput File Size: %d MiB\n", gTrans.input_size);
    printf("\tOutput File Size: %d MiB\n\n", gTrans.output_size);
    
    return 0;
}

3.读取dam数据到内存

/*----------------------------------------------------------------------------
 * DMA File Transfer Functions
 *----------------------------------------------------------------------------*/

static int transfer_file(axidma_dev_t dev, struct dma_transfer *trans, int dataLen)
{
    int rc;

    trans->output_size = dataLen * 8;
    trans->input_size = dataLen * 8;
    
    // Allocate a buffer for the input file, and read it into the buffer
    trans->input_buf = axidma_malloc(dev, trans->input_size);
    if (trans->input_buf == NULL) {
        fprintf(stderr, "Failed to allocate the input buffer.\n");
        rc = -ENOMEM;
        goto ret;
    }

    // Allocate a buffer for the output file
    trans->output_buf = axidma_malloc(dev, trans->output_size);
    if (trans->output_buf == NULL) {
        rc = -ENOMEM;
        goto free_input_buf;
    }

    // Perform the transfer
    // Perform the main transaction
    if(0 != axidma_oneway_transfer(axidma_dev, trans->output_channel, trans->output_buf, trans->output_size, true))
    {
        printf("axidma_oneway_transfer timeout.\n");
    }
    else
    {
        int i = 0;
        for(i = 0; i < dataLen; i++)
        {
            // printf("axidma_oneway_transfer buf[%04d]:%08x %08x %08x %08x %08x %08x %08x %08x .\n", i, \
            // ((char *)trans.output_buf)[i * 8 + 0], ((char *)trans.output_buf)[i * 8 + 1], ((char *)trans.output_buf)[i * 8 + 2], ((char *)trans.output_buf)[i * 8 + 3],\
            // ((char *)trans.output_buf)[i * 8 + 4], ((char *)trans.output_buf)[i * 8 + 5], ((char *)trans.output_buf)[i * 8 + 6], ((char *)trans.output_buf)[i * 8 + 7]);
        
            // u32 data_0 = ((char *)trans->output_buf)[i * 8 + 1] * 256 + ((char *)trans->output_buf)[i * 8 + 0];
            // u32 data_1 = ((char *)trans->output_buf)[i * 8 + 3] * 256 + ((char *)trans->output_buf)[i * 8 + 2];
            // u32 data_2 = ((char *)trans->output_buf)[i * 8 + 5] * 256 + ((char *)trans->output_buf)[i * 8 + 4];
            // u32 data_3 = ((char *)trans->output_buf)[i * 8 + 7] * 256 + ((char *)trans->output_buf)[i * 8 + 6];
            // printf("==> i:%05d  %05d %05d %05d %05d  \n",  i, data_0, data_1, data_2, data_3);
        } 
    }    


    
free_output_buf:
    axidma_free(dev, trans->output_buf, trans->output_size);
free_input_buf:
    axidma_free(dev, trans->input_buf, trans->input_size);
ret:
    return rc;
}

4.释放dma

//释放资源
int destoryDma()
{
    if(axidma_dev1 != NULL)
    {
        printf("destoryDma \n");
        axidma_destroy(axidma_dev1);   
    } 
    
        
    return 0;
}

5.使用流程

1.初始化DMA,

2.初始化DMA通道,

3.循环调用DMA传输函数

4.程序结束释放DMA

6.完整代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <errno.h>
#include <signal.h>
#include <fcntl.h>
#include <ctype.h>
#include <termios.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <time.h>
#include <pthread.h>
#include <sys/stat.h>

#include "libaxidma.h"

#define MAXLENGTH 4096
struct dma_transfer {
    int input_fd;           // The file descriptor for the input file
    int input_channel;      // The channel used to send the data
    int input_size;         // The amount of data to send
    void *input_buf;        // The buffer to hold the input data
    int output_fd;          // The file descriptor for the output file
    int output_channel;     // The channel used to receive the data
    int output_size;        // The amount of data to receive
    void *output_buf;       // The buffer to hold the output
};

void get_format_time_ms(char *str_time, int size) {
    struct tm *tm_t;
    struct timeval time;
 
    if (size < 32) {
        printf("input buff len less than 32");
    return;
    }
    gettimeofday(&time,NULL);
    tm_t = localtime(&time.tv_sec);
    if(NULL != tm_t) {
        sprintf(str_time,"%04d_%02d_%02d_%02d_%02d_%02d_%03ld",
            tm_t->tm_year+1900,
            tm_t->tm_mon+1,
            tm_t->tm_mday,
            tm_t->tm_hour,
            tm_t->tm_min,
            tm_t->tm_sec,
            time.tv_usec/1000);
    }
 
    return;

} 

axidma_dev_t axidma_dev1;
struct dma_transfer gTrans;
    
int initDma()
{
    
    // 初始化AXIDMA设备
    axidma_dev1 = axidma_init();
    if (axidma_dev1 == NULL) 
    {
        printf("Error: Failed to initialize the AXI DMA device.\n");
        return -1;
    }
     printf("Succeed to initialize the AXI DMA device.\n");

        
    return 0;
}

//释放资源
int destoryDma()
{
    if(axidma_dev1 != NULL)
    {
        printf("destoryDma \n");
        axidma_destroy(axidma_dev1);   
    } 
    
        
    return 0;
}

const array_t *tx_chans, *rx_chans;
int initInChannel()
{  
    // 如果还没有指定tx和rx通道,则获取收发通道
    tx_chans = axidma_get_dma_tx(axidma_dev1);
    
    
    if (tx_chans->len < 1) {
        printf("Error: No transmit channels were found.\n");
    }
    rx_chans = axidma_get_dma_rx(axidma_dev1);
    
    if (rx_chans->len < 1) {
        printf("Error: No receive channels were found.\n");
    }
    
     /* 如果用户没有指定通道,我们假设发送和接收通道是编号最低的通道。 */
    if (gTrans.input_channel != -1 && gTrans.output_channel != -1) 
    {
        gTrans.input_channel = tx_chans->data[0];
        gTrans.output_channel = rx_chans->data[0];
        printf("user system cfg:\n");
    }
    else
    {
        gTrans.input_channel = 0;
        gTrans.output_channel = 1;
        printf("user custom:\n");
    }

    printf("AXI DMAt File Transfer Info:\n");
    printf("\tTransmit Channel: %d\n", gTrans.input_channel);
    printf("\tReceive Channel: %d\n", gTrans.output_channel);
    printf("\tInput File Size: %d MiB\n", gTrans.input_size);
    printf("\tOutput File Size: %d MiB\n\n", gTrans.output_size);
    
    return 0;
}

/*----------------------------------------------------------------------------
 * DMA File Transfer Functions
 *----------------------------------------------------------------------------*/

static int transfer_file(axidma_dev_t dev, struct dma_transfer *trans, int dataLen)
{
    int rc;

    trans->output_size = dataLen * 8;
    trans->input_size = dataLen * 8;
    
    // Allocate a buffer for the input file, and read it into the buffer
    trans->input_buf = axidma_malloc(dev, trans->input_size);
    if (trans->input_buf == NULL) {
        fprintf(stderr, "Failed to allocate the input buffer.\n");
        rc = -ENOMEM;
        goto ret;
    }

    // Allocate a buffer for the output file
    trans->output_buf = axidma_malloc(dev, trans->output_size);
    if (trans->output_buf == NULL) {
        rc = -ENOMEM;
        goto free_input_buf;
    }

    // Perform the transfer
    // Perform the main transaction
    if(0 != axidma_oneway_transfer(axidma_dev, trans->output_channel, trans->output_buf, trans->output_size, true))
    {
        printf("axidma_oneway_transfer timeout.\n");
    }
    else
    {
        int i = 0;
        for(i = 0; i < dataLen; i++)
        {
            // printf("axidma_oneway_transfer buf[%04d]:%08x %08x %08x %08x %08x %08x %08x %08x .\n", i, \
            // ((char *)trans.output_buf)[i * 8 + 0], ((char *)trans.output_buf)[i * 8 + 1], ((char *)trans.output_buf)[i * 8 + 2], ((char *)trans.output_buf)[i * 8 + 3],\
            // ((char *)trans.output_buf)[i * 8 + 4], ((char *)trans.output_buf)[i * 8 + 5], ((char *)trans.output_buf)[i * 8 + 6], ((char *)trans.output_buf)[i * 8 + 7]);
        
            // u32 data_0 = ((char *)trans->output_buf)[i * 8 + 1] * 256 + ((char *)trans->output_buf)[i * 8 + 0];
            // u32 data_1 = ((char *)trans->output_buf)[i * 8 + 3] * 256 + ((char *)trans->output_buf)[i * 8 + 2];
            // u32 data_2 = ((char *)trans->output_buf)[i * 8 + 5] * 256 + ((char *)trans->output_buf)[i * 8 + 4];
            // u32 data_3 = ((char *)trans->output_buf)[i * 8 + 7] * 256 + ((char *)trans->output_buf)[i * 8 + 6];
            // printf("==> i:%05d  %05d %05d %05d %05d  \n",  i, data_0, data_1, data_2, data_3);
        } 
    }    


    
free_output_buf:
    axidma_free(dev, trans->output_buf, trans->output_size);
free_input_buf:
    axidma_free(dev, trans->input_buf, trans->input_size);
ret:
    return rc;
}

void GetSrcAdcDin()
{
//	u32 rxBufferPtr[1024] = {0};
	int j = 0;
	while(1)
    {
        transfer_file(axidma_dev1,&gTrans,256);
        sleep(1);
	}


}

int main(){

	initDma();
	initInChannel();
    
	GetSrcAdcDin();
    
	destoryDma();
    
	return 0;
}

### 配置和使用AXI_DMA #### 一、理解AXI_DMA的工作原理 AXI_DMA是一种用于加速数据传输的技术,在FPGA设计中广泛应用于PL(可编程逻辑)PS(处理系统)之间的高效数据搬运。对于Zynq系列器件而言,AXI_DMA能够显著提升数据吞吐量并降低CPU占用率[^2]。 #### 、搭建Linux驱动开发环境 为了能够在Linux环境中顺利配置和使用AXI_DMA,首先需要构建适合的开发平台。这通常涉及到准备交叉编译工具链以及定制化编译特定版本的Linux内核以支持目标硬件特性。具体操作包括下载源码包、配置选项设置(如启用DMA引擎)、执行make命令完成编译流程等步骤[^1]。 #### 三、编写AXI_DMA设备驱动程序 针对AXI_DMA模块,需创建相应的字符型设备节点,并实现open/read/write/ioctl等功能接口以便应用程序调用。在此过程中要特别注意内存映射机制的应用,确保用户空间可以直接访问物理地址范围内的缓冲区来提高效率;同时也要妥善管理中断事件的发生及其对应的响应函数注册过程[^3]。 ```c static int axi_dma_open(struct inode *inode, struct file *filp){ // 打开设备时初始化资源... } ssize_t axi_dma_read(struct file *filp, char __user *buf, size_t count, loff_t *ppos){ // 实现读取功能... } ``` #### 四、测试验证 当上述准备工作完成后即可进入调试阶段。可以通过简单的C语言程序向/dev/axidma文件描述符发起请求来进行基本的功能性检测。如果一切正常,则说明已经成功实现了基于Linux系统的AXI_DMA解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值