stm32DMA
源和目标地址必须按数据传输宽度对齐
支持循环的缓冲器管理
可编程的数据传输数目:最大为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);
}
posted on 2018-09-24 10:04 xiegangqingnian 阅读(490) 评论(0) 编辑 收藏 举报