基于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程序实现了以下功能:
核心功能:
- 内存到内存DMA:高效的内存复制
- 外设到内存DMA:ADC数据采集
- 内存到外设DMA:USART、SPI数据传输
- 双缓冲DMA:实时数据处理
- DMA中断处理:传输完成和错误检测
优化特性:
- 性能测试:吞吐量测量
- 错误恢复:自动检测和恢复
- 调试工具:状态监控和信息输出
- 灵活配置:支持多种传输模式
应用场景:
- 高速数据采集(ADC)
- 大容量数据传输(USART/SPI)
- 实时信号处理
- 内存密集型操作
- 降低CPU负载