源和目标地址必须按数据传输宽度对齐

支持循环的缓冲器管理

可编程的数据传输数目:最大为65536

每一个通道都有一组寄存器

DMA_CPARx、DMA_CMARx是没有差别的,它们都可以存放外设的地址、内存的地址。只是名字取的不一样而已。

DMA的工作特点:

DMA进行数据传输的必要条件:

1.剩余传输数据量大于0

2.DMA传输通道使能

3.通道上DMA数据传输有事件请求

  • 外设到xx方向的传输:
  • 假设是ADC到存储器的数据传输,显然是ADC的DMA传输的源地址是ADC的数据寄存器,并不是说只要DMA通道传输使能后,就立即进行数据传输。只有当一次ADC转化完成,ADC的DMA通道的传输时件才有效,DMA才会从ADC的数据寄存器读出数据,写入目的地址。当DMA在读取ADC的数据寄存器时,同时使ADC的DMA通道传输事件无效。显然,必须等到下一次ADC转换完成后,才能启动再一次的数据传输。
  • 存储器对xx的访问:
  • 因为数据是直接准备好的,不像ADC还需要等待数据到位。所以,不需要对应通道的事件。只要使能DMA数据传输就移植传输,直到达到设定的传输量。

当外设以DMA方式进行正在数据传输时,不可能再响应cpu的软件控制命令。

DMA传输对于高效能 嵌入式系统 算法和网络是很重要的

在实现DMA传输时,是由DMA控制器直接掌管总线,因此,存在这一个总线控制权的转移问题。

DMA的传输涉及到的几个基本的术语:

event:事件,触发控制器启动或停止DMA传输的操作

Transaction:事务,单子传输(最多可以到4个字节),由读和写组成

Cell transfer:元传输,单次共DCHXCSIZE个字节的数据传输,元传输由单个或多个事务组成。

Block tansfer:块传输,块传输总的字节数由DCHXSSIZ或DCHXDSIZ决定。块传输由单个或多个元传输组成。

事件是触发DMA控制器产生动作的方式,分为,START EVENT->启动传输,ABORT EVENT->取消传输,STOP EVENT->停止传输。

一般而言,DMA控制器包括一条地址总线,一条数据总线,和控制寄存器。

每个DMA控制器都有一组FIFO,起到DMA子系统和外设或存储器之间的缓冲器的作用。传输的源端和目标端都有一组FIFO存在。当资源紧张而不能完胜数据传输的话,则FIFO可以提供数据的暂存区,从而提高性能。

以下是我对看过一个例程关于DMA的分析:

这个例子功能是内存到内存的传输,分别提供了4个通道,从中断能够看到4个通道传输数据所用的时间。
#include "2440addr.h"
#include "2440lib.h"
#include "option.h"
#include "def.h"
#include "uart.h"
#include <string.h>

extern unsigned int PCLK;                //DMA采用的时钟是PCLK

//====================================================
// 函数声明区
//====================================================
void __irq Dma0Done(void); //DMA0中断函数
void __irq Dma1Done(void); //DMA1中断函数
void __irq Dma2Done(void); //DMA2中断函数
void __irq Dma3Done(void); //DMA3中断函数
void DMA_M2M(int ch,int srcAddr,int dstAddr,int tc,int dsz,int burst); //内存到内存的DMA数据传输函数
void Test_DMA(void); //DMA传输测试

//====================================================
//变量设置区
//====================================================
/* DMA特殊功能寄存器,设置一个结构体,用来存放DMA的 9个寄存器*/

typedef struct tagDMA
{
volatile U32 DISRC;     //0x0 DMA初始源寄存器
volatile U32 DISRCC;    //0x4 DMA初始源控制寄存器
volatile U32 DIDST;   //0x8 DMA初始目的寄存器
volatile U32 DIDSTC;    //0xc DMA初始目的控制寄存器
volatile U32 DCON;    //0x10 DMA控制寄存器
volatile U32 DSTAT;    //0x14 DMA状态寄存器
volatile U32 DCSRC;   //0x18 当前源寄存器
volatile U32 DCDST;    //0x1c 当前目的寄存器
volatile U32 DMASKTRIG;   //0x20 DMA掩码触发寄存器
}DMA;

static volatile int dmaDone;          //DMA传输完成与否标识 0未完成 1完成

