15 STM32F429 DMA

15 STM32F429 DMA

1.简介

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 资源。

image-20221003202245610

2.基本工作流程

1.固件知识

1.找到目标外设对应的数据流

DMA1

image-20221004194116536

DMA2

image-20221004194636210

每个外设请求都占用一个数据流通道,相同外设请求可以占用不同数据流通道。比如 SPI3_RX
请求,即 SPI3 数据发送请求,占用 DMA1 的数据流 0 的通道 0,因此当我们使用该请求时,我
们需要在把 DMA_S0CR 寄存器的 CHSEL[2:0] 设置为“000”,此时相同数据流的其他通道不被
选择,处于不可用状态,比如此时不能使用数据流 0 的通道 1 即 I2C1_RX 请求等等。
查阅表 DMA1 各个通道的请求映像 可以发现 SPI3_RX 请求不仅仅在数据流 0 的通道 0,同时数
据流 2 的通道 0 也是 SPI3_RX 请求,实际上其他外设基本上都有两个对应数据流通道,这两个
数据流通道都是可选的,这样设计是尽可能提供多个数据流同时使用情况选择。

每个 DMA 控制器具有 8 个数据流,每个数据流对应 8 个外设请求。在实现 DMA
传输之前,DMA 控制器会通过 DMA 数据流 x 配置寄存器 DMA_SxCR(x 为 0~7,对应 8 个 DMA
数据流) 的 CHSEL[2:0] 位选择对应的通道作为该数据流的目标外设

image-20221004194156221

image-20221004194210194

2.仲裁数据流使用DMA顺序

一个 DMA 控制器对应 8 个数据流,数据流包含要传输数据的源地址、目标地址、数据等等信息。
如果我们需要同时使用同一个 DMA 控制器 (DMA1 或 DMA2) 多个外设请求时,那必然需要同时
使用多个数据流,那究竟哪一个数据流具有优先传输的权利呢?这就需要仲裁器来管理判断了。
仲裁器管理数据流方法分为两个阶段。第一阶段属于软件阶段,我们在配置数据流时可以通过寄
存器设定它的优先级别,具体配置 DMA_SxCR 寄存器 PL[1:0] 位,可以设置为非常高、高、中和
低四个级别。第二阶段属于硬件阶段,如果两个或以上数据流软件设置优先级一样,则他们优先
级取决于数据流编号,编号越低越具有优先权,比如数据流 2 优先级高于数据流 3。

image-20221004201332325

3.选择通道与端口进行数据传输

1.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 内容将传输到目标中。2
FIFO 对于要求源地址和目标地址数据宽度不同时非常有用,比如源数据是源源不断的字节数据,
而目标地址要求输出字宽度的数据,即在实现数据传输时同时把原来 4 个 8 位字节的数据拼凑成
一个 32 位字数据。此时使用 FIFO 功能先把数据缓存起来,分别根据需要输出数据。
FIFO 另外一个作用使用于突发 (burst) 传输。

2.存储器端口、外设端口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ekNoW3Hz-1664942048256)(…/…/…/AppData/Roaming/Typora/typora-user-images/image-20221004202736456.png)]DMA 控制器实现双 AHB 主接口,更好利用总线矩阵和并行传输。DMA 控制器通过存储器端口
和外设端口与存储器和外设进行数据传输,关系见图两个 DMA 控制器系统实现 。DMA 控制器
的功能是快速转移内存数据,需要一个连接至源数据地址的端口和一个连接至目标地址的端口。
DMA2(DMA 控制器 2) 的存储器端口和外设端口都是连接到 AHB 总线矩阵,可以使用 AHB 总线
矩阵功能。DMA2 存储器和外设端口可以访问相关的内存地址,包括有内部 Flash、内部 SRAM、
AHB1 外设、AHB2 外设、APB2 外设和外部存储器空间。
DMA1 的存储区端口相比 DMA2 的要减少 AHB2 外设的访问权,同时 DMA1 外设端口是没有连
接至总线矩阵的,只有连接到 APB1 外设,所以 DMA1 不能实现存储器到存储器传输

