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);
}
stm32DMA的更多相关文章
- stm32DMA通道 ADC通道
DMA: 1.使用DAC的时候.将转化后得到的模拟信号通过IO口输出的时候.为什么还将IO口配置能输入模式 PS:stm32手冊上定义PA4和PA5分别和DAC1通道和DAC2通道相连 : DMA1 ...
- 學習 DT device tree 以 ST 的開發板 STM32F429i-disc1 為例
目標 因為對 device tree 不是很熟悉, 所以就將 device tree, 設為學習目標. 啟動 注意, 這篇隨筆的解說都放在最下面,會標 Explanation_XX,只要搜尋 Expl ...
- STM32学习的一些实例
第一讲:修炼STM32之乾坤大挪移术—— 如何用DMA神器搬运数据DMA,即直接存储器访问.DMA 传输方式无需 CPU 直接控制传输,通过硬件为 RAM 与 I/O 设备开辟一条直接传送数据的通路, ...
- STM32使用DMA发送串口数据
1.概述 上一篇文章<STM32使用DMA接收串口数据>讲解了如何使用DMA接收数据,使用DMA外设和串口外设,使用的中断是串口空闲中断.本篇文章主要讲解使用DMA发送数据,不会讲解基础的 ...
随机推荐
- Office2019都有哪些强大功能
前阵子是微软一年一度的Ignite大会.而其中最引人注目.也是与我们一般人最息息相关的消息,当然是Office 2019的正式发布. 尽管Office 2019所更新的功能,对于Office 365的 ...
- 机器学习进阶-目标追踪-SSD多进程执行 1.cv2.dnn.readnetFromCaffe(用于读取已经训练好的caffe模型) 2.delib.correlation_tracker(生成追踪器) 5.cv2.writer(将图片写入视频中) 6.cv2.dnn.blobFromImage(图片归一化) 10.multiprocessing.process(生成进程)
1. cv2.dnn.readNetFromCaffe(prototxt, model) 用于进行SSD网络的caffe框架的加载 参数说明:prototxt表示caffe网络的结构文本,model ...
- Java快速开发平台,JEECG 3.7.6性能增强版本发布
JEECG 3.7.6 性能增强版本发布 导读 ⊙Vue SPA单页面应用 ⊙Datagrid标签实现不同风格切换,支持BootstrapTable.EasyUI ⊙灵活通用代码生成器工厂 ...
- c# Type.InvokeMember用法
函数原型: public object InvokeMember(string, BindingFlags, Binder, object, object[]);string:你所要调用的函数名Bin ...
- python反汇编函数字节码
使用dis模块 >>> def test(): ... print(1) ... a = 1 ... print(a) ... >>> from dis impor ...
- Activity生命周期,切换,参数传递,bundle(包),值对象,Activity参数返回,Activity的启动模式
Activity代表手机屏幕的一屏,或是平板电脑中的一个窗口.它是android应用中最重要的组成单元之一,提供了和用户交互的可视化界面.在一个Activity中,可以添加很多组件,这些组件负责具体的 ...
- 浅谈分布式消息技术 Kafka(转)
一只神秘的程序猿. Kafka的基本介绍 Kafka是最初由Linkedin公司开发,是一个分布式.分区的.多副本的.多订阅者,基于zookeeper协调的分布式日志系统(也可以当做MQ系统),常见可 ...
- C#开发VS LUA开发
一个游戏公司,决定开始用U3D做一款新游戏,这个游戏类型从来没做过. 如果没有一个成熟的游戏框架,那么从头撸起. 是一开始就将LUA热更新考虑进来呢 还是先做成纯C#的框架呢? 考虑因素:游戏逻辑如果 ...
- angularjs 请求数据转换为Form Data传参
在angularjs中配置好服务,有时传参会导致后台借不到值或者后台直接报错: 这就与后台框架有关,如果后台是以public ModelAndView接收接口传过来的参数,这种情况,前台传参的形式比较 ...
- 初探 objc_msgSend函数
1.0 执行某个对象的方法 [receiver message] 被编译为: id objc_msgSend(id self,SEL op,...): objc_msgSend 发送信息的过程 ...