STM32F4_DMA直接存储器详解

news2024/10/5 8:07:50

目录

1. 什么是DMA

2. DMA的主要特性

3. DMA功能

3.1 DMA功能框图

3.2 DMA事务

3.3 通道选择

3.4 仲裁器

3.5 DMA数据流

3.6 源、目标和传输模式

3.6.1 外设到存储器模式

3.6.2 存储器到外设模式

3.6.3 存储器到存储器模式

3.7 指针递增

3.8 DMA内存占用

3.9 存储器映像

4. DMA中断

5. 初始化结构体

6. DMA相关寄存器

6.1 中断状态寄存器:DMA_LISR和DMA_HISR

6.1.1 DMA低中断状态寄存器:DMA_LISR

6.1.2 DMA高中断状态寄存器:DMA_HISR

6.2 DMA中断状态清除寄存器:DMA_LIFCR和DMA_HIFCR

6.3 DMA数据流x配置寄存器:DMA_SxCR

6.4 DMA数据流x数据项数寄存器:DMA_SxNDTR

6.5 DMA数据流x的外设地址寄存器:DMA_SxPAR

6.6 DMA数据流x的存储器地址寄存器:DMA_SxMOAR和DMA_SxM1AR

7. 库函数配置DMA

8. 实验程序

8.1 回车换行符

8.2 代码

8.2.1 main.c

8.2.2 DMA.c

8.2.3 DMA.h


1. 什么是DMA

        DMA全称为:Direct Memory Access;即直接存储器访问。

        DMA传输数据就是从地址A复制到地址B,地址A和地址B可以是外设和存储器之间,也可以是存储器和存储器之间。

        单片机的核心是CPU,我们知道CPU无时无刻不在处理着大量的事务,其中包括转移数据、计算数据、控制程序转移等等。正如中断一样,CPU处理的事务也有轻重缓急之分,有些事情对于CPU来说并不是那么重要,比方说转移数据、存储数据、复制数据,如果通过其他功能去处理这些事,而让原本应该处理这些事件的CPU转而去处理更加重要、复杂的事务,是不是可以更好的利用CPU资源呢?

        DMA就是基于这种设想而设计的,转移数据(特别是大量的数据费时费力)是可以不用CPU参与的,这部分CPU转而去处理更加复杂重要的事务,可以大大的增强CPU资源利用率。比方说我们希望资源 A 的数据复制到资源 B,是可以不用CPU参与的,只需要在资源 A 和资源 B 之间开辟一个DMA通道来复制数据即可。CPU可以转而去处理更加重要的事务。

        DMA的作用就是为了解决大量数据转移过度消耗CPU资源的问题。有了DMA使得CPU更专注于更加重要实用的操作、计算、控制等。

        DMA控制器基于复杂的总线矩阵架构,将功能强大的双 AHB 主总线架构与独立的 FIFO 结合在一起,优化了系统带宽。两个DMA控制器总共有16个数据流(每个控制器8个),每一个DMA控制器都用于管理一个或多个外设的存储器访问请求。每个数据流总共可以有多达8个通道(或称请求)。每个通道都有一个仲裁器,用于处理DMA请求间的优先级(类似于中断中的NVIC)。

2. DMA的主要特性

  • 双 AHB 主总线架构,一个用于存储器访问,另一个用于外设访问
  • 仅支持 32 位访问的 AHB 从编程接口
  • 每个DMA控制器有8个数据流,每个数据流有多达8个通道(或称请求)
  • 每个数据流有单独的四级 32 位先进先出存储器缓冲区 (FIFO),可用于 FIFO 模式或直接模式
  • 通过硬件可以将每个数据流配置为:
  •         —支持外设到存储器、存储器到外设和存储器到存储器传输的常规通道
  •         —也支持在存储器方双缓冲的双缓冲区通道
  • 8 个数据流中的每一个都连接到专用硬件 DMA 通道(请求)
  • DMA 数据流请求之间的优先级可用软件编程(4 个级别:非常高、高、中、低),在软件优先级相同的情况下可以通过硬件决定优先级(例如,请求 0 的优先级高于请求 1)
  • 每个数据流也支持通过软件触发存储器到存储器的传输

3. DMA功能

3.1 DMA功能框图

DMA控制器执行直接存储器传输:采用AHB主总线,它可以控制AHB总线矩阵来启动AHB事务;

直接存储器执行的事务包括:外设到存储器的传输、存储器到外设的传输、存储器到存储器的传输。

DMA控制器提供两个AHB主端口:AHB存储器端口(用于连接存储器)和 AHB外设端口(用于连接外设)。但是需要注意:当执行存储器与存储器之间的传输时,AHB外设端口必须也可以访问存储器。

3.2 DMA事务

DMA事务由给定数目的数据传输序列组成。要传输的数据项的数目及其宽度(8位、16位 或 32位)可由软件编程。

