STM32教程:简单易懂的DMA使用指南
一、介绍
MDA是英文Direct Memory Access的缩写,中文意思是直接存储器存取。它是单片机的一个外设,作用主要是用来数据传输。数据传输支持从外设到存储器或者存储器到存储器,这里的存储器可以是 SRAM 或者是 FLASH。 DMA 控制器包含了 DMA1 和 DMA2,其中 DMA1 有 7 个通道,DMA2 有 5 个通道,这里的通道 可以理解为传输数据的一种管道。
STM32F103
的
DMA
有以下一些特性:
●每个通道都直接连接专用的硬件
DMA
请求,每个通道都同样支持软件触发。这些功能
通过软件来配置。
●在七个请求间的优先权可以通过软件编程设置
(
共有四级:很高、高、中等和低
)
,假如
在相等优先权时由硬件决定
(
请求
0
优先于请求
1
,依此类推
)
。
●独立的源和目标数据区的传输宽度
(
字节、半字、全字
)
,模拟打包和拆包的过程。源和
目标地址必须按数据传输宽度对齐。
●支持循环的缓冲器管理。
●每个通道都有
3
个事件标志
(DMA
半传输,
DMA
传输完成和
DMA
传输出错
)
,这
3
个
事件标志逻辑或成为一个单独的中断请求。
●存储器和存储器间的传输。
●外设和存储器,存储器和外设的传输。
●闪存、
SRAM
、外设的
SRAM
、
APB1
、
APB2
和
AHB
外设均可作为访问的源和目标。
●可编程的数据传输数目:最大为
65536
。
二、功能框图
三、功能描述、
DMA
控制器和
Cortex™-M3
核心共享系统数据总线,执行直接存储器数据传输。当
CPU
和
DMA 同时访问相同的目标(RAM
或外设
)
时,
DMA
请求会暂停
CPU
访问系统总线达若干个周期,总线
仲裁器执行循环调度,以保证
CPU
至少可以得到一半的系统总线
(
存储器或外设
)
带宽。
四、DMA请求
如果外设要想通过
DMA
来传输数据,必须先给
DMA
控制器发送
DMA
请求,
DMA
收到请求信 号之后,控制器会给外设一个应答信号,当外设应答后且 DMA
控制器收到应答信号之后,就会
启动
DMA
的传输,直到传输完毕。
五、通道配置的过程
1.
在
DMA_CPARx
寄存器中设置外设寄存器的地址。当外设数据传输请求时,这个地址将
是数据传输的源或目标。
2.
在
DMA_CMARx
寄存器中设置数据存储器的地址。发生外设数据传输请求时,传输的数
据将从这个地址读出或写入这个地址。
3.
在
DMA_CNDTRx
寄存器中设置要传输的数据量。在每个数据传输后,这个数值递减。
4.
在
DMA_CCRx
寄存器的
PL[1:0]
位中设置通道的优先级。
5.
在
DMA_CCRx
寄存器中设置数据传输的方向、循环模式、外设和存储器的增量模式、外
设和存储器的数据宽度、传输一半产生中断或传输完成产生中断。
6.
设置
DMA_CCRx
寄存器的
ENABLE
位,启动该通道。
六、实验
M–>M(存储器传输到存储器)
函数的解释:(实验并没有全部的函数都用到)
//初始化DMA默认重置值
void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);
//DMA的通道的初始化
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);
//用默认值填充每个DMA_InitStruct成员。
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);
//DMA使能
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
//启用或禁用指定的DMAy通道的中断。
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);
//设置当前DMAy通道传输中的数据单元数。
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);
//返回当前中剩余数据单元的数量
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
//控制DMA的标志位
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);
void DMA_ClearFlag(uint32_t DMAy_FLAG);
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);
void DMA_ClearITPendingBit(uint32_t DMAy_IT);
DMA初始化函数的配置:
void DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
// 开启DMA时钟
RCC_AHBPeriphClockCmd(DMA_CLOCK, ENABLE);
// 源数据地址
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;
// 目标地址
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)aDST_Buffer;
// 方向:外设到存储器(这里的外设是内部的FLASH)
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
// 传输大小
DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;
// 外设(内部的FLASH)地址递增
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
// 内存地址递增
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
// 外设数据单位
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
// 内存数据单位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
// DMA模式,一次或者循环模式
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;
//DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
// 优先级:高
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
// 使能内存到内存的传输
DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
// 配置DMA通道
DMA_Init(DMA_CHANNEL, &DMA_InitStructure);
//清除DMA数据流传输完成标志位
DMA_ClearFlag(DMA_FLAG_TC);
// 使能DMA
DMA_Cmd(DMA_CHANNEL,ENABLE);
}
数据源设置
const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {
0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80};
接收设置
int32_t aDST_Buffer[BUFFER_SIZE];
#define SOFT_DELAY Delay(0x0FFFFF);
void Delay(__IO u32 nCount);
uint8_t Buffercmp(const uint32_t* pBuffer, uint32_t* pBuffer1, uint16_t BufferLength);
void DMA_Config(void);
通过LED的颜色来判断数据是否正确发送,红色为错误,蓝色为正确
int main(void)
{
/* 定义存放比较结果变量 */
uint8_t TransferStatus;
/* LED 端口初始化 */
LED_GPIO_Config();
/* 设置RGB彩色灯为紫色 */
LED_PURPLE;
/* 简单延时函数 */
Delay(0xFFFFFF);
/* DMA传输配置 */
DMA_Config();
/* 等待DMA传输完成 */
while(DMA_GetFlagStatus(DMA_FLAG_TC)==RESET)
{
}
/* 比较源数据与传输后数据 */
TransferStatus=Buffercmp(aSRC_Const_Buffer, aDST_Buffer, BUFFER_SIZE);
/* 判断源数据与传输后数据比较结果*/
if(TransferStatus==0)
{
/* 源数据与传输后数据不相等时RGB彩色灯显示红色 */
LED_RED;
}
else
{
/* 源数据与传输后数据相等时RGB彩色灯显示蓝色 */
LED_BLUE;
}
while (1)
{
}
}
作者:番茄炒西红柿996