stm32f10x_dma

STM32F10x DMA 外设标准库函数参考文档

数据类型定义

DMA_InitTypeDef - DMA初始化结构体

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;

成员说明:

  • DMA_PeripheralBaseAddr:指定DMA通道的外设基地址
  • DMA_MemoryBaseAddr:指定DMA通道的存储器基地址
  • DMA_DIR:指定传输方向,可选值见 @ref DMA_data_transfer_direction
  • DMA_BufferSize:指定缓冲区大小,以数据单元为单位(1-65535)
  • DMA_PeripheralInc:外设地址是否递增,可选值见 @ref DMA_peripheral_incremented_mode
  • DMA_MemoryInc:存储器地址是否递增,可选值见 @ref DMA_memory_incremented_mode
  • DMA_PeripheralDataSize:外设数据宽度,可选值见 @ref DMA_peripheral_data_size
  • DMA_MemoryDataSize:存储器数据宽度,可选值见 @ref DMA_memory_data_size
  • DMA_Mode:传输模式,可选值见 @ref DMA_circular_normal_mode
  • DMA_Priority:通道优先级,可选值见 @ref DMA_priority_level
  • DMA_M2M:存储器到存储器传输使能,可选值见 @ref DMA_memory_to_memory

枚举类型定义

DMA传输方向

#define DMA_DIR_PeripheralDST    ((uint32_t)0x00000010)  /* 存储器到外设 */
#define DMA_DIR_PeripheralSRC    ((uint32_t)0x00000000)  /* 外设到存储器 */

说明:

  • DMA_DIR_PeripheralDST:数据从存储器传输到外设
  • DMA_DIR_PeripheralSRC:数据从外设传输到存储器

DMA外设地址递增模式

#define DMA_PeripheralInc_Enable     ((uint32_t)0x00000040)  /* 外设地址递增使能 */
#define DMA_PeripheralInc_Disable    ((uint32_t)0x00000000)  /* 外设地址递增禁用 */

DMA存储器地址递增模式

#define DMA_MemoryInc_Enable     ((uint32_t)0x00000080)  /* 存储器地址递增使能 */
#define DMA_MemoryInc_Disable    ((uint32_t)0x00000000)  /* 存储器地址递增禁用 */

DMA外设数据宽度

#define DMA_PeripheralDataSize_Byte      ((uint32_t)0x00000000)  /* 8位数据宽度 */
#define DMA_PeripheralDataSize_HalfWord  ((uint32_t)0x00000100)  /* 16位数据宽度 */
#define DMA_PeripheralDataSize_Word      ((uint32_t)0x00000200)  /* 32位数据宽度 */

DMA存储器数据宽度

#define DMA_MemoryDataSize_Byte      ((uint32_t)0x00000000)  /* 8位数据宽度 */
#define DMA_MemoryDataSize_HalfWord  ((uint32_t)0x00000400)  /* 16位数据宽度 */
#define DMA_MemoryDataSize_Word      ((uint32_t)0x00000800)  /* 32位数据宽度 */

DMA传输模式

#define DMA_Mode_Circular    ((uint32_t)0x00000020)  /* 循环传输模式 */
#define DMA_Mode_Normal      ((uint32_t)0x00000000)  /* 正常传输模式 */

说明:

  • DMA_Mode_Circular:循环传输模式,传输完成后自动重新开始
  • DMA_Mode_Normal:正常传输模式,传输完成后停止

DMA通道优先级

#define DMA_Priority_VeryHigh    ((uint32_t)0x00003000)  /* 非常高优先级 */
#define DMA_Priority_High        ((uint32_t)0x00002000)  /* 高优先级 */
#define DMA_Priority_Medium      ((uint32_t)0x00001000)  /* 中等优先级 */
#define DMA_Priority_Low         ((uint32_t)0x00000000)  /* 低优先级 */

DMA存储器到存储器模式