3.编程端口

AHB 从器件编程端口是连接至 AHB2 外设的。AHB2 外设在使用 DMA 传输时需要相关控制信
号。

2.配置DMA开始工作

1.配置传输模式

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 寄存器作为目标地址。

image-20221004203620092

image-20221004203626818

image-20221004203645606

3.配置流控制器
1.控制传输项数

流控制器主要涉及到一个控制 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 才有这个功能,其他外设不具备此功能。

image-20221004204243306

image-20221004204250373

2.循环模式

循环模式相对应于一次模式。一次模式就是传输一次就停止传输,下一次传输需要手动控制,而
循环模式在传输一次后会自动按照相同配置重新传输,周而复始直至被控制停止或传输发生错
误。
通过 DMA_SxCR 寄存器的 CIRC 位可以使能循环模式

3.传输类型
突发模式

DMA 传输类型有单次 (Single) 传输和突发 (Burst) 传输。突发传输就是用非常短时间结合非常高
数据信号率传输数据,相对正常传输速度,突发传输就是在传输阶段把速度瞬间提高,实现高速
传输,在数据传输完成后恢复正常速度,有点类似达到数据块“秒传”效果。为达到这个效果突
发传输过程要占用 AHB 总线,保证要求每个数据项在传输过程不被分割,这样一次性把数据全
部传输完才释放 AHB 总线;而单次传输时必须通过 AHB 的总线仲裁多次控制才传输完成。
单次和突发传输数据使用具体情况参考表 22‑4。其中 PBURST[1:0] 和 MBURST[1:0] 位是位于
DMA_SxCR 寄存器中的,用于分别设置外设和存储器不同节拍数的突发传输,对应为单次传
输、4 个节拍增量传输、8 个节拍增量传输和 16 个节拍增量传输。PINC 位和 MINC 位是寄存器
DMA_SxCR 寄存器的第 9 和第 10 位,如果位被置 1 则在每次数据传输后数据地址指针自动递
增,其增量由 PSIZE 和 MSIZE 值决定,比如,设置 PSIZE 为半字大小,那么下一次传输地址将
是前一次地址递增 2。

image-20221004204824523

image-20221004205346872

ps: 节拍=MSIZE

例如FIFO级别1/4时候 4节拍1次突发即使一次性传输4个字节,在32位系统里1字=4字节,FIFO容量位4字=16字节,则当FIFO中存储的数据量达到4 * 1/4 = 1字,便会发送一次一次发4字节.

直接模式

默认情况下,DMA 工作在直接模式,不使能 FIFO 阈值级别。
直接模式在每个外设请求都立即启动对存储器传输的单次传输。直接模式要求源地址和目标地
址的数据宽度必须一致,所以只有 PSIZE 控制,而 MSIZE 值被忽略。突发传输是基于 FIFO 的所
以直接模式不被支持。另外直接模式不能用于存储器到存储器传输。
在直接模式下,如果 DMA 配置为存储器到外设传输那 DMA 会见一个数据存放在 FIFO 内,如
果外设启动 DMA 传输请求就可以马上将数据传输过去。

双缓冲模式

设置 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 双缓冲模式传输数据就可以非常好的解决这个问题,达到解码和输出音频数据
到音频电路同步进行的效果。

image-20221004210930323

image-20221004211008728

4.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 中断控制位将产生
    直接模式错误中断。

3.实验代码分析

M->M

1.宏定义

/* 相关宏定义,使用存储器到存储器传输必须使用DMA2 */
#define DMA_STREAM               DMA2_Stream0
#define DMA_CHANNEL              DMA_Channel_0
#define DMA_STREAM_CLOCK         RCC_AHB1Periph_DMA2 
#define DMA_FLAG_TCIF            DMA_FLAG_TCIF0

#define BUFFER_SIZE              32
#define TIMEOUT_MAX              10000 /* Maximum timeout value */

2.DMA结构体

