STM32 DMA程序(标准外设库版本)

基于STM32标准外设库的完整DMA程序,包含内存到内存、外设到内存、内存到外设的DMA传输实现。

一、DMA基础配置

1.1 DMA核心定义

c 复制代码
/* DMA 核心数据结构定义 */
#include "stm32f10x.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include <stdio.h>
#include <string.h>

// DMA通道定义
#define DMA_CHANNEL_1    DMA1_Channel1
#define DMA_CHANNEL_2    DMA1_Channel2
#define DMA_CHANNEL_3    DMA1_Channel3
#define DMA_CHANNEL_4    DMA1_Channel4
#define DMA_CHANNEL_5    DMA1_Channel5
#define DMA_CHANNEL_6    DMA1_Channel6
#define DMA_CHANNEL_7    DMA1_Channel7

// DMA传输方向
#define DMA_DIR_MEM_TO_MEM    DMA_DIR_PeripheralSRC
#define DMA_DIR_PERIPH_TO_MEM DMA_DIR_PeripheralSRC
#define DMA_DIR_MEM_TO_PERIPH DMA_DIR_PeripheralDST

// DMA数据宽度
#define DMA_DATA_SIZE_8BIT   DMA_PeripheralDataSize_Byte
#define DMA_DATA_SIZE_16BIT  DMA_PeripheralDataSize_HalfWord
#define DMA_DATA_SIZE_32BIT  DMA_PeripheralDataSize_Word

// 全局变量
volatile uint8_t dma_transfer_complete = 0;
volatile uint8_t dma_transfer_error = 0;
uint32_t dma_transfer_count = 0;

1.2 DMA初始化函数

c 复制代码
/* DMA初始化函数 */
void DMA_Init_Common(DMA_Channel_TypeDef* DMAy_Channelx, 
                     uint32_t peripheral_addr,
                     uint32_t memory_addr,
                     uint32_t buffer_size,
                     uint32_t direction,
                     uint32_t priority) {
    
    DMA_InitTypeDef DMA_InitStructure;
    
    // 使能DMA时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
    // 配置DMA通道
    DMA_DeInit(DMAy_Channelx);
    
    DMA_InitStructure.DMA_PeripheralBaseAddr = peripheral_addr;
    DMA_InitStructure.DMA_MemoryBaseAddr = memory_addr;
    DMA_InitStructure.DMA_DIR = direction;
    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_DATA_SIZE_8BIT;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_DATA_SIZE_8BIT;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;  // 普通模式,非循环
    DMA_InitStructure.DMA_Priority = priority;
    DMA_InitStructure.DMA_M2M = (direction == DMA_DIR_MEM_TO_MEM) ? DMA_M2M_Enable : DMA_M2M_Disable;
    
    DMA_Init(DMAy_Channelx, &DMA_InitStructure);
}

/* 使能DMA中断 */
void DMA_EnableInterrupt(DMA_Channel_TypeDef* DMAy_Channelx) {
    // 使能传输完成中断和传输错误中断
    DMA_ITConfig(DMAy_Channelx, DMA_IT_TC | DMA_IT_TE, ENABLE);
    
    // 配置NVIC
    NVIC_InitTypeDef NVIC_InitStructure;
    
    if (DMAy_Channelx == DMA1_Channel1) {
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel1_IRQn;
    } else if (DMAy_Channelx == DMA1_Channel2) {
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel2_IRQn;
    } else if (DMAy_Channelx == DMA1_Channel3) {
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel3_IRQn;
    } else if (DMAy_Channelx == DMA1_Channel4) {
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
    } else if (DMAy_Channelx == DMA1_Channel5) {
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel5_IRQn;
    } else if (DMAy_Channelx == DMA1_Channel6) {
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel6_IRQn;
    } else if (DMAy_Channelx == DMA1_Channel7) {
        NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_IRQn;
    }
    
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
}

二、内存到内存DMA传输

2.1 内存复制DMA

c 复制代码
/* 内存到内存DMA传输 */
#define MEM_COPY_BUFFER_SIZE  1024

