stm32项目之DMA实验

1.DMA 简介

        DMA(Direct Memory Access)—直接存储器存取,是单片机的一个外设,它的主要功能是用来搬数据,但是不需要占用CPU,即在传输数据的时候,CPU 可以干其他的事情,好像是多线程一样。数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是SRAM 或者是FLASH。DMA 控制器包含了DMA1 和DMA2,其中DMA1 有7 个通道,DMA2 有5 个通道,这里的通道可以理解为传输数据的一种管道。要注意的是DMA2 只存在于大容量的单片机中。

        简单来说:DMA就是一个数据搬运工,释放CPU。

1.1.仲裁器

        当发生多个DMA 通道请求时,就意味着有先后响应处理的顺序问题,这个就由仲裁器也管理。仲裁器管理DMA 通道请求分为两个阶段。

        第一阶段属于软件阶段,可以在DMA_CCRx 寄存器中设置,有4 个等级:非常高、高、中和低四个优先级。

        第二阶段属于硬件阶段,如果两个或以上的DMA 通道请求设置的优先级一样,则他们优先级取决于通道编号,编号越低优先权越高,比如通道0 高于通道1。在大容量产品和互联型产品中,DMA1 控制器拥有高于DMA2 控制器的优先级。

1.2.DMA 数据配置 

使用DMA,最核心就是配置要传输的数据,包括数据从哪里来,要到哪里去,传输的数据的单
位是什么,要传多少数据,是一次传输还是循环传输等等。

1.2.1从哪里来到哪里去

我们知道DMA 传输数据的方向有三个:从外设到存储器,从存储器到外设,从存储器到存储器
具体的方向DMA_CCR 位4 DIR 配置:0 表示从外设到存储器,1 表示从存储器到外设。这里面
涉及到的外设地址由DMA_CPAR 配置,存储器地址由DMA_CMAR 配置。

1.2.2要传多少,单位是什么

        当我们配置好数据要从哪里来到哪里去之后,我们还需要知道我们要传输的数据是多少,数据的单位是什么。
        以串口向电脑发送数据为例,我们可以一次性给电脑发送很多数据,具体多少由DMA_CNDTR配置,这是一个32 位的寄存器,一次最多只能传输65535 个数据。
        要想数据传输正确,源和目标地址存储的数据宽度还必须一致,串口数据寄存器是8 位的,所以我们定义的要发送的数据也必须是8 位。外设的数据宽度由DMA_CCRx 的PSIZE[1:0] 配置,可
以是8/16/32 位,存储器的数据宽度由DMA_CCRx 的MSIZE[1:0] 配置,可以是8/16/32 位。

1.2.3.什么时候传输完成


数据什么时候传输完成,我们可以通过查询标志位或者通过中断的方式来鉴别。每个DMA 通道
在DMA 传输过半、传输完成和传输错误时都会有相应的标志位,如果使能了该类型的中断后,
则会产生中断
。有关各个标志位的详细描述请参考DMA 中断状态寄存器DMA_ISR 的详细描述。
传输完成还分两种模式,是一次传输还是循环传输,一次传输很好理解,即是传输一次之后就停
止,要想再传输的话,必须关断DMA 使能后再重新配置后才能继续传输。循环传输则是一次传
输完成之后又恢复第一次传输时的配置循环传输,不断的重复。具体的由DMA_CCRx 寄存器的
CIRC 循环模式位控制 

2.编程

2.1.DMA_ InitTypeDef 初始化结构体

typedef struct
{
uint32_t DMA_PeripheralBaseAddr; // 外设地址
uint32_t DMA_MemoryBaseAddr; // 存储器地址
uint32_t DMA_DIR; // 传输方向
uint32_t DMA_BufferSize; // 传输数目
uint32_t DMA_PeripheralInc; // 外设地址增量模式
uint32_t DMA_MemoryInc; // 存储器地址增量模式
uint32_t DMA_PeripheralDataSize; // 外设数据宽度

uint32_t DMA_MemoryDataSize; // 存储器数据宽度
uint32_t DMA_Mode; // 模式选择
uint32_t DMA_Priority; // 通道优先级
uint32_t DMA_M2M; // 存储器到存储器模式
} DMA_InitTypeDef;

