目录
DMA简介
编辑 DMA框图
DMA基本结构编辑
DMA请求
数据宽度与对齐
Flash to SRAM
SRAM to 外设
DMA简介
DMA框图
总共就是CPU和存储器两个东西,Flash是主闪存,SRAM是运行内存,各个外设都可以看成寄存器也是一种SRAM存储器
寄存器是一种特殊的存储器,一方面,CPU可以对寄存器进行读写,就像读写运行内存一样,另一方面,寄存器的每一位的背后都连接了一根导线,这些导线可以用于控制外设电路的状态,比如置引脚高低电平,导通和断开开关,切换数据选择器或者多位结合起来,当做计数器,数据寄存器等,所以寄存器是连接软件和硬件的桥梁,软件读写寄存器,就相当于在控制硬件的执行
既然外设就是寄存器,寄存器本身就是是存储器,那使用DMA进行数据转运,就都可以归为一类问题了,就是从某个地址取内容,再放到另一个地址去
为了高效有条理地访问存储器,这里设计了一个总线矩阵,总线矩阵的左端是主动单元,拥有存储器的访问权,右边是被动单元,存储器只能被左边的主动单元读写,主动单元这里内核有DCode和系统总线,可以访问右边的存储器, 其中Dcode是专门访问Flash的,系统总线是访问其他东西的,另外由于DMA要转运数据,所以DMA也必须要有访问的主动权
在DMA1和DMA2里可以看到DMA1有七个通道DMA2有五个通道,各个通道可以分别设置它们转运数据的源地址和目的地址,这样就可以各自独立的工作了
下面有个仲裁器,这个是因为,虽然多个通道可以独立转运数据但是DMA总线只有一条,所以所有的通道只能分时复用这一条DMA总线,如果产生了冲突,那就会由仲裁器,根据通道的优先级来决定谁先用谁后用;另外在总线矩阵这里也会有个仲裁器,如果DMA和CPU都要访问同一个目标,那么DMA就会暂停CPU的访问,以防止冲突,不过总线仲裁器还是会保证CPU得到一半的总线带宽,使CPU也能正常的工作
AHB从设备也就是DMA自身的寄存器,因为DMA作为一个外设,它自己也这里连接在总线右边的AHB总线上,所有DMA既是总线矩阵的主动单元可以读写各种存储器,也是AHB总线上的被动单元,所以可以根据红线进行配置了
DMA请求,就是触发的意思,这条线的触发源是各个外设,所以这个DMA请求就是DMA的硬件触发源比如ADC转换完成,串口接收到数据,需要DMA转运数据的时候,就会通过蓝线向DMA发出硬件触发信号,之后DMA就可以执行数据转运的工作了,这就是DMA请求的作用·
DMA基本结构
1,
起始地址如果写Flsah地址就会去Flsah里找,只是个名字
地址是否自增就相当于指针p++,转运完一次数据,是否将下一个地址数据转运
2,
红色圈就是数据转运的两大站点,左边是外设寄存器站点,右边是存储器站点,在STM32里所说的存储器一般是特指Flash和SRAM,不包含外设寄存器,外设寄存器一般直接称为外设,所以就是外设到存储器,存储器到存储器这样来描述,虽然我们刚刚说了寄存器也是存储器的一种,但是STM32还是使用了外设和存储器来作为区分
DMA的数据转运可以是外设到存储器,也可以是存储器到外设,具体是向左还是向右有一个方向的参数可以进行控制,另外还一种转运方式是存储器到存储器,只有两种Flash到SRAM, SRAM到SRAM是,因为Flash是只读的
3,
自动重装器的作用是,传输计数器减到0之后是否要恢复到最初的值,比如传输计数器给5,如果不使用自动重装器,那转运5次后,DMA就结束了,如果使用自动重装器,那转运5次,计数器减到0后就会立即重装到初始值5,决定了转运的模式,如果不重装就是单次模式,如果重装就是循环模式
传输计数器,用来指定我总共需要转运几次的,这个传输计数器是一个自减计数器,比如写个5,那DMA只能进行5次数据转运,另外减到0后,之前自增的地址也会恢复到起始地址的位置,以方便DMA开始新一轮的转运
4,
DMA的触发控制,触发就是决定DMA需要在什么时机进行转运的,触发源有硬件触发和软件触发,具体选择哪个由M2M这个参数决定,当我们给M2M置1时,DMA就会选择软件触发,这个软件触发不是调用某个函数一次触发一次,这个软件触发的执行逻辑是,以最快的速度,连续不断地触发DMA,争取早日把传输计数器清零完成这一轮转换(连续触发)软件触发和循环模式不能同时用,当M2M置0就是硬件触发,硬件触发源可以选择ADC、串口、定时器等,使用硬件触发的转运一般都是与外设有关的转运,这些转运需要一些时机,比如ADC转换完成,串口收到数据,定时时间到,在硬件达到这些时机时传一个信号过来,来触发DMA进行转运
DMA请求
上图DMA触发的部分
EN=0数据选择器不工作,EN=1数据选择器工作
当M2M=1时选择软件触发
选择硬件触发是要看通道的
这里通道1的硬件触发是ADC1、定时器2的通道3和定时器4的通道1,到底选择哪个触发源呢,这个是对应的外设是否开启DMA输出来决定的,比如你要使用ADC1那会有个库函数ADC_DMACmd,必须使用这个库函数开启ADC1的这一路输出,才有效
之后这7个触发源,进入到仲裁器进行优先级判断,最终产生内部的DMA1请求,默认优先级是通道号越小,优先级越高,当然也可以在程序中配置优先级
数据宽度与对齐
前面DMA基本结构中数据宽,如果数据转运的两个站点,都有一个数据宽带的参数,如果数据宽带都一样,那就是正常的一个个转运,如果数据宽带不一样
如果把小的数据转到大的里面去,高位就会补0,如果把大的数据转到小的里面去,高位就会舍弃掉,如果数据宽带一样就没事
Flash to SRAM
内部Flash(CODE)的数据传输到内部的SRAM(变量)
DMA函数
//const关键字,把SRC_Buffer定义为常量类型,表示数据存储在内部Flash中
const uint32_t SRC_Buffer[4] = {0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
};//定义DMA传输的目标存储器,存储在内部SRAM中
uint32_t DST_Buffer[4] = {};/*
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;
*/void DMA_MTM_Init(void)
{RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
//这里DMA是挂载在AHB系统总线的DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)SRC_Buffer;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)DST_Buffer;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外设作为起点DMA_InitStructure.DMA_BufferSize = 4;//传输4个数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_Normal;//是否循环发送DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;//优先级最高DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//M2M使能DMA_Init(DMA1_Channel6,&DMA_InitStructure);//传输完了后,怎么判断是否传输成功呢//所以就要用到这个标志位,参数是通道6的发送结束DMA_ClearFlag(DMA1_FLAG_TC6);//使能DMADMA_Cmd(DMAy_Channel6, ENABLE);} //DMA怎么判断是否完全传输成功了呢
//写一个比较函数,通过对比两个存储器是否一样,通过返回值判断是否正确uint8_t Buffercmp(const uint32_t* pBuffer1,uint32_t* pBuffer2,uint32_t BufferLength)
{while(BufferLength --)//数据长度递减{if(* pBuffer1 != * pBuffer2)//判断俩数据源是否相等{return 0; //对应数据源不相等 返回0}pBuffer1++; //增值俩数据源地址指针pBuffer2--;return 1; //完成判断,并且数据相等,返回1}}
主函数中
int main()
{extern const uint32_t SRC_Buffer[4];extern uint32_t DST_Buffer[4];uint8_t statue = 0;LED_Init();DMA_MTM_Init();statue = Buffercmp(SRC_Buffer,DST_Buffer,4);if(statue == 0 ){GPIO_ResetBits(GPIOC, GPIO_Pin_13);}else{GPIO_SetBits(GPIOC, GPIO_Pin_13); }while(1){}}
SRAM to 外设
内部的SRAM(变量)传输到外设(串口)
DMA函数
void USART_DMA_Init(void)
{DMA_InitTypeDef DMA_InitStructure;RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)DST_SendBuffer;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)USART_DR_ADDR;DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//存储器作为起始点DMA_InitStructure.DMA_BufferSize = DMA_SIZE;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;DMA_Init(DMA1_Channel4, &DMA_InitStructure);DMA_ClearFlag(DMA1_IT_TC4);DMA_Cmd(DMA1_Channel4, ENABLE);}
main函数
extern uint8_t DST_SendBuffer[DMA_SIZE];uint16_t i = 0;for(i=0;i<DMA_SIZE;i++){DST_SendBuffer[i] = 'o';}uart_Init();USART_DMA_Init();USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
然后打开串口接收