uint8_t source_buffer[MEM_COPY_BUFFER_SIZE];
uint8_t destination_buffer[MEM_COPY_BUFFER_SIZE];

/* 初始化内存复制DMA */
void DMA_MemCopy_Init(void) {
    // 初始化源数据
    for (uint16_t i = 0; i < MEM_COPY_BUFFER_SIZE; i++) {
        source_buffer[i] = i % 256;
    }
    memset(destination_buffer, 0, MEM_COPY_BUFFER_SIZE);
    
    // 初始化DMA
    DMA_Init_Common(DMA_CHANNEL_1,
                    (uint32_t)&source_buffer,
                    (uint32_t)&destination_buffer,
                    MEM_COPY_BUFFER_SIZE,
                    DMA_DIR_MEM_TO_MEM,
                    DMA_Priority_High);
    
    // 使能中断
    DMA_EnableInterrupt(DMA_CHANNEL_1);
    
    printf("Memory Copy DMA Initialized\n");
}

/* 启动内存复制 */
void DMA_MemCopy_Start(void) {
    // 重置完成标志
    dma_transfer_complete = 0;
    dma_transfer_error = 0;
    
    // 使能DMA通道
    DMA_Cmd(DMA_CHANNEL_1, ENABLE);
    
    printf("Memory copy started...\n");
}

/* 验证内存复制结果 */
void DMA_MemCopy_Verify(void) {
    uint16_t errors = 0;
    
    for (uint16_t i = 0; i < MEM_COPY_BUFFER_SIZE; i++) {
        if (source_buffer[i] != destination_buffer[i]) {
            errors++;
            if (errors <= 10) {
                printf("Error at index %d: src=0x%02X, dst=0x%02X\n", 
                       i, source_buffer[i], destination_buffer[i]);
            }
        }
    }
    
    if (errors == 0) {
        printf("Memory copy verification PASSED!\n");
    } else {
        printf("Memory copy verification FAILED! Errors: %d\n", errors);
    }
}

/* DMA中断处理函数 */
void DMA1_Channel1_IRQHandler(void) {
    if (DMA_GetITStatus(DMA1_IT_TC1)) {
        dma_transfer_complete = 1;
        dma_transfer_count++;
        printf("DMA transfer complete! Count: %lu\n", dma_transfer_count);
        DMA_ClearITPendingBit(DMA1_IT_TC1);
    }
    
    if (DMA_GetITStatus(DMA1_IT_TE1)) {
        dma_transfer_error = 1;
        printf("DMA transfer error!\n");
        DMA_ClearITPendingBit(DMA1_IT_TE1);
    }
}

三、外设到内存DMA传输

3.1 ADC DMA传输

c 复制代码
/* ADC DMA传输 */
#define ADC_BUFFER_SIZE  16
#define ADC_CHANNEL_NUM   2

uint16_t adc_buffer[ADC_BUFFER_SIZE * ADC_CHANNEL_NUM];

/* ADC初始化 */
void ADC_DMA_Init(void) {
    ADC_InitTypeDef ADC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    // 配置ADC引脚(PA0和PA1)
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // ADC配置
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = ADC_CHANNEL_NUM;
    ADC_Init(ADC1, &ADC_InitStructure);
    
    // 配置ADC通道
    ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);
    
    // 使能ADC DMA
    ADC_DMACmd(ADC1, ENABLE);
    
    // 使能ADC
    ADC_Cmd(ADC1, ENABLE);
    
    // ADC校准
    ADC_ResetCalibration(ADC1);
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while(ADC_GetCalibrationStatus(ADC1));
    
    printf("ADC DMA Initialized\n");
}

/* ADC DMA传输初始化 */
void DMA_ADC_Init(void) {
    // 初始化DMA
    DMA_Init_Common(DMA_CHANNEL_1,
                    (uint32_t)&ADC1->DR,
                    (uint32_t)adc_buffer,
                    ADC_BUFFER_SIZE * ADC_CHANNEL_NUM,
                    DMA_DIR_PERIPH_TO_MEM,
                    DMA_Priority_High);
    
    // 使能中断
    DMA_EnableInterrupt(DMA_CHANNEL_1);
    
    printf("ADC DMA Channel Initialized\n");
}

