一、 DMA 简介
- DMA,即Direct Memory Access(直接存储区访问),是一种能够实现在外设寄存器与存储器之间或存储器与存储器之间高速传输数据的方法。
- DMA传输数据时,不需要任何CPU操作控制。
二、 DMA 工作模式
- STM32F4中DMA的工作模式分为外设到存储器传输、存储器到外设传输、存储器到存储器传输三种。
- 这里的外设通常指外设的数据寄存器,如USART、ADC、SPI、I2C等外设的数据寄存器。存储器通常指片内SRAM、片内Flash、片外存储器等。
外设到存储器传输 | 把外设数据寄存器中的数据传输至指定的内存空间 |
存储器到外设传输 | 把特定存储区内的数据传输至外设的数据寄存器,多用于外设的发送通信 |
存储器到存储器传输 | 把指定存储区的内容拷贝到另一个存储区空间 |
二、 DMA 功能框图
- STM32F4有两个DMA控制器,即DMA1和DMA2。
1. 数据流与通道
- 每个DMA控制器都有8个数据流,每个数据流都有8个外设通道。
- 在实现DMA传输前,DMA控制器会通过DMA数据流x配置寄存器DMA_SxCR(x为0~7,对应8个DMA数据流)的CHSEL[2:0]位确定一个外设通道:
- 选择外设通道的意义在于确定该数据流的源地址或目标地址。
- DMA1和DMA2的请求映射参考STM32F4xx中文参考手册
2.仲裁器
- 仲裁器用于裁决多个数据流的优先级。
- 仲裁器管理数据流分为软件阶段和硬件阶段。
- 软件阶段即通过寄存器配置数据流的优先级。具体涉及DMA_SxCR寄存器的PL[1:0]位,可设为非常高、高、中和低四个级别:
- 若两个及以上的数据流优先级软件设置一样,则通过数据流编号取决优先级,编号越低优先级越高,即数据流2的优先级高于数据流3。
3.FIFO
- 每个数据流都独立拥有四级32位FIFO(先进先出存储器缓冲区),即大小为4个字。
- DMA传输具有直接模式和FIFO模式。
直接模式 | 立即响应DMA传输请求,把数据传过去 |
FIFO模式 | 临时存储数据,待数据量达到阈值级别时,再发送数据 |
- FIFO模式在源地址和目标地址数据宽度不同的情况下十分有用。如源数据是源源不断的字节数据,目标地址要求输出字宽度的数据,此时可以使用FIFO功能先把数据缓存起来,然后根据需要输出数据。
- FIFO 模式也可用于突发(burst)传输。
四、 应用
目标1:通过 DMA 把 STM32 内部 FLASH 的数据传输到内部 SRAM
#include "dma.h"
const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {
0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80};
uint32_t aDST_Buffer[BUFFER_SIZE];
void MtoM_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
__IO uint32_t Timeout = TIMEOUT_MAX;
RCC_AHB1PeriphClockCmd(DMA_STREAM_CLOCK, ENABLE);
DMA_DeInit(DMA_STREAM);
while(DMA_GetCmdStatus(DMA_STREAM) != DISABLE);
DMA_InitStructure.DMA_Channel = DMA_CHANNEL;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) aSRC_Const_Buffer;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) aDST_Buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;
DMA_InitStructure.DMA_BufferSize = (uint32_t)BUFFER_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_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA_STREAM, &DMA_InitStructure);
DMA_Cmd(DMA_STREAM, ENABLE);
Timeout = TIMEOUT_MAX;
while ((DMA_GetCmdStatus(DMA_STREAM) != ENABLE) && (Timeout-- > 0));
if (Timeout == 0)
{
while (1)
{
LED0_TOGGLE;
delay_ms(500);
}
}
}
uint8_t Buffercmp(const uint32_t* pBuffer1,
uint32_t* pBuffer2, uint16_t BufferLength)
{
while(BufferLength--)
{
if(*pBuffer1 != *pBuffer2)
{
return 0;
}
pBuffer1++;
pBuffer2++;
}
return 1;
}
#ifndef __DMA_H
#define __DMA_H
#include "stm32f4xx.h"
#include "delay.h"
#include "led.h"
#define DMA_STREAM DMA2_Stream0
#define DMA_CHANNEL DMA_Channel_0
#define DMA_STREAM_CLOCK RCC_AHB1Periph_DMA2
#define DMA_FLAG_TCIF DMA_FLAG_TCIF0
#define BUFFER_SIZE 32
#define TIMEOUT_MAX 10000
extern const uint32_t aSRC_Const_Buffer[BUFFER_SIZE];
extern uint32_t aDST_Buffer[BUFFER_SIZE];
void MtoM_DMA_Config(void);
uint8_t Buffercmp(const uint32_t* pBuffer1,
uint32_t* pBuffer2, uint16_t BufferLength);
#endif
#include "stm32f4xx.h"
#include "dma.h"
#include "usart.h"
int main(void)
{
uint8_t TransferStatus;
LED_Config();
USART1_Config();
MtoM_DMA_Config();
while (DMA_GetFlagStatus(DMA_STREAM, DMA_FLAG_TCIF)==DISABLE);
TransferStatus = Buffercmp(aSRC_Const_Buffer, aDST_Buffer, BUFFER_SIZE);
if( TransferStatus == 0)
{
printf("数据发送错误~");
}
else
{
printf("数据发送无误~");
}
while(1)
{
}
}
目标2:通过 DMA 把 STM32 内部 SRAM 的数据传输到 USART2, 再通过 USART2 接收中断把数据转发到上位机。
#include "dma.h"
const uint32_t aSRC_Const_Buffer[BUFFER_SIZE]= {
0x01020304,0x05060708,0x090A0B0C,0x0D0E0F10,
0x11121314,0x15161718,0x191A1B1C,0x1D1E1F20,
0x21222324,0x25262728,0x292A2B2C,0x2D2E2F30,
0x31323334,0x35363738,0x393A3B3C,0x3D3E3F40,
0x41424344,0x45464748,0x494A4B4C,0x4D4E4F50,
0x51525354,0x55565758,0x595A5B5C,0x5D5E5F60,
0x61626364,0x65666768,0x696A6B6C,0x6D6E6F70,
0x71727374,0x75767778,0x797A7B7C,0x7D7E7F80};
uint32_t aDST_Buffer[BUFFER_SIZE];
uint8_t SendBuff[SENDBUFF_SIZE];
void MtoM_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
__IO uint32_t Timeout = TIMEOUT_MAX;
RCC_AHB1PeriphClockCmd(DMA_STREAM_CLOCK, ENABLE);
DMA_DeInit(DMA_STREAM);
while(DMA_GetCmdStatus(DMA_STREAM) != DISABLE);
DMA_InitStructure.DMA_Channel = DMA_CHANNEL;
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) aSRC_Const_Buffer;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) aDST_Buffer;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToMemory;
DMA_InitStructure.DMA_BufferSize = (uint32_t)BUFFER_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_High;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(DMA_STREAM, &DMA_InitStructure);
DMA_Cmd(DMA_STREAM, ENABLE);
Timeout = TIMEOUT_MAX;
while ((DMA_GetCmdStatus(DMA_STREAM) != ENABLE) && (Timeout-- > 0));
if (Timeout == 0)
{
while (1)
{
LED0_TOGGLE;
delay_ms(500);
}
}
}
void PtoM_DMA_Config(void)
{
DMA_InitTypeDef DMA_InitStructure;
__IO uint32_t Timeout = TIMEOUT_MAX;
RCC_AHB1PeriphClockCmd(USART2_DMA_CLK, ENABLE);
DMA_DeInit(USART2_DMA_STREAM);
while(DMA_GetCmdStatus(USART2_DMA_STREAM) != DISABLE);
DMA_InitStructure.DMA_Channel = USART2_DMA_CHANNEL;
DMA_InitStructure.DMA_PeripheralBaseAddr = USART2_DR_BASE;
DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) SendBuff;
DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
DMA_InitStructure.DMA_BufferSize = (uint32_t)SENDBUFF_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;
DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
DMA_Init(USART2_DMA_STREAM, &DMA_InitStructure);
DMA_Cmd(USART2_DMA_STREAM, ENABLE);
Timeout = TIMEOUT_MAX;
while ((DMA_GetCmdStatus(USART2_DMA_STREAM) != ENABLE) && (Timeout-- > 0));
if (Timeout == 0)
{
while (1)
{
LED0_TOGGLE;
delay_ms(500);
}
}
}
uint8_t Buffercmp(const uint32_t* pBuffer1,
uint32_t* pBuffer2, uint16_t BufferLength)
{
while(BufferLength--)
{
if(*pBuffer1 != *pBuffer2)
{
return 0;
}
pBuffer1++;
pBuffer2++;
}
return 1;
}
#ifndef __DMA_H
#define __DMA_H
#include "stm32f4xx.h"
#include "delay.h"
#include "led.h"
#define DMA_STREAM DMA2_Stream0
#define DMA_CHANNEL DMA_Channel_0
#define DMA_STREAM_CLOCK RCC_AHB1Periph_DMA2
#define DMA_FLAG_TCIF DMA_FLAG_TCIF0
#define BUFFER_SIZE 32
#define TIMEOUT_MAX 10000
#define USART2_DR_BASE (USART2_BASE+0x04)
#define SENDBUFF_SIZE 5000
#define USART2_DMA_CLK RCC_AHB1Periph_DMA1
#define USART2_DMA_CHANNEL DMA_Channel_4
#define USART2_DMA_STREAM DMA1_Stream6
extern const uint32_t aSRC_Const_Buffer[BUFFER_SIZE];
extern uint32_t aDST_Buffer[BUFFER_SIZE];
extern uint8_t SendBuff[SENDBUFF_SIZE];
void MtoM_DMA_Config(void);
void PtoM_DMA_Config(void);
uint8_t Buffercmp(const uint32_t* pBuffer1,
uint32_t* pBuffer2, uint16_t BufferLength);
#endif
#include "usart.h"
static void USART1_NVIC_Config(void);
static void USART2_NVIC_Config(void);
void USART1_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_AHB1PeriphClockCmd(USART1_TX_CLK | USART1_RX_CLK, ENABLE);
RCC_APB2PeriphClockCmd(USART1_CLK, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed;
GPIO_InitStructure.GPIO_Pin = USART1_TX_PIN;
GPIO_Init(USART1_TX_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = USART1_RX_PIN;
GPIO_Init(USART1_RX_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(USART1_TX_PORT, USART1_TX_SOURCE, USART1_TX_AF);
GPIO_PinAFConfig(USART1_RX_PORT, USART1_RX_SOURCE, USART1_RX_AF);
USART_InitStructure.USART_BaudRate = USART1_BAUDRATE;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
USART1_NVIC_Config();
USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
USART_Cmd(USART1, ENABLE);
}
void USART2_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_AHB1PeriphClockCmd(USART2_TX_CLK | USART2_RX_CLK, ENABLE);
RCC_APB1PeriphClockCmd(USART2_CLK, ENABLE);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed;
GPIO_InitStructure.GPIO_Pin = USART2_TX_PIN;
GPIO_Init(USART2_TX_PORT, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = USART2_RX_PIN;
GPIO_Init(USART2_RX_PORT, &GPIO_InitStructure);
GPIO_PinAFConfig(USART2_TX_PORT, USART2_TX_SOURCE, USART2_TX_AF);
GPIO_PinAFConfig(USART2_RX_PORT, USART2_RX_SOURCE, USART2_RX_AF);
USART_InitStructure.USART_BaudRate = USART2_BAUDRATE;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART2, &USART_InitStructure);
USART2_NVIC_Config();
USART_ITConfig(USART2, USART_IT_RXNE, DISABLE);
USART_Cmd(USART2, ENABLE);
}
void USART1_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannel = USART1_EXTI_IRQ;
NVIC_Init(&NVIC_InitStructure);
}
void USART2_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannel = USART2_EXTI_IRQ;
NVIC_Init(&NVIC_InitStructure);
}
void Usart_SendByte(USART_TypeDef* USARTx, uint8_t data)
{
USART_SendData(USARTx, data);
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
}
void Usart_SendHalfWord(USART_TypeDef* USARTx, uint16_t data)
{
uint8_t temp_h, temp_l;
temp_h = (data & 0XFF00)>>8;
temp_l = data & 0XFF;
USART_SendData(USARTx,temp_h);
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
USART_SendData(USARTx,temp_l);
while (USART_GetFlagStatus(USARTx, USART_FLAG_TXE) == RESET);
}
void Usart_SendString(USART_TypeDef * USARTx, char *str)
{
unsigned int k=0;
do
{
Usart_SendByte(USARTx, *(str + k) );
k++;
} while( *(str + k) != '\0' );
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET);
}
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, (uint8_t) ch);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return (ch);
}
int fgetc(FILE *f)
{
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(USART1);
}
#ifndef __UART_H
#define __UART_H
#include <stdio.h>
#include "stm32f4xx.h"
#define USART1_BAUDRATE 115200
#define USART1_CLK RCC_APB2Periph_USART1
#define USART1_EXTI_IRQ USART1_IRQn
#define USART1_IRQHandler USART1_IRQHandler
#define USART1_TX_CLK RCC_AHB1Periph_GPIOA
#define USART1_TX_PORT GPIOA
#define USART1_TX_PIN GPIO_Pin_9
#define USART1_TX_SOURCE GPIO_PinSource9
#define USART1_TX_AF GPIO_AF_USART1
#define USART1_RX_CLK RCC_AHB1Periph_GPIOA
#define USART1_RX_PORT GPIOA
#define USART1_RX_PIN GPIO_Pin_10
#define USART1_RX_SOURCE GPIO_PinSource10
#define USART1_RX_AF GPIO_AF_USART1
#define USART2_BAUDRATE 115200
#define USART2_CLK RCC_APB1Periph_USART2
#define USART2_EXTI_IRQ USART2_IRQn
#define USART2_IRQHandler USART2_IRQHandler
#define USART2_TX_CLK RCC_AHB1Periph_GPIOA
#define USART2_TX_PORT GPIOA
#define USART2_TX_PIN GPIO_Pin_2
#define USART2_TX_SOURCE GPIO_PinSource2
#define USART2_TX_AF GPIO_AF_USART2
#define USART2_RX_CLK RCC_AHB1Periph_GPIOA
#define USART2_RX_PORT GPIOA
#define USART2_RX_PIN GPIO_Pin_3
#define USART2_RX_SOURCE GPIO_PinSource3
#define USART2_RX_AF GPIO_AF_USART2
void USART1_Config(void);
void USART2_Config(void);
void Usart_SendByte(USART_TypeDef* USARTx, uint8_t data);
void Usart_SendHalfWord(USART_TypeDef* USARTx, uint16_t data);
void Usart_SendString(USART_TypeDef * USARTx, char *str);
#endif
- stm32f4xx_it.c添加 usart2 的中断接收服务函数
void USART2_IRQHandler(void)
{
uint8_t ucTemp;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
{
ucTemp = USART_ReceiveData(USART2);
USART_SendData(USART2, ucTemp);
}
}
#include "stm32f4xx.h"
#include "usart.h"
#include "delay.h"
#include "led.h"
#include "dma.h"
int main(void)
{
uint16_t i;
LED_Config();
USART2_Config();
PtoM_DMA_Config();
for(i = 0; i < SENDBUFF_SIZE; i++)
{
SendBuff[i] = 'Q';
}
USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);
while(1)
{
delay_ms(500);
LED0_TOGGLE;
}
}