本帖最后由 sun1986821 于 2011-10-26 18:47 编辑
前记:
因为之前有点忙,所以收到板子已经有段时间了(应该没超过3个月大限把 = = ),一直没有时间写笔记。不过实验到时做了有些。现在发帖估计赶不上我那轮的选美了,不知道能不能把我排到下一轮选美。不行的话就算了,拿回我的100押金我还是倍感欣慰的~~~闲话少说,开始上帖!
正文:
DMA(Direct Memory Access,直接内存存取) 是所有现代电脑的重要特色,他允许不同速度的硬件装置来沟通,而不需要依于CPU的大量中断负载。否则,CPU需要从来源把每一片段的资料复制到暂存器,然后把他们再次写回到新的地方。在这个时间中,CPU对于其他的工作来说就无法使用。DMA传输将数据从一个地址空间复制到另外一个地址空间。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实行和完成。典型的例子就是移动一个外部内存的区块到芯片内部更快的内存区。像是这样的操作并没有让处理器工作拖延,反而可以被重新排程去处理其他的工作。DMA传输对于高效能嵌入式系统算法和网络是很重要的。以上内容转自百度百科。
Nuvoton里面的DMA功能叫做PDMA(Peripheral Direct Memory Access 外设直接内存存取)和传统的DMA略有区别,大概是指这个DMA通道只能为少数几种特定的外围设备提供吧,这是本人的理解。作为一个低端高性价比芯片,能够做到在几个外设之间进行数据传递而不占用CPU也已经算很不错了。以下是我做的一个小实验,把一个普通的数组数据作为例子通过PDMA通道传递到UART0,在由UART0输出。
//#include <stdio.h>
#include "Driver\DrvUART.h"
#include "Driver\DrvGPIO.h"
#include "Driver\DrvSYS.h"
#include "Driver\DrvPDMA.h"
#include "Driver\DrvSPI.h"
#include "NUC1xx.h"
/* 为简化步骤,只用了一个简单的8位字符数组作为要传输的源数据 */
uint8_t Flash_Buffer[8] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H'};
STR_PDMA_T sPDMA;
/* PDMA2的一个callback函数 用于一次传递完后产生中断调用 */
void PDMA2_Interrupt_Callback();
int main(){
STR_PDMA_T
PDMA_ConfigStruct;
STR_UART_T
UartParam;
/* 对时钟、串口进行必要的初始化 */
UNLOCKREG();
/** Enable GPIO block clock */
DrvSYS_SetOscCtrl(E_SYS_XTL12M,ENABLE);
/* Waiting for 12M Xtal stalble */
DrvSYS_Delay(5000);
/* Run 48Mhz */
DrvSYS_Open(48000);
DrvSYS_Delay(5000);
DrvGPIO_InitFunction(E_FUNC_UART0);
UartParam.u32BaudRate
= 9600;
UartParam.u8cDataBits
= DRVUART_DATABITS_8;
UartParam.u8cStopBits
= DRVUART_STOPBITS_1;
UartParam.u8cParity
= DRVUART_PARITY_NONE;
UartParam.u8cRxTriggerLevel
= DRVUART_FIFO_1BYTES;
/* Select UART Clock Source From 12Mhz */
DrvSYS_SelectIPClockSource(E_SYS_UART_CLKSRC,0);
/* Open the UART */
DrvUART_Open(UART_PORT0, &UartParam);
/* 输出一句初始化完毕 */
DrvUART_Write(UART_PORT0,"\nfinish init\n", 13);
DrvSYS_Delay(5000);
/* 对PDMA功能进行初始化 */
DrvPDMA_Init ();
DrvPDMA_SetCHForAPBDevice(eDRVPDMA_CHANNEL_2, eDRVPDMA_UART0, eDRVPDMA_WRITE_APB);
/** 填入PDMA设置参数 */
PDMA_ConfigStruct.sSrcCtrl.u32Addr
= (uint32_t)Flash_Buffer;
PDMA_ConfigStruct.sDestCtrl.u32Addr
= UART0_BASE;
PDMA_ConfigStruct.u8TransWidth
= eDRVPDMA_WIDTH_8BITS;
PDMA_ConfigStruct.sSrcCtrl.eAddrDirection
= eDRVPDMA_DIRECTION_INCREMENTED;
PDMA_ConfigStruct.sDestCtrl.eAddrDirection = eDRVPDMA_DIRECTION_FIXED;
PDMA_ConfigStruct.u8Mode
= eDRVPDMA_MODE_MEM2APB;
PDMA_ConfigStruct.i32ByteCnt
= 8;
DrvPDMA_Open(eDRVPDMA_CHANNEL_2,&PDMA_ConfigStruct);
/** 使能PDMA2中断 */
DrvPDMA_EnableInt(eDRVPDMA_CHANNEL_2, eDRVPDMA_BLKD );
/** 装入中断回调函数 */
DrvPDMA_InstallCallBack(eDRVPDMA_CHANNEL_2, eDRVPDMA_BLKD, (PFN_DRVPDMA_CALLBACK) PDMA2_Interrupt_Callback );
/** 使能PDMA2的数据传输功能 */
DrvPDMA_CHEnableTransfer(eDRVPDMA_CHANNEL_2);
DrvUART_EnablePDMA(UART_PORT0);
while(1);
return 0;
}
/** 当传输任务执行完毕,输出一句“f I n i s h”表示任务结束 */
void PDMA2_Interrupt_Callback(){
DrvUART_Write(UART_PORT0,"\nf i n i s h\n", 13);
while(1);
}
本来作为一个深化想写一段从SPI设备到UART设备之间的PDMA数据传输程序的,因为时间有限没能完成。 大概的思想就是开通两个PDMA通道 实现SPI→Memory→UART。前者使用PDMA1,后者用PDMA2。当PDMA1发生中断则在中断服务函数中使能一次PDMA2,PDMA2执行完继续回到PDMA1的传输中。不过还不清楚是否可行,因为担心SPI的数据源源不断涌进来的话,整个流程就乱了。这个以后考虑吧,这次先到这。下次打算做一下EEPROM的IIC通信。
|