最近练了一段时间的DMA传输,现做如下的总结,分享自己获得心得以及遇到的一些问题。 在系统运行时,当需要传输大量数据时,可以采用DMA的方式进行传输,以解脱出CPU来处理其他命令。
Nios II中的DMA传输有以下三种形式: 1、 存储器到存储器 这种情况下需要同时打开发送通道和接收通道,而且源地址和目标地址都是自增的。 tx = alt_dma_txchan_open("/dev/dma_0");//打开发送通道 dma_res = alt_dma_txchan_send(tx, tx_buf, 32, NULL, NULL); // tx_buf是源地址 rx = alt_dma_rxchan_open("/dev/dma_0");//打开接收通道 dma_res = alt_dma_rxchan_prepare(rx, rx_buf, 32, dma_done, NULL); // rx_buf是目标地址,dma_done()是DMA完成后被调用的回调函数。
2、 存储器到外设 这种情况下只要打开发送通道,而且源地址是自增的,目标地址是固定的。 tx = alt_dma_txchan_open("/dev/dma_0"); // 打开发送通道 alt_dma_txchan_ioctl(tx, ALT_DMA_TX_ONLY_ON, (void *)dst_addr); // dst_addr是目标地址 dma_res = alt_dma_txchan_send(tx, tx_buf, 32, dma_done, NULL); // tx_buf是源地址
3、 外设到存储器 这种情况下只要打开接收通道,而且源地址是固定的,目标地址是自增的。 rx = alt_dma_rxchan_open("/dev/dma_0"); // 打开接收通道 alt_dma_rxchan_ioctl(rx, ALT_DMA_RX_ONLY_ON, (void *)source_addr); // source_addr是源地址 dma_res = alt_dma_rxchan_prepare(rx, rx_buf, 32, dma_done, NULL); // rx_buf是目标地址
其中通过alt_dma_txchan_ioctl,alt_dma_rxchan_ioctl还可以设置每次发送和接收的字节数。
下面给出两个实例,说明DMA传输的过程。
1、 存储器到存储器 下面程序为SDRAM到onchip-memory的数据传输。 硬件连接图示:
程序如下: #include <stdio.h> #include <stdlib.h> #include <sys/alt_dma.h> #include "system.h" static volatile int rx_done = 0; int rc; alt_dma_txchan txchan; alt_dma_rxchan rxchan; static char buff[256]; void* tx_data = (void*) buff; /* 源地址 */ void* rx_buffer = (void*) 0x01801000; /* 目标地址,从上图看到0x01801000为onchip-memory的地址*/ //DMA传输结束回调函数 static void done_t(void* handle, void* data) {
rx_done++;
}
int main (int argc, char* argv[], char* envp[]) {
/* 打开发送通道 */ if ((txchan = alt_dma_txchan_open("/dev/dma")) == NULL) { printf ("Failed to open transmit channel\n"); exit (1); } /* 打开接收通道 */ if ((rxchan = alt_dma_rxchan_open("/dev/dma")) == NULL) { printf ("Failed to open receive channel\n"); exit (1); } /* 开始发送数据 */ 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); } /* 开始接收数据*/ if ((rc = alt_dma_rxchan_prepare (rxchan, rx_buffer, 128, done_t, NULL)) < 0) { printf ("Failed to post read request, reason = %i\n", rc); exit (1); } /* 等待传输结束 */ while (!rx_done); printf ("Transfer successful!\n");
return 0; } 程序运行结束后在Nios IDE的console界面中显示: Transfer successful! 表明传输成功。
2、 存储器到UART 下面程序为SDRAM到UART的数据传输 硬件连接图:
程序如下: #include <stdio.h> #include <stdlib.h> #include "sys/alt_dma.h" #include "altera_avalon_uart_regs.h" #include "system.h" #include "alt_types.h"
static volatile int tx_done = 0; volatile static alt_u8 chr[20] = {1,2,3,4,6,5,7,8,9,10,11,12,13,14,15,16,17,18,19,20} ;//待发送的数据 //回调函数 static void done (void* handle) { tx_done++; } int main() { int rc; alt_dma_txchan txchan;
void* source_buff_ptr = (void*) chr; /* 源地址 */ void* destination_buff_ptr = (void*)IOADDR_ALTERA_AVALON_UART_TXDATA(UART_BASE); /* 目标地址IOADDR_ALTERA_AVALON_UART_TXDATA(UART_BASE)函数读出txdata的地址 */
/* 打开发送通道 */ if ((txchan = alt_dma_txchan_open("/dev/dma")) == NULL) { printf ("Failed to open transmit channel\n"); exit (1); } /* 设置目标地址固定 */ if ((rc = alt_dma_txchan_ioctl(txchan, ALT_DMA_TX_ONLY_ON, destination_buff_ptr)) < 0) { printf ("Failed to set ioctl, reason = %i\n", rc); exit (1); } //设置每次发送一个字节,即8位,因为UART每次只发送8位 if((rc = alt_dma_txchan_ioctl(txchan,ALT_DMA_SET_MODE_8 ,NULL))<0) { printf("Failed to set mode 8\n"); exit(1); } /* 开始发送 */ if ((rc = alt_dma_txchan_send(txchan, source_buff_ptr, 20, done, NULL)) < 0) { printf ("Failed to post transmit request, reason = %i\n", rc); exit (1); } /* 等待发送结束 */ while (!tx_done); printf ("Transfer successful!\n"); return 0; }
不过该程序执行结束后,在串口调试器中显示Transfer successful!,却没有显示DMA发送的那20个数字。不知道哪里需要修改,放在这里和大家讨论下。
|