STM32F103-DMA配置-标准库(私人笔记)

参考:【STM32】 DMA原理,步骤超细详解,一文看懂DMA-CSDN博客

为了DMA输出PWM进行的配置,如果不想看前面介绍,可以直接跳转到后面函数配置,然后按需参考自行更改。

1、基本介绍

1.1、定义

全称Direct Memory Access,即直接存储器访问。

  • 无须CPU的干预,通过DMA数据可以快速地移动。这就节省了CPU的资源来做其他操作。
  • DMA提供通路,将数据从一个地址空间复制到另一个地址空间,在外设↔存储器之间或者存储器↔存储器之间提供高速的数据传输。

1.2、传输方向

  • 存储器 ↔ 存储器(仅DMA2可以)
  • 外设 ↔ 存储器
  • 外设 ↔ 外设

存储器就是数据原地址,外设就是传输的目标地址。

1.3、传输参数

  • 数据原地址;
  • 传输的目标地址;
  • 数据传输量;
  • 进行多少次传输的传输模式。

用户参数设置主要涉及源地址目标地址传输数据量这三个。只要传输数据量不为0,且DMA是启动状态,就会发送数据传输。

1.4、主要特征

  • 每个通道都直接连接专用的硬件DMA请求,但同样支持软件触发。

  • 同一个DMA模块上,多请求间的优先权通过软件设置(共四级很高、高、中等、低),优先权配置相等时由硬件决定(请求0>请求1,以此类推);

  • 独立数据源和目标数据区的传输宽度字节、半字、全字)。源和目标地址的数据必须按传输宽度对齐。

  • 支持循环的缓冲器管理。

  • 每个通道都有3个事件标志DMA半传输、DMA传输完成、DMA传输错误),这3个标志可成为一个单独的中断请求。

    • 中断相关标志位:
    标志位描述
    DMAx_IT_TCx传输完成中断标志(Transfer Complete)
    DMAx_IT_HTx半传输完成中断标志(Half Transfer Complete)
    DMAx_IT_TEx传输错误中断标志(Transfer Error)
    • 状态相关标志位:
    标志位描述
    DMAx_FLAG_TCx传输完成标志(Transfer Complete)
    DMAx_FLAG_HTx半传输完成标志(Half Transfer Complete)
    DMAx_FLAG_TEx传输错误标志(Transfer Error)
    DMAx_FLAG_GLx全局标志(Global Flag,包含上述所有标志的汇总)
  • 闪存、SRAM、外设的SRAM、APB1、APB2、AHB外设均可作为访问的源和目标。

  • 可编程的数据传输数目:最大为65535(2^16 - 1)

1.5、STM32上的DMA资源

STM32最多有2个DMA控制器(DMA2仅存在大容量产品中),12个独立的可配置的通道(请求)DMA1有7个通道DMA2有5个通道。每个通道专门用来管理来自于一个或多个外设对存储器访问的请求。还有一个仲裁器来协调各个 DMA 请求的优先权。

  • 小容量产品:闪存存储器容量在16K~32K字节之间的微控制器。
  • 中容量产品:闪存存储器容量在64K~128K字节之间的微控制器。
  • 大容量产品:闪存存储器容量在256K~512K字节之间的微控制器。

1.6、传输模式

  • 方法1:DMA_Mode_Normal,正常模式。当一次DMA数据传输完后,停止DMA传送 ,也就是只传输一次。
  • 方法2:DMA_Mode_Circular,循环传输模式。但传输结束时。硬件会自动将传输数据量寄存器进行重装,进行下一轮数据传输。

2、基本配置

2.1、结构体

DMA_InitTypeDef 定义于文件“stm32f10x_dma.h”

typedef struct {
    uint32_t DMA_PeripheralBaseAddr;    // 设置DMA源地址
    uint32_t DMA_MemoryBaseAddr;        // 设置DMA目的地址
    uint32_t DMA_DIR;                   // 设置数据传输方向,决定数据是从外设→内存还是从内存→外设,也就是外设是源地还是目的地
    uint32_t DMA_BufferSize;            // 设置传输大小
    uint32_t DMA_PeripheralInc;         // 设置外设地址是否自增
    uint32_t DMA_MemoryInc;             // 设置传输数据时内存地址是否递增,需要开启
    uint32_t DMA_PeripheralDataSize;    // 外设的数据长度是字节传输(8bits)、半字传输(16bits)还是字传输(32bits)
    uint32_t DMA_MemoryDataSize;        // 设置内存的数据长度
    uint32_t DMA_Mode;                  // 设置DMA的模式,正常模式/循环模式,是否循环发送
    uint32_t DMA_Priority;              // 设置DMA通道的优先级,有低、中、高、超高四种模式
    uint32_t DMA_M2M;                   // 设置是否是存储器到存储器模式传输
} DMA_InitTypeDef;

2.2、初始化

/**
 * @brief  初始化DMA
 * @param  DMA_CHx               DMA通道
 * @param  peripheral_base_addr 外设地址
 * @param  memory_base_addr     内存地址
 * @param  buffer_size          缓冲区大小
 */
