1.DMA
可以提供
外设寄存器(也是存储器的一种,只是可以用来读写,是软件和硬件的桥梁)和存储器
,或者
存储器和存储器
之间的高速数据传输,无须
CPU
干预,节省了
CPU
的资源。
外设寄存器:一般指的是
DR
数据寄存器比如
ADC/
串口的数据寄存器
存储器:运行内存
SRAM
和程序存储器
flash,
即存储变量数组和程序代码的地方。
2.
STM32F103C8T6
DMA
资源:
DMA1
(
7
个通道)每个通道都支持软件触发和特定的硬件
3.触发(每个
DMA
有特点的触发源)
软件触发:存储器转运到存储器(一次性转运完数据就结束)软件触发所有通道都可以使用
硬件触发:
外设的数据
转运到存储器(外设的数据有特定的时间节点的限制),
硬件触发要选择指定的
DMA
通道。
4.DMA的基本结构框图
DMA触发的条件:
- 传输计数器大于0
- 触发源有触发信号。若为软件触发,则一直都会有触发信号。
- DMA使能,使用DMACmd函数
- ps:若需要DMA搬运ADC的数据,则选择硬件触发源,还需开启ADC到DMA的硬件触发信号
5.使用DMA的步骤
DMA初始化函数:
第一步:开启DMA时钟:RCC_AHBPeriphClockCmd
第二步:使用结构体初始化DMA:
包括外设和存储器的起始地址,数据宽度,地址是否自增、传输方向、传输计数器(传输计数器是否需要自动重装)、选择触发源、通道优先级等
DMA转运执行函数(调用一次次函数就执行一次DMA转运):
第一步:先使得DMA使能
第二步:给传输计数器赋值
第三步:DMA使能
第四步:在while循环中使用DMA_GetITStatus获得DMA是否转运完成的标志位。
第五步:使用DMA_ClearFlag清除转运完成的标志位。
主函数:
第一步:调用DMA初始化函数
第二步:在while循环中调用DMA转运执行函数。
代码部分(使用DMA搬运数据---软件触发)
//模块一:DMA初始化函数+DMA执行转运函数
#include "stm32f10x.h" // Device header
uint16_t MyDMA_Size;//传输计数器的值,全局变量
/*初始化DMA:
RCC开启DMA时钟
外设和存储器的起始地址,数据宽度,地址是否自增
传输方向
传输计数器(传输计数器是否需要自动重装)
选择触发源
通道优先级
开关控制
若为硬件触发需要调用XXX_DMACmd,开启触发信号输出。
如果需要DMA的中断,就需要调用DMA_ITConfig开启中断输出,并在NVIC配置相应的中断通道。*/
void MyDMA_Init(uint32_t AddrA,uint32_t AddrB,uint16_t Size)//地址必须是32位的
{
MyDMA_Size=Size;//把初始化的Size赋值给全局变量MyDMA_Size
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
DMA_InitTypeDef DMA_InitStructure;
//把地址提取成初始化函数的参数,在初始化时,想转运哪个数组,就把相应数组的地址传进来就可以了
DMA_InitStructure.DMA_PeripheralBaseAddr=AddrA;//转运目标起始地址
DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte; //外设的字节
DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Enable;//外设地址是否自增
//把地址提取成初始化函数的参数,在初始化时,想转运哪个数组,就把相应数组的地址传进来就可以了
DMA_InitStructure.DMA_MemoryBaseAddr=AddrB; //被转运起始地址
DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;//存储器字节
DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable; //存储器地址是否自增
DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralSRC; //传输方向
DMA_InitStructure.DMA_BufferSize=Size; //传输计数器的值,不同的传输数量传输的次数不同
DMA_InitStructure.DMA_Mode=DMA_Mode_Normal; //数据传输的模式,即需不需要自动重装功能
DMA_InitStructure.DMA_M2M=DMA_M2M_Enable; //触发方式,此处选择软件触发
DMA_InitStructure.DMA_Priority=DMA_Priority_Medium; //配置优先级
DMA_Init(DMA1_Channel1,&DMA_InitStructure); //DMA1,通道1,DMA初始化
DMA_Cmd(DMA1_Channel1,DISABLE); //DMA失能,不让DMA初始化就开始转运,而放在DMA转运函数中
/*DMA工作的条件:
1.传输计数器大于0
2.触发源有触发信号 若为软件触发,则一直都会有触发信号
3.DMA使能。
*/
}
//调用一次次函数就执行一次DMA转运
void MyDMA_Transfer(void)
{
//若想给传输计数器再次赋值,需要将DMA失能:DMA_Cmd(DMA1_Channel1,DISABLE)
DMA_Cmd(DMA1_Channel1, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel1,MyDMA_Size); //给传输计数器再次赋值
DMA_Cmd(DMA1_Channel1,ENABLE); //DMA使能
while (DMA_GetITStatus(DMA1_FLAG_TC1)==RESET);//若未转运完成,则一直等待转运完成
DMA_ClearFlag(DMA1_FLAG_TC1);
}
//主函数
#include "stm32f10x.h" // Device header
#include"Delay.h"
#include"OLED.h"
#include"MyDMA.h"
uint8_t DataA[]={0x01,0x02,0x03,0x04};
uint8_t DataB[]={0,0,0,0};
int main(void)
{
//初始化OLED;
OLED_Init();
//调用此函数DateA数组的数据,立刻传输到DataB中
MyDMA_Init((uint32_t)DataA,(uint32_t)DataB,4);
OLED_ShowString(1,1,"DataA");
OLED_ShowString(3,1,"DataB");
OLED_ShowHexNum(1,8,(uint32_t)DataA,8);
OLED_ShowHexNum(3,8,(uint32_t)DataB,8);
while(1)
{
DataA[0]++;
DataA[1]++;
DataA[2]++;
DataA[3]++;
OLED_ShowHexNum(2,1,DataA[0],2);
OLED_ShowHexNum(2,4,DataA[1],2);
OLED_ShowHexNum(2,7,DataA[2],2);
OLED_ShowHexNum(2,10,DataA[3],2);
OLED_ShowHexNum(4,1,DataB[0],2);
OLED_ShowHexNum(4,4,DataB[1],2);
OLED_ShowHexNum(4,7,DataB[2],2);
OLED_ShowHexNum(4,10,DataB[3],2);
Delay_ms(1000);
MyDMA_Transfer();
OLED_ShowHexNum(2,1,DataA[0],2);
OLED_ShowHexNum(2,4,DataA[1],2);
OLED_ShowHexNum(2,7,DataA[2],2);
OLED_ShowHexNum(2,10,DataA[3],2);
OLED_ShowHexNum(4,1,DataB[0],2);
OLED_ShowHexNum(4,4,DataB[1],2);
OLED_ShowHexNum(4,7,DataB[2],2);
OLED_ShowHexNum(4,10,DataB[3],2);
Delay_ms(1000);
}
}