历史上的今天
今天是:2024年09月18日(星期三)
2019年09月18日 | 第21章 DMA—直接存储区访问—零死角玩转STM32-F429系列
2019-09-18 来源:eefocus
本章参考资料:《STM32F4xx中文参考手册》DMA控制器章节。
学习本章时,配合《STM32F4xx中文参考手册》DMA控制器章节一起阅读,效果会更佳,特别是涉及到寄存器说明的部分。本章内容专业名称较多,内容丰富也较难理解,但非常有必要细读研究。
特别说明,本章内容是以STM32F42xxx系列资源讲解。
21.1 DMA简介
DMA(Direct Memory Access,直接存储区访问)为实现数据高速在外设寄存器与存储器之间或者存储器与存储器之间传输提供了高效的方法。之所以称之为高效,是因为DMA传输实现高速数据移动过程无需任何CPU操作控制。从硬件层次上来说,DMA控制器是独立于Cortex-M4内核的,有点类似GPIO、USART外设一般,只是DMA的功能是可以快速移动内存数据。
STM32F4xx系列的DMA功能齐全,工作模式众多,适合不同编程环境要求。STM32F4xx系列的DMA支持外设到存储器传输、存储器到外设传输和存储器到存储器传输三种传输模式。这里的外设一般指外设的数据寄存器,比如ADC、SPI、I2C、DCMI等等外设的数据寄存器,存储器一般是指片内SRAM、外部存储器、片内Flash等等。
外设到存储器传输就是把外设数据寄存器内容转移到指定的内存空间。比如进行ADC采集时我们可以利用DMA传输把AD转换数据转移到我们定义的存储区中,这样对于多通道采集、采样频率高、连续输出数据的AD采集是非常高效的处理方法。
存储区到外设传输就是把特定存储区内容转移至外设的数据寄存器中,这种多用于外设的发送通信。
存储器到存储器传输就是把一个指定的存储区内容拷贝到另一个存储区空间。功能类似于C语言内存拷贝函数memcpy,利用DMA传输可以达到更高的传输效率,特别是DMA传输是不占用CPU的,可以节省很多CPU资源。
21.2 DMA功能框图
STM32F4xx系列的DMA可以实现外设寄存器与存储器之间或者存储器与存储器之间传输三种模式,这要得益于DMA控制器是采样AHB主总线的,可以控制AHB总线矩阵来启动AHB事务。图 211为DMA控制器的框图。

图 211 DMA框图
1. ①外设通道选择
STM32F4xx系列资源丰富,具有两个DMA控制器,同时外设繁多,为实现正常传输,DMA需要通道选择控制。每个DMA控制器具有8个数据流,每个数据流对应8个外设请求。在实现DMA传输之前,DMA控制器会通过DMA数据流x配置寄存器DMA_SxCR(x为0~7,对应8个DMA数据流)的CHSEL[2:0]位选择对应的通道作为该数据流的目标外设。
外设通道选择要解决的主要问题是决定哪一个外设作为该数据流的源地址或者目标地址。
DMA请求映射情况参考表 211和表 212。
表 211 DMA1请求映射

表 212 DMA2请求映射