#define DMA_M2M_Enable     ((uint32_t)0x00004000)  /* 存储器到存储器模式使能 */
#define DMA_M2M_Disable    ((uint32_t)0x00000000)  /* 存储器到存储器模式禁用 */

DMA中断标志定义

基本中断类型

#define DMA_IT_TC    ((uint32_t)0x00000002)  /* 传输完成中断 */
#define DMA_IT_HT    ((uint32_t)0x00000004)  /* 半传输中断 */
#define DMA_IT_TE    ((uint32_t)0x00000008)  /* 传输错误中断 */

DMA1具体中断标志

#define DMA1_IT_GL1    ((uint32_t)0x00000001)  /* DMA1通道1全局中断 */
#define DMA1_IT_TC1    ((uint32_t)0x00000002)  /* DMA1通道1传输完成中断 */
#define DMA1_IT_HT1    ((uint32_t)0x00000004)  /* DMA1通道1半传输中断 */
#define DMA1_IT_TE1    ((uint32_t)0x00000008)  /* DMA1通道1传输错误中断 */
/* ... 其他通道中断标志 ... */

DMA2具体中断标志

#define DMA2_IT_GL1    ((uint32_t)0x10000001)  /* DMA2通道1全局中断 */
#define DMA2_IT_TC1    ((uint32_t)0x10000002)  /* DMA2通道1传输完成中断 */
#define DMA2_IT_HT1    ((uint32_t)0x10000004)  /* DMA2通道1半传输中断 */
#define DMA2_IT_TE1    ((uint32_t)0x10000008)  /* DMA2通道1传输错误中断 */
/* ... 其他通道中断标志 ... */

DMA状态标志定义

DMA1状态标志

#define DMA1_FLAG_GL1    ((uint32_t)0x00000001)  /* DMA1通道1全局标志 */
#define DMA1_FLAG_TC1    ((uint32_t)0x00000002)  /* DMA1通道1传输完成标志 */
#define DMA1_FLAG_HT1    ((uint32_t)0x00000004)  /* DMA1通道1半传输标志 */
#define DMA1_FLAG_TE1    ((uint32_t)0x00000008)  /* DMA1通道1传输错误标志 */
/* ... 其他通道状态标志 ... */

DMA2状态标志

#define DMA2_FLAG_GL1    ((uint32_t)0x10000001)  /* DMA2通道1全局标志 */
#define DMA2_FLAG_TC1    ((uint32_t)0x10000002)  /* DMA2通道1传输完成标志 */
#define DMA2_FLAG_HT1    ((uint32_t)0x10000004)  /* DMA2通道1半传输标志 */
#define DMA2_FLAG_TE1    ((uint32_t)0x10000008)  /* DMA2通道1传输错误标志 */
/* ... 其他通道状态标志 ... */

标准库函数详解

1. DMA_DeInit

/**
 * @brief  将 DMA 通道寄存器恢复为复位默认值,用于重新配置前清空旧传输状态
 * @param  DMAy_Channelx: 指定的DMA通道,可以是DMA1_Channel1到DMA1_Channel7或DMA2_Channel1到DMA2_Channel5
 * @retval
 * @example
 *     DMA_DeInit(DMA1_Channel1);  // 复位DMA1通道1
 */
void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);

功能说明:

  • 清除方向、地址、计数等全部通道配置
  • 切换传输源/目的或模式前先 DeInit
  • 传输异常后可重置通道再 Init

2. DMA_Init

/**
 * @brief  初始化 DMA 通道(源/目的地址、方向、宽度、模式等),建立外设与内存间的搬运路径
 * @param  DMAy_Channelx: 指定的DMA通道
 * @param  DMA_InitStruct: 指向DMA_InitTypeDef结构体的指针,包含配置信息
 * @retval
 * @example
 *     DMA_InitTypeDef DMA_InitStructure;
 *     DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
 *     DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)buffer;
 *     DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
 *     DMA_InitStructure.DMA_BufferSize = 100;
 *     DMA_Init(DMA1_Channel5, &DMA_InitStructure);
 */
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);