void Timer_Start(int divider)              //0:16us,1:32us 2:64us 3:128us,解释:如果选择的是divider==3,则,计数一次所消耗的时间是128us。向下递减,用的是看门狗定时器,看门狗定时器的时钟源是PCLK。可以                                                                                                                      用作普通16位定时器的中断输出,也可以用于系统复位。
{
rWTCON = ((PCLK/1000000-1)<<8)|(divider<<3); //Watch-dog timer control register,  计数一次所消耗的时间
rWTDAT = 0xffff; //Watch-dog timer data register
rWTCNT = 0xffff; //Watch-dog count register

// Watch-dog timer enable & interrupt disable
rWTCON = (rWTCON & ~(1<<5) & ~(1<<2)) |(1<<5);
}

//=================================================================
int Timer_Stop(void)                                                     //计数停止,返回值为计数的个数
{
rWTCON = ((PCLK/1000000-1)<<8);
return (0xffff - rWTCNT); //用到的是递减计数,返回的是计数次数
}

/********************************************************************
// 语法格式 : void Main(void)
// 功能描述 : DMA操作实验主程序
// 实现功能:
// 实现DMA方式内存到内存的拷贝动作,修改DMA设置
// 并比较其工作效率,实验包括:DMA0-DMA3
// 入口参数 : 无
// 出口参数 : 无
*********************************************************************/
void Main(void)
{
memcpy((U8 *)0x0,(U8 *)0x30000000,0x1000);

SetSysFclk(FCLK_400M);    //设置系统时钟 400M
ChangeClockDivider(2,1);    //设置分频 1:4:8
CalcBusClk();         //计算总线频

Uart_Select(0);
Uart_Init(0,115200);

Uart_Printf("\n---DMA操作实验主程序---\n");

Test_DMA();

Uart_Printf("\nDMA测试结束\n");
while(1);
}