1) DMA_PeripheralBaseAddr:外设地址,设定DMA_CPAR 寄存器的值;一般设置为外设的数据
寄存器地址,如果是存储器到存储器模式则设置为其中一个存储器地址
2) DMA_Memory0BaseAddr:存储器地址,设定DMA_CMAR 寄存器值;一般设置为我们自定义
存储区的首地址。
3) DMA_DIR:传输方向选择,可选外设到存储器、存储器到外设。它设定DMA_CCR 寄存器的
DIR[1:0] 位的值。这里并没有存储器到存储器的方向选择,当使用存储器到存储器时,只需要把
其中一个存储器当作外设使用即可
4) DMA_BufferSize:设定待传输数据数目,初始化设定DMA_CNDTR 寄存器的值。
5) DMA_PeripheralInc:如果配置为DMA_PeripheralInc_Enable,使能外设地址自动递增功能,它设定DMA_CCR 寄存器的PINC 位的值;一般外设都是只有一个数据寄存器,所以一般不会使能
该位。
6) DMA_MemoryInc:如果配置为DMA_MemoryInc_Enable,使能存储器地址自动递增功能,它
设定DMA_CCR 寄存器的MINC 位的值;我们自定义的存储区一般都是存放多个数据的,所以
要使能存储器地址自动递增功能。
7) DMA_PeripheralDataSize:外设数据宽度,可选字节(8 位)、半字(16 位) 和字(32 位),它设定
DMA_CCR 寄存器的PSIZE[1:0] 位的值。
8) DMA_MemoryDataSize:存储器数据宽度,可选字节(8 位)、半字(16 位) 和字(32 位),它设定
DMA_CCR 寄存器的MSIZE[1:0] 位的值。当外设和存储器之间传数据时,两边的数据宽度应该
设置为一致大小
9) DMA_Mode:DMA 传输模式选择,可选一次传输或者循环传输,它设定DMA_CCR 寄存器的
CIRC 位的值。

10) DMA_Priority:软件设置通道的优先级,有4 个可选优先级分别为非常高、高、中和低,它设
定DMA_CCR 寄存器的PL[1:0] 位的值。DMA 通道优先级只有在多个DMA 通道同时使用时才有意义,如果是单个通道,优先级可以随便设置。
11) DMA_M2M:存储器到存储器模式,使用存储器到存储器时用到,设定DMA_CCR 的位14
MEN2MEN 即可启动存储器到存储器模式。

2.2。实现

1) 使能DMA 时钟;
2) 配置DMA 数据参数;
3) 使能DMA,进行传输;
4) 等待传输完成

这些是实现步骤,这里只贴部分主要的代码。

DMA_InitTypeDef DMA_InitStructure;

u16 DMA1_MEM_LEN;//保存DMA每次数据传送的长度 	    
//DMA1的各通道配置
//这里的传输形式是固定的,这点要根据不同的情况来修改
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_CHx:DMA通道CHx
//cpar:外设地址
//cmar:存储器地址
//cndtr:数据传输量 
void MYDMA_Config(DMA_Channel_TypeDef* DMA_CHx,u32 cpar,u32 cmar,u16 cndtr)
{
 	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);	//使能DMA传输
	
  DMA_DeInit(DMA_CHx);   //将DMA的通道1寄存器重设为缺省值

	DMA1_MEM_LEN=cndtr;
	DMA_InitStructure.DMA_PeripheralBaseAddr = cpar;  //DMA外设基地址
	DMA_InitStructure.DMA_MemoryBaseAddr = cmar;  //DMA内存基地址
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;  //数据传输方向,从内存读取发送到外设
	DMA_InitStructure.DMA_BufferSize = cndtr;  //DMA通道的DMA缓存的大小
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;  //外设地址寄存器不变
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;  //内存地址寄存器递增
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //数据宽度为8位
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //数据宽度为8位
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  //工作在正常缓存模式
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级 
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //DMA通道x没有设置为内存到内存传输
	DMA_Init(DMA_CHx, &DMA_InitStructure);  //根据DMA_InitStruct中指定的参数初始化DMA的通道USART1_Tx_DMA_Channel所标识的寄存器
	  	
} 
//开启一次DMA传输
void MYDMA_Enable(DMA_Channel_TypeDef*DMA_CHx)
{ 
	DMA_Cmd(DMA_CHx, DISABLE );  //关闭USART1 TX DMA1 所指示的通道      
 	DMA_SetCurrDataCounter(DMA_CHx,DMA1_MEM_LEN);//DMA通道的DMA缓存的大小
 	DMA_Cmd(DMA_CHx, ENABLE);  //使能USART1 TX DMA1 所指示的通道 
}	  

使能之后,就可以进行数据搬运了。 

代码:后期会整理比较好的代码再上传。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值