10-DMA数据传输.md 4.4 KB


tags: [source-summary] type: source source: "尚硅谷嵌入式技术之STM32单片机(进阶篇)V1.0.1 — DMA章节 + 配套代码28~31" author: "尚硅谷研究院" date: 2026-07-15

created: 2026-07-15

DMA数据传输

用生活理解:DMA 就像公司里的快递员——以前你要自己去取文件(CPU 轮询/中断读外设),现在快递员帮你送了(DMA 控制器自动搬运),你就可以专心做其他重要工作。快递送到后喊你一声(完成中断)即可。


DMA 概述

DMA(Direct Memory Access)允许外设与存储器之间直接传输数据,不需要 CPU 参与

传统方式(无 DMA)

CPU 读取外设数据寄存器 → CPU 写入内存 → CPU 继续下一字节

DMA 方式

CPU 配置 DMA 通道后 → DMA 控制器自动搬运 → 搬运完成通知 CPU

STM32F103 DMA 特性

  • 2 个 DMA 控制器:DMA1(7 通道)、DMA2(5 通道)
  • 支持:外设到内存、内存到外设、内存到内存
  • 存储器增量模式(地址自动递增)
  • 循环模式(自动重传)
  • 优先级可配(4 级:低/中/高/最高)

通道映射

DMA1 通道 外设请求
CH1 ADC1、TIM2_CH3、TIM4_CH1
CH2 USART1_TX、SPI1_RX、TIM1_CH1
CH3 USART1_RX、SPI1_TX、TIM1_CH2
CH4 SPI2_RX、SPI1_RX、USART1_TX
CH5 SPI2_TX、USART2_TX、TIM1_CH4
CH6 USART2_RX、ADC3、TIM1_CH1
CH7 USART3_TX、ADC3、TIM1_CH2

实验:DMA 内存到内存传输

软件设计(寄存器版)

文件:stm32_base/28_dma_mem2mem_register/Hardware/DMA/dma.c

#include "dma.h"

#define SRC_ADDR  ((uint32_t)0x20000000)   // 源地址(SRAM)
#define DST_ADDR  ((uint32_t)0x20000010)   // 目的地址(SRAM偏移)

void DMA_Init(void)
{
    // 1. 开启DMA1时钟
    RCC->AHBENR |= RCC_AHBENR_DMA1EN;

    // 2. 清除通道1的所有标志
    DMA1->IFCR |= DMA_IFCR_CGIF1;           // |= 写1清除所有标志

    // 3. 配置DMA通道1
    DMA1_Channel1->CPAR  = SRC_ADDR;         // 外设地址 = 源地址
    DMA1_Channel1->CMAR  = DST_ADDR;         // 存储器地址 = 目的地址
    DMA1_Channel1->CNDTR = 10;               // 传输次数 = 10(10个32位字)

    // 4. 配置控制寄存器CCR
    DMA1_Channel1->CCR |= DMA_CCR1_MEM2MEM;  // MEM2MEM=1: 内存到内存模式
    DMA1_Channel1->CCR |= DMA_CCR1_PL;        // PL[1:0]=11: 最高优先级
    DMA1_Channel1->CCR |= DMA_CCR1_MSIZE;     // MSIZE=10: 存储器数据宽度32位
    DMA1_Channel1->CCR |= DMA_CCR1_PSIZE;     // PSIZE=10: 外设数据宽度32位
    DMA1_Channel1->CCR |= DMA_CCR1_MINC;      // MINC=1: 存储器地址递增
    DMA1_Channel1->CCR |= DMA_CCR1_PINC;      // PINC=1: 外设地址递增
    DMA1_Channel1->CCR |= DMA_CCR1_CIRC;      // CIRC=1: 循环模式
    DMA1_Channel1->CCR |= DMA_CCR1_TCIE;      // TCIE=1: 传输完成中断使能

    // 5. 使能通道
    DMA1_Channel1->CCR |= DMA_CCR1_EN;

    // 6. NVIC使能DMA中断
    NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}

// DMA传输完成中断
void DMA1_Channel1_IRQHandler(void)
{
    if (DMA1->ISR & DMA_ISR_TCIF1)           // 检查传输完成标志
    {
        DMA1->IFCR |= DMA_IFCR_CTCIF1;       // 清除完成标志
        // 传输完成:目的地址已包含源数据的副本
    }
}

核心速查表

DMA寄存器 功能
CCR.EN 通道使能
CCR.PL[1:0] 通道优先级
CCR.MSIZE[1:0] / PSIZE[1:0] 数据宽度(8/16/32位)
CCR.MINC / PINC 地址递增使能
CCR.CIRC 循环模式
CCR.DIR 传输方向(外设→内存 / 内存→外设)
CCR.MEM2MEM 内存到内存模式(无视外设请求)
CNDTR 传输次数(每次传输后自减)
CPAR 外设地址
CMAR 存储器地址
ISR 中断状态寄存器(TCIF/HTIF/TEIF等)
IFCR 中断标志清除寄存器(写1清除)

常见问题与避坑

  1. DMA 传输没开始 → 检查 CCR.EN 是否使能、CNDTR 是否设置、外设请求是否使能
  2. 传输完没有中断 → 检查 CCR.TCIE
  3. CNDTR 一直为 0 → 传输完成后的值;循环模式下复位后重新装载
  4. DMA 与外设配合错误 → 注意 MSIZE/PSIZE 要与外设寄存器宽度匹配
  5. 内存到内存模式启动失败 → 先配置好所有寄存器再 EN=1;注意通道启动后 CPAR/CMAR 不可修改