文章目录
1. DMA概述
2. 存储器映像
3. DMA框架图
4. DMA请求
5. 数据宽度与对齐
6. DMA数据转运
7. ADC扫描模式和DMA
8. 代码示例
1. DMA概述
DMA(Direct Memory Access)可以直接访问STM32内部的存储器,DMA是一种技术,允许外设和存储器之间直接传输数据,而无需通过CPU进行数据搬运。这种技术大大提高了数据传输效率,减少了CPU的负担,使CPU能够专注于其他计算任务。
DMA可以提供外设(外设寄存器,⼀般是外设的数据寄存器DR(Data Register),比如ADC的数据寄存器,串口的数据寄存器)和存储器(运行内存(SRAM)和程序存储器(Flash)是存储变量数组和程序代码的地方)或者存储器与存储器之间的高速数据传输,非常适合用于需要高带宽数据流的应用,例如音频数据传输、图像处理等。
STM32系列一共有12个独立可配置的通道: DMA1(7个通道), DMA2(5个通道)。但是在STM32F103C8T6中,只有一个个DMA1控制器。每个通道都可以配置为不同的外设或存储器数据传输。
例如:DMA1的通道1可以配置为ADC(模数转换器)数据传输通道,DMA1的通道2可以配置为USART(通用同步/异步收发器)数据传输通道等。
- 软件触发:可以通过编程控制DMA的启动,例如在需要传输数据时,通过软件命令触发DMA操作。
- 硬件触发:一些外设(例如ADC转换完成、USART接收到数据)可以配置为在特定事件发生时自动触发DMA进行数据传输。这种硬件触发机制可以进一步提高系统的实时性能。
2. 存储器映像
类型 | 起始地址 | 存储器 | 用途 |
ROM | 0x0800 0000 | 程序存储器Flash | 存储C语言编译后的程序代码。这个区域主要用于存储用户的应用程序代码,通常是通过编程器或BootLoader进行烧录。 |
0x1FFF F000 | 系统存储器 | 存储BootLoader,用于串口下载。这个区域包含了STM32内部的系统启动加载程序,用户可以通过串口等方式下载新程序。 | |
0x1FFF F800 | 选项字节 | 存储一些独立于程序代码的配置参数。这些配置参数包括读保护、写保护等安全设置。 | |
RAM | 0x2000 0000 | 运行内存SRAM | 存储运行过程中的临时变量。这个区域用于程序运行时的堆栈、全局变量、动态分配的内存等。 |
0x4000 0000 | 外设寄存器 | 存储各个外设的配置参数。这个区域用于映射STM32的各个外设的寄存器地址,程序通过访问这些地址来配置和控制外设。 | |
0xE000 0000 | 内核外设寄存器 | 存储内核各个外设的配置参数。这个区域主要包含Cortex-M3核心的系统控制寄存器、调试接口等。 |
计算机系统的五大组成部分
- 运算器:负责执行计算和逻辑操作,是计算机进行数据处理的主要部件。
- 控制器:负责指挥和协调计算机各部分的工作,控制指令的执行顺序和操作。
- 存储器:用于存储数据和程序,是计算机的重要组成部分,分为主存储器和辅助存储器。
- 输入设备:用于将外部信息输入到计算机中,如键盘、鼠标、扫描仪等。
- 输出设备:用于将计算机处理结果输出到外部,如显示器、打印机等。
存储器的分类
存储器主要分为两大类:只读存储器(ROM)和随机存储器(RAM)。
ROM(只读存储器):
ROM是一种非易失性存储器,即使在断电后,其内容也不会丢失。可以分为以下几种:
- 程序存储器(Flash):主要用于存储经过编译的程序代码。一般情况下,程序从主闪存开始运行。
- 系统存储器:存储启动加载程序(BootLoader),该程序通常在芯片出厂时写入,不允许用户修改。
- 选项字节:用于存储配置数据,如Flash的读写保护设置和看门狗配置等。尽管它们也属于ROM范畴,但其存储介质也是Flash。
RAM(随机存储器):
RAM是一种易失性存储器,断电后其内容会丢失。主要分为以下几种用途:
- 运行内存(SRAM):用于存储程序中定义的变量、数组和结构体等临时数据。
- 外设寄存器:用于存储初始化各个外设的配置数据,以及外设运行时读写的数据。这些寄存器的存储介质也是SRAM。
- 内核外设:例如NVIC(中断控制器)和SysTick(系统定时器)的存储器,这些也是用来控制和管理计算机的外设功能。
3. DMA框架图
左上角是Cortex-M3内核,其中包含了CPU和内核外设,其他部分可以视为存储器。Flash是主闪存,SRAM是运行内存,各个外设可以看成寄存器,它们也是一种SRAM存储器。寄存器是一种特殊的存储器,CPU可以像读写内存一样对寄存器进行操作。同时,寄存器的每一位连接着控制外设电路的导线,用于控制引脚的高低电平、导通和断开等。寄存器是软件与硬件的桥梁,通过读写寄存器,软件实际上在控制硬件。
为了高效管理存储器,设计了一个总线矩阵。总线矩阵的左边是主动单元(具有存储器访问权),右边是被动单元(只能被动读取)。内核中有DCode总线(专门访问Flash)和系统总线(访问其他存储器)。DMA作为主动单元,也需要总线访问权,有自己的总线(DMA1和DMA2),它们分别有7个和5个通道,负责设置数据的源地址和目标地址。仲裁器决定通道的优先级,确保公平访问。
在总线矩阵中,如果DMA和CPU同时访问同一目标,DMA会暂停CPU的访问,但总线仲裁器会保证CPU得到一半的总线带宽。DMA本身也有自己的配置寄存器,连接在AHB总线上,因此DMA既是主动单元也可以作为被动单元接受访问。DMA请求是硬件触发信号,用于在ADC转换完成或串口接收到数据时,触发DMA进行数据转运。
Flash是ROM的一种,通过总线直接访问时,无论是CPU还是DMA,都只能读取数据,不能写入。如果DMA的目标地址是Flash区域,会导致转运错误。写入Flash需要通过接口控制器,先擦除再写入数据。
DMA的数据转运可以从外设到存储器,也可以从存储器到外设,还可以在存储器之间进行。每个站点(外设或存储器)有三个参数:
- 起始地址:包括外设端的起始地址和存储器端的起始地址,决定数据从哪里来,到哪里去。
- 数据宽度:指定一次转运的数据宽度,可以是字节(8位),半字(16位),或字(32位)。例如,ADC的数据为16位(uint16_t),因此应选择半字。
- 地址自增:决定每次转运后,地址是否移动到下一个位置。例如,在ADC扫描模式下,外设地址不需自增,而存储器地址需要自增,以防止数据覆盖。
传输计数器与自动重装器
- 传输计数器:指定总共转运的次数,是一个自减计数器。例如,设置为5,则DMA只能转运5次,每次转运后计数器减1。当计数器减到0后,DMA停止转运,并恢复到起始地址。
- 自动重装器:决定计数器减到0后是否自动恢复到初始值。如果使用自动重装器,DMA会在计数器减到0后自动重装初始值,实现循环模式;否则是单次模式。
DMA触发控制
DMA的触发源有硬件触发和软件触发,通过M2M参数选择。当M2M为1时,使用软件触发,DMA以最快速度连续不断地触发,直到传输计数器清0。软件触发和循环模式不能同时使用,因为软件触发清零后不重装,循环模式清零后自动重装。
当M2M为0时,使用硬件触发,常用于与外设有关的转运。例如,ADC转换完成、串口接收到数据或定时时间到时,硬件触发DMA进行转运。
DMA启用条件
DMA准备进行转运的条件包括:
- 开关控制:DMA_Cmd必须使能。
- 传输计数器:必须大于0。
- 触发源:必须有触发信号。每次触发,传输计数器自减1。当计数器等于0且无自动重装时,DMA停止转运。此时,需要禁用DMA(DMA_Cmd给DISABLE),重设传输计数器,然后重新启用DMA(DMA_Cmd给ENABLE),才能继续工作。写传输计数器时,必须先关闭DMA,再进行设置。
4. DMA请求
这张图显示了DMA1的请求映像,其中包括DMA的7个通道。每个通道都有一个数据选择器,可以选择硬件触发或软件触发。左边列出了硬件触发源,每个通道的硬件触发源是不同的。例如:
- 若要选择ADC1来触发,则必须选择通道1。
- 若要选择TIM2的更新事件来触发,则必须选择通道2。
每个通道的硬件触发源是固定的,所以要使用某个硬件触发源,就必须使用其对应的通道。而如果使用软件触发,则可以任意选择通道。
硬件触发与软件触发
-
硬件触发:每个硬件触发源对应固定的通道。例如,使用ADC1作为触发源时,需要调用库函数
ADC_DMACmd
来开启ADC1的DMA输出。同理,使用定时器2的通道3时,需要使用TIM_DMACmd
函数来控制DMA输出。具体选择哪个触发源,取决于你开启了哪个外设的DMA输出。 -
软件触发:软件触发时,通道选择是任意的,不受硬件触发源的限制。
优先级仲裁
这7个触发源进入仲裁器,进行优先级判断,最终产生内部的DMA1请求。默认情况下,通道号越小,优先级越高。但是,这个优先级可以在程序中进行配置。
5. 数据宽度与对齐
第一列表示源端宽度,第二列表示目标宽度,第三列表示传输数量。以下是不同情况下的操作:
源端宽度和目标宽度都是8位
-
源端0位置读数据B0,目标0位置写数据B0。
-
依次将B1、B2、B3从源端挪到目标端。
源端宽度8位,目标宽度16位
-
源端读取B0,目标写入00B0。
-
源端读取B1,目标写入00B1,依此类推。
-
目标宽度大于源端宽度时,目标数据前面多出来的空位补0。
源端宽度8位,目标宽度32位
-
类似地,源端读取8位数据,目标写入32位数据时,前面空位补0。
源端宽度16位,目标宽度8位
-
源端读取B1B0,只写入B0,舍弃高位B1。
-
源端读取B3B2,只写入B2,舍弃高位B3。
总结
-
当目标宽度大于源端宽度时,目标数据前面空位补0。
-
当目标宽度小于源端宽度时,舍弃多余的高位数据。
-
当源端和目标宽度相同时,数据直接传输,无需额外处理。
6. DMA数据转运
将SRAM中的数组DataA转运到另一个数组DataB中,参数配置如下:
-
外设地址:DataA数组的首地址
-
存储器地址:DataB数组的首地址
-
数据宽度:两个数组的类型都是uint8_t,所以数据宽度为8位(字节)传输
-
地址自增:
- 如果两个站点的地址都自增,转运完成后,DataB数组的所有数据将与DataA数组相同。
- 如果源地址(DataA)不自增,而目标地址(DataB)自增,转运完成后,DataB的所有数据都会等于DataA[0]。
- 如果源地址(DataA)自增,而目标地址(DataB)不自增,DataB[0]将等于DataA的最后一个数,DataB的其他数据保持不变。
- 如果两个地址都不自增,DataA[0]转到DataB[0],其他数据不变。
-
方向参数:从外设站点(DataA)转运到存储器站点(DataB)。
-
传输计数器:设置为7,不需要自动重装。
-
触发选择:选择软件触发。
最后,调用DMA_Cmd使能DMA,转运7次后,传输计数器减到0,DMA停止,转运完成。需要注意的是,这种数据转运是一种复制转运,转运完成后DataA的数据不会消失。
7. ADC扫描模式和DMA
左边展示的是ADC扫描模式下的转运流程。每次触发后,7个通道依次进行AD转换,并将转换结果存放在ADC_DR寄存器中。每个通道转换完成后,进行一次DMA数据转运,同时自增目标地址,防止数据覆盖。
DMA配置
- 外设地址:设置为ADC_DR寄存器的地址。
- 存储器地址:在SRAM中定义一个数组ADValue,并将其地址作为存储器地址。
- 数据宽度:因为ADC_DR和SRAM数组都使用uint16_t数据类型,数据宽度设置为16位(半字)传输。
- 地址自增:
- 外设地址不自增。
- 存储器地址自增。
- 传输方向:从外设站点(ADC_DR)到存储器站点(ADValue数组)。
- 传输计数器:设置为7,与通道数量相同。
- 自动重装:根据ADC的配置决定。
- 如果ADC是单次扫描,DMA传输计数器不需要自动重装,完成一次转换后停止。
- 如果ADC是连续扫描,DMA可以选择自动重装,在ADC启动下一轮转换时,DMA也启动下一轮转运,使ADC和DMA同步工作。
- 触发选择:选择ADC的硬件触发。ADC扫描模式在单个通道完成转换后,不会置任何标志位或产生中断,但会产生DMA请求,触发DMA转运。
8. 代码示例
STM32基于DMA数据转运和AD多通道
STM32基于DMA数据转运和AD多通道-CSDN博客