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.