DMA (direct memory acess)直接内存存储
功能
直接存储器存取(DMA)用来提供在外设和存储器之间或者存储器和存储器之间的高速数据传输。
无须CPU干预,数据可以通过DMA快速地移动,这就节省了CPU的资源来做其他操作。
STM32含有2个DMA
两个DMA控制器有12个通道(DMA1有7个通道, DMA2有5个通道),每个通道专门用来管理来自
于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个DMA请求的优先权。
应用实例
1.通过DMA 存储器到存储器
1.硬件设计
通过RGB灯显示数据是否传输过去。
2.软件设计
1.编程要点
- 使能DMA时钟
- 配置DMA数据参数
- 使能DMA,进行传输
- 等待传输完成,并对源数据和目标地址数据进行比较
2.代码
1.DMA.h的基本宏定义
#define DMA_CHANNEL DMA1_CHANNEL6 ;//DMA通道
#define DMA_CLOCK RCC_AHBPeriph_DMA1; //DMA时钟 **DMA 挂载在AHB总线上**
#define DMA_FLAG_TC DMA1_FLAG_TC6; //传输完成标志
#define BUFFER_SIZE 32 ; //要发送数据大小
const unit32_t aSRC_Const_Buffer[BUFFER_SIZE]={/*源数据*/};//SRC 发送源
unit32_t aDST_Buffer[BUFFER_SIZE];// DST 接受源
const ???(补充)
2.DMA 数据配置
void DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure; //定义一个初始化结构体
RCC_AHBPeriphClockCmd(DMA_CLOCK,ENABLE); //开启DMA时钟
DMA_InitStructure.DMA_PeripheralBaseAddr = (unit32_t)aSRC_Const_Buffer;// 源数据地址
DMA_InitStructure.DMA_MemoryBaseAddr = (unit32_t)aDST_Buffer; //目标数据地址
DMA_InitStructure.DMA_DIR = DMA_DIR_PeriperalSRC ; // 方向 (1)
DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE; //传输数据大小 ‘数字’单位为发送端设置的单位
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_InitStructure.DMA_Mode = DMA_Mode_Nomal; //DMA模式 一次/循环
// DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High; // DMA传输优先级
DMA_InitStructure.DMA_M2M =DMA_M2A_Enable; //DMA_M2M 存储器到存储器模式开启
DMA_Init(DMA_CHANNEL,&DMA_Init_Structure); //将配置好的结构体赋予选中的DMA通道
DMA_Cmd(DMA_CHANNEL,ENABLE); //使能DMA通道
}
3.存储器数据对比
uint8_t Buffercmp(const uint32_t* pBuffer,
uint32_t* pBuffer1, uint16_t BufferLength)
{
/* 数据长度递减 */
while(BufferLength--)
{
/*判断两个数据是否相等*/
if(*pBuffer != *pBuffer1)
{
/* 对应数据源不相等,退出函数,返回0*/
return 0;
}
/* 递增两个源的地址指针 */
pBuffer++;
pBuffer1++;
}
/* 完成判断且对应数据相等,返回1*/
return 1;
}
判断指定长度的两个数据源是否完全相等,如果完全相等返回 1;只要其中一对数据不相等返回 0。它需要三个形参,前两个是两个数据源的地址,第三个是要比较数据长度。
4.主函数
int main(void)
{
/* 定义存放比较结果的变量*/
uint8_t TransferStatus;
/* LED初始化配置*/
LED_GPIO_Config();
/* 设置LED灯颜色 */
LED_PURPLE;
/*简单延时函数*/
Delay(0xFFFFFF);
/* DMA初始化配置*/
DMA_Config();
/* 等待DMA传输完成 */
while(DMA_GetFlagStatus(DMA_FLAG_TC)==RESET) //
{
}
/*比较源数据和传输后数据 (调用之前写好的Buffercmp函数)*/
TransferStatus=Buffercmp(aSRC_Const_Buffer, aDST_Buffer, BUFFER_SIZE);
/* 结果*/
if(TransferStatus==0)
{
/* 不相等,返回值为0时亮红灯*/
LED_RED;
}
else
{
/* 相等,返回值为1时亮蓝灯*/
LED_BLUE;
}
while (1)
{
}
}
DMA_GetFlagStatus 函数获取 DMA 事件标志位的当前状态,这里获取 DMA 数据传输完成这个标志位,使用循环持续等待直到该标志位被置位,即 DMA 传输完成这个事件发生,然后退出循环,运行之后程序。
2.外设(串口)到存储器使用DMA传输
“暂时省略”
附录:
1.方向:
/** @defgroup DMA_data_transfer_direction
* @{
*/
#define DMA_DIR_PeripheralDST ((uint32_t)0x00000010) //存储器到外设(外设为目标)
#define DMA_DIR_PeripheralSRC ((uint32_t)0x00000000) //外设到存储器(外设为发射源)
#define IS_DMA_DIR(DIR) (((DIR) == DMA_DIR_PeripheralDST) || \
((DIR) == DMA_DIR_PeripheralSRC))
2.配置外设和存储器传输时候的状态
- 地址是否递增
外设一般不开启,存储器一般开启 - 传输数据单位
一般为一个字节 1 byte=8bit
在普通x86的PC机中:
byte:8bit(古老的,以及一些特殊应用的地方,byte不是8bit)
half word:8bit
word:16bit
double word:32bit
quadword:64bit
octaword:128bit
但是,在ARM,AMBA总线中,定义会有区别:
byte:8bit
half word:16bit
word:32bit
double word:64bit
唯一需要注意的是,word的定义不一样,从而导致二者之间的差异
/** @defgroup DMA_peripheral_incremented_mode //外设地址递增
* @{
*/
#define DMA_PeripheralInc_Enable ((uint32_t)0x00000040)
#define DMA_PeripheralInc_Disable ((uint32_t)0x00000000)
#define IS_DMA_PERIPHERAL_INC_STATE(STATE) (((STATE) == DMA_PeripheralInc_Enable) || \
((STATE) == DMA_PeripheralInc_Disable))
/** @defgroup DMA_peripheral_data_size //外设传输单位
* @{
*/
#define DMA_PeripheralDataSize_Byte ((uint32_t)0x00000000) //字节 8bit
#define DMA_PeripheralDataSize_HalfWord ((uint32_t)0x00000100) //半字 16bit
#define DMA_PeripheralDataSize_Word ((uint32_t)0x00000200) //对字 32bit
#define IS_DMA_PERIPHERAL_DATA_SIZE(SIZE) (((SIZE) == DMA_PeripheralDataSize_Byte) || \
((SIZE) == DMA_PeripheralDataSize_HalfWord) || \
((SIZE) == DMA_PeripheralDataSize_Word)
/** @defgroup DMA_memory_incremented_mode //存储器地址递增
* @{
*/
#define DMA_MemoryInc_Enable ((uint32_t)0x00000080)
#define DMA_MemoryInc_Disable ((uint32_t)0x00000000)
#define IS_DMA_MEMORY_INC_STATE(STATE) (((STATE) == DMA_MemoryInc_Enable) || \
((STATE) == DMA_MemoryInc_Disable))
/** @defgroup DMA_memory_data_size //存储器传输单位
* @{
*/
#define DMA_MemoryDataSize_Byte ((uint32_t)0x00000000)
#define DMA_MemoryDataSize_HalfWord ((uint32_t)0x00000400)
#define DMA_MemoryDataSize_Word ((uint32_t)0x00000800)
#define IS_DMA_MEMORY_DATA_SIZE(SIZE) (((SIZE) == DMA_MemoryDataSize_Byte) || \
((SIZE) == DMA_MemoryDataSize_HalfWord) || \
((SIZE) == DMA_MemoryDataSize_Word))
3.DMA 传输模式
/** @defgroup DMA_circular_normal_mode
* @{
*/
#define DMA_Mode_Circular ((uint32_t)0x00000020) //循环模式
#define DMA_Mode_Normal ((uint32_t)0x00000000) //正常模式
#define IS_DMA_MODE(MODE) (((MODE) == DMA_Mode_Circular) || ((MODE) == DMA_Mode_Normal))
/**