每个DMA传输包含三项操作:

  •         通过 DMA_SxPAR 或 DMA_SxM0AR 寄存器寻址,从外设数据寄存器或存储器单元中加载数据。
  •         通过 DMA_SxPAR 或 DMA_SxM0AR 寄存器寻址,将加载的数据存储到外设数据寄存器或存储器单元。
  •         DMA_SxNDTR 计数器在数据存储结束后递减,该计数器中包含仍需执行的事务数。

        在产生事件后,外设会向DMA控制器发送请求信号。DMA控制器根据通道优先级处理该请求。只要DMA控制器访问外设,DMA控制器就会向外设发送确认信号。外设获得DMA控制器的确认信号后,便会立即释放其请求。一旦外设使请求变得失效,那么DMA控制器就会释放确认信号。如果有更多的请求,外设可以启动下一个事务。

3.3 通道选择

        每一个数据流都与一个DMA请求相关联,此DMA请求可以从8个可能的通道请求中选出。此选择由DMA_SxCR寄存器中的CHSEL[2:0]位控制。

3.4 仲裁器

仲裁器为两个AHB主端口(存储器和外设端口)提供基于请求优先级的 8 个DMA数据流请求管理,并启动 外设/存储器 访问序列。

优先级管理分为两个阶段:

  •         软件:每个数据流优先级都可以在DMA_SxCR寄存器中配置。分为:非常高优先级、高优先级、中优先级、低优先级
  •         硬件:如果两个请求具有相同的软件优先级,则编号低的数据流优先于编号高的数据流。例如:数据流 2 的优先级高于数据流 4 的优先级。

3.5 DMA数据流

8个DMA控制器数据流都能提供源和目标(传输的双方)之间的单向传输链路。

每个数据流配置后都可以执行:

  •         常规类型事务存储器到外设、外设到存储器或存储器到存储器的传输。
  •         双缓冲区类型事务使用存储器的两个存储器指针的双缓冲区传输。

3.6 源、目标和传输模式

源传输和目标传输在整个 4GB 区域(也就是地址在0x0000 0000和0xFFFF FFFF之间)都可以寻址外设和存储器。

传输方向使用的是DMA_SxCR寄存器中的DIR[1:0]位进行配置,有三种可能的传输方向存储器到外设外设到存储器存储器到存储器

3.6.1 外设到存储器模式

使能了这个模式(将DMA_SxCR寄存器中的位EN置1)时,每次产生外设请求,数据流都会启动数据源到FIFO的传输。

FIFO简介:

FIFO用于在源数据传输到目标之前临时存储这些数据。

每个数据流都有一个独立的4字FIFO,阈值级别可由软件配置为1/4、1/2、3/4或满。为了使能FIFO阈值级别,必须通过将DMA_SxFCR寄存器中的DMDIS位置1来禁止直接模式。FIFO的结构随源与目标数据宽度而不同。

当达到了FIFO(源数据传输到目标之前临时存储)的阈值级别时,FIFO的内容移出并存储到目标中。

如果 DMA_SxNDTR 寄存器达到零、外设请求传输终止(在使用外设流控制器的情况下)或 DMA_SxCR 寄存器中的 EN 位由软件清零,传输即会停止。

在直接模式下(当 DMA_SxFCR 寄存器中的 DMDIS 值为“0”时),不使用 FIFO 的阈值级别控制:每完成一次从外设到 FIFO 的数据传输后,相应的数据立即就会移出并存储到目标中。

3.6.2 存储器到外设模式

使能了这种模式(将DMA_SxCR寄存器中的EN位置1)时,数据流会立即启动传输,从源完全填充FIFO。

每次发生外设请求,FIFO的内容都会移出并存储到目标中。当FIFO的级别小于或等于预定义的阈值级别时,将使用存储器中的数据完全重载FIFO。

如果 DMA_SxNDTR 寄存器达到零、外设请求传输终止(在使用外设流控制器的情况下)或 DMA_SxCR 寄存器中的 EN 位由软件清零,传输即会停止。

3.6.3 存储器到存储器模式

DMA通道在没有外设请求触发的情况下同样可以工作。

通过将DMA_SxCR寄存器中的使能位(EN)置1来使能数据流时,数据流会立即开始填充FIFO,直至达到阈值级别。达到阈值级别后,FIFO的内容便会移出,并存储到目标中。

如果DMA_SxNDTR寄存器达到零或DMA_SxCR寄存器中的EN位由软件清零,传输即会停止。

注意

  •         使用存储器到存储器模式时,不允许循环模式和直接模式。
  •         只有DMA2控制器能够执行存储器到存储器的传输。

3.7 指针递增

根据DMA_SxCR寄存器中的PINC和MINC位的状态,外设和存储器指针在每次传输后可以自动向后递增或保持常量。