每个外设请求都占用一个数据流通道,相同外设请求可以占用不同数据流通道。比如SPI3_RX请求,即SPI3数据发送请求,占用DMA1的数据流0的通道0,因此当我们使用该请求时,我们需要在把DMA_S0CR寄存器的CHSEL[2:0]设置为"000",此时相同数据流的其他通道不被选择,处于不可用状态,比如此时不能使用数据流0的通道1即I2C1_RX请求等等。
查阅表 211可以发现SPI3_RX请求不仅仅在数据流0的通道0,同时数据流2的通道0也是SPI3_RX请求,实际上其他外设基本上都有两个对应数据流通道,这两个数据流通道都是可选的,这样设计是尽可能提供多个数据流同时使用情况选择。
2. ②仲裁器
一个DMA控制器对应8个数据流,数据流包含要传输数据的源地址、目标地址、数据等等信息。如果我们需要同时使用同一个DMA控制器(DMA1或DMA2)多个外设请求时,那必然需要同时使用多个数据流,那究竟哪一个数据流具有优先传输的权利呢?这就需要仲裁器来管理判断了。
仲裁器管理数据流方法分为两个阶段。第一阶段属于软件阶段,我们在配置数据流时可以通过寄存器设定它的优先级别,具体配置DMA_SxCR寄存器PL[1:0]位,可以设置为非常高、高、中和低四个级别。第二阶段属于硬件阶段,如果两个或以上数据流软件设置优先级一样,则他们优先级取决于数据流编号,编号越低越具有优先权,比如数据流2优先级高于数据流3。
3. ③FIFO
每个数据流都独立拥有四级32位FIFO(先进先出存储器缓冲区)。DMA传输具有FIFO模式和直接模式。
直接模式在每个外设请求都立即启动对存储器传输。在直接模式下,如果DMA配置为存储器到外设传输那DMA会见一个数据存放在FIFO内,如果外设启动DMA传输请求就可以马上将数据传输过去。
FIFO用于在源数据传输到目标地址之前临时存放这些数据。可以通过DMA数据流xFIFO控制寄存器DMA_SxFCR的FTH[1:0]位来控制FIFO的阈值,分别为1/4、1/2、3/4和满。如果数据存储量达到阈值级别时,FIFO内容将传输到目标中。
FIFO对于要求源地址和目标地址数据宽度不同时非常有用,比如源数据是源源不断的字节数据,而目标地址要求输出字宽度的数据,即在实现数据传输时同时把原来4个8位字节的数据拼凑成一个32位字数据。此时使用FIFO功能先把数据缓存起来,分别根据需要输出数据。
FIFO另外一个作用使用于突发(burst)传输。
4. ④存储器端口、⑤外设端口
DMA控制器实现双AHB主接口,更好利用总线矩阵和并行传输。DMA控制器通过存储器端口和外设端口与存储器和外设进行数据传输,关系见图 212。DMA控制器的功能是快速转移内存数据,需要一个连接至源数据地址的端口和一个连接至目标地址的端口。
DMA2(DMA控制器2)的存储器端口和外设端口都是连接到AHB总线矩阵,可以使用AHB总线矩阵功能。DMA2存储器和外设端口可以访问相关的内存地址,包括有内部Flash、内部SRAM、AHB1外设、AHB2外设、APB2外设和外部存储器空间。
DMA1的存储区端口相比DMA2的要减少AHB2外设的访问权,同时DMA1外设端口是没有连接至总线矩阵的,只有连接到APB1外设,所以DMA1不能实现存储器到存储器传输。

图 212 两个DMA控制器系统实现
5. ⑥编程端口
AHB从器件编程端口是连接至AHB2外设的。AHB2外设在使用DMA传输时需要相关控制信号。
21.3 DMA数据配置
DMA工作模式多样,具有多种可能工作模式,具体可能配置见表 213。
表 213 DMA配置可能情况