功能说明:

  • 配置外设到内存、内存到外设或内存到内存
  • 设置数据宽度、地址是否递增、循环/普通模式
  • 需在 DMA_Cmd 前完成,并匹配外设 DMA 请求线

3. DMA_StructInit

/**
 * @brief  将 DMA_InitTypeDef 成员填为安全默认值,避免未赋值字段导致随机配置
 * @param  DMA_InitStruct: 指向DMA_InitTypeDef结构体的指针
 * @retval
 * @example
 *     DMA_InitTypeDef DMA_InitStructure;
 *     DMA_StructInit(&DMA_InitStructure);
 */
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);

功能说明:

  • 提供合理的默认地址、宽度、优先级等
  • 先 StructInit 再按需修改各成员
  • 降低多字段 DMA 配置的遗漏风险

4. DMA_Cmd

/**
 * @brief  启动或停止 DMA 通道;ENABLE 后按配置自动搬运,DISABLE 后暂停
 * @param  DMAy_Channelx: 指定的DMA通道
 * @param  NewState: DMA通道的新状态,可以是ENABLE或DISABLE
 * @retval
 * @example
 *     DMA_Cmd(DMA1_Channel1, ENABLE);   // 启动DMA传输
 *     DMA_Cmd(DMA1_Channel1, DISABLE);  // 停止DMA传输
 */
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);

功能说明:

  • Init 完成且外设 DMA 请求使能后 ENABLE 开始传输
  • DISABLE 可中止当前传输(剩余计数停止递减)
  • 修改 CNDTR 前须先 DISABLE 通道

5. DMA_ITConfig

/**
 * @brief  打开/关闭 DMA 中断(传输完成/半传输/错误),配合 NVIC 获知搬运进度
 * @param  DMAy_Channelx: 指定的DMA通道
 * @param  DMA_IT: 指定的DMA中断源,可以是DMA_IT_TC、DMA_IT_HT、DMA_IT_TE的组合
 * @param  NewState: 中断的新状态,可以是ENABLE或DISABLE
 * @retval
 * @example
 *     DMA_ITConfig(DMA1_Channel1, DMA_IT_TC, ENABLE);  // 使能传输完成中断
 */
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);

功能说明:

  • TC:整缓冲传完,常用于通知应用处理数据
  • HT:半缓冲完成,适合双缓冲乒乓操作
  • TE:传输错误,需排查地址或总线问题

6. DMA_SetCurrDataCounter

/**
 * @brief  设置本次传输的数据单元个数,决定 DMA 搬多少数据后停止
 * @param  DMAy_Channelx: 指定的DMA通道
 * @param  DataNumber: 要传输的数据数量(1-65535)
 * @retval
 * @example
 *     DMA_SetCurrDataCounter(DMA1_Channel1, 1024);  // 设置传输1024个数据单元
 */
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber); 

功能说明:

  • 仅在通道 DISABLE 时写入 CNDTR
  • 重复启动传输前需重新设置计数
  • 单元大小由 Init 中的 Memory/Periph DataSize 决定

7. DMA_GetCurrDataCounter

/**
 * @brief  读取通道剩余未传输的数据单元数,用于监控进度或判断完成
 * @param  DMAy_Channelx: 指定的DMA通道
 * @retval 剩余的数据传输数量
 * @example
 *     uint16_t remaining = DMA_GetCurrDataCounter(DMA1_Channel1);
 */
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);

功能说明:

  • 传输进行中 CNDTR 递减,为 0 表示完成
  • 可计算已传输量 = 初始值 - 剩余值
  • 轮询等待完成时可替代中断

8. DMA_GetFlagStatus