通过单个寄存器访问外设源或目标数据时,禁止递增模式十分有用。

如果使能了递增模式,则根据在DMA_SxCR寄存器PSIZE和MSIZE位中编程的数据宽度,下一次传输的地址将是前一次传输的地址递增1(对于字节)、2(对于半字)或4(对于字)。

为了优化封装操作,可以不管AHB外设端口上传输的数据的大小,将外设地址的增量偏移大小固定下来。DMA_SxCR寄存器中的PINCOS位用于将增量偏移大小与外设AHB端口或32位地址上的数据大小对齐。PINCOS位仅对AHB外设端口有影响。

如果将PINCOS位置1,则不论PSIZE值是多少,下一次传输的地址总比前一次传输的地址递增4(自动与32位地址对齐)。但是,AHB存储器端口不受此操作影响。

如果AHB外设端口或AHB存储器端口分别请求突发事务,为了满足AMBA协议,则需要将 PINC 或 MINC 位置1。

3.8 DMA内存占用 

在STM32控制器中,芯片采用Cortex-MX架构,总线结构有了很大的优化,DMA占用另外的地址总线,并不会与CPU的系统总线发生冲突。也就是说,DMA的使用不会影响CPU的运行速度

3.9 存储器映像

我们知道计算机系统的5大组成部分是运算器、控制器、存储器、输入设备和输出设备。其中运算器和控制器通常合在一起,称为CPU。所以计算机的关键部分就是CPU和存储器。

4. DMA中断

对于每个DMA数据流,可在发生以下事件时产生中断:

  •         达到半传输
  •         传输完成
  •         传输错误
  •         FIFO错误(上溢、下溢或FIFO级别错误)
  •         直接模式错误

注意:在将使能控制位置 “1” 前,应将相应的事件标志清零,否则会立即产生中断。

总结:

数据的传输主要需要 1. 数据的源地址(数据从哪里发出的)2. 数据传输位置的目标地址(需要将数据传输到哪里)3. 传递数据多少的数据传输量 4. 进行多少次传输的传输模式 

用户在使用DMA直接存储器传输时,去掉了传统数据传输需要CPU寄存器参与的环节,主要涉及:源地址、目标地址、数据传输量这三个参数,需要用户设置好这三个参数,DMA控制器就会启动数据传输,当剩余传输数据量为0时,就会达到传输终点,结束DMA传输。也可以说只要剩余传输量不是0,并且DMA还处于使能状态,那么就会发生数据传输。

特殊的:DMA还存在循环传输模式,当到达传输终点时会重新启动DMA传输。

DMA的应用主要是单纯的DMA转运(不依靠CPU的情况)和ADC搭配DMA使用,其中ADC搭配DMA使用是非常普遍的。

首先理解为什么ADC需要搭配DMA使用? 单纯的ADC是可以独立进行工作的,并且通过中断服务函数获取每次采样的值,每次采样完毕后存储到数据寄存器中,但是ADC有一个致命的缺陷,也可以是不足,就是ADC每次转换得到的结果会覆盖上一次的值。而配合DMA进行传输,不仅仅可以利用DMA进行多路信号的采集,实现一次性处理,最重要的是不会出现转换结果覆盖的情况。

5. 初始化结构体

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;

1. 数据从哪里来,要到哪里去?

uint32_t DMA_PeripheralBaseAddr;   //外设地址

uint32_t DMA_MemoryBaseAddr;    //存储器地址     

前两个定义数据从哪里来:M->P  P->M  M->M

uint32_t DMA_DIR;   //传输方向

传输方向定义要到哪里去。

外设地址:DMA_CPAR      存储器地址:DMA_CMAR        传输方向:DMA_CCR:DIR

2. 数据要传多少,传的单位是什么?

uint32_t DMA_BufferSize;      //传输数目

uint32_t DMA_PeripheralInc;    //外设地址增量模式

uint32_t DMA_MemoryInc;     //存储器地址增量模式

uint32_t DMA_PeripheralDataSize;     //外设数据宽度

uint32_t DMA_MemoryDataSize;    //存储器数据宽度

3. 什么时候传输结束

uint32_t DMA_Mode;   //模式选择

模式选择:DMA_CCRx:CIRC

传输过半、传输完成、传输出错,DMA_ISR; 

6. DMA相关寄存器

6.1 中断状态寄存器:DMA_LISR和DMA_HISR

中断状态寄存器:DMA_LISR和DMA_HISR,每个寄存器管理四个数据流(总共8个),DMA_LISR寄存器用于管理数据流0~3,而DMA_HISR用于管理数据流4~7。这两个寄存器各位的描述一模一样,只是管理的数据流不同。

6.1.1 DMA低中断状态寄存器:DMA_LISR

DMA低中断状态寄存器:DMA_LISR(DMA low interrupt status register)