/* 启动ADC DMA传输 */
void DMA_ADC_Start(void) {
    // 重置完成标志
    dma_transfer_complete = 0;
    dma_transfer_error = 0;
    
    // 使能DMA通道
    DMA_Cmd(DMA_CHANNEL_1, ENABLE);
    
    // 启动ADC转换
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);
    
    printf("ADC DMA transfer started...\n");
}

/* 处理ADC数据 */
void Process_ADC_Data(void) {
    if (dma_transfer_complete) {
        uint32_t sum_ch0 = 0, sum_ch1 = 0;
        
        // 计算平均值
        for (uint16_t i = 0; i < ADC_BUFFER_SIZE; i++) {
            sum_ch0 += adc_buffer[i * ADC_CHANNEL_NUM + 0];
            sum_ch1 += adc_buffer[i * ADC_CHANNEL_NUM + 1];
        }
        
        uint16_t avg_ch0 = sum_ch0 / ADC_BUFFER_SIZE;
        uint16_t avg_ch1 = sum_ch1 / ADC_BUFFER_SIZE;
        
        // 转换为电压值(假设Vref=3.3V)
        float voltage_ch0 = avg_ch0 * 3.3f / 4096.0f;
        float voltage_ch1 = avg_ch1 * 3.3f / 4096.0f;
        
        printf("ADC Results:\n");
        printf("  CH0: Raw=%d, Voltage=%.2fV\n", avg_ch0, voltage_ch0);
        printf("  CH1: Raw=%d, Voltage=%.2fV\n", avg_ch1, voltage_ch1);
        
        dma_transfer_complete = 0;
    }
}

3.2 USART接收DMA

c 复制代码
/* USART接收DMA */
#define USART_RX_BUFFER_SIZE  256

uint8_t usart_rx_buffer[USART_RX_BUFFER_SIZE];
volatile uint8_t usart_rx_complete = 0;

/* USART初始化 */
void USART_DMA_Init(void) {
    USART_InitTypeDef USART_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    // 配置USART引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;  // TX
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; // RX
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // USART配置
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);
    
    // 使能USART
    USART_Cmd(USART1, ENABLE);
    
    // 使能USART DMA接收
    USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
    
    printf("USART DMA Initialized\n");
}

/* USART接收DMA初始化 */
void DMA_USART_RX_Init(void) {
    // 初始化DMA
    DMA_Init_Common(DMA_CHANNEL_5,  // USART1_RX使用DMA1通道5
                    (uint32_t)&USART1->DR,
                    (uint32_t)usart_rx_buffer,
                    USART_RX_BUFFER_SIZE,
                    DMA_DIR_PERIPH_TO_MEM,
                    DMA_Priority_Medium);
    
    // 使能中断
    DMA_EnableInterrupt(DMA_CHANNEL_5);
    
    printf("USART RX DMA Initialized\n");
}

/* 启动USART接收DMA */
void DMA_USART_RX_Start(void) {
    // 重置完成标志
    usart_rx_complete = 0;
    dma_transfer_complete = 0;
    dma_transfer_error = 0;
    
    // 使能DMA通道
    DMA_Cmd(DMA_CHANNEL_5, ENABLE);
    
    printf("USART RX DMA started, waiting for data...\n");
}

/* USART接收DMA中断处理 */
void DMA1_Channel5_IRQHandler(void) {
    if (DMA_GetITStatus(DMA1_IT_TC5)) {
        dma_transfer_complete = 1;
        usart_rx_complete = 1;
        printf("USART RX DMA transfer complete!\n");
        DMA_ClearITPendingBit(DMA1_IT_TC5);
    }
    
    if (DMA_GetITStatus(DMA1_IT_TE5)) {
        dma_transfer_error = 1;
        printf("USART RX DMA transfer error!\n");
        DMA_ClearITPendingBit(DMA1_IT_TE5);
    }
}