1. DMA传输模式
DMA2支持全部三种传输模式,而DMA1只有外设到存储器和存储器到外设两种模式。模式选择可以通过DMA_SxCR寄存器的DIR[1:0]位控制,进而将DMA_SxCR寄存器的EN位置1就可以使能DMA传输。
在DMA_SxCR寄存器的PSIZE[1:0]和MSIZE[1:0]位分别指定外设和存储器数据宽度大小,可以指定为字节(8位)、半字(16位)和字(32位),我们可以根据实际情况设置。直接模式要求外设和存储器数据宽度大小一样,实际上在这种模式下DMA数据流直接使用PSIZE,MSIZE不被使用。
2. 源地址和目标地址
DMA数据流x外设地址DMA_SxPAR(x为0~7)寄存器用来指定外设地址,它是一个32位数据有效寄存器。DMA数据流x存储器0地址DMA_SxM0AR(x为0~7) 寄存器和DMA数据流x存储器1地址DMA_SxM1AR(x为0~7) 寄存器用来存放存储器地址,其中DMA_SxM1AR只用于双缓冲模式,DMA_SxM0AR和DMA_SxM1AR都是32位数据有效的。
当选择外设到存储器模式时,即设置DMA_SxCR寄存器的DIR[1:0] 位为"00",DMA_SxPAR寄存器为外设地址,也是传输的源地址,DMA_SxM0AR寄存器为存储器地址,也是传输的目标地址。对于存储器到存储器传输模式,即设置DIR[1:0] 位为"10"时,采用与外设到存储器模式相同配置。而对于存储器到外设,即设置DIR[1:0]位为"01"时,DMA_SxM0AR寄存器作为为源地址,DMA_SxPAR寄存器作为目标地址。
3. 流控制器
流控制器主要涉及到一个控制DMA传输停止问题。DMA传输在DMA_SxCR寄存器的EN位被置1后就进入准备传输状态,如果有外设请求DMA传输就可以进行数据传输。很多情况下,我们明确知道传输数据的数目,比如要传1000个或者2000个数据,这样我们就可以在传输之前设置DMA_SxNDTR寄存器为要传输数目值,DMA控制器在传输完这么多数目数据后就可以控制DMA停止传输。
DMA数据流x数据项数DMA_SxNDTR(x为0~7)寄存器用来记录当前仍需要传输数目,它是一个16位数据有效寄存器,即最大值为65535,这个值在程序设计是非常有用也是需要注意的地方。我们在编程时一般都会明确指定一个传输数量,在完成一次数目传输后DMA_SxNDTR计数值就会自减,当达到零时就说明传输完成。
如果某些情况下在传输之前我们无法确定数据的数目,那DMA就无法自动控制传输停止了,此时需要外设通过硬件通信向DMA控制器发送停止传输信号。这里有一个大前提就是外设必须是可以发出这个停止传输信号,只有SDIO才有这个功能,其他外设不具备此功能。
4. 循环模式
循环模式相对应于一次模式。一次模式就是传输一次就停止传输,下一次传输需要手动控制,而循环模式在传输一次后会自动按照相同配置重新传输,周而复始直至被控制停止或传输发生错误。
通过DMA_SxCR寄存器的CIRC位可以使能循环模式。
5. 传输类型
DMA传输类型有单次(Single)传输和突发(Burst)传输。突发传输就是用非常短时间结合非常高数据信号率传输数据,相对正常传输速度,突发传输就是在传输阶段把速度瞬间提高,实现高速传输,在数据传输完成后恢复正常速度,有点类似达到数据块"秒传"效果。为达到这个效果突发传输过程要占用AHB总线,保证要求每个数据项在传输过程不被分割,这样一次性把数据全部传输完才释放AHB总线;而单次传输时必须通过AHB的总线仲裁多次控制才传输完成。
单次和突发传输数据使用具体情况参考表 214。其中PBURST[1:0]和MBURST[1:0]位是位于DMA_SxCR寄存器中的,用于分别设置外设和存储器不同节拍数的突发传输,对应为单次传输、4个节拍增量传输、8个节拍增量传输和16个节拍增量传输。PINC位和MINC位是寄存器DMA_SxCR寄存器的第9和第10位,如果位被置1则在每次数据传输后数据地址指针自动递增,其增量由PSIZE和MSIZE值决定,比如,设置PSIZE为半字大小,那么下一次传输地址将是前一次地址递增2。
表 214 DMA传输类型

突发传输与FIFO密切相关,突发传输需要结合FIFO使用,具体要求FIFO阈值一定要是内存突发传输数据量的整数倍。FIFO阈值选择和存储器突发大小必须配合使用,具体参考表 215。
表 215 FIFO阈值配置

