STM32F10x DMA introduction and dma usart data transmission and reception

Publisher:skyhcgLatest update time:2019-01-04 Source: eefocusKeywords:STM32F10x  DMA  dma  usart Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere

DMA method

1. Introduction to 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 settings and uart input and output 

There are two ways of input and output of uart DMA, one is polling mode and the other is interrupt mode. 

(1) Polling method 

The first is DMA initialization. This is an example of DMA Polling initialization.


/* Private functions ---------------------------------------------------------*/


/**

  * @brief  Main program

  * @param  None

  * @retval None

  */

int main(void)

{


    /* Initialize DMA and usart, gpio related clock*/

    RCC_Configuration();


    ...

    /* Configure the DMA */

    DMA_Configuration(); //DMA configuration

    ...

    /*USART initialization*/

    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

    Here, set the CR3 register to support DMA input and output*/

    USART_DMACmd(USARTy, USART_DMAReq_Rx | USART_DMAReq_Tx, ENABLE);

    ...

    /* Enable the corresponding input and output 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);


  /*After this sentence is finished, the TxBuffer1 data set in the following DMA_Configuration() function should be

    To be output, you can wait for the sending to be completed as follows, and then wait for the input and output buffers to be compared after receiving*/

  /* 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 indicates the size of the data to be transmitted each time. If it is a cyclic method, it will stop after one transmission. If you want to continue the transmission next time, you need to reset CNTR. See the following example

}


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; //The memory data is put into the usart dr register through DMA

  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;

}



When the channel is configured in non-circular mode, no DMA operation will be generated after the transfer is completed (that is, the transfer count becomes 0). To start a new DMA transfer, it is necessary to rewrite the transfer count in the DMA_CNDTRx register while the DMA channel is closed. 

example:


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) Interrupt mode 

Next, let's talk about how to generate an interrupt after the DMA transfer is completed. When half of the data is transferred, the half transfer flag (HTIF) is set to 1, and when the half transfer interrupt enable bit (HTIE) is set, an interrupt request will be generated. After the data transfer is completed, the transfer completion flag (TCIF) is set to 1, and when the transfer completion interrupt enable bit (TCIE) is set, an interrupt request will be generated. 

There is a TCIE (Transfer completion interrupt enable) bit in the CCR register of DMA 

This bit is set and cleared by software. 

0: Disable TC interrupt 

1: Enable TC interrupt 

So in order to use DMA interrupts, we need the following code: 

[cpp] view plaincopy 

DMA1_Channel7->CCR |= DMA_IT_TC; //Transfer complete interrupt enable 

In addition, whether the interrupt can be responded to depends on NVIC, so the following code is also necessary: 

[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); 

Here is a simple example program. The uCOS semaphore is also used in the example program. The interrupt processing function notifies Task through the semaphore that the DMA transfer is completed:


#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_Heat();  

    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_Heat();  

    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();  

}  


Keywords:STM32F10x  DMA  dma  usart Reference address:STM32F10x DMA introduction and dma usart data transmission and reception

Previous article:STM32 DMA buffersize meaning and setting
Next article:Understanding and using DMA of STM32

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号