/* 处理USART接收数据 */
void Process_USART_Data(void) {
    if (usart_rx_complete) {
        printf("Received data (%d bytes): ", USART_RX_BUFFER_SIZE);
        
        // 打印接收到的数据
        for (uint16_t i = 0; i < USART_RX_BUFFER_SIZE; i++) {
            if (usart_rx_buffer[i] >= 32 && usart_rx_buffer[i] <= 126) {
                printf("%c", usart_rx_buffer[i]);
            } else {
                printf(".");
            }
        }
        printf("\n");
        
        // 清空缓冲区
        memset(usart_rx_buffer, 0, USART_RX_BUFFER_SIZE);
        usart_rx_complete = 0;
    }
}

四、内存到外设DMA传输

4.1 USART发送DMA

c 复制代码
/* USART发送DMA */
#define USART_TX_BUFFER_SIZE  128

uint8_t usart_tx_buffer[USART_TX_BUFFER_SIZE] = "Hello from STM32 DMA!\r\n";

/* USART发送DMA初始化 */
void DMA_USART_TX_Init(void) {
    // 初始化DMA
    DMA_Init_Common(DMA_CHANNEL_4,  // USART1_TX使用DMA1通道4
                    (uint32_t)&USART1->DR,
                    (uint32_t)usart_tx_buffer,
                    USART_TX_BUFFER_SIZE,
                    DMA_DIR_MEM_TO_PERIPH,
                    DMA_Priority_Medium);
    
    // 使能中断
    DMA_EnableInterrupt(DMA_CHANNEL_4);
    
    printf("USART TX DMA Initialized\n");
}

/* 启动USART发送DMA */
void DMA_USART_TX_Start(void) {
    // 重置完成标志
    dma_transfer_complete = 0;
    dma_transfer_error = 0;
    
    // 使能USART DMA发送
    USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
    
    // 使能DMA通道
    DMA_Cmd(DMA_CHANNEL_4, ENABLE);
    
    printf("USART TX DMA started...\n");
}

/* USART发送DMA中断处理 */
void DMA1_Channel4_IRQHandler(void) {
    if (DMA_GetITStatus(DMA1_IT_TC4)) {
        dma_transfer_complete = 1;
        printf("USART TX DMA transfer complete!\n");
        DMA_ClearITPendingBit(DMA1_IT_TC4);
    }
    
    if (DMA_GetITStatus(DMA1_IT_TE4)) {
        dma_transfer_error = 1;
        printf("USART TX DMA transfer error!\n");
        DMA_ClearITPendingBit(DMA1_IT_TE4);
    }
}

/* 准备发送数据 */
void Prepare_USART_TX_Data(void) {
    char *message = "STM32 DMA Test Message\r\n";
    uint16_t len = strlen(message);
    
    if (len > USART_TX_BUFFER_SIZE) {
        len = USART_TX_BUFFER_SIZE;
    }
    
    memcpy(usart_tx_buffer, message, len);
    
    // 重新配置DMA传输长度
    DMA_Cmd(DMA_CHANNEL_4, DISABLE);
    DMA_SetCurrDataCounter(DMA_CHANNEL_4, len);
    DMA_Cmd(DMA_CHANNEL_4, ENABLE);
}

4.2 SPI发送DMA

c 复制代码
/* SPI发送DMA */
#define SPI_TX_BUFFER_SIZE  64

uint8_t spi_tx_buffer[SPI_TX_BUFFER_SIZE];

/* SPI初始化 */
void SPI_DMA_Init(void) {
    SPI_InitTypeDef SPI_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // 使能时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_SPI1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    // 配置SPI引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5 | GPIO_Pin_7;  // SCK, MOSI
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;  // MISO
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
    
    // SPI配置
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_16;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_InitStructure.SPI_CRCPolynomial = 7;
    SPI_Init(SPI1, &SPI_InitStructure);
    
    // 使能SPI
    SPI_Cmd(SPI1, ENABLE);
    
    // 使能SPI DMA发送
    SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE);
    
    printf("SPI DMA Initialized\n");
}