位27、21、11、5 TCIFx:数据流x传输完成中断标志

                0:数据流x上无传输完成事件

                1:数据流x上发生传输完成事件

位26、20、10、4 HTIFx:数据流x半传输中断标志

                0:数据流x上无半传输事件

                1:数据流x上发生半传输事件

位25、19、9、3 TEIFx:数据流x传输错误中断标志

                0:数据流x上无传输错误

                1:数据流x上发生传输错误

位24、18、8、2 DMEIFx:数据流x直接模式错误中断标志

                0:数据流x上无直接模式错误

                1:数据流x上发生直接模式错误

位22、16、6、0 FEIFx:数据流x FIFO错误中断标志

                0:数据流x上无FIFO错误事件

                1:数据流x上发生FIFO错误事件

6.1.2 DMA高中断状态寄存器:DMA_HISR

DMA高中断状态寄存器:DMA_HISR(DMA high interrupt status register)

位27、21、11、5 TCIFx:数据流x传输完成中断标志

                0:数据流x上无传输完成事件

                1:数据流x上发生传输完成事件

位26、20、10、4 HTIFx:数据流x半传输中断标志

                0:数据流x上无半传输事件

                1:数据流x上发生半传输事件

位25、19、9、3 TEIFx:数据流x传输错误中断标志

                0:数据流x上无传输错误

                1:数据流x上发生传输错误

位24、18、8、2 DMEIFx:数据流x直接模式错误中断标志

                0:数据流x上无直接模式错误

                1:数据流x上发生直接模式错误

位22、16、6、0 FEIFx:数据流x FIFO错误中断标志

                0:数据流x上无FIFO错误事件

                1:数据流x上发生FIFO错误事件

总结:如果开启了中断状态寄存器中对应的中断,则在达成条件后就会跳到中断服务函数里面去,即使没有开启,我们也可以通过查询这些位来获得当前DMA传输的状态。我们常用的是该寄存器的TCIFx位,也就是数据流x的DMA传输完成与否标志。

需要注意的是:此寄存器为只读寄存器,所以在这些位被置位以后,只能通过其他的操作来清除。

6.2 DMA中断状态清除寄存器:DMA_LIFCR和DMA_HIFCR

DMA中断状态清除寄存器:DMA_LIFCR和DMA_HIFCR,每个寄存器控制4个数据流,DMA_LIFCR寄存器管理数据流0~3,DMA_HIFCR管理数据流4~7。同样地,每个寄存器各位的描述完全相同,只是管理的数据流不同

DMA中断状态清除寄存器:DMA_LIFCR

6.3 DMA数据流x配置寄存器:DMA_SxCR

DMA数据流x配置寄存器:DMA_SxCR(DMA stream x configuration register)

该寄存器是DMA传输的核心控制寄存器。该寄存器控制着DMA的很多信息,包括数据宽度、外设及存储器的宽度、优先级、增量模式、传输方向、中断允许、使能都是通过该寄存器来设置的。该寄存器具体每一位的功能可自行查看中文参考手册

6.4 DMA数据流x数据项数寄存器:DMA_SxNDTR

DMA数据流x数据项数寄存器:DMA_SxNDTR(DMA stream x number of data register)

该寄存器控制着DMA数据流x的每次传输所要传输的数据量。其设置范围是0~65535。并且该寄存器的值会随着传输的进行而减少,当该寄存器的值为0的时候就代表此次数据传输已经全部发送完成了。所以可以通过这个寄存器的值来知道当前DMA传输的进度。

6.5 DMA数据流x的外设地址寄存器:DMA_SxPAR

DMA数据流x的外设地址寄存器:DMA_SxPAR(DMA stream x peripheral address register)

该寄存器用来存储STM32F4外设的地址,比如使用的是串口1,那么对应的该寄存器必须写入(&USART1_DR)0x4001 1004。如果使用其他外设,就修改成相应外设的地址就行。

6.6 DMA数据流x的存储器地址寄存器:DMA_SxMOAR和DMA_SxM1AR

其中DMA_SxM1AR仅在双缓冲模式下,才有效。DMA_SxM0AR,该寄存器和DMA_CPARx差不多,但是是用来放存储器的地址的。比如我们使用SendBuff[8200]数组来做存储器,那么我们在DMA_SxM0AR中写入&SendBuff就可以了。 

7. 库函数配置DMA

本次实验用到串口1的发送,属于DMA2的数据流7,通道4。

1. 使能DMA2时钟,等待数据流可配置

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);     //DMA2时钟使能 

while (DMA_GetCmdStatus(DMA_Streamx) != DISABLE){}      //等待DMA可配置 

对配置寄存器DMA_SxCR进行设置,等待其最低位为0,也就是DMA禁止传输了,才对DMA进行配置,while函数中获取寄存器状态不等于DISABLE

一旦离开while循环,那么就意味着状态位等于DISABLE了,也就是DMA禁止传输了。

