前言
本篇博客主要学习了解DMA的工作原理和部分寄存器解析,针对ADC多通道来对代码部分,应用部分作详细讲解,掌握代码编程原理。本篇博客大部分是自己收集和整理,如有侵权请联系我删除。
本次博客开发板使用的是正点原子精英版,芯片是STM32F103ZET6,需要资料可以@我拿取。
交流群:717237739
本博客内容原创,创作不易,转载请注明
————————————————
一 . DMA的基本介绍
- DMA(Direct Memory Access)直接存储器存取
- DMA可以提供外设和存储器或者存储器和存储器之间的高速数据传输,无须CPU干预,节省了CPU的资源
- DMA传输将数据从一个地址空间复制到另一个地址空间。当CPU初始化这个传输动作,传输动作本身是由DMA控制器来实现和完成的。
- DMA传输方式无需CPU直接控制传输,也没有中断处理方式那样保留现场和恢复现场过程,通过硬件为RAM和IO设备开辟一条直接传输数据的通道,使得CPU的效率大大提高。
作用:为CPU减负。
因此:转移数据(尤其是转移大量数据)是可以不需要CPU参与。比如希望外设A的数据拷贝到外设B,只要给两种外设提供一条数据通路,直接让数据由A拷贝到B 不经过CPU的处理,
二 . DMA的特性
- 12个独立的可配置的通道(请求):DMA1有7个通道,DMA2有5个通道
- 每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过软件来配置。
- 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) 。
在相等的优先级下,DMA1通道2的优先级是 大于 DMA1通道4,通道越低优先级越高。
- 独立数据源和目标数据区的 传输宽度(字节、半字、全字) ,模拟打包和拆包的过程。源和目标地址必须按数据传输宽度对齐。
- 支持循环的缓冲器管理
- 每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志
- 逻辑或成为一个单独的中断请求。
- 存储器和存储器间的传输
- 外设和存储器、存储器和外设之间的传输
- 闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标。
- 可编程的数据传输数目:最大为65535
三. 存储器映像
四 . DMA功能框图
1.功能描述
2.DMA仲裁器
3.DMA通道
1.DMA1 控制器
2.DMA 1 各通道一览:
3.DMA 2 控制器
4.DMA 2 各通道一览:
5.DMA传输方式
DMA的作用就是实现数据的直接传输,而去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及四种情况的数据传输,但本质上是一样的,都是从内存的某一区域传输到内存的另一区域(外设的数据寄存器本质上就是内存的一个存储单元)。四种情况的数据传输如下:
- 外设到内存
- 内存到外设
- 内存到内存
- 外设到外设
6 .DMA传输参数
我们知道,数据传输,首先需要的是
1 数据的源地址
2 数据传输位置的目标地址
3 传递数据多少的数据传输量
4 进行多少次传输的传输模式 DMA所需要的核心参数
当用户将参数设置好,主要涉及源地址、目标地址、传输数据量这三个,DMA控制器就会启动数据传输,当剩余传输数据量为0时 达到传输终点,结束DMA传输 ,当然,DMA 还有循环传输模式 当到达传输终点时会重新启动DMA传输。
也就是说只要剩余传输数据量不是0,而且DMA是启动状态,那么就会发生数据传输。
6. stm32中DMA的circle和normal模式的区别
在STM32系列微控制器上,DMA(Direct Memory Access,直接内存访问)是一种用于高效数据传输的重要功能。DMA的Circle(循环)模式和Normal(普通)模式是两种常见的DMA传输模式,它们在数据传输方面有一些区别。
Circle(循环)模式:
- 在Circle模式下,DMA传输可以循环执行,即在完成一次传输后会自动重新开始下一次传输,形成一个循环。这种模式适用于需要连续、循环传输数据的场景。
- 在循环模式下,DMA传输会持续不断地从源地址读取数据,并将数据写入目标地址,直到达到设定的传输长度或触发停止条件。
- 循环模式下的DMA传输通常用于周期性的数据传输,如音频、视频流等连续数据流的传输。
Normal(普通)模式:
- 在Normal模式下,DMA传输只会执行一次,传输完毕后就会停止。这种模式适用于单次数据传输的场景。
- 在普通模式下,DMA传输会从源地址读取数据,并将数据写入目标地址,直到达到设定的传输长度或触发停止条件,然后传输停止。
- 普通模式下的DMA传输适用于需要一次性传输数据的情况,如初始化数据、配置信息等。
需要注意的是,循环模式和普通模式都可以设置传输长度、源地址和目标地址等参数,区别主要在于传输的执行方式和传输结束后是否重新开始。
在使用DMA时,需要根据具体的应用需求选择适合的模式。
如果需要连续、循环传输数据,可以选择循环模式;
如果只需进行单次传输,可以选择普通模式。
同时,还需要注意设置适当的传输长度和停止条件,以确保传输的准确性和可靠性。
五 . DMA 的配置和应用
1.DMA运作过程图解:
下面看有与没有DMA的情况下,ADC采集的数据是怎样存放到SRAM中的?
没有DMA
1.如果没有DMA,CPU传输数据还要以内核作为中转站,比如要将ADC采集的数据转移到到SRAM中,这个过程是这样的:
内核通过DCode经过总线矩阵协调,从获取AHB存储的外设ADC采集的数据,
然后内核再通过DCode经过总线矩阵协调把数据存放到内存SRAM中。
参考博客链接:https://blog.csdn.net/as480133937/article/details/104927922
2.DMA数据流(仅存在于STM32F4 /M4 内核上)了解即可
在设置了DMA的通道之后,还要选择通道对应外设的数据流
3.指针增量
存储器到存储器:源和目标的指针都需要设置为增量模式
存储器到外设: 存储器地址设置为增量模式,外设地址设置为非增量模式。
4.通道配置过程:
0 .开启DMA时钟
1. 在DMA_CPARx寄存器中设置外设寄存器的地址。发生外设数据传输请求时,这个地址将
是数据传输的源或目标。
2. 在DMA_CMARx寄存器中设置数据存储器的地址。发生外设数据传输请求时,传输的数
据将从这个地址读出或写入这个地址。
3. 在DMA_CNDTRx寄存器中设置要传输的数据量。在每个数据传输后,这个数值递减。
4. 在DMA_CCRx寄存器的PL[1:0]位中设置通道的优先级。
5. 在DMA_CCRx寄存器中设置数据传输的方向、循环模式、外设和存储器的增量模式、外
设和存储器的数据宽度。 ---CCRX寄存器
6. 设置DMA_CCRx寄存器的ENABLE位,启动该通道。
5 .DMA中断
每个DMA通道都可以在DMA传输过半、传输完成和传输错误时产生中断。为应用的灵活性考虑,通过设置寄存器的不同位来打开这些中断。
六. DMA配置过程
这个部分我们结合ADC来进行讲解使用:
DMA配置参数包括:通道地址、优先级、数据传输方向、存储器/外设数据宽度、存储器/外设地址是否增量、循环模式、数据传输量。
ADC+DMA多通道框图:
ADC详解在另一个博客:ADC 讲解
配置初始化代码:
#include "stm32f10x.h" // Device header
uint16_t AD_Value[4];
void AD_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//开启对应通道
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);
ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);
ADC_InitTypeDef ADC_InitStructure;
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //数据右对齐
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//转换由软件而不是外部触发启动
ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; //模数转换工作在循环转换模式
ADC_InitStructure.ADC_ScanConvMode = ENABLE; //模数转换工作在多通道模式
ADC_InitStructure.ADC_NbrOfChannel = 4; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
DMA_InitTypeDef DMA_InitStructure;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR; // ADC- DMA外设基地址
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; //半字传输,数据宽度为16位
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址不自增
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value; //存储器数据存储地址 数组
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; //半字传输,数据宽度为16位
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //存储器内存地址寄存器递增
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //数据传输方向,从外设读取发送到存储器
DMA_InitStructure.DMA_BufferSize = 4; //扫描通道:4个
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //循环模式
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //DMA通道x没有设置为内存到内存传输
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道 x拥有中优先级
DMA_Init(DMA1_Channel1, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel1, ENABLE); //使能DMA
ADC_DMACmd(ADC1, ENABLE); //使能ADC_DMA传输
ADC_Cmd(ADC1, ENABLE); //使能ADC
ADC_ResetCalibration(ADC1); //使能复位校准
while (ADC_GetResetCalibrationStatus(ADC1) == SET); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while (ADC_GetCalibrationStatus(ADC1) == SET); //等待校准结束
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
}
库函数讲解,摘自群友的笔记:
总结:
DMA的使用并不复杂,搞清楚数据的方向,还是是否自增就行了,多使用多实践,大家如果对我的博客有疑问或者错误,可以@我修改,大家相互交流。
交流群:717237739
如果觉得有用点赞关注收藏三连,多谢支持
点赞收藏关注博主,不定期分享单片机知识,互相学习交流。
————————————————