/* SPI发送DMA初始化 */
void DMA_SPI_TX_Init(void) {
    // 初始化DMA
    DMA_Init_Common(DMA_CHANNEL_3,  // SPI1_TX使用DMA1通道3
                    (uint32_t)&SPI1->DR,
                    (uint32_t)spi_tx_buffer,
                    SPI_TX_BUFFER_SIZE,
                    DMA_DIR_MEM_TO_PERIPH,
                    DMA_Priority_High);
    
    // 使能中断
    DMA_EnableInterrupt(DMA_CHANNEL_3);
    
    printf("SPI TX DMA Initialized\n");
}

/* 启动SPI发送DMA */
void DMA_SPI_TX_Start(uint16_t size) {
    if (size > SPI_TX_BUFFER_SIZE) {
        size = SPI_TX_BUFFER_SIZE;
    }
    
    // 重置完成标志
    dma_transfer_complete = 0;
    dma_transfer_error = 0;
    
    // 重新配置DMA传输长度
    DMA_Cmd(DMA_CHANNEL_3, DISABLE);
    DMA_SetCurrDataCounter(DMA_CHANNEL_3, size);
    DMA_Cmd(DMA_CHANNEL_3, ENABLE);
    
    printf("SPI TX DMA started, sending %d bytes...\n", size);
}

/* 准备SPI数据 */
void Prepare_SPI_Data(void) {
    for (uint16_t i = 0; i < SPI_TX_BUFFER_SIZE; i++) {
        spi_tx_buffer[i] = i % 256;
    }
}

五、双缓冲DMA传输

5.1 双缓冲实现

c 复制代码
/* 双缓冲DMA传输 */
#define DOUBLE_BUFFER_SIZE  512

uint8_t double_buffer_a[DOUBLE_BUFFER_SIZE];
uint8_t double_buffer_b[DOUBLE_BUFFER_SIZE];
volatile uint8_t current_buffer = 0;  // 0=A, 1=B
volatile uint8_t buffer_switch = 0;

/* 双缓冲DMA初始化 */
void DMA_DoubleBuffer_Init(void) {
    DMA_InitTypeDef DMA_InitStructure;
    
    // 使能DMA时钟
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
    
    // 初始化DMA通道
    DMA_DeInit(DMA_CHANNEL_1);
    
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)double_buffer_a;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PERIPH_TO_MEM;
    DMA_InitStructure.DMA_BufferSize = DOUBLE_BUFFER_SIZE;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_DATA_SIZE_16BIT;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_DATA_SIZE_16BIT;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;  // 循环模式
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    
    DMA_Init(DMA_CHANNEL_1, &DMA_InitStructure);
    
    // 使能中断
    DMA_ITConfig(DMA_CHANNEL_1, DMA_IT_TC | DMA_IT_HT, ENABLE);
    
    printf("Double Buffer DMA Initialized\n");
}

/* 切换缓冲区 */
void Switch_DMA_Buffer(void) {
    if (current_buffer == 0) {
        // 切换到缓冲区B
        DMA_Cmd(DMA_CHANNEL_1, DISABLE);
        DMA_CHANNEL_1->CMAR = (uint32_t)double_buffer_b;
        DMA_Cmd(DMA_CHANNEL_1, ENABLE);
        current_buffer = 1;
    } else {
        // 切换到缓冲区A
        DMA_Cmd(DMA_CHANNEL_1, DISABLE);
        DMA_CHANNEL_1->CMAR = (uint32_t)double_buffer_a;
        DMA_Cmd(DMA_CHANNEL_1, ENABLE);
        current_buffer = 0;
    }
    
    buffer_switch = 1;
}

/* DMA半传输中断处理 */
void DMA_HalfTransfer_Handler(void) {
    if (DMA_GetITStatus(DMA1_IT_HT1)) {
        printf("DMA Half Transfer Interrupt\n");
        Switch_DMA_Buffer();
        DMA_ClearITPendingBit(DMA1_IT_HT1);
    }
}