2. 初始化DMA2数据流7,包括配置通道,外设地址,存储器地址,传输数据量等

void DMA_Init(DMA_Stream_TypeDef* DMAy_Streamx, DMA_InitTypeDef* DMA_InitStruct);  //初始化DMA

typedef struct 
{  
uint32_t DMA_Channel;   //设置DMA数据流对应通道,可供选择的通道请求多达8个,取值为DMA_Channel_0~DMA_Channel_0;   
uint32_t DMA_PeripheralBaseAddr;  //设置DMA传输的外设地址
uint32_t DMA_Memory0BaseAddr;   //设置DMA内存地址,存放DMA传输数据的地址
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;  //设置DMA模式是否循环采集
uint32_t DMA_Priority;    //设置DMA通道优先级  低 中 高 超高
uint32_t DMA_FIFOMode;   //设置是否开启FIFO模式
uint32_t DMA_FIFOThreshold;   //选择FIFO阈值
uint32_t DMA_MemoryBurst;    //配置存储器突发传输配置
uint32_t DMA_PeripheralBurst;   //配置外设突发传输设置
}DMA_InitTypeDef; 

DMA_InitStructure.DMA_Channel = chx; //通道选择  
DMA_InitStructure.DMA_PeripheralBaseAddr = par;//DMA外设地址  
DMA_InitStructure.DMA_Memory0BaseAddr = mar;//DMA 存储器0地址  
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;//存储器到外设模式  
DMA_InitStructure.DMA_BufferSize = ndtr;//数据传输量 
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设非增量模式  
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//存储器增量模式  
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据长度:8位
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //存储器数据长度:8位  
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;// 使用普通模式  
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//中等优先级  
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;  
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;  
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;//单次传输  
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single; //外设突发单次传输 
DMA_Init(DMA_Streamx, &DMA_InitStructure);//初始化DMA Stream 

3. 使能串口1的DMA发送

USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);     //使能串口1的DMA发送 

4. 使能DMA2数据流7,启动传输

DMA_Cmd (DMA2_Stream7,ENABLE);   //使能DMA数据流

5. 查询DMA传输状态

FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG)    //查询DMA传输状态

ag.  DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7);   //查询DMA数据流7传输状态

uint16_t DMA_GetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx);   //获取当前剩余数据量大小

ag.  DMA_GetCurrDataCounter(DMA1_Channel4);   //获取DMA数据流7还有多少个数据没有传输

void DMA_SetCurrDataCounter(DMA_Stream_TypeDef* DMAy_Streamx, uint16_t Counter);   //设置DMA数据流传输的数据流大小

8. 实验程序

DMA通常情况下是配合ADC一起进行使用的,可以很直接的解决ADC的短板。

在初始化的ADC代码的基础之上添加DMA初始化,修改器结构体变量即可。

        本次实验实现串口1的DMA发送,即USART1_TX,就必须选择DMA2的数据流7,通道4来进行DMA传输。

        因为:

8.1 回车换行符

if(t>=j)//加入换行符
		{
			if(mask)
			{
				SendBuff[i]=0x0a;
				t=0;
			}
			else
			{
				SendBuff[i]=0x0d;
				mask++;
			}
		}


//回车换行符一般用于windows的TXT文件,包含两个字符“\r\n”,即先回车,光标回到首行,然后换行,光标另起一行

0x0a和0x0d都是回车键的ASCII码值,是window系统中定义的控制字符。
当我们按键盘上的回车键时,系统接收到其实是0x0d 0x0a两个控制字符,所以显示出来具有换行功能。

8.2 代码

8.2.1 main.c

#include "stm32f4xx.h"
#include "delay.h"
#include "usart.h"
#include "LED.h"
#include "lcd.h"
#include "usmart.h"
#include "KEY.h"
#include "DMA.h"

//LCD状态设置函数
void led_set(u8 sta)//只要工程目录下有usmart调试函数,主函数就必须调用这两个函数
{
	LED1=sta;
}
//函数参数调用测试函数
void test_fun(void(*ledset)(u8),u8 sta)
{
	led_set(sta);
}
//发送数据长度,最好等于sizeof(TEXT_TO_SEND)+2的整数倍
#define SEND_BUF_SIZE 8200
u8 SendBuff[SEND_BUF_SIZE];  //发送数据缓冲区
const u8 TEXT_TO_SEND[]={"ALIENTEK Explorer STM32F4 DMA 串口实验"};  //const修饰的地址存在了Flash中

