DMA方式
1. DMA 介绍
Direct memory access (DMA) is used in order to provide high-speed data transfer between
peripherals and memory and between memory and memory. Data can be quickly moved by
DMA without any CPU action. This keeps CPU resources free for other operations.
The DMA controller combines a powerful dual AHB master bus architecture with
independent FIFO to optimize the bandwidth of the system, based on a complex bus matrix
architecture.
The two DMA controllers have 16 streams in total (8 for each controller), each dedicated to
managing memory access requests from one or more peripherals. Each stream can have
up to 8 channels (requests) in total. And each has an arbiter for handling the priority
between DMA requests.
2. DMA main features
The main DMA features are:
● Dual AHB master bus architecture, one dedicated to memory accesses and one
dedicated to peripheral accesses
● AHB slave programming interface supporting only 32-bit accesses
● 8 streams for each DMA controller, up to 8 channels (requests) per stream
● Four separate 32 first-in, first-out memory buffers (FIFOs) per stream, that can be used
in FIFO mode or direct mode:
– FIFO mode: with threshold level software selectable between 1/4, 1/2 or 3/4 of the
FIFO size
– Direct mode
Each DMA request immediately initiates a transfer from/to the memory. When it is
configured in direct mode (FIFO disabled), to transfer data in memory-to-peripheral mode, the DMA preloads only one data from the memory to the internal FIFO to ensure an immediate data transfer as soon as a DMA request is triggered
by a peripheral.
● Each stream can be configured by hardware to be:
– a regular channel that supports peripheral-to-memory, memory-to-peripheral and
memory-to-memory transfers
– a double buffer channel that also supports double buffering on the memory side
● Each of the 8 streams are connected to dedicated hardware DMA channels (requests)
● Priorities between DMA stream requests are software-programmable (4 levels
consisting of very high, high, medium, low) or hardware in case of equality (request 0
has priority over request 1, etc.)
● Each stream also supports software trigger for memory-to-memory transfers (only
available for the DMA2 controller)
● Each stream request can be selected among up to 8 possible channel requests. This
selection is software-configurable and allo ws several peripherals to initiate DMA
requests
● The number of data items to be transferred can be managed either by the DMA
controller or by the peripheral:
– DMA flow controller: the number of data items to be transferred is software-programmable from 1 to 65535
– Peripheral flow controller: the number of data items to be transferred is unknown
and controlled by the source or the destination peripheral that signals the end of
the transfer by hardware
● Independent source and destination transfer width (byte, half-word, word): when the
data widths of the source and destination are not equal, the DMA automatically
packs/unpacks the necessary transfers to optimize the bandwidth. This feature is only
available in FIFO mode
● Incrementing or nonincrementing addressing for source and destination
● Supports incremental burst transfers of 4, 8 or 16 beats. The size of the burst is
software-configurable, usually equal to half the FIFO size of the peripheral
● Each stream supports circular buffer management
● 5 event flags (DMA Half Transfer, DMA Transfer complete, DMA Transfer Error, DMA
FIFO Error, Direct Mode Error) logically ORed together in a single interrupt request for
each stream
3.DMA设置以及uart输入输出
uart DMA的输入输出有两种方式,一种是polling方式,一种是中断方式。
(1) polling方式
首先是DMA的初始化,这个是一个DMA Polling方式初始化例子。
/* Private functions ---------------------------------------------------------*/
/**
* @brief Main program
* @param None
* @retval None
*/
int main(void)
{
/* 初始化DMA和usart,gpio相关的clock*/
RCC_Configuration();
...
/* Configure the DMA */
DMA_Configuration();//DMA配置
...
/*USART初始化*/
USART_InitStructure.USART_BaudRate = 230400;
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;
/* Configure USARTy */
USART_Init(USARTy, &USART_InitStructure);
....
/* Enable USARTy DMA Rx and TX request
这里设置CR3寄存器支持DMA输入输出 */
USART_DMACmd(USARTy, USART_DMAReq_Rx | USART_DMAReq_Tx, ENABLE);
...
/*使能相应的输入输出DMA channel*/
/* Enable USARTy TX DMA1 Channel */
DMA_Cmd(USARTy_Tx_DMA_Channel, ENABLE);
/* Enable USARTy RX DMA1 Channel */
DMA_Cmd(USARTy_Rx_DMA_Channel, ENABLE);
...
/* Enable the USARTy */
USART_Cmd(USARTy, ENABLE);
/*这一句完了之后,下面DMA_Configuration()函数里边设置的TxBuffer1数据应该就会
被输出,可以按如下方式等待发送完毕,然后再等待接收后比较输入输出的buffer*/
/* Wait until USARTy TX DMA1 Channel Transfer Complete */
while (DMA_GetFlagStatus(USARTy_Tx_DMA_FLAG) == RESET)
{
}
/* Wait until USARTy RX DMA1 Channel Transfer Complete */
while (DMA_GetFlagStatus(USARTy_Rx_DMA_FLAG) == RESET)
{
}
TransferStatus1 = Buffercmp(TxBuffer2, RxBuffer1, TxBufferSize2);
//CNTR表示每次要传输的数据大小,如果循环方式的话,传输完一次之后就会停止,想继续传输下一次,需要重新设置CNTR。看下面的例子
}
void DMA_Configuration(void)
{
DMA_InitTypeDef DMA_InitStructure;
/* USARTy TX DMA1 Channel (triggered by USARTy Tx event) Config */
DMA_DeInit(USARTy_Tx_DMA_Channel);
DMA_InitStructure.DMA_PeripheralBaseAddr = USARTy_DR_Base;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)TxBuffer1;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//memory的数据通过DMA放到usart dr寄存器中
DMA_InitStructure.DMA_BufferSize = TxBufferSize1;
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_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(USARTy_Tx_DMA_Channel, &DMA_InitStructure);
/* USARTy RX DMA1 Channel (triggered by USARTy Rx event) Config */
DMA_DeInit(USARTy_Rx_DMA_Channel);
DMA_InitStructure.DMA_PeripheralBaseAddr = USARTy_DR_Base;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)RxBuffer1;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = TxBufferSize2;
DMA_Init(USARTy_Rx_DMA_Channel, &DMA_InitStructure);
}
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
{
uint32_t tmpreg = 0;
/* Check the parameters */
assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx));
assert_param(IS_DMA_DIR(DMA_InitStruct->DMA_DIR));
assert_param(IS_DMA_BUFFER_SIZE(DMA_InitStruct->DMA_BufferSize));
assert_param(IS_DMA_PERIPHERAL_INC_STATE(DMA_InitStruct->DMA_PeripheralInc));
assert_param(IS_DMA_MEMORY_INC_STATE(DMA_InitStruct->DMA_MemoryInc));
assert_param(IS_DMA_PERIPHERAL_DATA_SIZE(DMA_InitStruct->DMA_PeripheralDataSize));
assert_param(IS_DMA_MEMORY_DATA_SIZE(DMA_InitStruct->DMA_MemoryDataSize));
assert_param(IS_DMA_MODE(DMA_InitStruct->DMA_Mode));
assert_param(IS_DMA_PRIORITY(DMA_InitStruct->DMA_Priority));
assert_param(IS_DMA_M2M_STATE(DMA_InitStruct->DMA_M2M));
/*--------------------------- DMAy Channelx CCR Configuration -----------------*/
/* Get the DMAy_Channelx CCR value */
tmpreg = DMAy_Channelx->CCR;
/* Clear MEM2MEM, PL, MSIZE, PSIZE, MINC, PINC, CIRC and DIR bits */
tmpreg &= CCR_CLEAR_Mask;
/* Configure DMAy Channelx: data transfer, data size, priority level and mode */
/* Set DIR bit according to DMA_DIR value */
/* Set CIRC bit according to DMA_Mode value */
/* Set PINC bit according to DMA_PeripheralInc value */
/* Set MINC bit according to DMA_MemoryInc value */
/* Set PSIZE bits according to DMA_PeripheralDataSize value */
/* Set MSIZE bits according to DMA_MemoryDataSize value */
/* Set PL bits according to DMA_Priority value */
/* Set the MEM2MEM bit according to DMA_M2M value */
tmpreg |= DMA_InitStruct->DMA_DIR | DMA_InitStruct->DMA_Mode |
DMA_InitStruct->DMA_PeripheralInc | DMA_InitStruct->DMA_MemoryInc |
DMA_InitStruct->DMA_PeripheralDataSize | DMA_InitStruct->DMA_MemoryDataSize |
DMA_InitStruct->DMA_Priority | DMA_InitStruct->DMA_M2M;
/* Write to DMAy Channelx CCR */
DMAy_Channelx->CCR = tmpreg;
/*--------------------------- DMAy Channelx CNDTR Configuration ---------------*/
/* Write to DMAy Channelx CNDTR */
DMAy_Channelx->CNDTR = DMA_InitStruct->DMA_BufferSize;
/*--------------------------- DMAy Channelx CPAR Configuration ----------------*/
/* Write to DMAy Channelx CPAR */
DMAy_Channelx->CPAR = DMA_InitStruct->DMA_PeripheralBaseAddr;
/*--------------------------- DMAy Channelx CMAR Configuration ----------------*/
/* Write to DMAy Channelx CMAR */
DMAy_Channelx->CMAR = DMA_InitStruct->DMA_MemoryBaseAddr;
}
当通道配置为非循环模式时,传输结束后(即传输计数变为0)将不再产生DMA操作。要开始新的DMA传输,需要在关闭DMA通道的情况下,在DMA_CNDTRx寄存器中重新写入传输数目。
例子:
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct)
{
uint32_t tmpreg = 0;
/* Check the parameters */
assert_param(IS_DMA_ALL_PERIPH(DMAy_Channelx));
assert_param(IS_DMA_DIR(DMA_InitStruct->DMA_DIR));
assert_param(IS_DMA_BUFFER_SIZE(DMA_InitStruct->DMA_BufferSize));
assert_param(IS_DMA_PERIPHERAL_INC_STATE(DMA_InitStruct->DMA_PeripheralInc));
assert_param(IS_DMA_MEMORY_INC_STATE(DMA_InitStruct->DMA_MemoryInc));
assert_param(IS_DMA_PERIPHERAL_DATA_SIZE(DMA_InitStruct->DMA_PeripheralDataSize));
assert_param(IS_DMA_MEMORY_DATA_SIZE(DMA_InitStruct->DMA_MemoryDataSize));
assert_param(IS_DMA_MODE(DMA_InitStruct->DMA_Mode));
assert_param(IS_DMA_PRIORITY(DMA_InitStruct->DMA_Priority));
assert_param(IS_DMA_M2M_STATE(DMA_InitStruct->DMA_M2M));
/*--------------------------- DMAy Channelx CCR Configuration -----------------*/
/* Get the DMAy_Channelx CCR value */
tmpreg = DMAy_Channelx->CCR;
/* Clear MEM2MEM, PL, MSIZE, PSIZE, MINC, PINC, CIRC and DIR bits */
tmpreg &= CCR_CLEAR_Mask;
/* Configure DMAy Channelx: data transfer, data size, priority level and mode */
/* Set DIR bit according to DMA_DIR value */
/* Set CIRC bit according to DMA_Mode value */
/* Set PINC bit according to DMA_PeripheralInc value */
/* Set MINC bit according to DMA_MemoryInc value */
/* Set PSIZE bits according to DMA_PeripheralDataSize value */
/* Set MSIZE bits according to DMA_MemoryDataSize value */
/* Set PL bits according to DMA_Priority value */
/* Set the MEM2MEM bit according to DMA_M2M value */
tmpreg |= DMA_InitStruct->DMA_DIR | DMA_InitStruct->DMA_Mode |
DMA_InitStruct->DMA_PeripheralInc | DMA_InitStruct->DMA_MemoryInc |
DMA_InitStruct->DMA_PeripheralDataSize | DMA_InitStruct->DMA_MemoryDataSize |
DMA_InitStruct->DMA_Priority | DMA_InitStruct->DMA_M2M;
/* Write to DMAy Channelx CCR */
DMAy_Channelx->CCR = tmpreg;
/*--------------------------- DMAy Channelx CNDTR Configuration ---------------*/
/* Write to DMAy Channelx CNDTR */
DMAy_Channelx->CNDTR = DMA_InitStruct->DMA_BufferSize;
/*--------------------------- DMAy Channelx CPAR Configuration ----------------*/
/* Write to DMAy Channelx CPAR */
DMAy_Channelx->CPAR = DMA_InitStruct->DMA_PeripheralBaseAddr;
/*--------------------------- DMAy Channelx CMAR Configuration ----------------*/
/* Write to DMAy Channelx CMAR */
DMAy_Channelx->CMAR = DMA_InitStruct->DMA_MemoryBaseAddr;
}
(2) 中断方式
下面再说说如何在DMA传输完成之后产生中断。当传输一半的数据后,半传输标志(HTIF)被置1,当设置了允许半传输中断位(HTIE)时,将产生一个中断请求。在数据传输结束后,传输完成标志(TCIF)被置1,当设置了允许传输完成中断位(TCIE)时,将产生一个中断请求。
DMA 的CCR 寄存器中有1位TCIE (Transfer complete interrupt enable)
该位由软件设置和清除。
0:禁止TC中断
1:允许TC中断
所以为了使用DMA中断,我们需要下面的代码:
[cpp] view plaincopy
DMA1_Channel7->CCR |= DMA_IT_TC; //Transfer complete interrupt enable
另外能否响应中断,还要NVIC说了算,所以下面的代码也不能少:
[cpp] view plaincopy
NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 5;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
下面还是给个简单的示例程序,示例程序中还用到了uCOS的信号量,中断处理函数通过信号量通知Task 完成了DMA传输:
#include "stm32f10x.h"
#include "uart.h"
#include "led.h"
#include "COMMRTOS.H"
#include "ucos_ii.h"
#define TASK_STK_SIZE 128
OS_STK TaskStartStk[TASK_STK_SIZE];
void USART2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);
/* Configure USART Tx as alternate function push-pull */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
/* Configure USART Rx as input floating */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_PinRemapConfig(GPIO_Remap_USART2, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
USART_InitStructure.USART_BaudRate = 9600;
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(USART2, &USART_InitStructure);
USART_Cmd(USART2, ENABLE);
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void UART2_TX_DMA_Init(uint8_t *p_str, uint16_t cnt)
{
// DMA_InitTypeDef DMA_InitStructure;
// DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &(USART2->DR);
// DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) str;
// DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
// DMA_InitStructure.DMA_BufferSize = 14;
// 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_Low;
// DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
// DMA_Init(DMA1_Channel7, &DMA_InitStructure);
DMA1_Channel7->CPAR = (uint32_t) &(USART2->DR);
DMA1_Channel7->CMAR = (uint32_t) p_str;
DMA1_Channel7->CNDTR = cnt;
DMA1_Channel7->CCR = DMA_DIR_PeripheralDST | DMA_Priority_Low |
DMA_Mode_Normal | DMA_PeripheralInc_Disable |
DMA_MemoryInc_Enable | DMA_PeripheralDataSize_Byte |
DMA_MemoryDataSize_Byte | DMA_M2M_Disable;
}
OS_EVENT *UART2_DMA_TX_Sem;
uint8_t str[] = "Hello World!!!";
void TaskStart(void *pdata)
{
unsigned char err;
SysTick_Config(SystemCoreClock/10);
USART2_Init();
USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);
UART2_TX_DMA_Init(str);
UART2_DMA_TX_Sem = OSSemCreate(1);
for(;;)
{
LED_Spark();
OSSemPend(UART2_DMA_TX_Sem, 0, &err);
//DMA_Cmd(DMA1_Channel7, DISABLE);
DMA1_Channel7->CCR &= (uint16_t)(~DMA_CCR1_EN);
//DMA_Init(DMA1_Channel7, &DMA_InitStructure);
DMA1_Channel7->CNDTR = 14;
//DMA_Cmd(DMA1_Channel7, ENABLE);
DMA1_Channel7->CCR |= DMA_CCR1_EN;
//USART_DMACmd(USART2, USART_DMAReq_Tx, ENABLE);
OSTimeDly(10);
}
}
int main(void)
{
SystemInit();
LED_Init();
OSInit();
OSTaskCreate(TaskStart, (void *)0, &(TaskStartStk[TASK_STK_SIZE-1]), 1);
OSStart();
for(;;)
{
}
}
void DMA1_Channel7_IRQHandler(void)
{
OS_CPU_SR cpu_sr;
OS_ENTER_CRITICAL(); /* Tell uC/OS-II that we are starting an ISR */
OSIntNesting++;
OS_EXIT_CRITICAL();
OSSemPost(UART2_DMA_TX_Sem);
//UART_PutChar(USART2, '+');
DMA1->IFCR = DMA1_FLAG_TC7;
OSIntExit();
}
上一篇:stm32 DMA 的 buffersize 意义与设置
下一篇:STM32之DMA的认识和使用
推荐阅读最新更新时间:2024-03-16 16:21