/**
 * @brief  查询 DMA 标志位(TC/HT/TE),判断传输完成、半完成或错误
 * @param  DMAy_FLAG: 指定的DMA标志位
 * @retval FlagStatus: 标志位状态(SET或RESET)
 * @example
 *     if (DMA_GetFlagStatus(DMA1_FLAG_TC1) == SET) {
 *         // 传输完成处理
 *     }
 */
FlagStatus DMA_GetFlagStatus(uint32_t DMAy_FLAG);

功能说明:

  • 轮询模式下等待 TC 再处理缓冲
  • TE 置位表示传输错误
  • 标志与通道号对应(如 TC1 对应 Channel1)

9. DMA_ClearFlag

/**
 * @brief  清除 DMA 标志位,避免重复检测到同一完成/错误事件
 * @param  DMAy_FLAG: 指定要清除的DMA标志位
 * @retval
 * @example
 *     DMA_ClearFlag(DMA1_FLAG_TC1);  // 清除传输完成标志
 */
void DMA_ClearFlag(uint32_t DMAy_FLAG);

功能说明:

  • 轮询处理 TC/HT 后必须清除
  • 中断模式下也可配合使用
  • 未清除会导致误以为再次完成

10. DMA_GetITStatus

/**
 * @brief  在中断服务程序中判断具体 DMA 中断源是否挂起
 * @param  DMAy_IT: 指定的DMA中断标志
 * @retval ITStatus: 中断状态(SET或RESET)
 * @example
 *     if (DMA_GetITStatus(DMA1_IT_TC1) == SET) {
 *         // 处理传输完成中断
 *     }
 */
ITStatus DMA_GetITStatus(uint32_t DMAy_IT);

功能说明:

  • 检查使能位与挂起位组合
  • ISR 中区分 TC/HT/TE 分支
  • 比 GetFlagStatus 更适合中断上下文

11. DMA_ClearITPendingBit

/**
 * @brief  清除 DMA 中断挂起位,ISR 处理完毕后必须调用以免重复进中断
 * @param  DMAy_IT: 指定要清除的DMA中断挂起位
 * @retval
 * @example
 *     DMA_ClearITPendingBit(DMA1_IT_TC1);  // 清除传输完成中断挂起位
 */
void DMA_ClearITPendingBit(uint32_t DMAy_IT);

功能说明:

  • 处理完 TC/HT/TE 中断后调用
  • 未清除会导致中断反复触发
  • 与 ClearFlag 配合确保状态干净

使用示例

USART接收DMA配置示例

#include "stm32f10x.h"

#define BUFFER_SIZE 100
uint8_t rx_buffer[BUFFER_SIZE];

void DMA_USART_RX_Config(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    
    // 使能DMA1时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
    // 复位DMA通道
    DMA_DeInit(DMA1_Channel5);
    
    // 配置DMA初始化结构体
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)rx_buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = BUFFER_SIZE;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    
    // 初始化DMA通道
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);
    
    // 使能DMA传输完成中断
    DMA_ITConfig(DMA1_Channel5, DMA_IT_TC, ENABLE);
    
    // 启动DMA传输
    DMA_Cmd(DMA1_Channel5, ENABLE);
}

存储器到存储器传输示例

#define DATA_SIZE 1024
uint32_t src_buffer[DATA_SIZE];
uint32_t dst_buffer[DATA_SIZE];

void DMA_M2M_Transfer(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    
    // 使能DMA1时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
    // 复位DMA通道
    DMA_DeInit(DMA1_Channel1);
    
    // 配置存储器到存储器传输
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)src_buffer;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)dst_buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize = DATA_SIZE;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;
    
    // 初始化并启动DMA传输
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    DMA_Cmd(DMA1_Channel1, ENABLE);
    
    // 等待传输完成
    while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
    DMA_ClearFlag(DMA1_FLAG_TC1);
}

DMA 中断函数