void DMA_Config(DMA_Channel_TypeDef *DMA_CHx, uint32_t peripheral_base_addr, uint32_t memory_base_addr, uint32_t buffer_size)
{
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);                    // 使能DMA1时钟

    DMA_DeInit(DMA_CHx);                                                  // 将DMA的通道x寄存器重设为缺省值
    DMA_InitStructure.DMA_PeripheralBaseAddr = peripheral_base_addr;      // 启动传输前装入实际RAM地址
    DMA_InitStructure.DMA_MemoryBaseAddr = memory_base_addr;              // 接收缓冲区首地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;                    // 数据传输方向,从内存读取到外设。按需更改
    DMA_InitStructure.DMA_BufferSize = buffer_size;                       // DMA缓存的大小(数据量)
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;      // 外设地址递增
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;               // 内存地址递增
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; // 数据宽度为16位
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;   // 数据宽度为16位
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;                       // 工作在循环模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;                   // 高优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;                          // 没有设置为内存到内存传输
    DMA_Init(DMA_CHx, &DMA_InitStructure);                                // 对DMA通道x进行初始化
    DMA_ITConfig(DMA_CHx, DMA_IT_TC, ENABLE);                             // 开启DMA传输完成中断

    // 配置 DMA 中断优先级和使能
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

    if (DMA_CHx == DMA1_Channel1)
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    else if (DMA_CHx == DMA1_Channel2)
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
    else if (DMA_CHx == DMA1_Channel3)
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel3_IRQn;
    else if (DMA_CHx == DMA1_Channel4)
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    DMA_Cmd(DMA_CHx, ENABLE);                                             // 使DMA通道开始工作

    // 按需开启
    TIM_DMACmd(TIM3, TIM_DMA_Update, ENABLE);                             // 当定时器的计数器达到ARR(自动重装载值)时触发
    // TIM_DMACmd(TIM3, TIM_DMA_CC4, ENABLE);                             // 当定时器的计数器达到CCR4(比较寄存器)时触发
    // USART_DMACmd(USART2, USART_DMAReq_Rx, ENABLE);                     // 开启串口DMA接收
}
  • 解析
结构成员参数描述
DMA_DIR1. DMA_DIR_PeripheralDST
2. DMA_DIR_PeripheralSRC
1. 外设作为数据传输的目的地
2. 外设作为数据传输的来源
DMA_BufferSize1~65535DMA 通道的 DMA 缓存的大小。
DMA_PeripheralDataSize 或者DMA_MemoryDataSize 的值决定
DMA_PeripheralInc1. DMA_PeripheralInc_Enable
2. DMA_PeripheralInc_Disable
1. 外设地址寄存器递增
2. 外设地址寄存器不变
DMA_MemoryInc1. DMA_PeripheralInc_Enable
2. DMA_PeripheralInc_Disable
1. 内存地址寄存器递增
2. 内存地址寄存器不变
DMA_PeripheralDataSize1. DMA_PeripheralDataSize_Byte
2. DMA_PeripheralDataSize_HalfWord
3. DMA_PeripheralDataSize_Word
1. 数据宽度为 8 位
2. 数据宽度为 16 位
3. 数据宽度为 32 位
DMA_MemoryDataSize1. DMA_MemoryDataSize_Byte
2. DMA_MemoryDataSize_HalfWord
3. DMA_MemoryDataSize_Word
1. 数据宽度为 8 位
2. 数据宽度为 16 位
3. 数据宽度为 32 位
DMA_Mode1. DMA_Mode_Circular
2. DMA_Mode_Normal
1. 工作在循环缓存模式
2. 工作在正常缓存模式
DMA_Priority1. DMA_Priority_VeryHigh DMA
2. DMA_Priority_High DMA
3. DMA_Priority_Medium DMA
4. DMA_Priority_Low DMA
1. 通道 x 拥有非常高优先级
2. 通道 x 拥有高优先级
3. 通道 x 拥有中优先级
4. 通道 x 拥有低优先级
DMA_M2M1. DMA_M2M_Enable DMA
2. DMA_M2M_Disable DMA
1. 通道 x 设置为内存到内存传输
2. 通道 x 没有设置为内存到内存传输

2.3、中断

void DMA1_Channel3_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA1_IT_TC3) != RESET)
	{
			DMA_ClearITPendingBit(DMA1_IT_TC3);
			
			/* 在此处写处理 */

    }
}

知识点补充:

  • 下面两个是DMA触发频率控制。这个是控制DMA的发送,当达到ARR/CCR4时触发一次DMA数据发送。
TIM_DMACmd(TIM3, TIM_DMA_Update, ENABLE); // 当定时器的计数器达到ARR(自动重装载值)时触发
TIM_DMACmd(TIM3, TIM_DMA_CC4, ENABLE);	  // 当定时器的捕获/比较通道 4 达到CCR4(设定的比较值)时触发
  • DMA中断都是发送完数据块才进入的。
void DMA1_Channel3_IRQHandler(void){}
  • 以F1系列为例,DMA同一个时刻只可以开一个通道进入,因为DMA前面就是通道多路选择器。优先级高的优先响应,然后传输间隙,DMA有空闲时间,低优先级的也可以响应,高低优先级同时来了,优先响应高。
  • DMA执行顺序是按照硬件顺序执行的(例如:DMA1_Channel1 > DMA1_Channel2 > DMA1_Channel3 > ...),当相同硬件优先级的时候(例如DMA1_Channel1DMA2_Channel1 可能有相同的优先级),软件优先级高的才起决定作用。
  • 使能双缓冲区模式时,自动使能循环模式。使能双缓冲区模式时,不允许配置存储器到存储器模式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值