6. 直接模式
默认情况下,DMA工作在直接模式,不使能FIFO阈值级别。
直接模式在每个外设请求都立即启动对存储器传输的单次传输。直接模式要求源地址和目标地址的数据宽度必须一致,所以只有PSIZE控制,而MSIZE值被忽略。突发传输是基于FIFO的所以直接模式不被支持。另外直接模式不能用于存储器到存储器传输。
在直接模式下,如果DMA配置为存储器到外设传输那DMA会见一个数据存放在FIFO内,如果外设启动DMA传输请求就可以马上将数据传输过去。
7. 双缓冲模式
设置DMA_SxCR寄存器的DBM位为1可启动双缓冲传输模式,并自动激活循环模式。双缓冲不应用与存储器到存储器的传输。双缓冲模式下,两个存储器地址指针都有效,即DMA_SxM1AR寄存器将被激活使用。开始传输使用DMA_SxM0AR寄存器的地址指针所对应的存储区,当这个存储区数据传输完DMA控制器会自动切换至DMA_SxM1AR寄存器的地址指针所对应的另一块存储区,如果这一块也传输完成就再切换至DMA_SxM0AR寄存器的地址指针所对应的存储区,这样循环调用。
当其中一个存储区传输完成时都会把传输完成中断标志TCIF位置1,如果我们使能了DMA_SxCR寄存器的传输完成中断,则可以产生中断信号,这个对我们编程非常有用。另外一个非常有用的信息是DMA_SxCR寄存器的CT位,当DMA控制器是在访问使用DMA_SxM0AR时CT=0,此时CPU不能访问DMA_SxM0AR,但可以向DMA_SxM1AR填充或者读取数据;当DMA控制器是在访问使用DMA_SxM1AR时CT=1,此时CPU不能访问DMA_SxM1AR,但可以向DMA_SxM0AR填充或者读取数据。另外在未使能DMA数据流传输时,可以直接写CT位,改变开始传输的目标存储区。
双缓冲模式应用在需要解码程序的地方是非常有效的。比如MP3格式音频解码播放,MP3是被压缩的文件格式,我们需要特定的解码库程序来解码文件才能得到可以播放的PCM信号,解码需要一定的实际,按照常规方法是读取一段原始数据到缓冲区,然后对缓冲区内容进行解码,解码后才输出到音频播放电路,这种流程对CPU运算速度要求高,很容易出现播放不流畅现象。如果我们使用DMA双缓冲模式传输数据就可以非常好的解决这个问题,达到解码和输出音频数据到音频电路同步进行的效果。
8. DMA中断
每个DMA数据流可以在发送以下事件时产生中断:
1) 达到半传输:DMA数据传输达到一半时HTIF标志位被置1,如果使能HTIE中断控制位将产生达到半传输中断;
2) 传输完成:DMA数据传输完成时TCIF标志位被置1,如果使能TCIE中断控制位将产生传输完成中断;
3) 传输错误:DMA访问总线发生错误或者在双缓冲模式下试图访问"受限"存储器地址寄存器时TEIF标志位被置1,如果使能TEIE中断控制位将产生传输错误中断;
4) FIFO错误:发生FIFO下溢或者上溢时FEIF标志位被置1,如果使能FEIE中断控制位将产生FIFO错误中断;
5) 直接模式错误:在外设到存储器的直接模式下,因为存储器总线没得到授权,使得先前数据没有完成被传输到存储器空间上,此时DMEIF标志位被置1,如果使能DMEIE中断控制位将产生直接模式错误中断。
21.4 DMA初始化结构体详解
标准库函数对每个外设都建立了一个初始化结构体xxx_InitTypeDef(xxx为外设名称),结构体成员用于设置外设工作参数,并由标准库函数xxx_Init()调用这些设定参数进入设置外设相应的寄存器,达到配置外设工作环境的目的。
结构体xxx_InitTypeDef和库函数xxx_Init配合使用是标准库精髓所在,理解了结构体xxx_InitTypeDef每个成员意义基本上就可以对该外设运用自如了。结构体xxx_InitTypeDef定义在stm32f4xx_xxx.h(后面xxx为外设名称)文件中,库函数xxx_Init定义在stm32f4xx_xxx.c文件中,编程时我们可以结合这两个文件内注释使用。
DMA_ InitTypeDef初始化结构体
1 typedef struct {
2 uint32_t DMA_Channel; //通道选择
3 uint32_t DMA_PeripheralBaseAddr; //外设地址
4 uint32_t DMA_Memory0BaseAddr; //存储器0地址
5 uint32_t DMA_DIR; //传输方向
6 uint32_t DMA_BufferSize; //数据数目
7 uint32_t DMA_PeripheralInc; //外设递增
8 uint32_t DMA_MemoryInc; //存储器递增
9 uint32_t DMA_PeripheralDataSize; //外设数据宽度
10 uint32_t DMA_MemoryDataSize; //存储器数据宽度
11 uint32_t DMA_Mode; //模式选择
12 uint32_t DMA_Priority; //优先级
13 uint32_t DMA_FIFOMode; //FIFO模式
14 uint32_t DMA_FIFOThreshold; //FIFO阈值
15 uint32_t DMA_MemoryBurst; //存储器突发传输
16 uint32_t DMA_PeripheralBurst; //外设突发传输
17 } DMA_InitTypeDef;
5) DMA_Channel:DMA请求通道选择,可选通道0至通道7,每个外设对应固定的通道,具体设置值需要查表 211和表 212;它设定DMA_SxCR寄存器的CHSEL[2:0]位的值。例如,我们使用模拟数字转换器ADC3规则采集4个输入通道的电压数据,查表 212可知使用通道2。
6) DMA_PeripheralBaseAddr:外设地址,设定DMA_SxPAR寄存器的值;一般设置为外设的数据寄存器地址,如果是存储器到存储器模式则设置为其中一个存储区地址。ADC3的数据寄存器ADC_DR地址为((uint32_t)ADC3+0x4C)。
7) DMA_Memory0BaseAddr:存储器0地址,设定DMA_SxM0AR寄存器值;一般设置为我们自定义存储区的首地址。我们程序先自定义一个16位无符号整形数组ADC_ConvertedValue[4]用来存放每个通道的ADC值,所以把数组首地址(直接使用数组名即可)赋值给DMA_Memory0BaseAddr。
8) DMA_DIR:传输方向选择,可选外设到存储器、存储器到外设以及存储器到存储器。它设定DMA_SxCR寄存器的DIR[1:0]位的值。ADC采集显然使用外设到存储器模式。
9) DMA_BufferSize:设定待传输数据数目,初始化设定DMA_SxNDTR寄存器的值。这里ADC是采集4个通道数据,所以待传输数目也就是4。
10) DMA_PeripheralInc:如果配置为DMA_PeripheralInc_Enable,使能外设地址自动递增功能,它设定DMA_SxCR寄存器的PINC位的值;一般外设都是只有一个数据寄存器,所以一般不会使能该位。ADC3的数据寄存器地址是固定并且只有一个所以不使能外设地址递增。
11) DMA_MemoryInc:如果配置为DMA_MemoryInc_Enable,使能存储器地址自动递增功能,它设定DMA_SxCR寄存器的MINC位的值;我们自定义的存储区一般都是存放多个数据的,所以使能存储器地址自动递增功能。我们之前已经定义了一个包含4个元素的数字用来存放数据,使能存储区地址递增功能,自动把每个通道数据存放到对应数组元素内。
12) DMA_PeripheralDataSize:外设数据宽度,可选字节(8位)、半字(16位)和字(32位),它设定DMA_SxCR寄存器的PSIZE[1:0]位的值。ADC数据寄存器只有低16位数据有效,使用半字数据宽度。
13) DMA_MemoryDataSize:存储器数据宽度,可选字节(8位)、半字(16位)和字(32位),它设定DMA_SxCR寄存器的MSIZE[1:0]位的值。保存ADC转换数据也要使用半字数据宽度,这跟我们定义的数组是相对应的。
14) DMA_Mode:DMA传输模式选择,可选一次传输或者循环传输,它设定DMA_SxCR寄存器的CIRC位的值。我们希望ADC采集是持续循环进行的,所以使用循环传输模式。
15) DMA_Priority:软件设置数据流的优先级,有4个可选优先级分别为非常高、高、中和低,它设定DMA_SxCR寄存器的PL[1:0]位的值。DMA优先级只有在多个DMA数据流同时使用时才有意义,这里我们设置为非常高优先级就可以了。
16) DMA_FIFOMode:FIFO模式使能,如果设置为DMA_FIFOMode_Enable表示使能FIFO模式功能;它设定DMA_SxFCR寄存器的DMDIS位。ADC采集传输使用直接传输模式即可,不需要使用FIFO模式。
17) DMA_FIFOThreshold:FIFO阈值选择,可选4种状态分别为FIFO容量的1/4、1/2、3/4和满;它设定DMA_SxFCR寄存器的FTH[1:0]位; DMA_FIFOMode设置为DMA_FIFOMode_Disable,那DMA_FIFOThreshold值无效。ADC采集传输不使用FIFO模式,设置改值无效。
18) DMA_MemoryBurst:存储器突发模式选择,可选单次模式、4节拍的增量突发模式、8节拍的增量突发模式或16节拍的增量突发模式,它设定DMA_SxCR寄存器的MBURST[1:0]位的值。ADC采集传输是直接模式,要求使用单次模式。
19) DMA_PeripheralBurst:外设突发模式选择,可选单次模式、4节拍的增量突发模式、8节拍的增量突发模式或16节拍的增量突发模式,它设定DMA_SxCR寄存器的PBURST[1:0]位的值。ADC采集传输是直接模式,要求使用单次模式。
21.5 DMA存储器到存储器模式实验
DMA工作模式多样,具体如何使用需要配合实际传输条件具体分析。接下来我们通过两个实验详细讲解DMA不同模式下的使用配置,加深我们对DMA功能的理解。
DMA运行高效,使用方便,在很多测试实验都会用到,这里先详解存储器到存储器和存储器到外设这两种模式,其他功能模式在其他章节会有很多使用到的情况,也会有相关的分析。
存储器到存储器模式可以实现数据在两个内存的快速拷贝。我们先定义一个静态的源数据,然后使用DMA传输把源数据拷贝到目标地址上,最后对比源数据和目标地址的数据,看看是否传输准确。
21.5.1 硬件设计
DMA存储器到存储器实验不需要其他硬件要求,只用到RGB彩色灯用于指示程序状态,关于RGB彩色灯电路可以参考GPIO章节。
21.5.2 软件设计
这里只讲解核心的部分代码,有些变量的设置,头文件的包含等并没有涉及到,完整的代码请参考本章配套的工程。这个实验代码比较简单,主要程序代码都在main.c文件中。
1. 编程要点
1) 使能DMA数据流时钟并复位初始化DMA数据流;
2) 配置DMA数据流参数;
3) 使能DMA数据流,进行传输;
4) 等待传输完成,并对源数据和目标地址数据进行比较。
2. 代码分析
DMA宏定义及相关变量定义
代码清单 211 DMA数据流和相关变量定义
1 /* 相关宏定义,使用存储器到存储器传输必须使用DMA2 */
2 #define DMA_STREAM DMA2_Stream0
3 #define DMA_CHANNEL DMA_Channel_0
4 #define DMA_STREAM_CLOCK RCC_AHB1Periph_DMA2
5 #define DMA_FLAG_TCIF DMA_FLAG_TCIF0
史海拾趣
|
核心的IC就是运放,或者LM324,LM358,通过双向可控硅来控制加热芯通断! 温度控制采用热电偶,或者热敏电阻;电源采用220VAC直接RC降压。 成本比较低廉,损坏之后可以参考此电路进行维修!… 查看全部问答> |
|
本帖最后由 paulhyde 于 2014-9-15 03:05 编辑 74HC595驱动两位数码管74HC595驱动两位数码管 … 查看全部问答> |
|
本帖最后由 paulhyde 于 2014-9-15 09:39 编辑 各位高手帮忙指点一下,按照下图中的cxa1238芯片的22脚接的本振,可是怎么调频率总是680k左右,我的载频是35M,所以本振应该是在45M左右,可是怎么调可调电容,它频率总不变啊!!!!找也没找出原因 ...… 查看全部问答> |
|
BGA焊接,你也是高手 BGA的焊接,对DIY爱好者始终是个难题,很多人对它望而却步。手工焊接时,如果第一次没有成功,很可能将它这个芯片丢弃,实在可惜。我们可以研究一下工厂里对它的焊接过程,找一找手工焊接的方法。 本帖的目的 ...… 查看全部问答> |
|
【TIVA C Launchpad 学习笔记六】定时器四种模式:单次计时,周期计时,数捕获,计时捕获 模式一&模式二:单次计时&周期计时/******************************************* 开发坏境:CCSv5.4 开发板:TIVA C Launchpad(TM4C123GH6PM) 程序功能:16位定时器,单次定时模式和周期性定时模式 程序说明: 编程者:Linchpin *************** ...… 查看全部问答> |
|
请教各位兄弟姐妹,我用STM32F103ZET6+ENC28J60学习LWIP,当用TCP服务器模式时,通信正常,用TCP客户端模式时,能ping通,但是连接不上,请各位帮忙想想是什么问题,以下是主要代码。 int main(void) { ...… 查看全部问答> |