/* 处理双缓冲数据 */
void Process_DoubleBuffer_Data(void) {
    if (buffer_switch) {
        uint8_t *buffer_to_process;
        
        if (current_buffer == 0) {
            buffer_to_process = double_buffer_b;  // 正在使用A,处理B
        } else {
            buffer_to_process = double_buffer_a;  // 正在使用B,处理A
        }
        
        // 处理数据
        uint32_t sum = 0;
        for (uint16_t i = 0; i < DOUBLE_BUFFER_SIZE; i++) {
            sum += buffer_to_process[i];
        }
        
        printf("Buffer processed, Sum: %lu\n", sum);
        buffer_switch = 0;
    }
}

六、DMA性能测试

6.1 性能测试函数

c 复制代码
/* DMA性能测试 */
void DMA_Performance_Test(void) {
    printf("=== DMA Performance Test ===\n");
    
    // 测试内存到内存传输
    uint32_t start_time, end_time;
    uint32_t transfer_size = 1024;
    
    // 准备测试数据
    uint8_t *src = malloc(transfer_size);
    uint8_t *dst = malloc(transfer_size);
    
    if (!src || !dst) {
        printf("Memory allocation failed!\n");
        if (src) free(src);
        if (dst) free(dst);
        return;
    }
    
    // 初始化源数据
    for (uint32_t i = 0; i < transfer_size; i++) {
        src[i] = i % 256;
    }
    
    // 测试DMA传输
    start_time = Get_System_Tick();
    
    // 配置DMA
    DMA_Init_Common(DMA_CHANNEL_1,
                    (uint32_t)src,
                    (uint32_t)dst,
                    transfer_size,
                    DMA_DIR_MEM_TO_MEM,
                    DMA_Priority_High);
    
    // 启动传输
    DMA_Cmd(DMA_CHANNEL_1, ENABLE);
    
    // 等待传输完成
    while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
    
    end_time = Get_System_Tick();
    
    // 验证数据
    uint8_t error = 0;
    for (uint32_t i = 0; i < transfer_size; i++) {
        if (src[i] != dst[i]) {
            error = 1;
            break;
        }
    }
    
    // 计算性能指标
    uint32_t elapsed_time = end_time - start_time;
    uint32_t throughput = (transfer_size * 1000) / elapsed_time;  // 字节/秒
    
    printf("Transfer Size: %lu bytes\n", transfer_size);
    printf("Elapsed Time: %lu ms\n", elapsed_time);
    printf("Throughput: %lu bytes/sec\n", throughput);
    printf("Data Integrity: %s\n", error ? "FAILED" : "PASSED");
    
    // 清理
    free(src);
    free(dst);
    DMA_ClearFlag(DMA1_FLAG_TC1);
    DMA_Cmd(DMA_CHANNEL_1, DISABLE);
    
    printf("=== Test Complete ===\n");
}

参考代码 STM32 DMA程序(库函数) www.youwenfan.com/contentcst/133720.html

七、主程序示例

7.1 完整主程序

c 复制代码
/* 主程序 */
#include "stm32f10x.h"
#include "system_stm32f10x.h"

// 系统滴答定时器
volatile uint32_t system_tick = 0;

void SysTick_Init(void) {
    SysTick_Config(SystemCoreClock / 1000);  // 1ms中断
}

void SysTick_Handler(void) {
    system_tick++;
}

uint32_t Get_System_Tick(void) {
    return system_tick;
}