DMA 本身没有统一的「外设级」中断入口;每个通道对应独立(或共享)的 NVIC IRQ。应用层在启动通道前需:

  1. DMA_ITConfig 打开 TC / HT / TE
  2. NVIC_Init 使能对应 DMAx_Channelx_IRQn
  3. DMAx_Channelx_IRQHandler 中用 DMA_GetITStatus 判断源,处理完后 DMA_ClearITPendingBit

通道与中断向量对照(STM32F10x)

DMA 通道NVIC 通道中断服务函数
DMA1 Channel1DMA1_Channel1_IRQnDMA1_Channel1_IRQHandler
DMA1 Channel2DMA1_Channel2_IRQnDMA1_Channel2_IRQHandler
DMA1 Channel3DMA1_Channel3_IRQnDMA1_Channel3_IRQHandler
DMA1 Channel4DMA1_Channel4_IRQnDMA1_Channel4_IRQHandler
DMA1 Channel5DMA1_Channel5_IRQnDMA1_Channel5_IRQHandler
DMA1 Channel6DMA1_Channel6_IRQnDMA1_Channel6_IRQHandler
DMA1 Channel7DMA1_Channel7_IRQnDMA1_Channel7_IRQHandler
DMA2 Channel1DMA2_Channel1_IRQnDMA2_Channel1_IRQHandler
DMA2 Channel2DMA2_Channel2_IRQnDMA2_Channel2_IRQHandler
DMA2 Channel3DMA2_Channel3_IRQnDMA2_Channel3_IRQHandler
DMA2 Channel4 / 5DMA2_Channel4_5_IRQnDMA2_Channel4_5_IRQHandler共用

注意:DMA2 的 Channel4 与 Channel5 共用一个 IRQ。进入 DMA2_Channel4_5_IRQHandler 后,必须分别用 DMA2_IT_TCx / DMA2_IT_HTx / DMA2_IT_TEx 判断是哪一路。

中断类型说明

中断宏(通道级)触发时机典型用途
TCDMA_IT_TC / DMAx_IT_TCn整缓冲传完(CNDTR→0)通知应用处理完整一帧
HTDMA_IT_HT / DMAx_IT_HTn传到一半(CNDTR = N/2)循环模式下乒乓双缓冲
TEDMA_IT_TE / DMAx_IT_TEn传输错误地址非法、总线故障等,需停通道排查
GLDMAx_IT_GLn全局(TC|HT|TE 任一)较少单独使用;清挂起时注意范围

对应 API 回顾:

步骤函数作用
使能中断源DMA_ITConfig(通道, DMA_IT_TC | DMA_IT_HT | DMA_IT_TE, ENABLE)打开通道 CCR 中的中断使能位
查询挂起DMA_GetITStatus(DMAx_IT_TCn)ISR 内判断具体中断源
清除挂起DMA_ClearITPendingBit(DMAx_IT_TCn)必须调用,否则会反复进中断
(可选)清标志DMA_ClearFlag(DMAx_FLAG_TCn)与挂起位对应的状态标志

NVIC 配置示例(USART1 RX → DMA1 Channel5)

/**
 * @brief  配置 DMA1_Channel5 的 NVIC,并打开 TC / HT / TE 中断
 * @note   须在 DMA_Init、DMA_Cmd 之前或之后均可,但 IRQHandler 生效前三者都要就绪:
 *         1) DMA_ITConfig  2) NVIC_Init  3) 外设侧 DMA 请求已使能
 */
void DMA1_Channel5_NVIC_Config(void)
{
    NVIC_InitTypeDef NVIC_InitStructure;

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

    /* 传输完成 + 半传输 + 错误;按需裁剪 */
    DMA_ITConfig(DMA1_Channel5, DMA_IT_TC | DMA_IT_HT | DMA_IT_TE, ENABLE);
}

中断服务函数:传输完成(TC)

