2020-12-28 04:01 AM
Hi there,
I'm working on an STM32F051 with Standard Peripheral Libraries.
I using the UART to communicate with a PC and actually evrything work with Interrupts.
To get best perfomance, I started to implemnet DMA trasnfert.
For the RX, I use the DMA in circular mode and the reception works very well.
After that I started to implements Uart Tx with DMA but I have some question about that.
I use a circular buffer for the reception with two index:
My concern is how to handle the circular buffer.
In detail how to specify every time where get the data and how many data send.
It seems that I can specify the source buffer and the data length only during the DMA Channel initialization.
This is my DMA initialization:
uint8_t dmaConfigId;
DMA_InitTypeDef DMA_InitStructure;
dmaConfigId = UartConfig[uartId].dmaConfigId;
/** DMA Clock Enable */
RCC_AHBPeriphClockCmd( DmaConfiguration[dmaConfigId].dmaClock, ENABLE );
/** Deinitialize DMA Channels */
DMA_DeInit( DmaConfiguration[dmaConfigId].rxDmaChannel );
DMA_DeInit( DmaConfiguration[dmaConfigId].txDmaChannel );
/** DMA Channel Common Configuration */
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
/** Usart Rx DMA Channel Configuration - Circular Mode */
DMA_InitStructure.DMA_BufferSize = UartConfig[uartId].RxSize;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)UartConfig[uartId].Rx;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
DMA_InitStructure.DMA_PeripheralBaseAddr = DmaConfiguration[dmaConfigId].peripheralRxRegister;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_Init( DmaConfiguration[dmaConfigId].rxDmaChannel, &DMA_InitStructure );
/** Usart Tx DMA Channel Configuration - Normal Mode */
DMA_InitStructure.DMA_BufferSize = UartConfig[uartId].TxSize;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)UartConfig[uartId].Tx;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_Priority = DMA_Priority_Low;
DMA_InitStructure.DMA_PeripheralBaseAddr = DmaConfiguration[dmaConfigId].peripheralTxRegister;
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_Init( DmaConfiguration[dmaConfigId].txDmaChannel, &DMA_InitStructure );
/** Enable the USARTx Rx and Rx requests */
USART_DMACmd( UartConfig[uartId].USARTx, (USART_DMAReq_Rx | USART_DMAReq_Tx), ENABLE );
/**
* Enable DMA Interrupts
*
* Non è necessario attivare gli interrupt in ricezione del DMA
* perchè il trasferimento DMA avviene in automatico con la USART
*/
/** Abilitazione dell'interrupt di Transfer Complete in trasmissione */
DMA_ITConfig( DmaConfiguration[dmaConfigId].txDmaChannel, DMA_IT_TC, ENABLE );
/** NVIC Init */
NVIC_Initialize( DmaConfiguration[dmaConfigId].txIrq, 0 );
/** UART Rx - Enable DMA Channel */
DMA_Cmd( DmaConfiguration[dmaConfigId].rxDmaChannel, ENABLE );
The UART2_TX_DMA_IT_TC is rised correctly, and into this ISR, I should stard another DMA trasnfer if more data are added to my buffer.
The problem is that I don't know how to specify size and buffer address before start a new DMA transfer.
These are the DMA function the I can use:
/* Function used to set the DMA configuration to the default reset state ******/
void DMA_DeInit(DMA_Channel_TypeDef* DMAy_Channelx);
/* Initialization and Configuration functions *********************************/
void DMA_Init(DMA_Channel_TypeDef* DMAy_Channelx, DMA_InitTypeDef* DMA_InitStruct);
void DMA_StructInit(DMA_InitTypeDef* DMA_InitStruct);
void DMA_Cmd(DMA_Channel_TypeDef* DMAy_Channelx, FunctionalState NewState);
/* Data Counter functions******************************************************/
void DMA_SetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx, uint16_t DataNumber);
uint16_t DMA_GetCurrDataCounter(DMA_Channel_TypeDef* DMAy_Channelx);
/* Interrupts and flags management functions **********************************/
void DMA_ITConfig(DMA_Channel_TypeDef* DMAy_Channelx, uint32_t DMA_IT, FunctionalState NewState);
FlagStatus DMA_GetFlagStatus(uint32_t DMA_FLAG);
void DMA_ClearFlag(uint32_t DMA_FLAG);
ITStatus DMA_GetITStatus(uint32_t DMA_IT);
void DMA_ClearITPendingBit(uint32_t DMA_IT);
Thanks in advance fot the help
2020-12-28 07:43 AM
For each new transfer, you repeat the call to DMA_Init with the new settings. I don't think a call to DMA_DeInit is required after a transfer completes, but could be wrong there.
2020-12-28 08:04 AM
@TDK thanks for the reply.
I tought that but I worring about the efficiency of call DMA_Init at each DMA transfer.
What happen if I call DMA_Cmd( DmaConfiguration[dmaConfigId].txDmaChannel, DISABLE )?
Is the DMA buffer index resetted to 0 when I re-enable the channel? Or it remains at the value when I disable the DMA?
2020-12-28 11:33 AM
If efficiency is critical you'll need to decompose the library code to the minimal sequences. I suspect you just need to disable the controller and change the address/count.
2020-12-29 03:12 AM
Hi @Community member ,
I broked down the DMA_Init and I wrapped some of its instructions inside a new custom function that chages the DMA Buffer address and DMA Buffer lenght.
In this way I can handle my circular buffer with DMA without re-initialize the DMA Channel each time.
/** Disable DMA Channel */
DMA_Cmd( DmaConfiguration[dmaConfigId].txDmaChannel, DISABLE );
/** Configure Memory Base Address */
DmaConfiguration[dmaConfigId].txDmaChannel->CMAR = (uint32_t)&UartConfig[uartId].Tx[Uarts[uartId].TRp];
/** Configure Buffer Size */
DmaConfiguration[dmaConfigId].txDmaChannel->CNDTR = (uint32_t)dataSizeToBufferEnd;
/** Start DMA Transfer */
DMA_Cmd( DmaConfiguration[dmaConfigId].txDmaChannel, ENABLE );