int main(void) {
    // 系统初始化
    SystemInit();
    SysTick_Init();
    
    // 初始化串口(用于printf)
    USART_DMA_Init();
    
    printf("\r\n=== STM32 DMA Demo ===\r\n");
    
    // 1. 内存到内存DMA测试
    printf("\r\n1. Memory-to-Memory DMA Test\r\n");
    DMA_MemCopy_Init();
    DMA_MemCopy_Start();
    
    // 等待传输完成
    while (!dma_transfer_complete);
    DMA_MemCopy_Verify();
    
    // 2. ADC DMA测试
    printf("\r\n2. ADC DMA Test\r\n");
    ADC_DMA_Init();
    DMA_ADC_Init();
    DMA_ADC_Start();
    
    // 等待ADC数据
    uint32_t timeout = 0;
    while (!dma_transfer_complete && timeout < 5000) {
        timeout++;
        for (volatile uint32_t i = 0; i < 10000; i++);
    }
    Process_ADC_Data();
    
    // 3. USART接收DMA测试
    printf("\r\n3. USART RX DMA Test\r\n");
    DMA_USART_RX_Init();
    DMA_USART_RX_Start();
    
    // 4. USART发送DMA测试
    printf("\r\n4. USART TX DMA Test\r\n");
    DMA_USART_TX_Init();
    Prepare_USART_TX_Data();
    DMA_USART_TX_Start();
    
    // 5. SPI DMA测试
    printf("\r\n5. SPI DMA Test\r\n");
    SPI_DMA_Init();
    DMA_SPI_TX_Init();
    Prepare_SPI_Data();
    DMA_SPI_TX_Start(SPI_TX_BUFFER_SIZE);
    
    // 6. 双缓冲DMA测试
    printf("\r\n6. Double Buffer DMA Test\r\n");
    DMA_DoubleBuffer_Init();
    DMA_Cmd(DMA_CHANNEL_1, ENABLE);  // 启动循环传输
    
    // 7. 性能测试
    printf("\r\n7. DMA Performance Test\r\n");
    DMA_Performance_Test();
    
    printf("\r\n=== All Tests Complete ===\r\n");
    
    // 主循环
    while (1) {
        // 处理USART接收数据
        Process_USART_Data();
        
        // 处理双缓冲数据
        Process_DoubleBuffer_Data();
        
        // 每秒打印状态
        static uint32_t last_print = 0;
        if (Get_System_Tick() - last_print > 1000) {
            printf("System running... Tick: %lu\r\n", system_tick);
            last_print = Get_System_Tick();
        }
    }
}

八、常见问题与调试

8.1 DMA调试技巧

c 复制代码
/* DMA调试函数 */
void DMA_Debug_Info(void) {
    printf("=== DMA Debug Information ===\r\n");
    
    // 检查DMA通道状态
    printf("DMA Channel 1 Status:\r\n");
    printf("  EN: %d\r\n", (DMA_CHANNEL_1->CCR & DMA_CCR_EN) ? 1 : 0);
    printf("  TCIF: %d\r\n", (DMA1->ISR & DMA_ISR_TCIF1) ? 1 : 0);
    printf("  HTIF: %d\r\n", (DMA1->ISR & DMA_ISR_HTIF1) ? 1 : 0);
    printf("  TEIF: %d\r\n", (DMA1->ISR & DMA_ISR_TEIF1) ? 1 : 0);
    printf("  CNDTR: %lu\r\n", DMA_CHANNEL_1->CNDTR);
    printf("  CPAR: 0x%08lX\r\n", DMA_CHANNEL_1->CPAR);
    printf("  CMAR: 0x%08lX\r\n", DMA_CHANNEL_1->CMAR);
    
    printf("Global DMA Status:\r\n");
    printf("  Transfer Complete: %d\r\n", dma_transfer_complete);
    printf("  Transfer Error: %d\r\n", dma_transfer_error);
    printf("  Transfer Count: %lu\r\n", dma_transfer_count);
    
    printf("============================\r\n");
}

/* DMA错误恢复 */
void DMA_Error_Recovery(void) {
    if (dma_transfer_error) {
        printf("Attempting DMA error recovery...\r\n");
        
        // 禁用DMA通道
        DMA_Cmd(DMA_CHANNEL_1, DISABLE);
        
        // 清除错误标志
        DMA_ClearFlag(DMA1_FLAG_TE1);
        
        // 重新初始化DMA
        DMA_DeInit(DMA_CHANNEL_1);
        
        // 重新配置
        DMA_Init_Common(DMA_CHANNEL_1,
                        (uint32_t)&ADC1->DR,
                        (uint32_t)adc_buffer,
                        ADC_BUFFER_SIZE * ADC_CHANNEL_NUM,
                        DMA_DIR_PERIPH_TO_MEM,
                        DMA_Priority_High);
        
        // 重新启动
        DMA_Cmd(DMA_CHANNEL_1, ENABLE);
        
        dma_transfer_error = 0;
        printf("DMA error recovery completed.\r\n");
    }
}