void Test_DMA(void)
{
//DMA Ch 0 _NONCACHE_STARTADDRESS = 0x30400000
DMA_M2M(0,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x80000,0,0); //byte,single
DMA_M2M(0,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x40000,1,0); //halfword,single
DMA_M2M(0,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x20000,2,0); //word,single
DMA_M2M(0,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x20000,0,1); //byte,burst
DMA_M2M(0,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x10000,1,1); //halfword,burst
DMA_M2M(0,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000, 0x8000,2,1); //word,burst

//DMA Ch 1
DMA_M2M(1,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x80000,0,0); //byte,single
DMA_M2M(1,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x40000,1,0); //halfword,single
DMA_M2M(1,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x20000,2,0); //word,single
DMA_M2M(1,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x20000,0,1); //byte,burst
DMA_M2M(1,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x10000,1,1); //halfword,burst
DMA_M2M(1,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000, 0x8000,2,1); //word,burst

//DMA Ch 2
DMA_M2M(2,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x80000,0,0); //byte,single
DMA_M2M(2,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x40000,1,0); //halfword,single
DMA_M2M(2,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x20000,2,0); //word,single
DMA_M2M(2,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x20000,0,1); //byte,burst
DMA_M2M(2,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x10000,1,1); //halfword,burst
DMA_M2M(2,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000, 0x8000,2,1); //word,burst

//DMA Ch 3
DMA_M2M(3,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x80000,0,0); //byte,single
DMA_M2M(3,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x40000,1,0); //halfword,single
DMA_M2M(3,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x20000,2,0); //word,single
DMA_M2M(3,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x20000,0,1); //byte,burst
DMA_M2M(3,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000,0x10000,1,1); //halfword,burst
DMA_M2M(3,_NONCACHE_STARTADDRESS,_NONCACHE_STARTADDRESS+0x800000, 0x8000,2,1); //word,burst
}
/********************************************************************
// 语法格式:void DMA_M2M(int ch,int srcAddr,int dstAddr,int tc,int dsz,int burst)
// 功能描述: DMA方式内存拷贝
// 入口参数:
// : int ch:DMA通道 0-DMA0, 1-DMA1, 2-DMA2, 3-DMA3
// : int srcAddr:源地址
// : int dstAddr:目的地址
// : int tc:初始传输计数值
// : int dsz:传输数据宽度 0:1字节 1:2字节 2:4字节
// : int burst:自动传输的传输宽度 0-单元传输(一个字节) 1-突发模式传输(四个字节)模式选择
// 出口参数: 无
*********************************************************************/
void DMA_M2M(int ch,int srcAddr,int dstAddr,int tc,int dsz,int burst)
{
int i,time;
volatile U32 memSum0=0,memSum1=0;
DMA *pDMA;                        //创建一个DMA格式的结构体
int length;

length=tc*(burst ? 4:1)*((dsz==0)+(dsz==1)*2+(dsz==2)*4);  //确定一次传输的字节数( 传输单元模式 * 传输数据宽度 )
                            //length为传输的总字节数
Uart_Printf("[DMA%d MEM2MEM Test]\n",ch);

switch(ch)
{
case 0:
pISR_DMA0 = (unsigned)Dma0Done;              //DMA0中断清除挂起,设置dmaDone==1
EnableIrq(BIT_DMA0);                    //open DMA0 INTERRUPT,DMA0中断使能
pDMA=(void *)0x4b000000;
break;
case 1:
pISR_DMA1 = (unsigned)Dma1Done;
EnableIrq(BIT_DMA1);                    //open DMA1 INTERRUPT
pDMA=(void *)0x4b000040;
break;
case 2:
pISR_DMA2 = (unsigned)Dma2Done;
EnableIrq(BIT_DMA2);                    //open DMA2 INTERRUPT
pDMA=(void *)0x4b000080;
break;
case 3:
pISR_DMA3 = (unsigned)Dma3Done;
EnableIrq(BIT_DMA3);                    //open DMA3 INTERRUPT
pDMA=(void *)0x4b0000c0;
break;
}

Uart_Printf("DMA%d %8xh->%8xh,size=%xh(tc=%xh),dsz=%d,burst=%d\n",ch,
srcAddr,dstAddr,length,tc,dsz,burst);

Uart_Printf("Initialize the src.\n");

for(i=srcAddr;i<(srcAddr+length);i+=4)
{
*((U32 *)i)=i^0x55aa5aa5;                     //向源地址写入任意数据 写入长度为length
memSum0+=i^0x55aa5aa5;                     //将写入数据累加,为校验读出数据的准确性
}

Uart_Printf("DMA%d start\n",ch);

dmaDone=0;

pDMA->DISRC=srcAddr;                     //设置源地址
pDMA->DISRCC=(0<<1)|(0<<0);                   //设置源控制寄存器 inc,AHB
pDMA->DIDST=dstAddr;                     //设置目的地址
pDMA->DIDSTC=(0<<1)|(0<<0);                        //设置目的控制寄存器 inc,AHB

pDMA->DCON=(1<<31)|(1<<30)|(1<<29)|(burst<<28)|(1<<27)|
(0<<23)|(1<<22)|(dsz<<20)|(tc);
                                           //DMA控制寄存器 HS,AHB sync,enable interrupt,whole, SW request mode,relaod off

pDMA->DMASKTRIG=(1<<1)|1; //DMA on, SW_TRIG

Timer_Start(3);                                         //128us resolution,看门狗开始计时。

while(dmaDone==0);
time=Timer_Stop();                        //这里的time指的是计数次数

Uart_Printf("DMA transfer done.\n");
Uart_Printf("time = %u MS\n", time*128/1000);            //time =计数次数*0.128ms

DisableIrq(BIT_DMA0);
DisableIrq(BIT_DMA1);
DisableIrq(BIT_DMA2);
DisableIrq(BIT_DMA3);

for(i=dstAddr;i<dstAddr+length;i+=4)
{
memSum1+=*((U32 *)i)=i^0x55aa5aa5;
}

Uart_Printf("\n memSum0=%x,memSum1=%x\n",memSum0,memSum1);
if(memSum0==memSum1)
Uart_Printf("DMA test result--------------------------------------O.K.\n");
else
Uart_Printf("DMA test result--------------------------------------ERROR!!!\n");

}
//====================================================
// 语法格式:void __irq Dma0Done(void)
// 功能描述: DMA0中断函数
// 更新DMA0标识
// 入口参数: 无
// 出口参数: 无
//====================================================