int main(void)
{
	u16 i;
	u8 t=0,j,mask=0;
	float pro=0; //进度
	delay_init(168);
	uart_init(115200);
	LED_Init();
	LCD_Init();
	Key_Init();
	
	//DMA2 数据流7,CH4 外设串口1 存储器为SendBuff,长度为:SEND_BUF_SIZE
	MyDMA_Config(DMA2_Stream7,DMA_Channel_4,(u32)&USART1->DR,(u32)SendBuff,SEND_BUF_SIZE);
	
	POINT_COLOR=RED;
	LCD_ShowString(30,50,200,16,16,"Explorer STM32F4");
	LCD_ShowString(30,70,200,16,16,"DMA Test");
	LCD_ShowString(30,90,200,16,16,"ATOM@ALIENTEK");
	LCD_ShowString(30,110,200,16,16,"2023/20/23");
	LCD_ShowString(30,130,200,16,16,"KEY0:START");
	
	POINT_COLOR=BLUE;
	//显示提示信息
	j=sizeof(TEXT_TO_SEND);
	for(i=0;i<SEND_BUF_SIZE;i++) //填充ASCII字符集数据
	{
		if(t>=j)//加入换行符
		{
			if(mask)
			{
				SendBuff[i]=0x0a;
				t=0;
			}
			else
			{
				SendBuff[i]=0x0d;
				mask++;
			}
		}
		else  //复制TEXT_TO_SEND语句
		{
			mask=0;
			SendBuff[i]=TEXT_TO_SEND[t];
			t++;
		}
	}
	POINT_COLOR=BLUE;
	i=0;
	while(1)
	{
		t=KEY_Scan(0);
		if(t==1)
		{
			printf("\r\nDMA DATA:\r\n");
			LCD_ShowString(30,150,200,16,16,"Start Transimit……");
			LCD_ShowString(30,170,200,16,16,"   %");//显示百分号 
			
			USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE); //使能串口1DMA发送
			
			MyDMA_Eable(DMA2_Stream7,SEND_BUF_SIZE);  //开始一次DMA传输
			while(1)
			{
				if(DMA_GetFlagStatus(DMA2_Stream7,DMA_FLAG_TCIF7)!=RESET) //等待DMA2_Stream7传输完成
				{
					DMA_ClearFlag(DMA2_Stream7,DMA_FLAG_TCIF7); //清除传输完成标志
					break;
				}
				pro=DMA_GetCurrDataCounter(DMA2_Stream7); //得到当前剩余数据数
				pro=1-pro/SEND_BUF_SIZE;  //得到百分比
				pro=pro*100;
				LCD_ShowNum(30,170,pro,3,16);
			}
			LCD_ShowNum(30,170,100,3,16); //显示100%
			LCD_ShowString(30,150,200,16,16,"Transimit Finished");
		}
		i++;
		delay_ms(10);
		if(i==20)
		{
			LED0=!LED0;
			i=0;
			
		}
	}
}


8.2.2 DMA.c

#include "stm32f4xx.h"                
#include "DMA.h"

//DMA各通道配置
//从存储器->外设模式/8位数据宽度/存储器增量模式
//DMA_Streamx:DMA数据流  DMA1_Stream0~7/DMA2_Stream0~7
//chx:DMA通道选择  DMA_Channel_0~DMA_Channel_7
//par:外设地址
//mar:寄存器地址
//ndtr:数据传输量

void MyDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr)
{
	if((u32)DMA_Streamx>(u32)DMA2)    // 得到当前Stream是属于DMA1还是DMA2
		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);//DMA2时钟使能
	else
		RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA1,ENABLE);//DMA1时钟使能
	
	DMA_DeInit(DMA_Streamx);
	while(DMA_GetCmdStatus(DMA_Streamx)!=DISABLE); //等待DMA可配置,当离开while循环后,DMA就已经使能了
		
	//配置DMA结构体
	DMA_InitTypeDef DMA_InitStructure;
	DMA_InitStructure.DMA_BufferSize=ndtr; //数据传输量
	DMA_InitStructure.DMA_Channel=chx;  //DMA通道选择
	DMA_InitStructure.DMA_DIR=DMA_DIR_MemoryToPeripheral; //存储器到外设模式
	DMA_InitStructure.DMA_FIFOMode=DMA_FIFOMode_Disable; //FIFO模式禁止
	DMA_InitStructure.DMA_FIFOThreshold=DMA_FIFOThreshold_Full; //FIFO阈值
	DMA_InitStructure.DMA_Memory0BaseAddr=mar;
	DMA_InitStructure.DMA_MemoryBurst=DMA_MemoryBurst_Single; //存储器突发单次传输
	DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_Byte;//存储器数据长度,8位
	DMA_InitStructure.DMA_MemoryInc=DMA_MemoryInc_Enable;//存储器增量模式,也就是存储器依次向后递增
	DMA_InitStructure.DMA_Mode=DMA_Mode_Normal; //使用普通模式
	DMA_InitStructure.DMA_PeripheralBaseAddr=par;
	DMA_InitStructure.DMA_PeripheralBurst=DMA_PeripheralBurst_Single; //外设突发单次传输
	DMA_InitStructure.DMA_PeripheralDataSize=DMA_PeripheralDataSize_Byte;//外设数据长度,8个字节
	DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable; //外设非增量模式,也就是外设的指针不是依次向后递增的
	DMA_InitStructure.DMA_Priority=DMA_Priority_Medium; //中等优先级
	DMA_Init(DMA_Streamx,&DMA_InitStructure);
}