/**
 * @brief  DMA1 通道5 中断服务函数(USART1 RX DMA 常用)
 * @note   1. 先 GetITStatus 判断源,再处理数据,最后 ClearITPendingBit
 *         2. ISR 尽量短:置标志 / 搬指针,耗时处理放到主循环或任务
 */
void DMA1_Channel5_IRQHandler(void)
{
    /* 传输完成:整缓冲已写满 */
    if (DMA_GetITStatus(DMA1_IT_TC5) == SET)
    {
        /* 普通模式:此时通道已停,可处理 rx_buffer 全部数据 */
        /* 循环模式:继续收,本帧数据可交给应用层 */
        // ProcessRxBuffer(rx_buffer, BUFFER_SIZE);

        DMA_ClearITPendingBit(DMA1_IT_TC5);
    }
}

中断服务函数:半传输 + 完成(乒乓缓冲)

循环模式下同时使能 HTTC,可实现「前半缓冲 / 后半缓冲」交替处理,降低延迟:

#define DMA_RX_BUF_SIZE  256
uint8_t dma_rx_buf[DMA_RX_BUF_SIZE];

/**
 * @brief  循环 DMA:半缓冲与全缓冲中断交替处理
 *         HT → 处理 buf[0 .. N/2-1]
 *         TC → 处理 buf[N/2 .. N-1]
 */
void DMA1_Channel5_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA1_IT_HT5) == SET)
    {
        /* 前半区已写满,后半区仍在接收 —— 尽快处理前半,勿阻塞过久 */
        // ProcessRxBuffer(&dma_rx_buf[0], DMA_RX_BUF_SIZE / 2);
        DMA_ClearITPendingBit(DMA1_IT_HT5);
    }

    if (DMA_GetITStatus(DMA1_IT_TC5) == SET)
    {
        /* 后半区已写满,下一轮又从前半开始写 */
        // ProcessRxBuffer(&dma_rx_buf[DMA_RX_BUF_SIZE / 2], DMA_RX_BUF_SIZE / 2);
        DMA_ClearITPendingBit(DMA1_IT_TC5);
    }

    if (DMA_GetITStatus(DMA1_IT_TE5) == SET)
    {
        /* 传输错误:停通道、查地址/对齐,必要时 DeInit 后重建 */
        DMA_Cmd(DMA1_Channel5, DISABLE);
        DMA_ClearITPendingBit(DMA1_IT_TE5);
        // DMA_ErrorHandler();
    }
}

中断服务函数:DMA2 Channel4/5 共用入口

/**
 * @brief  DMA2 通道4与通道5共用中断向量,必须分别判断
 */
void DMA2_Channel4_5_IRQHandler(void)
{
    /* ----- Channel4 ----- */
    if (DMA_GetITStatus(DMA2_IT_TC4) == SET)
    {
        // Channel4 传输完成处理
        DMA_ClearITPendingBit(DMA2_IT_TC4);
    }
    if (DMA_GetITStatus(DMA2_IT_TE4) == SET)
    {
        DMA_Cmd(DMA2_Channel4, DISABLE);
        DMA_ClearITPendingBit(DMA2_IT_TE4);
    }

    /* ----- Channel5 ----- */
    if (DMA_GetITStatus(DMA2_IT_TC5) == SET)
    {
        // Channel5 传输完成处理
        DMA_ClearITPendingBit(DMA2_IT_TC5);
    }
    if (DMA_GetITStatus(DMA2_IT_TE5) == SET)
    {
        DMA_Cmd(DMA2_Channel5, DISABLE);
        DMA_ClearITPendingBit(DMA2_IT_TE5);
    }
}

完整流程:USART1 DMA 接收 + 中断

#include "stm32f10x.h"
#include "misc.h"

#define RX_BUF_SIZE  128
uint8_t usart1_rx_buf[RX_BUF_SIZE];
volatile uint8_t usart1_rx_tc_flag = 0;  /* 主循环查询的完成标志 */