typedef struct {
uint32_t DMA_Channel;//通道选择
uint32_t DMA_PeripheralBaseAddr;//外设地址
uint32_t DMA_Memory0BaseAddr;//存储器 0 地址
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_FIFOMode;//FIFO 模式
uint32_t DMA_FIFOThreshold;//FIFO 阈值
uint32_t DMA_MemoryBurst;//存储器突发传输
uint32_t DMA_PeripheralBurst;//外设突发传输
} DMA_InitTypeDef;

3.主函数

int main(void)
{
  /* 定义存放比较结果变量 */
  uint8_t TransferStatus;
  
	/* LED 端口初始化 */
	LED_GPIO_Config();
    
  /* 设置RGB彩色灯为紫色 */
  LED_PURPLE;  
  
  /* 简单延时函数 */
  Delay(0xFFFFFF);  
  
  /* DMA传输配置 */
  DMA_Config(); 
  
  /* 等待DMA传输完成 */
  while(DMA_GetFlagStatus(DMA_STREAM,DMA_FLAG_TCIF)==DISABLE)
  {
    
  }   
  
  /* 比较源数据与传输后数据 */
  TransferStatus=Buffercmp(aSRC_Const_Buffer, aDST_Buffer, BUFFER_SIZE);
  
  /* 判断源数据与传输后数据比较结果*/
  if(TransferStatus==0)  
  {
    /* 源数据与传输后数据不相等时RGB彩色灯显示红色 */
    LED_RED;
  }
  else
  { 
    /* 源数据与传输后数据相等时RGB彩色灯显示蓝色 */
    LED_BLUE;
  }

	while (1)
	{		
	}
}
DMA_Config()
static void DMA_Config(void)
{
  DMA_InitTypeDef  DMA_InitStructure;
  __IO uint32_t    Timeout = TIMEOUT_MAX;
    
  /* 使能DMA时钟 */
  RCC_AHB1PeriphClockCmd(DMA_STREAM_CLOCK, ENABLE);
  
  /* 复位初始化DMA数据流 */
  DMA_DeInit(DMA_STREAM);

  /* 确保DMA数据流复位完成 */
  while (DMA_GetCmdStatus(DMA_STREAM) != DISABLE)
  {
  }
  
  /* DMA数据流通道选择 */
  DMA_InitStructure.DMA_Channel = DMA_CHANNEL;  
  /* 源数据地址 */
  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)aSRC_Const_Buffer;
  /* 目标地址 */
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)aDST_Buffer;
  /* 存储器到存储器模式 */
  DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;
  /* 数据数目 */
  DMA_InitStructure.DMA_BufferSize = (uint32_t)BUFFER_SIZE;
  /* 使能自动递增功能 */
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
  /* 使能自动递增功能 */
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  /* 源数据是字大小(32位) */
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
  /* 目标数据也是字大小(32位) */
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
  /* 一次传输模式,存储器到存储器模式不能使用循环传输 */
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  /* DMA数据流优先级为高 */
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  /* 禁用FIFO模式 */
  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数据流参数配置 */
  DMA_Init(DMA_STREAM, &DMA_InitStructure);
  
  /* 清除DMA数据流传输完成标志位 */
  DMA_ClearFlag(DMA_STREAM,DMA_FLAG_TCIF);
  
  /* 使能DMA数据流,开始DMA数据传输 */
  DMA_Cmd(DMA_STREAM, ENABLE);

  /* 检测DMA数据流是否有效并带有超时检测功能 */
  Timeout = TIMEOUT_MAX;
  while ((DMA_GetCmdStatus(DMA_STREAM) != ENABLE) && (Timeout-- > 0))
  {
  }
   
  /* 判断是否超时 */
  if (Timeout == 0)
  {
    /* 超时就让程序运行下面循环:RGB彩色灯闪烁 */
    while (1)
    {      
      LED_RED;
      Delay(0xFFFFFF);
      LED_RGBOFF;
      Delay(0xFFFFFF);
    }
  } 
}







  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值