//开启一次DMA传输

//DMA_Streamx:DMA数据流  DMA1_Stream0~7/DMA2_Stream0~7
//ndtr:数据传输量
void MyDMA_Eable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr)
{
	DMA_Cmd(DMA_Streamx,DISABLE);   //关闭DMA传输,只有DMA传输被关闭,才能获取DMA传输状态位
	while(DMA_GetCmdStatus(DMA_Streamx)!=DISABLE);  //确保DMA可以被设置
	DMA_SetCurrDataCounter(DMA_Streamx,ndtr); //数据传输量
	DMA_Cmd(DMA_Streamx,ENABLE);  //开启DMA传输
}

8.2.3 DMA.h

#ifndef _DMA__H_
#define _DMA__H_

void MyDMA_Config(DMA_Stream_TypeDef *DMA_Streamx,u32 chx,u32 par,u32 mar,u16 ndtr);

void MyDMA_Eable(DMA_Stream_TypeDef *DMA_Streamx,u16 ndtr);


#endif

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/554900.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

<SQL>《SQL命令(含例句)精心整理版(1)》

《SQL命令精心整理》 1 SQL基础2 关键字 select & distinct3 排序检索 - order by & desc & asc4 where 语句5 操作符 -and & or & not & in6 通配符6.1 LIKE % 谓词 下划线 方括号 7 计算、拼接、别名 1 SQL基础 名词概念数据库&#xff08;database…

Mit6.006-problemSet03

3-1 哈希练习&#xff08;Hash Practice&#xff09; (a) 按顺序插入整数keys A[47, 61, 36, 52, 56, 33, 92]到尺寸为7的哈希表中&#xff0c;使用哈希函数 h ( k ) ( 10 k 4 ) m o d 7 h(k)(10k4)mod7 h(k)(10k4)mod7。哈希表的每个插槽&#xff0c;存储一个key&#xff…

【Eslint】vscode 配置 eslint 教程

文章目录 一、初始化配置二、文件配置2.1、.eslintrc.js 文件2.2、.eslintignore 文件2.3、settings.json 文件 一、初始化配置 操作步骤&#xff1a; 选择&#xff1a;上下方向键确定&#xff1a;enter 回车键退出&#xff1a;Ctrl c 安装&#xff1a;npm install -g eslint初…

Java高并发核心编程(JUC)—线程池详细笔记

线程池 基本概述 线程池&#xff1a;一个容纳多个线程的容器&#xff0c;容器中的线程可以重复使用&#xff0c;省去了频繁创建和销毁线程对象的操作 线程池作用&#xff1a; 降低资源消耗&#xff0c;减少了创建和销毁线程的次数&#xff0c;每个工作线程都可以被重复利用…

加密与解密 调试篇 动态调试技术

OllyDbg调试器的使用 CPU窗口 我们进行载入的时候 主要返回的是CPU窗口 是最主要的窗口 对应面板的C 反汇编窗口 我们先查看CPU窗口 打开后是有 5个面板 主要查看反汇编窗口 我们可以对这些列进行操作 操作都是进行双击地址&#xff1a; 显示被双击行地址的相对地址 再次双…

23种设计模式之代理模式(Proxy Pattern)

前言&#xff1a;大家好&#xff0c;我是小威&#xff0c;24届毕业生&#xff0c;在一家满意的公司实习。本篇文章将23种设计模式中的代理模式&#xff0c;此篇文章为一天学习一个设计模式系列文章&#xff0c;后面会分享其他模式知识。 如果文章有什么需要改进的地方还请大佬不…

扩展磁盘大小

此虚拟机之前硬盘大小为40G&#xff0c;需扩展为500G 1.虚拟机设置中&#xff0c;扩展硬盘大小为500G 2.启动此虚拟机&#xff0c;查看当前磁盘大小 3.查看磁盘情况 可查看到当前磁盘大小总共537G&#xff0c;实际使用37G 4.对磁盘分区 刷新分区 查看磁盘情况&#xff0c;可查…

目标检测数据预处理——根据部件类别按照特定位置拼图,缩小学习空间

首先放效果图&#xff0c;更直观看到本片是要干嘛的&#xff1a; 如图&#xff0c;就是将大图划分为44宫格的&#xff0c;4个部件类的目标框按照固定位置拼图&#xff0c;其中head、body的大图为每个宫格一张图&#xff0c;hand、foot的小图为每个宫格22张图&#xff08;因为h…

LInux相关操作命令