void USART1_DMA_RX_Init(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    DMA_DeInit(DMA1_Channel5);
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr     = (uint32_t)usart1_rx_buf;
    DMA_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_BufferSize         = RX_BUF_SIZE;
    DMA_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte;
    DMA_InitStructure.DMA_Mode               = DMA_Mode_Normal; /* 或 Circular */
    DMA_InitStructure.DMA_Priority           = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M                = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);

    /* 打开 DMA 通道中断源 */
    DMA_ITConfig(DMA1_Channel5, DMA_IT_TC | DMA_IT_TE, ENABLE);

    /* 配置 NVIC */
    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    DMA_Cmd(DMA1_Channel5, ENABLE);

    /* 外设侧:使能 USART1 DMA 接收请求(须已完成 USART 初始化) */
    USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
}

void DMA1_Channel5_IRQHandler(void)
{
    if (DMA_GetITStatus(DMA1_IT_TC5) == SET)
    {
        usart1_rx_tc_flag = 1;           /* 通知主循环 */
        DMA_ClearITPendingBit(DMA1_IT_TC5);
    }

    if (DMA_GetITStatus(DMA1_IT_TE5) == SET)
    {
        DMA_Cmd(DMA1_Channel5, DISABLE);
        DMA_ClearITPendingBit(DMA1_IT_TE5);
    }
}

/* 主循环中: */
/*
void loop(void)
{
    if (usart1_rx_tc_flag)
    {
        usart1_rx_tc_flag = 0;
        // 处理 usart1_rx_buf[0 .. RX_BUF_SIZE-1]

        // 普通模式需重新装载计数并再次启动:
        DMA_Cmd(DMA1_Channel5, DISABLE);
        DMA_SetCurrDataCounter(DMA1_Channel5, RX_BUF_SIZE);
        DMA_Cmd(DMA1_Channel5, ENABLE);
    }
}
*/

ISR 编写要点

  1. 先判后清GetITStatus → 业务处理 → ClearITPendingBit;遗漏清除会连续重入。
  2. ISR 要短:只置标志、换缓冲指针;协议解析、打印放到主循环。
  3. 普通模式重启:TC 后通道停止,再次传输前必须 DISABLESetCurrDataCounterENABLE
  4. 循环模式:一般不必重启通道;用 HT+TC 做乒乓即可。
  5. TE 处理:建议 DMA_Cmd(..., DISABLE),检查地址对齐与外设请求,再 DeInit/Init。
  6. 向量名勿写错:如 DMA1_Channel5_IRQHandler,须与启动文件中的弱符号一致,否则中断进不到用户函数。

注意事项

  1. 通道选择:不同外设对应特定的DMA通道,需要查阅参考手册确认正确的通道映射

  2. 数据对齐:确保外设和存储器的数据宽度配置正确,避免数据对齐错误

  3. 缓冲区大小:DMA_BufferSize的有效范围是1-65535,超出范围会导致不可预期的行为

  4. 循环模式限制:存储器到存储器传输不能使用循环模式

  5. 中断处理:使用中断时,必须在中断服务程序中清除相应的中断标志位(DMA_ClearITPendingBit);DMA2 Channel4/5 共用向量,须分别判断

  6. 优先级冲突:多个DMA通道同时工作时,硬件优先级为DMA1 > DMA2,通道号小的优先级高

  7. 地址递增:根据实际需求配置地址递增模式,FIFO类外设通常不递增地址

  8. 传输监控:可以通过DMA_GetCurrDataCounter()函数实时监控传输进度

总结

STM32F10x DMA外设提供了强大的数据传输功能,支持外设到存储器、存储器到外设以及存储器到存储器的传输。通过合理配置DMA参数,可以大大减轻CPU负担,提高系统整体性能。配合 TC / HT / TE 中断 与 NVIC,可在不阻塞主循环的情况下获知搬运进度;关键注意正确的通道向量、GetITStatus/ClearITPendingBit 配对,以及普通模式下的计数重装。