stm32之DMA
1.DMA直接存储器访问,用来提供外设寄存器(GPIO口,TIMx,USART等)<->存储器(SRAM(程序运行在这个空间),Flash(程序存储在这里面),u8 data[]--都是运行在SRAM空间里的),存储器<->存储器(这种模式不可以和循环模式同时使用)之间数据传送,节省了MCU的资源。
外设:片上外设:芯片上的,GPIO口,USART等。
片外外设:连接的外部设备,例如dht11,MQ2等。
内核:是芯片架构,里面有各种逻辑电路,类似于人脑。
2.DMA1通道选择:ADC1的DMA通道就是DMA1,不管是ADC1的哪个通道,使用DMA对应的都是DMA1的通道1;同样ADC2对应DMA2的通道2;
3.DMA的作用:DMA传输方式无需CPU直接控制传输, 也没有中断处理方式那样保留现场和恢复现场过程, 通过硬件为RAM和IO设备开辟一条直接传输数据的通道, 使得CPU的效率大大提高。
4.DMA的工作原理
当用户将参数设置好, 主要涉及源地址、 目标地址、传输数据量这三个, DMA控制器就会启动数据传输, 传输的终点就是剩余传输数据量为0( 循环传输不是这样的) 。换句话说只要剩余传输数据量不是0, 而且DMA是启动状态, 那么就会发生数据传输。
5.DMA主要特性
● 12个独立的可配置的通道(请求): DMA1有7个通道, DMA2有5个通道
● 每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置。
● 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) 。
● 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。
● 支持循环的缓冲器管理
● 每个通道都有3个事件标志(DMA半传输、 DMA传输完成和DMA传输出错),这3个事件标志逻辑或成为一个单独的中断请求。
● 存储器和存储器间的传输
● 外设和存储器、存储器和外设之间的传输
● 闪存、 SRAM、外设的SRAM、 APB1、 APB2和AHB外设均可作为访问的源和目标。
● 可编程的数据传输数目:最大为65535
6.仲裁器
仲裁器根据通道请求的优先级来启动外设/存储器的访问。
优先权管理分2个阶段:
● 软件:每个通道的优先权可以在DMA_CCRx寄存器中设置,有4个等级:
─ 最高优先级
─ 高优先级
─ 中等优先级
─ 低优先级
● 硬件:如果2个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优
先权。举个例子,通道2优先于通道4。
注意: 在大容量产品和互联型产品中, DMA1控制器拥有高于DMA2控制器的优先级
7.DMA的处理过程
在发生一个事件后, 外设向DMA控制器发送一个请求信号。 DMA控制器根据通道的优先权处理请求。 当DMA控制器开始访问发出请求的外设时, DMA控制器立
即发送给它一个应答信号。 当从DMA控制器得到应答信号时, 外设立即释放它的请求。 一旦外设释放了这个请求, DMA控制器同时撤销应答信号。 如果有更多的
请求时, 外设可以启动下一个周期。
8.DMA的请求映像
9.DMA的配置过程(通道1-DMA1为例)
1.使能DMAx的时钟:RCC->AHBENR |=(1<<0);
2.是否使用DMA模式:ADCx->CR2的第8位DMA决定--ADC1->CR2 |= (1<<8);
3.设置模式:存储器到存储器模式、外设<->存储器模式:DMAx的通道几的CCR寄存器的第14位MEM2MEM决定:DMA1_Channel1->CCR &=~(1<<14);//非存储器到存储器模式
4.设置外设寄存器地址:外设寄存器储存在ADCx->DR中:DMA1_Channel1->CPAR = (u32)&(ADC1->DR);
5.设置数据寄存的地址:DMA1_Channel1->CMAR = (u32)dmabuff;//dmabuff自己定义的数组的数组名,在进行强转
6.设置要传输的数据量:DMAx的CNDTR寄存器:DMA1_Channel1->CNDTR = sizeof(dmabuff)/sizeof(dmabuff[0]);
7.设置通道优先级:DMA1_Channel1->CCR |=(2<<12);
8.设置数据传输方向:DMA1_Channel1->CCR &=~(1<<4);
9.设置循环或非循环模式:DMA1_Channel1->CCR |=(1<<5);
循环模式用于处理循环缓冲区和连续的数据传输(如ADC的扫描模式)。 在DMA_CCRx寄存器中的CIRC位用于开启这一功能。当启动了循环模式, 数据传输的数目变为0时, 最后一次传输结束时, DMA_CNDTRx寄存器的内容会自动地被重新加载为其初始数值, 内部的当前外设/存储器地址寄存器也被重新加载为DMA_CPARx/DMA_CMARx寄存器 设定的初始基地址, DMA操作将会继续进行。
当通道配置为非循环模式时, 传输结束后(即传输计数变为0)将不再产生DMA操作。 要开始新的DMA传输, 需要在关闭DMA通道的情况下, 在DMA_CNDTRx寄存器中重新写入传输数目。
10.外设增量不增量模式:DMA1_Channel1->CCR &=~(1<<6);
11.存储器增量不增量模式:DMA1_Channel1->CCR |=(1<<7);
12.外设数据宽度:DMA1_Channel1->CCR |=(1<<8);
13.存储器数据宽度:DMA1_Channel1->CCR |=(1<<10);
14.启动DMA通道:
15.启动规则通道转换:ADC1->CR2 |=(1<<22);
/************************库函数类型****************************/
1 void LDR_DMAConfig(void) 2 { 3 #if reg_progream 4 5 //使能DMA1时钟 6 RCC->AHBENR |=(1<<0); 7 ADC1->CR2 |= (1<<8);//使能ADC DMA传输 8 //设置外设寄存器地址 9 DMA1_Channel1->CPAR = (u32)&(ADC1->DR); 10 //设置数据存储器的地址 11 DMA1_Channel1->CMAR = (u32)dmabuff; 12 //设置要传输的数据量 13 DMA1_Channel1->CNDTR = sizeof(dmabuff)/sizeof(dmabuff[0]); 14 //设置通道的优先级:高 15 DMA1_Channel1->CCR |=(2<<12); 16 //设置数据传输的方向:从外设读 17 DMA1_Channel1->CCR &=~(1<<4); 18 //非存储器到存储器模式 19 DMA1_Channel1->CCR &=~(1<<14); 20 //循环模式 21 DMA1_Channel1->CCR |=(1<<5); 22 //外设不增量模式 23 DMA1_Channel1->CCR &=~(1<<6); 24 //存储器增量模式 25 DMA1_Channel1->CCR |=(1<<7); 26 //外设数据宽度:16位 27 DMA1_Channel1->CCR |=(1<<8); 28 //存储器数据宽度:16位 29 DMA1_Channel1->CCR |=(1<<10); 30 //启动DMA通道 31 DMA1_Channel1->CCR |=(1<<0); 32 //启动规则通道转换 33 ADC1->CR2 |=(1<<22); 34 #else 35 DMA_InitTypeDef DMA_InitStruct; 36 //使能DMA1时钟 37 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE); 38 ADC_DMACmd(ADC1,ENABLE);//使能DMA传输 39 //外设基地址 40 DMA_InitStruct.DMA_PeripheralBaseAddr = (u32)&(ADC1->DR); 41 //存储器基地址 42 DMA_InitStruct.DMA_MemoryBaseAddr = (u32)dmabuff; 43 //传输的数据量 44 DMA_InitStruct.DMA_BufferSize = sizeof(dmabuff)/sizeof(dmabuff[0]); 45 //优先级:高 46 DMA_InitStruct.DMA_Priority = DMA_Priority_High; 47 //数据传输的方向:外设为数据源 48 DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; 49 //非存储器到存储器模式 50 DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; 51 //循环模式 52 DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; 53 //外设不增量 54 DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 55 //存储器增量 56 DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; 57 //外设数据宽度:16位 58 DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; 59 //存储器数据宽度:16位 60 DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; 61 DMA_Init(DMA1_Channel1,&DMA_InitStruct); 62 //使能DMA1通道1 63 DMA_Cmd(DMA1_Channel1,ENABLE); 64 65 //使能指定ADC软件转换功能 66 ADC_SoftwareStartConvCmd(ADC1,ENABLE); 67 #endif 68 }
10.获取存储值--lenth的数组大小一般是外设数目也可以直接将lenth换成外设数目的具体数值。
1 void DMA_Value(void) 2 { 3 u8 i=0; 4 //取出转换的数目右移20位是因为前20还是存在的, 5 //右移20位只不过是把他移到前面了,+1是因为0000表示1 6 u8 lenth=((ADC1->SQR1 &(0xf<<20))>>20)+1; 7 for(i=0;i<lenth;i++) 8 { 9 printf("dmabuff[%d]=%d ",i,dmabuff[i]); 10 } 11 if(i>=lenth) 12 { 13 printf("\r\n"); 14 } 15 }