Test summary of STM32 using DMA controller

In the process of using serial port DMA test, some problems are encountered. Through the test, the problems are found, and the application of DMA has a new understanding, just to share with you, please give more advice on the shortcomings.

DMA initialization

// DMA request channel corresponding to serial port
#define  USART_TX_DMA_CHANNEL        DMA1_Channel4
#define  USART_TX_DMA_IRQ       DMA1_Channel4_IRQn
#define  USART_TX_DMA_IRQHandler         DMA1_Channel4_IRQHandler
// Peripheral register address
#define  USART_DR_ADDRESS           (USART1_BASE+0x04)
// Amount of data sent at one time
#define  SENDBUFF_SIZE               250
/**
  * @brief  USARTx TX DMA Configuration, memory to peripheral (usart1 - > DR)
  * @param  nothing
  * @retval nothing
  */
void USARTx_DMA_Config(void)
{
 DMA_InitTypeDef DMA_InitStructure;
 
 DMA_DeInit(USART_TX_DMA_CHANNEL);
 
 // Turn on DMA clock
 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
 // Set DMA source address: serial port data register address*/
 DMA_InitStructure.DMA_PeripheralBaseAddr = USART_DR_ADDRESS;
 // Memory address (pointer to variable to transfer)
 DMA_InitStructure.DMA_MemoryBaseAddr = (u32)SendBuff;
 // Direction: from memory to peripherals 
 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
 // Transmission size 
 DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE;
 // Peripheral address does not increase     
 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
 // Memory address auto increment
 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
 // Peripheral data unit 
 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
 // Memory data unit
 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;  
 // DMA mode, once or cycle mode
 DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;  //Single mode, need to manually load data transmission quantity
// DMA? Initstructure.dma? Mode = DMA? Mode? Circular; / / number of auto reload data transfers
 // Priority: medium 
 DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; 
 // Disable memory to memory transfer
 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
 // Configure DMA channel     
 DMA_Init(USART_TX_DMA_CHANNEL, &DMA_InitStructure);  
 
 NVIC_DMA_Configuration();
 
 // Enabling DMA
 DMA_Cmd(USART_TX_DMA_CHANNEL,DISABLE);  
 //Enable DMA transfer complete interrupt
 DMA_ITConfig(USART_TX_DMA_CHANNEL,DMA_IT_TC,ENABLE);
}

In this test, the main problem encountered is: DMA mode is configured as single mode (DMA mode normal), so that after completing a data transmission, the CNDTR value of the channel transmission number register is 0. According to the manual, when CNDTR is 0, even if the channel is turned on, no data transmission will be generated. Therefore, the CNDTR register needs to be reconfigured in the next data transfer.

Here I write a function to configure the CNDTR register. It should be noted that the CNDTR register can only write data when the channel is not working (EN=0 for DMA ﹣ ccrx).

void USART_DMA_Enable(void)
{
 USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE);
 DMA_Cmd(USART_TX_DMA_CHANNEL, DISABLE ); //Close the channel indicated by USART1 TX DMA1
 DMA_SetCurrDataCounter(USART_TX_DMA_CHANNEL,SENDBUFF_SIZE);//Set DMA cache size
 DMA_Cmd(USART_TX_DMA_CHANNEL, ENABLE); //Enable the channel indicated by USART1 TX DMA1
}

DMA cycle mode
In view of the above problems, we can also use the DMA cycle mode to solve.
In DMA loop mode, when the CNDTR register content changes to 0, it will be automatically overloaded to the previously configured value. Manual overloading is not required.

Of course, in this mode, it should be noted that when the channel is turned on, data transmission will be carried out all the time. Therefore, it is necessary to close DMA channel after a data transmission.

Here is the DMA interrupt used by me. In the interrupt, turn off the DMA channel.

void USART_TX_DMA_IRQHandler(void)
{
// if(DMA_GetITStatus(DMA1_IT_TC4) != RESET)
// {
//  DMA_ClearITPendingBit(DMA1_IT_TC4);
  DMA_Cmd (USART_TX_DMA_CHANNEL,DISABLE);
  DMA_ClearFlag(DMA1_FLAG_TC4|DMA1_FLAG_GL4|DMA1_FLAG_HT4);//Clear channel 4 transmission completion flag
  // Enable serial port transmit interrupt
  USART_ITConfig(DEBUG_USARTx, USART_IT_TC, ENABLE); 
// }
}

In cyclic mode, as long as DMA channel is enabled for data transmission, CNDTR register does not need to be reset

void USART_DMA_Enable(void)
{
 USART_DMACmd(DEBUG_USARTx, USART_DMAReq_Tx, ENABLE);
// DMA CMD (USART TX DMA channel, disable); / / close the channel indicated by USART1 TX DMA1
// DMA? Setcurrdatacounter (USART? TX? DMA? Channel, sendbuffer? Size); / / set the size of DMA cache
 DMA_Cmd(USART_TX_DMA_CHANNEL, ENABLE); //Enable the channel indicated by USART1 TX DMA1
}

To sum up, it is recommended to turn off DMA channel before DMA initialization and turn on when data transmission is required, no matter whether DMA mode is single or cyclic.

Published 2 original articles, praised 0 and visited 7
Private letter follow

Posted by JeditL on Mon, 10 Feb 2020 22:34:51 -0800