目录 1、Linux用户和用户组管理 用户管理命令 系统管理 top命令 ps命令 kill命令 关机命令 重启命令 为什么学习命令 Linux刚面世是并没有图形界面&#xff0c;所有操作都靠命令完成&#xff0c;如磁盘操作、文件存取、目录操作、进程管理、文件权限等工作中&#xff…

react-naive工作原理

react-naive工作原理是从react的工作原理衍生出来的 react的工作原理 在react中&#xff0c;virtual dom 就像一个中间层&#xff0c;介于开发者描述的视图与实际在页面上渲染的视图之间。为了在浏览器上渲染出可交互的用户界面&#xff0c;开发者必须操作浏览器的Dom&#x…

系统分析师经典易错题,解题思路三

UML2.0包括14种图:分别如下:类图(class diagram),类图描述了一组类、接口、协作和他们之间的关系。在OO系统的建模中,最常见的图就是类图。类图给出了系统的静态设计视图,活动图的类图给出了系统的静态进程视图。对象图(object diagram)对象图描述了一组对象及它们之间…

linux centos 安装JDK、tomcat、nginx教程记录

一、安装jdk 1、查看linux系统的jdk位数&#xff08;64/32位&#xff09; 查看本机位数命令&#xff1a; sudo uname --m 2、进入jdk下载官网 Java Downloads | Oracle 现在默认是最新的jdk20 以为我是之前的项目&#xff0c;使用的是jdk1.8_181版本&#xff0c;所以我需要…

零基础自学网络安全/web安全,看这一篇就够了

作为一个安全从业人员&#xff0c;我自知web安全的概念太过于宽泛&#xff0c;我本人了解的也并不够精深&#xff0c;还需要继续学习。 但看到这个问题之后又想说说自己的看法&#xff0c;所以今天随手写写关于web安全的内容&#xff0c;希望对初次遇到web安全问题的同学提供帮…

Cadence+SPB16.2入门教程(上)

第1章焊盘制作 1.1用Pad Designer制作焊盘 Allegro中制作焊盘的工作叫Pad Designer,所有SMD焊盘、通孔焊盘以及过孔都用该工具来制作。 打开程序->Cadence SPB 16.2->PCB Editer utilities->Pad Designer,弹出焊盘制作的界面,如图1.1所示。 在Units下拉框中选择…

【CloudCompare教程】005:点云滤波处理大全

本文讲述基于cloudcompare软件的点云滤波方法及案例,包括:高斯滤波、低通滤波、双边滤波、统计滤波、CSF地面滤波等等。 文章目录 一、高斯滤波二、低通滤波三、双侧滤波四、统计滤波五、CSF地面滤波滤波(Wave filtering)是将信号中特定波段频率滤除的操作,是抑制和防止干…

5月22号软件资讯更新合集.....

DHorse v1.1.1 发布&#xff0c;基于 k8s 的发布平台 综述 DHorse 是一个简单易用、以应用为中心的云原生 DevOps 系统&#xff0c;具有持续集成、持续部署、微服务治理等功能&#xff0c;无需安装依赖 Docker、Maven、Node 等环境即可发布 Java 和 Node 应用&#xff0c;主要…

Telnet远程登录设备管理

Telnet远程登录原理 为了方便通过命令管理设备,可以使用Telnet协议对设备进行管理。 Telnet协议与使用Console接口管理设备不同,无需专用线缆直连设备的Console接口,只要IP地址可达、能够和设备的TCP 23端口通信即可。 支持通过Telnet协议进行管理的设备被称为Telnet服务器…

你还不知道~~这个是什么意思吗,还以为是作者写错了

文章目录 前言一、来个例子二、按位非~三、小知识 前言 主要是来学习一下js中运算符的相关的知识 一、来个例子 ~~(Math.random() * 10)看起来像是要获取随机数的。 我们先把括号内的东西粘到控制台看看&#xff1a; 结果&#xff1a; (Math.random() * 10) //4.47062635057…

面试冲冲冲

目录 一、数据库MYSQL 1.1 Oracle与Mysql的区别 1.2 Mysql索引 1.3 Mysql事务 1.4 Mysql存储引擎 1.5 B树 1.6 锁 1.7 优化 一、数据库MYSQL 1.1 Oracle与Mysql的区别 Oracle与Mysql的区别_oracle和mysql区别_顾优秀的博客-CSDN博客 浅谈MySQL和Oracle的区别_oracle…

都说聚合配送好,它到底能解决哪些同城配送难题?

自外卖经济兴起以来&#xff0c;即时配送一直呈现爆炸式增长。与该领域相关的商家、平台、骑手、用户数量和订单数量&#xff0c;也在保持着快速增长。其服务类别已从外卖扩展到人们生活的各个方面&#xff0c;需求增量稳步增长。 市场配送渠道虽多 配送问题仍是大难题 到目…