void __irq Dma0Done(void)
{
dmaDone=1;
ClearPending(BIT_DMA0);

}
//====================================================
// 语法格式:void __irq Dma1Done(void)
// 功能描述: DMA1中断函数
// 更新DMA标识
// 入口参数: 无
// 出口参数: 无
//====================================================
void __irq Dma1Done(void)
{
dmaDone=1;
ClearPending(BIT_DMA1);

}
//====================================================
// 语法格式:void __irq Dma2Done(void)
// 功能描述: DMA2中断函数
// 更新DMA标识
// 入口参数: 无
// 出口参数: 无
//====================================================
void __irq Dma2Done(void)
{
dmaDone=1;
ClearPending(BIT_DMA2);

}
//====================================================
// 语法格式:void __irq Dma3Done(void)
// 功能描述: DMA3中断函数
// 更新DMA标识
// 入口参数: 无
// 出口参数: 无
//====================================================
void __irq Dma3Done(void)
{
dmaDone=1;
ClearPending(BIT_DMA3);

}

stm32DMA的更多相关文章

  1. stm32DMA通道 ADC通道

    DMA: 1.使用DAC的时候.将转化后得到的模拟信号通过IO口输出的时候.为什么还将IO口配置能输入模式 PS:stm32手冊上定义PA4和PA5分别和DAC1通道和DAC2通道相连  : DMA1 ...

  2. 學習 DT device tree 以 ST 的開發板 STM32F429i-disc1 為例

    目標 因為對 device tree 不是很熟悉, 所以就將 device tree, 設為學習目標. 啟動 注意, 這篇隨筆的解說都放在最下面,會標 Explanation_XX,只要搜尋 Expl ...

  3. STM32学习的一些实例

    第一讲:修炼STM32之乾坤大挪移术—— 如何用DMA神器搬运数据DMA,即直接存储器访问.DMA 传输方式无需 CPU 直接控制传输,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路, ...

  4. STM32使用DMA发送串口数据

    1.概述 上一篇文章<STM32使用DMA接收串口数据>讲解了如何使用DMA接收数据,使用DMA外设和串口外设,使用的中断是串口空闲中断.本篇文章主要讲解使用DMA发送数据,不会讲解基础的 ...

随机推荐

  1. 关于vector变量的size,是一个无符号数引发的bug。LeetCode 3 sum

    class Solution { public: vector<vector<int>> threeSum(vector<int>& a) { vector ...

  2. ACM__容器之vector

    今天做题碰到了深搜的题,有一种存图方式需要用到vector,对vector不是很熟悉,回顾了一下 vector都知道是一个容器,但并不准确,它是一个多功能的能够操作多种数据结构和算法的模板类和函数库. ...

  3. python语言中的数据类型之元组

    数据类型 元组       tuple 元组:不可变类型 用途:元组就是一个不可变的列表,当需要存不改动的值时可用元组 定义方式:在()内用逗号分隔开多个任意类型的元素 t=(1,2.2,'aa',( ...

  4. Angular2学习笔记

    Angular2 这里 Angular2 是指采用 TypeScript 语言的 Angular 2.0及以上版本.与采用 JavaScript 语言的 AngularJS 相比,Angular2 不 ...

  5. Set和List的区别

    一: Set 不允许重复,List允许重复 二: Set 无序,List有序 . 这里的无序和有序, 是说的添加顺序和元素顺序的一致性. 比如添加时是obj1,obj2,obj3 ,那么list存储他 ...

  6. 微信定时获取token

    为了使第三方开发者能够为用户提供更多更有价值的个性化服务,微信公众平台开放了许多接口,包括自定义菜单接口.客服接口.获取用户信息接口.用户分组接口.群发接口等,开发者在调用这些接口时,都需要传入一个相 ...

  7. 3.Java的基本数据类型.md

    Java支持的类型分为两类: •基本类型(Primitive Type):boolean和数值类型 ◦整型:byte.short.int.long.char ◦浮点:float.double •nul ...

  8. Structs复习 字符编码问题

    <?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" ...

  9. python批量操作Linux服务器脚本,ssh密码登录(执行命令、上传、下载)(一)

     -*-          paramiko.util.log_to_file(         ssh = paramiko.SSHClient()          ssh.set_missing ...

  10. Windows Server 2012开启磁盘性能计数器

    Windows Server 2012默认情况下已经禁用了磁盘性能计数器,打开任务管理器后,无法像Win8一样在性能选项卡中看到“磁盘”使用情况,可能是因为微软考虑到安装此服务器系统的硬件都会非常好, ...