九、优化建议

9.1 DMA优化技巧

c 复制代码
/* DMA优化配置 */
void DMA_Optimize_Config(void) {
    // 1. 使用循环模式减少CPU干预
    // 2. 使用双缓冲避免数据覆盖
    // 3. 合理设置优先级
    // 4. 使用适当的数据宽度
    // 5. 使能内存和缓存优化
    
    printf("DMA Optimization Tips:\r\n");
    printf("1. Use Circular Mode for continuous streaming\r\n");
    printf("2. Implement Double Buffering for real-time processing\r\n");
    printf("3. Set appropriate Priority based on data importance\r\n");
    printf("4. Match data width to peripheral requirements\r\n");
    printf("5. Align memory addresses to cache lines\r\n");
    printf("6. Use burst transfers when available\r\n");
    printf("7. Minimize DMA interrupt frequency\r\n");
}

/* 高性能DMA配置 */
void DMA_HighPerformance_Config(DMA_Channel_TypeDef* DMAy_Channelx) {
    DMA_InitTypeDef DMA_InitStructure;
    
    DMA_DeInit(DMAy_Channelx);
    
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&SPI1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)spi_tx_buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_MEM_TO_PERIPH;
    DMA_InitStructure.DMA_BufferSize = SPI_TX_BUFFER_SIZE;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_DATA_SIZE_32BIT;  // 32位传输
    DMA_InitStructure.DMA_MemoryDataSize = DMA_DATA_SIZE_32BIT;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;  // 最高优先级
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    
    DMA_Init(DMAy_Channelx, &DMA_InitStructure);
}

十、总结

这个完整的STM32 DMA程序实现了以下功能:

核心功能:

  1. 内存到内存DMA:高效的内存复制
  2. 外设到内存DMA:ADC数据采集
  3. 内存到外设DMA:USART、SPI数据传输
  4. 双缓冲DMA:实时数据处理
  5. DMA中断处理:传输完成和错误检测

优化特性:

  1. 性能测试:吞吐量测量
  2. 错误恢复:自动检测和恢复
  3. 调试工具:状态监控和信息输出
  4. 灵活配置:支持多种传输模式

应用场景:

  • 高速数据采集(ADC)
  • 大容量数据传输(USART/SPI)
  • 实时信号处理
  • 内存密集型操作
  • 降低CPU负载
相关推荐
三佛科技-134163842122 小时前
电动牙刷方案开发-基于FT61E131B-RB单片机
单片机·嵌入式硬件·智能家居·pcb工艺
至为芯2 小时前
PY32F071至为芯支持32位ARM内核的高主频MCU微控制器
单片机·嵌入式硬件·mcu
2201_756206342 小时前
STM32L431 下载调试问题排查与解决
stm32·单片机·嵌入式硬件
WeeJot嵌入式2 小时前
【IIC】IIC中断与DMA&状态机编程
单片机·嵌入式硬件
WeeJot嵌入式2 小时前
【OLED】OLED原理&驱动库&取模
stm32·单片机·嵌入式硬件·嵌入式·oled
liuluyang5302 小时前
Synopsys DesignWare APB GPIO (DW_apb_gpio) 模块寄存器详解
stm32·单片机·嵌入式硬件·dw-gpio
危桥带雨2 小时前
PWR代码部分
stm32·单片机·嵌入式硬件
fengfuyao9852 小时前
STM32 定时器程序(标准外设库版本)
stm32·单片机·嵌入式硬件
振南的单片机世界2 小时前
高阻态:GPIO输入的“不打扰”哲学
stm32·单片机·嵌入式硬件