Nios II 中的DMA

本文详细介绍了在NIOS II环境下实现DMA的基本概念、传输类型、关键操作及其实现过程,包括如何配置DMA设备、连接内存与外设、使用回调函数处理DMA传输完成等核心步骤。通过一个简单的复制SDRAM内存缓冲区到on_chip_memory的例子,展示了如何在程序中集成DMA操作,同时讨论了DMA在不同场景下的应用和与中断的关系。

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

转载地址:http://www.china-vision.net/blog/user1/6/archives/2006/200696122323.html


有了上一讲HAL的基础,我们来关注一下DMANIOS 中的实现和编程。DMA是个老问题了,从8086/8088一直到现在,完成不需要CPU参与的数据搬家,源和目标可以是内存也可以是设备,在NIOS II中通过基于HAL编程完成。
  
下图是三中基本的DMA传输:

 NIOS IIHAL DMA设备模式中,DMA传输被分为两类:transmit  receiveNIOS提供两种设备驱动实现transmit channelsreceive channelstransmit channels把缓冲区数据发送到目标设备,receive channels读取设备数据存放到缓冲区。

DMA的概念其实挺简单,无非就是把一定长度的数据从源地址传送到目标地址。其中有一点比较重要的是对于地址的操作方式,一种是地址自增,另一种是地址固定。就是说DMA控制器读或写完一个数据后,对地址是自动增加,下次读写下一个地址,还是不变,下次还是读写同一个地址。由于地址固定模式一般是用在外设,所以在Nios的文档中,使用地址固定模式的一方就称为设备(Peripheral),而使用地址自增模式的一方则称为内存(Memory)。
    为了适应大家不同的开发环境,下面我们完成一个相对简单的 DMA 操作,复制 SDRAM 内存缓冲区到 on_chip_memory 中,如果我们在库工程属性中设置了 SDRAM 为主内存,那么程序中分配的数组缓冲区就在 SDRAM 中,我们用指针赋值让指针指向 on_chip_memory 。这个操作完全可以在程序中用 memcpy 来实现,我们趋简就繁,就是为了尝试一下 DMAJ
   
首先我们在 SOPC Builder 中增加一个名字为 dma_0 DMA 设备。两个表单设置都选默认。

 第二步,DMA设备有三个PORT,两个MASTER PORT:read_masterwrite_master,一个SLAVE PORT:control_port_slave。需要在SOPC BUILDER中设置AVALONE交换总线,设置read_mastersdram连接,write_masteron_chip_memory连接,具体见下图(交叉点为黑色)

sopc builder中生成系统,并在Quartus II中编译下载,硬件部分就OK了。如果你的DMA操作不是内存到内存的,而是内存到设备,或者设备到内存,那么你需要在上面这一步中加以设置,设备只支持读写,是CPU读写还是DMA读写设备不加以区分。
在程序中,我们要使用DMA必须包含:sys/alt_dma.h
因为是内存DMA操作,所以我们必须实现transmit channelsreceive channels,这在NIOS II中就是打开两个设备。在NIOS II IDE中生成一个以Hello World为模板的memory_dma工程项目修改一下程序如下:

#i nclude <stdio.h>
#i nclude <stdlib.h>
#i nclude <sys/alt_dma.h>
#i nclude "system.h"
static volatile int rx_done = 0;
/*
*   Callback function that obtains notification that the data has
*   been received.
*/
static void done (void* handle, void* data)
{
  rx_done++;
}
int main (int argc, char* argv[], char* envp[])
{
  int rc;
  static char buff[256];
  alt_dma_txchan txchan;
  alt_dma_rxchan rxchan;
  void* tx_data = (void*) buff;                 /* pointer to data to send */
  void* rx_buffer = (void*) 0x01000000; /* on_chip_memory addr*/
  
  /* Create the transmit channel */
  if ((txchan = alt_dma_txchan_open("/dev/dma_0")) == NULL)
  {
    printf ("Failed to open transmit channel\n");
    exit (1);
  }
  /* Create the receive channel */
  if ((rxchan = alt_dma_rxchan_open("/dev/dma_0")) == NULL)
  {
    printf ("Failed to open receive channel\n");
    exit (1);
  }
  /* Post the transmit request */
  if ((rc = alt_dma_txchan_send (txchan,
                                  tx_data,
                                  128,
                                  NULL,
                                  NULL)) < 0)
  {
    printf ("Failed to post transmit request, reason = %i\n", rc);
    exit (1);
  }
  
  /* Post the receive request */
  if ((rc = alt_dma_rxchan_prepare (rxchan,
                                    rx_buffer,
                                    128,
                                    done,
                                    NULL)) < 0)
  {
    printf ("Failed to post read request, reason = %i\n", rc);
    exit (1);
  }
   
   
   /* wait for transfer to complete */
    while (!rx_done);
        printf ("Transfer successful!\n");
    return 0;
}


我们很多人对DMA理解的很深入,在其他嵌入式领域有丰富的经验,在其他系统上的实现问题很自然会想在NIOS II中是怎么完成的呢,比如DMA完成以后需要中断吗?如何知道DMA传输完成等等,在上面的程序中,实际上是通过回调函数完成的,回调函数在Windows系统的WIN API中以及驱动开发中被大量使用。
   
好了,DMA就是如此,还有一些相关的函数需要去尝试一下。尝试非常重要,在资料欠缺的时候,需要创建环境去实验,你的理解是这样的,按这样的理解会有这样的结果,实际做一下到底是怎样的,不符合?是理解错了吗?不断尝试,收益无限

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值